diff options
Diffstat (limited to 'drivers')
48 files changed, 13915 insertions, 11445 deletions
diff --git a/drivers/SCsub b/drivers/SCsub index c633a0591b..9c8b16d3e5 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -25,6 +25,8 @@ SConscript("winmidi/SCsub") # Graphics drivers if env["vulkan"]: SConscript("vulkan/SCsub") +if env["d3d12"]: + SConscript("d3d12/SCsub") if env["opengl3"]: SConscript("gl_context/SCsub") SConscript("gles3/SCsub") diff --git a/drivers/d3d12/SCsub b/drivers/d3d12/SCsub new file mode 100644 index 0000000000..a49d55ebc0 --- /dev/null +++ b/drivers/d3d12/SCsub @@ -0,0 +1,161 @@ +#!/usr/bin/env python + +import os +from pathlib import Path + +Import("env") + +env_d3d12_rdd = env.Clone() + +thirdparty_obj = [] + + +# DirectX Headers (must take precedence over Windows SDK's). + +env.Prepend(CPPPATH=["#thirdparty/directx_headers/include/directx"]) +env_d3d12_rdd.Prepend(CPPPATH=["#thirdparty/directx_headers/include/directx"]) +env_d3d12_rdd.Prepend(CPPPATH=["#thirdparty/directx_headers/include/dxguids"]) + + +# Direct3D 12 Memory Allocator. + +env.Append(CPPPATH=["#thirdparty/d3d12ma"]) +env_d3d12_rdd.Append(CPPPATH=["#thirdparty/d3d12ma"]) + + +# Agility SDK. + +if env["agility_sdk_path"] != "": + env_d3d12_rdd.Append(CPPDEFINES=["AGILITY_SDK_ENABLED"]) + if env["agility_sdk_multiarch"]: + env_d3d12_rdd.Append(CPPDEFINES=["AGILITY_SDK_MULTIARCH_ENABLED"]) + + +# PIX. + +if env["pix_path"] != "": + env_d3d12_rdd.Append(CPPDEFINES=["PIX_ENABLED"]) + env_d3d12_rdd.Append(CPPPATH=[env["pix_path"] + "/Include"]) + + +# Mesa (SPIR-V to DXIL functionality). + +mesa_dir = (env["mesa_libs"] + "/godot-mesa").replace("\\", "/") +mesa_gen_dir = (env["mesa_libs"] + "/godot-mesa/generated").replace("\\", "/") +mesa_absdir = Dir(mesa_dir).abspath +mesa_gen_absdir = Dir(mesa_dir + "/generated").abspath + +custom_build_steps = [ + [ + "src/compiler", + "glsl/ir_expression_operation.py enum > %s/ir_expression_operation.h", + "ir_expression_operation.h", + ], + ["src/compiler/nir", "nir_builder_opcodes_h.py > %s/nir_builder_opcodes.h", "nir_builder_opcodes.h"], + ["src/compiler/nir", "nir_constant_expressions.py > %s/nir_constant_expressions.c", "nir_constant_expressions.c"], + ["src/compiler/nir", "nir_intrinsics_h.py --outdir %s", "nir_intrinsics.h"], + ["src/compiler/nir", "nir_intrinsics_c.py --outdir %s", "nir_intrinsics.c"], + ["src/compiler/nir", "nir_intrinsics_indices_h.py --outdir %s", "nir_intrinsics_indices.h"], + ["src/compiler/nir", "nir_opcodes_h.py > %s/nir_opcodes.h", "nir_opcodes.h"], + ["src/compiler/nir", "nir_opcodes_c.py > %s/nir_opcodes.c", "nir_opcodes.c"], + ["src/compiler/nir", "nir_opt_algebraic.py > %s/nir_opt_algebraic.c", "nir_opt_algebraic.c"], + ["src/compiler/spirv", "vtn_generator_ids_h.py spir-v.xml %s/vtn_generator_ids.h", "vtn_generator_ids.h"], + [ + "src/microsoft/compiler", + "dxil_nir_algebraic.py -p ../../../src/compiler/nir > %s/dxil_nir_algebraic.c", + "dxil_nir_algebraic.c", + ], + ["src/util", "format_srgb.py > %s/format_srgb.c", "format_srgb.c"], + ["src/util/format", "u_format_table.py u_format.csv --header > %s/u_format_pack.h", "u_format_pack.h"], + ["src/util/format", "u_format_table.py u_format.csv > %s/u_format_table.c", "u_format_table.c"], +] + +mesa_gen_include_paths = [mesa_gen_dir + "/src"] + +# See update_mesa.sh for explanation. +for v in custom_build_steps: + subdir = v[0] + cmd = v[1] + gen_filename = v[2] + + in_dir = str(Path(mesa_absdir + "/" + subdir)) + out_full_path = mesa_dir + "/generated/" + subdir + out_file_full_path = out_full_path + "/" + gen_filename + + if gen_filename.endswith(".h"): + mesa_gen_include_paths += [out_full_path.replace("\\", "/")] + +mesa_private_inc_paths = [v[0] for v in os.walk(mesa_absdir)] +mesa_private_inc_paths = [v.replace(mesa_absdir, mesa_dir) for v in mesa_private_inc_paths] +mesa_private_inc_paths = [v.replace("\\", "/") for v in mesa_private_inc_paths] +# Avoid build results depending on if generated files already exist. +mesa_private_inc_paths = [v for v in mesa_private_inc_paths if not v.startswith(mesa_gen_dir)] +mesa_private_inc_paths.sort() +# Include the list of the generated ones now, so out-of-the-box sources can include generated headers. +mesa_private_inc_paths += mesa_gen_include_paths +# We have to blacklist some because we are bindly adding every Mesa directory +# to the include path and in some cases that causes the wrong header to be included. +mesa_blacklist_inc_paths = [ + "src/c11", +] +mesa_blacklist_inc_paths = [mesa_dir + "/" + v for v in mesa_blacklist_inc_paths] +mesa_private_inc_paths = [v for v in mesa_private_inc_paths if v not in mesa_blacklist_inc_paths] + +# Added by ourselves. +extra_defines = [ + "WINDOWS_NO_FUTEX", +] + +mesa_ver = Path(mesa_absdir + "/VERSION.info") +if not mesa_ver.is_file(): + mesa_ver = Path(mesa_absdir + "/VERSION") + +# These defines are inspired by the Meson build scripts in the original repo. +extra_defines += [ + "__STDC_CONSTANT_MACROS", + "__STDC_FORMAT_MACROS", + "__STDC_LIMIT_MACROS", + ("PACKAGE_VERSION", '\\"' + mesa_ver.read_text().strip() + '\\"'), + ("PACKAGE_BUGREPORT", '\\"https://gitlab.freedesktop.org/mesa/mesa/-/issues\\"'), + "PIPE_SUBSYSTEM_WINDOWS_USER", + ("_Static_assert", "static_assert"), +] + +if env.msvc: + extra_defines += [ + "_USE_MATH_DEFINES", + "VC_EXTRALEAN", + "_CRT_SECURE_NO_WARNINGS", + "_CRT_SECURE_NO_DEPRECATE", + "_SCL_SECURE_NO_WARNINGS", + "_SCL_SECURE_NO_DEPRECATE", + "_ALLOW_KEYWORD_MACROS", + ("_HAS_EXCEPTIONS", 0), + "NOMINMAX", + "HAVE_STRUCT_TIMESPEC", + ] +else: + extra_defines += [ + ("__REQUIRED_RPCNDR_H_VERSION__", 475), + "HAVE_STRUCT_TIMESPEC", + ] + +# This is needed since rendering_device_d3d12.cpp needs to include some Mesa internals. +env_d3d12_rdd.Prepend(CPPPATH=mesa_private_inc_paths) +# For the same reason as above, the defines must be the same as in the 3rd-party code itself. +env_d3d12_rdd.Append(CPPDEFINES=extra_defines) + + +# Add all. + +env.drivers_sources += thirdparty_obj + + +# Godot source files. + +driver_obj = [] +env_d3d12_rdd.add_source_files(driver_obj, "*.cpp") +env.drivers_sources += driver_obj + +# Needed to force rebuilding the driver files when the thirdparty code is updated. +env.Depends(driver_obj, thirdparty_obj) diff --git a/drivers/d3d12/d3d12_context.cpp b/drivers/d3d12/d3d12_context.cpp new file mode 100644 index 0000000000..0ba3f59119 --- /dev/null +++ b/drivers/d3d12/d3d12_context.cpp @@ -0,0 +1,1136 @@ +/**************************************************************************/ +/* 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 <guiddef.h> + +#include <dxguids.h> +#endif + +// Note: symbol is not available in MinGW and old MSVC import libraries. +const CLSID CLSID_D3D12DeviceFactoryGodot = __uuidof(ID3D12DeviceFactory); + +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_OPTIONS6 options6 = {}; + res = md.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, sizeof(options6)); + if (SUCCEEDED(res)) { + if (options6.VariableShadingRateTier >= D3D12_VARIABLE_SHADING_RATE_TIER_1) { + vrs_capabilities.draw_call_supported = true; + if (options6.VariableShadingRateTier >= D3D12_VARIABLE_SHADING_RATE_TIER_2) { + vrs_capabilities.primitive_supported = true; + vrs_capabilities.primitive_in_multiviewport = options6.PerPrimitiveShadingRateSupportedWithViewportIndexing; + vrs_capabilities.ss_image_supported = true; + vrs_capabilities.ss_image_tile_size = options6.ShadingRateImageTileSize; + vrs_capabilities.additional_rates_supported = options6.AdditionalShadingRatesSupported; + } + } + } + + D3D12_FEATURE_DATA_D3D12_OPTIONS12 options12 = {}; + res = md.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &options12, sizeof(options12)); + if (SUCCEEDED(res)) { + format_capabilities.relaxed_casting_supported = options12.RelaxedFormatCastingSupported; + } + + if (vrs_capabilities.draw_call_supported || vrs_capabilities.primitive_supported || vrs_capabilities.ss_image_supported) { + print_verbose("- D3D12 Variable Rate Shading supported:"); + if (vrs_capabilities.draw_call_supported) { + print_verbose(" Draw call"); + } + if (vrs_capabilities.primitive_supported) { + print_verbose(String(" Per-primitive (multi-viewport: ") + (vrs_capabilities.primitive_in_multiviewport ? "yes" : "no") + ")"); + } + if (vrs_capabilities.ss_image_supported) { + print_verbose(String(" Screen-space image (tile size: ") + itos(vrs_capabilities.ss_image_tile_size) + ")"); + } + if (vrs_capabilities.additional_rates_supported) { + print_verbose(String(" Additional rates: ") + (vrs_capabilities.additional_rates_supported ? "yes" : "no")); + } + } else { + print_verbose("- D3D12 Variable Rate Shading not supported"); + } + + if (multiview_capabilities.is_supported) { + print_verbose("- D3D12 multiview supported:"); + print_verbose(" max view count: " + itos(multiview_capabilities.max_view_count)); + //print_verbose(" max instances: " + itos(multiview_capabilities.max_instance_count)); // Hardcoded; not very useful at the moment. + } else { + print_verbose("- D3D12 multiview not supported"); + } + + if (format_capabilities.relaxed_casting_supported) { + print_verbose("- Relaxed casting supported"); + } else { + print_verbose("- Relaxed casting not supported"); + } + + return OK; +} + +Error D3D12Context::_initialize_debug_layers() { + ComPtr<ID3D12Debug> debug_controller; + HRESULT res; + if (device_factory) { + res = device_factory->GetConfigurationInterface(CLSID_D3D12Debug, 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<IDXGIFactory6> factory6; + dxgi_factory.As(&factory6); + + // TODO: Use IDXCoreAdapterList, which gives more comprehensive information. + LocalVector<IDXGIAdapter1 *> adapters; + while (true) { + IDXGIAdapter1 *curr_adapter = nullptr; + if (factory6) { + if (factory6->EnumAdapterByGpuPreference(adapters.size(), DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&curr_adapter)) == DXGI_ERROR_NOT_FOUND) { + break; + } + } else { + if (dxgi_factory->EnumAdapters1(adapters.size(), &curr_adapter) == DXGI_ERROR_NOT_FOUND) { + break; + } + } + adapters.push_back(curr_adapter); + } + + ERR_FAIL_COND_V_MSG(adapters.size() == 0, ERR_CANT_CREATE, "Adapters enumeration reported zero accessible devices."); + + // The device should really be a preference, but for now choosing a discrete GPU over the + // integrated one is better than the default. + + int32_t adapter_index = -1; + int type_selected = -1; + LocalVector<RenderingDevice::DeviceType> adapter_types; + print_verbose("D3D12 devices:"); + for (uint32_t i = 0; i < adapters.size(); ++i) { + DXGI_ADAPTER_DESC1 desc = {}; + adapters[i]->GetDesc1(&desc); + + String name = desc.Description; + String dev_type; + RenderingDevice::DeviceType type = {}; + if (((desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE))) { + type = RenderingDevice::DEVICE_TYPE_CPU; + } else { + type = desc.DedicatedVideoMemory ? RenderingDevice::DEVICE_TYPE_DISCRETE_GPU : RenderingDevice::DEVICE_TYPE_INTEGRATED_GPU; + } + adapter_types.push_back(type); + + switch (type) { + case RenderingDevice::DEVICE_TYPE_DISCRETE_GPU: { + dev_type = "Discrete"; + } break; + case RenderingDevice::DEVICE_TYPE_INTEGRATED_GPU: { + dev_type = "Integrated"; + } break; + case RenderingDevice::DEVICE_TYPE_VIRTUAL_GPU: { + dev_type = "Virtual"; + } break; + case RenderingDevice::DEVICE_TYPE_CPU: { + dev_type = "CPU"; + } break; + default: { + dev_type = "Other"; + } break; + } + print_verbose(" #" + itos(i) + ": " + name + ", " + dev_type); + + switch (type) { + case RenderingDevice::DEVICE_TYPE_DISCRETE_GPU: { + if (type_selected < 4) { + type_selected = 4; + adapter_index = i; + } + } break; + case RenderingDevice::DEVICE_TYPE_INTEGRATED_GPU: { + if (type_selected < 3) { + type_selected = 3; + adapter_index = i; + } + } break; + case RenderingDevice::DEVICE_TYPE_VIRTUAL_GPU: { + if (type_selected < 2) { + type_selected = 2; + adapter_index = i; + } + } break; + case RenderingDevice::DEVICE_TYPE_CPU: { + if (type_selected < 1) { + type_selected = 1; + adapter_index = i; + } + } break; + default: { + if (type_selected < 0) { + type_selected = 0; + adapter_index = i; + } + } break; + } + } + + int32_t user_adapter_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU. + if (user_adapter_index >= 0 && user_adapter_index < (int32_t)adapters.size()) { + adapter_index = user_adapter_index; + } + + ERR_FAIL_COND_V_MSG(adapter_index == -1, ERR_CANT_CREATE, "None of D3D12 devices supports hardware rendering."); + + gpu = adapters[adapter_index]; + for (uint32_t i = 0; i < adapters.size(); ++i) { + adapters[i]->Release(); + } + + adapter_type = adapter_types[adapter_index]; + + ComPtr<IDXGIFactory5> factory5; + dxgi_factory.As(&factory5); + if (factory5) { + BOOL result = FALSE; // sizeof(bool) != sizeof(BOOL), in general. + HRESULT res = factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &result, sizeof(result)); + if (SUCCEEDED(res)) { + tearing_supported = result; + } else { + ERR_PRINT("CheckFeatureSupport failed with error " + vformat("0x%08ux", (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<ID3D12InfoQueue> info_queue; + res = r_basics.device.As(&info_queue); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + +#if 0 // This causes crashes. Needs investigation. + ComPtr<ID3D12InfoQueue1> info_queue_1; + device.As(&info_queue_1); + if (info_queue_1) { + // Custom printing supported (added in Windows 10 Release Preview build 20236). + + info_queue_1->SetMuteDebugOutput(TRUE); + + res = info_queue_1->RegisterMessageCallback(&_debug_message_func, D3D12_MESSAGE_CALLBACK_IGNORE_FILTERS, nullptr, 0); + ERR_FAIL_COND_V(!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; 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<IDXGISwapChain1> 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_D3D12SDKConfiguration, 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<int, Window> &E : windows) { + Window *w = &E.value; + w->current_buffer = w->swapchain->GetCurrentBackBufferIndex(); + barriers[n++] = CD3DX12_RESOURCE_BARRIER::Transition(w->render_targets[w->current_buffer].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET); + } + 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<int, Window> &E : windows) { + Window *w = &E.value; + barriers[n++] = CD3DX12_RESOURCE_BARRIER::Transition(w->render_targets[w->current_buffer].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT); + } + + 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<int, Window> &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 new file mode 100644 index 0000000000..ac90d38467 --- /dev/null +++ b/drivers/d3d12/d3d12_context.h @@ -0,0 +1,261 @@ +/**************************************************************************/ +/* 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 <dxgi1_6.h> + +#include <wrl/client.h> + +#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<ID3D12DeviceFactory> device_factory; + ComPtr<IDXGIFactory2> dxgi_factory; + ComPtr<IDXGIAdapter> gpu; + DeviceLimits gpu_limits = {}; + struct DeviceBasics { + ComPtr<ID3D12Device> device; + ComPtr<ID3D12CommandQueue> queue; + ComPtr<ID3D12Fence> 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<ID3D12Fence> frame_fence; + HANDLE frame_fence_event = nullptr; + + struct Window { + HWND hwnd = nullptr; + ComPtr<IDXGISwapChain3> swapchain; + UINT swapchain_flags = 0; + UINT sync_interval = 1; + UINT present_flags = 0; + ComPtr<ID3D12Resource> 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<LocalDevice, true> local_device_owner; + + HashMap<DisplayServer::WindowID, Window> windows; + + // Commands. + + LocalVector<ID3D12CommandList *> command_list_queue; + uint32_t command_list_count = 1; + + static void _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/d3d12_godot_nir_bridge.h b/drivers/d3d12/d3d12_godot_nir_bridge.h new file mode 100644 index 0000000000..f66c4edf89 --- /dev/null +++ b/drivers/d3d12/d3d12_godot_nir_bridge.h @@ -0,0 +1,60 @@ +/**************************************************************************/ +/* d3d12_godot_nir_bridge.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_GODOT_NIR_BRIDGE_H +#define D3D12_GODOT_NIR_BRIDGE_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// This one leaves room for potentially extremely copious bindings in a set. +static const uint32_t GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER = 100000000; +// This one leaves room for potentially big sized arrays. +static const uint32_t GODOT_NIR_BINDING_MULTIPLIER = 100000; + +static const uint64_t GODOT_NIR_SC_SENTINEL_MAGIC = 0x45678900; // This must be as big as to be VBR-ed as a 32 bits number. +static const uint64_t GODOT_NIR_SC_SENTINEL_MAGIC_MASK = 0xffffffffffffff00; +static const uint64_t GODOT_NIR_SC_SENTINEL_ID_MASK = 0x00000000000000ff; + +typedef struct GodotNirCallbacks { + void *data; + void (*report_resource)(uint32_t p_register, uint32_t p_space, uint32_t p_dxil_type, void *p_data); + void (*report_sc_bit_offset_fn)(uint32_t p_sc_id, uint64_t p_bit_offset, void *p_data); + void (*report_bitcode_bit_offset_fn)(uint64_t p_bit_offset, void *p_data); +} GodotNirCallbacks; + +#ifdef __cplusplus +} +#endif + +#endif // D3D12_GODOT_NIR_BRIDGE_H diff --git a/drivers/d3d12/d3d12ma.cpp b/drivers/d3d12/d3d12ma.cpp new file mode 100644 index 0000000000..571ec952e7 --- /dev/null +++ b/drivers/d3d12/d3d12ma.cpp @@ -0,0 +1,51 @@ +/**************************************************************************/ +/* d3d12ma.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" + +#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 "-Wduplicated-branches" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + +#if defined(_MSC_VER) +#pragma warning(disable : 4189 4324 4505) +#endif + +#include "thirdparty/d3d12ma/D3D12MemAlloc.cpp" diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp new file mode 100644 index 0000000000..aed82b4ce8 --- /dev/null +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -0,0 +1,5516 @@ +/**************************************************************************/ +/* rendering_device_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_device_driver_d3d12.h" + +#include "core/config/project_settings.h" +#include "core/io/marshalls.h" +#include "d3d12_context.h" +#include "d3d12_godot_nir_bridge.h" +#include "thirdparty/zlib/zlib.h" + +// No point in fighting warnings in Mesa. +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4200) // "nonstandard extension used: zero-sized array in struct/union". +#pragma warning(disable : 4806) // "'&': unsafe operation: no value of type 'bool' promoted to type 'uint32_t' can equal the given constant". +#endif + +#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 "dxil_validator.h" +#include "nir_spirv.h" +#include "nir_to_dxil.h" +#include "spirv_to_dxil.h" +extern "C" { +#include "dxil_spirv_nir.h" +} + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if !defined(_MSC_VER) +#include <guiddef.h> + +#include <dxguids.h> +#endif + +// Mesa may define this. +#ifdef UNUSED +#undef UNUSED +#endif + +#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 + +static const D3D12_RANGE VOID_RANGE = {}; + +static const uint32_t ROOT_CONSTANT_SPACE = RDD::MAX_UNIFORM_SETS + 1; +static const uint32_t ROOT_CONSTANT_REGISTER = 0; +static const uint32_t RUNTIME_DATA_SPACE = RDD::MAX_UNIFORM_SETS + 2; +static const uint32_t RUNTIME_DATA_REGISTER = 0; + +#ifdef DEV_ENABLED +//#define DEBUG_COUNT_BARRIERS +#endif + +/*****************/ +/**** GENERIC ****/ +/*****************/ + +// NOTE: RD's packed format names are reversed in relation to DXGI's; e.g.:. +// - DATA_FORMAT_A8B8G8R8_UNORM_PACK32 -> DXGI_FORMAT_R8G8B8A8_UNORM (packed; note ABGR vs. RGBA). +// - DATA_FORMAT_B8G8R8A8_UNORM -> DXGI_FORMAT_B8G8R8A8_UNORM (not packed; note BGRA order matches). +// TODO: Add YUV formats properly, which would require better support for planes in the RD API. + +const RenderingDeviceDriverD3D12::D3D12Format RenderingDeviceDriverD3D12::RD_TO_D3D12_FORMAT[RDD::DATA_FORMAT_MAX] = { + /* DATA_FORMAT_R4G4_UNORM_PACK8 */ {}, + /* DATA_FORMAT_R4G4B4A4_UNORM_PACK16 */ { DXGI_FORMAT_B4G4R4A4_UNORM, DXGI_FORMAT_B4G4R4A4_UNORM, D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(1, 2, 3, 0) }, + /* DATA_FORMAT_B4G4R4A4_UNORM_PACK16 */ { DXGI_FORMAT_B4G4R4A4_UNORM, DXGI_FORMAT_B4G4R4A4_UNORM, D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(3, 2, 1, 0) }, + /* DATA_FORMAT_R5G6B5_UNORM_PACK16 */ { DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM }, + /* DATA_FORMAT_B5G6R5_UNORM_PACK16 */ { DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(2, 1, 0, 3) }, + /* DATA_FORMAT_R5G5B5A1_UNORM_PACK16 */ { DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(1, 2, 3, 0) }, + /* DATA_FORMAT_B5G5R5A1_UNORM_PACK16 */ { DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(3, 2, 1, 0) }, + /* DATA_FORMAT_A1R5G5B5_UNORM_PACK16 */ { DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM }, + /* DATA_FORMAT_R8_UNORM */ { DXGI_FORMAT_R8_TYPELESS, DXGI_FORMAT_R8_UNORM }, + /* DATA_FORMAT_R8_SNORM */ { DXGI_FORMAT_R8_TYPELESS, DXGI_FORMAT_R8_SNORM }, + /* DATA_FORMAT_R8_USCALED */ { DXGI_FORMAT_R8_TYPELESS, DXGI_FORMAT_R8_UINT }, + /* DATA_FORMAT_R8_SSCALED */ { DXGI_FORMAT_R8_TYPELESS, DXGI_FORMAT_R8_SINT }, + /* DATA_FORMAT_R8_UINT */ { DXGI_FORMAT_R8_TYPELESS, DXGI_FORMAT_R8_UINT }, + /* DATA_FORMAT_R8_SINT */ { DXGI_FORMAT_R8_TYPELESS, DXGI_FORMAT_R8_SINT }, + /* DATA_FORMAT_R8_SRGB */ {}, + /* DATA_FORMAT_R8G8_UNORM */ { DXGI_FORMAT_R8G8_TYPELESS, DXGI_FORMAT_R8G8_UNORM }, + /* DATA_FORMAT_R8G8_SNORM */ { DXGI_FORMAT_R8G8_TYPELESS, DXGI_FORMAT_R8G8_SNORM }, + /* DATA_FORMAT_R8G8_USCALED */ { DXGI_FORMAT_R8G8_TYPELESS, DXGI_FORMAT_R8G8_UINT }, + /* DATA_FORMAT_R8G8_SSCALED */ { DXGI_FORMAT_R8G8_TYPELESS, DXGI_FORMAT_R8G8_SINT }, + /* DATA_FORMAT_R8G8_UINT */ { DXGI_FORMAT_R8G8_TYPELESS, DXGI_FORMAT_R8G8_UINT }, + /* DATA_FORMAT_R8G8_SINT */ { DXGI_FORMAT_R8G8_TYPELESS, DXGI_FORMAT_R8G8_SINT }, + /* DATA_FORMAT_R8G8_SRGB */ {}, + /* DATA_FORMAT_R8G8B8_UNORM */ {}, + /* DATA_FORMAT_R8G8B8_SNORM */ {}, + /* DATA_FORMAT_R8G8B8_USCALED */ {}, + /* DATA_FORMAT_R8G8B8_SSCALED */ {}, + /* DATA_FORMAT_R8G8B8_UINT */ {}, + /* DATA_FORMAT_R8G8B8_SINT */ {}, + /* DATA_FORMAT_R8G8B8_SRGB */ {}, + /* DATA_FORMAT_B8G8R8_UNORM */ {}, + /* DATA_FORMAT_B8G8R8_SNORM */ {}, + /* DATA_FORMAT_B8G8R8_USCALED */ {}, + /* DATA_FORMAT_B8G8R8_SSCALED */ {}, + /* DATA_FORMAT_B8G8R8_UINT */ {}, + /* DATA_FORMAT_B8G8R8_SINT */ {}, + /* DATA_FORMAT_B8G8R8_SRGB */ {}, + /* DATA_FORMAT_R8G8B8A8_UNORM */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UNORM }, + /* DATA_FORMAT_R8G8B8A8_SNORM */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_SNORM }, + /* DATA_FORMAT_R8G8B8A8_USCALED */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UINT }, + /* DATA_FORMAT_R8G8B8A8_SSCALED */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_SINT }, + /* DATA_FORMAT_R8G8B8A8_UINT */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UINT }, + /* DATA_FORMAT_R8G8B8A8_SINT */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_SINT }, + /* DATA_FORMAT_R8G8B8A8_SRGB */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB }, + /* DATA_FORMAT_B8G8R8A8_UNORM */ { DXGI_FORMAT_B8G8R8A8_TYPELESS, DXGI_FORMAT_B8G8R8A8_UNORM }, + /* DATA_FORMAT_B8G8R8A8_SNORM */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_SNORM }, + /* DATA_FORMAT_B8G8R8A8_USCALED */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UINT }, + /* DATA_FORMAT_B8G8R8A8_SSCALED */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_SINT }, + /* DATA_FORMAT_B8G8R8A8_UINT */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UINT }, + /* DATA_FORMAT_B8G8R8A8_SINT */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_SINT }, + /* DATA_FORMAT_B8G8R8A8_SRGB */ { DXGI_FORMAT_B8G8R8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB }, + /* DATA_FORMAT_A8B8G8R8_UNORM_PACK32 */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UNORM }, + /* DATA_FORMAT_A8B8G8R8_SNORM_PACK32 */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_SNORM }, + /* DATA_FORMAT_A8B8G8R8_USCALED_PACK32 */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UINT }, + /* DATA_FORMAT_A8B8G8R8_SSCALED_PACK32 */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_SINT }, + /* DATA_FORMAT_A8B8G8R8_UINT_PACK32 */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UINT }, + /* DATA_FORMAT_A8B8G8R8_SINT_PACK32 */ { DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_SINT }, + /* DATA_FORMAT_A8B8G8R8_SRGB_PACK32 */ { DXGI_FORMAT_B8G8R8A8_TYPELESS, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB }, + /* DATA_FORMAT_A2R10G10B10_UNORM_PACK32 */ { DXGI_FORMAT_R10G10B10A2_TYPELESS, DXGI_FORMAT_R10G10B10A2_UNORM, D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(2, 1, 0, 3) }, + /* DATA_FORMAT_A2R10G10B10_SNORM_PACK32 */ {}, + /* DATA_FORMAT_A2R10G10B10_USCALED_PACK32 */ { DXGI_FORMAT_R10G10B10A2_TYPELESS, DXGI_FORMAT_R10G10B10A2_UINT, D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(2, 1, 0, 3) }, + /* DATA_FORMAT_A2R10G10B10_SSCALED_PACK32 */ {}, + /* DATA_FORMAT_A2R10G10B10_UINT_PACK32 */ { DXGI_FORMAT_R10G10B10A2_TYPELESS, DXGI_FORMAT_R10G10B10A2_UINT, D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(2, 1, 0, 3) }, + /* DATA_FORMAT_A2R10G10B10_SINT_PACK32 */ {}, + /* DATA_FORMAT_A2B10G10R10_UNORM_PACK32 */ { DXGI_FORMAT_R10G10B10A2_TYPELESS, DXGI_FORMAT_R10G10B10A2_UNORM }, + /* DATA_FORMAT_A2B10G10R10_SNORM_PACK32 */ {}, + /* DATA_FORMAT_A2B10G10R10_USCALED_PACK32 */ { DXGI_FORMAT_R10G10B10A2_TYPELESS, DXGI_FORMAT_R10G10B10A2_UINT }, + /* DATA_FORMAT_A2B10G10R10_SSCALED_PACK32 */ {}, + /* DATA_FORMAT_A2B10G10R10_UINT_PACK32 */ { DXGI_FORMAT_R10G10B10A2_TYPELESS, DXGI_FORMAT_R10G10B10A2_UINT }, + /* DATA_FORMAT_A2B10G10R10_SINT_PACK32 */ {}, + /* DATA_FORMAT_R16_UNORM */ { DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UNORM }, + /* DATA_FORMAT_R16_SNORM */ { DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_SNORM }, + /* DATA_FORMAT_R16_USCALED */ { DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UINT }, + /* DATA_FORMAT_R16_SSCALED */ { DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_SINT }, + /* DATA_FORMAT_R16_UINT */ { DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UINT }, + /* DATA_FORMAT_R16_SINT */ { DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_SINT }, + /* DATA_FORMAT_R16_SFLOAT */ { DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_FLOAT }, + /* DATA_FORMAT_R16G16_UNORM */ { DXGI_FORMAT_R16G16_TYPELESS, DXGI_FORMAT_R16G16_UNORM }, + /* DATA_FORMAT_R16G16_SNORM */ { DXGI_FORMAT_R16G16_TYPELESS, DXGI_FORMAT_R16G16_SNORM }, + /* DATA_FORMAT_R16G16_USCALED */ { DXGI_FORMAT_R16G16_TYPELESS, DXGI_FORMAT_R16G16_UINT }, + /* DATA_FORMAT_R16G16_SSCALED */ { DXGI_FORMAT_R16G16_TYPELESS, DXGI_FORMAT_R16G16_SINT }, + /* DATA_FORMAT_R16G16_UINT */ { DXGI_FORMAT_R16G16_TYPELESS, DXGI_FORMAT_R16G16_UINT }, + /* DATA_FORMAT_R16G16_SINT */ { DXGI_FORMAT_R16G16_TYPELESS, DXGI_FORMAT_R16G16_SINT }, + /* DATA_FORMAT_R16G16_SFLOAT */ { DXGI_FORMAT_R16G16_TYPELESS, DXGI_FORMAT_R16G16_FLOAT }, + /* DATA_FORMAT_R16G16B16_UNORM */ {}, + /* DATA_FORMAT_R16G16B16_SNORM */ {}, + /* DATA_FORMAT_R16G16B16_USCALED */ {}, + /* DATA_FORMAT_R16G16B16_SSCALED */ {}, + /* DATA_FORMAT_R16G16B16_UINT */ {}, + /* DATA_FORMAT_R16G16B16_SINT */ {}, + /* DATA_FORMAT_R16G16B16_SFLOAT */ {}, + /* DATA_FORMAT_R16G16B16A16_UNORM */ { DXGI_FORMAT_R16G16B16A16_TYPELESS, DXGI_FORMAT_R16G16B16A16_UNORM }, + /* DATA_FORMAT_R16G16B16A16_SNORM */ { DXGI_FORMAT_R16G16B16A16_TYPELESS, DXGI_FORMAT_R16G16B16A16_SNORM }, + /* DATA_FORMAT_R16G16B16A16_USCALED */ { DXGI_FORMAT_R16G16B16A16_TYPELESS, DXGI_FORMAT_R16G16B16A16_UINT }, + /* DATA_FORMAT_R16G16B16A16_SSCALED */ { DXGI_FORMAT_R16G16B16A16_TYPELESS, DXGI_FORMAT_R16G16B16A16_SINT }, + /* DATA_FORMAT_R16G16B16A16_UINT */ { DXGI_FORMAT_R16G16B16A16_TYPELESS, DXGI_FORMAT_R16G16B16A16_UINT }, + /* DATA_FORMAT_R16G16B16A16_SINT */ { DXGI_FORMAT_R16G16B16A16_TYPELESS, DXGI_FORMAT_R16G16B16A16_SINT }, + /* DATA_FORMAT_R16G16B16A16_SFLOAT */ { DXGI_FORMAT_R16G16B16A16_TYPELESS, DXGI_FORMAT_R16G16B16A16_FLOAT }, + /* DATA_FORMAT_R32_UINT */ { DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_UINT }, + /* DATA_FORMAT_R32_SINT */ { DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_SINT }, + /* DATA_FORMAT_R32_SFLOAT */ { DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_FLOAT }, + /* DATA_FORMAT_R32G32_UINT */ { DXGI_FORMAT_R32G32_TYPELESS, DXGI_FORMAT_R32G32_UINT }, + /* DATA_FORMAT_R32G32_SINT */ { DXGI_FORMAT_R32G32_TYPELESS, DXGI_FORMAT_R32G32_SINT }, + /* DATA_FORMAT_R32G32_SFLOAT */ { DXGI_FORMAT_R32G32_TYPELESS, DXGI_FORMAT_R32G32_FLOAT }, + /* DATA_FORMAT_R32G32B32_UINT */ { DXGI_FORMAT_R32G32B32_TYPELESS, DXGI_FORMAT_R32G32B32_UINT }, + /* DATA_FORMAT_R32G32B32_SINT */ { DXGI_FORMAT_R32G32B32_TYPELESS, DXGI_FORMAT_R32G32B32_SINT }, + /* DATA_FORMAT_R32G32B32_SFLOAT */ { DXGI_FORMAT_R32G32B32_TYPELESS, DXGI_FORMAT_R32G32B32_FLOAT }, + /* DATA_FORMAT_R32G32B32A32_UINT */ { DXGI_FORMAT_R32G32B32A32_TYPELESS, DXGI_FORMAT_R32G32B32A32_UINT }, + /* DATA_FORMAT_R32G32B32A32_SINT */ { DXGI_FORMAT_R32G32B32A32_TYPELESS, DXGI_FORMAT_R32G32B32A32_SINT }, + /* DATA_FORMAT_R32G32B32A32_SFLOAT */ { DXGI_FORMAT_R32G32B32A32_TYPELESS, DXGI_FORMAT_R32G32B32A32_FLOAT }, + /* DATA_FORMAT_R64_UINT */ {}, + /* DATA_FORMAT_R64_SINT */ {}, + /* DATA_FORMAT_R64_SFLOAT */ {}, + /* DATA_FORMAT_R64G64_UINT */ {}, + /* DATA_FORMAT_R64G64_SINT */ {}, + /* DATA_FORMAT_R64G64_SFLOAT */ {}, + /* DATA_FORMAT_R64G64B64_UINT */ {}, + /* DATA_FORMAT_R64G64B64_SINT */ {}, + /* DATA_FORMAT_R64G64B64_SFLOAT */ {}, + /* DATA_FORMAT_R64G64B64A64_UINT */ {}, + /* DATA_FORMAT_R64G64B64A64_SINT */ {}, + /* DATA_FORMAT_R64G64B64A64_SFLOAT */ {}, + /* DATA_FORMAT_B10G11R11_UFLOAT_PACK32 */ { DXGI_FORMAT_R11G11B10_FLOAT, DXGI_FORMAT_R11G11B10_FLOAT }, + /* DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32 */ { DXGI_FORMAT_R9G9B9E5_SHAREDEXP, DXGI_FORMAT_R9G9B9E5_SHAREDEXP }, + /* DATA_FORMAT_D16_UNORM */ { DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UNORM, 0, DXGI_FORMAT_D16_UNORM }, + /* DATA_FORMAT_X8_D24_UNORM_PACK32 */ { DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_UNKNOWN, 0, DXGI_FORMAT_D24_UNORM_S8_UINT }, + /* DATA_FORMAT_D32_SFLOAT */ { DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_FLOAT, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, DXGI_FORMAT_D32_FLOAT }, + /* DATA_FORMAT_S8_UINT */ {}, + /* DATA_FORMAT_D16_UNORM_S8_UINT */ {}, + /* DATA_FORMAT_D24_UNORM_S8_UINT */ { DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_UNKNOWN, 0, DXGI_FORMAT_D24_UNORM_S8_UINT }, + /* DATA_FORMAT_D32_SFLOAT_S8_UINT */ { DXGI_FORMAT_R32G8X24_TYPELESS, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, DXGI_FORMAT_D32_FLOAT_S8X24_UINT }, + /* DATA_FORMAT_BC1_RGB_UNORM_BLOCK */ { DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT_BC1_UNORM, D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(0, 1, 2, D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1) }, + /* DATA_FORMAT_BC1_RGB_SRGB_BLOCK */ { DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT_BC1_UNORM_SRGB, D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(0, 1, 2, D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1) }, + /* DATA_FORMAT_BC1_RGBA_UNORM_BLOCK */ { DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT_BC1_UNORM }, + /* DATA_FORMAT_BC1_RGBA_SRGB_BLOCK */ { DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT_BC1_UNORM_SRGB }, + /* DATA_FORMAT_BC2_UNORM_BLOCK */ { DXGI_FORMAT_BC2_TYPELESS, DXGI_FORMAT_BC2_UNORM }, + /* DATA_FORMAT_BC2_SRGB_BLOCK */ { DXGI_FORMAT_BC2_TYPELESS, DXGI_FORMAT_BC2_UNORM_SRGB }, + /* DATA_FORMAT_BC3_UNORM_BLOCK */ { DXGI_FORMAT_BC3_TYPELESS, DXGI_FORMAT_BC3_UNORM }, + /* DATA_FORMAT_BC3_SRGB_BLOCK */ { DXGI_FORMAT_BC3_TYPELESS, DXGI_FORMAT_BC3_UNORM_SRGB }, + /* DATA_FORMAT_BC4_UNORM_BLOCK */ { DXGI_FORMAT_BC4_TYPELESS, DXGI_FORMAT_BC4_UNORM }, + /* DATA_FORMAT_BC4_SNORM_BLOCK */ { DXGI_FORMAT_BC4_TYPELESS, DXGI_FORMAT_BC4_SNORM }, + /* DATA_FORMAT_BC5_UNORM_BLOCK */ { DXGI_FORMAT_BC5_TYPELESS, DXGI_FORMAT_BC5_UNORM }, + /* DATA_FORMAT_BC5_SNORM_BLOCK */ { DXGI_FORMAT_BC5_TYPELESS, DXGI_FORMAT_BC5_SNORM }, + /* DATA_FORMAT_BC6H_UFLOAT_BLOCK */ { DXGI_FORMAT_BC6H_TYPELESS, DXGI_FORMAT_BC6H_UF16 }, + /* DATA_FORMAT_BC6H_SFLOAT_BLOCK */ { DXGI_FORMAT_BC6H_TYPELESS, DXGI_FORMAT_BC6H_SF16 }, + /* DATA_FORMAT_BC7_UNORM_BLOCK */ { DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM }, + /* DATA_FORMAT_BC7_SRGB_BLOCK */ { DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM_SRGB }, + /* DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK */ {}, + /* DATA_FORMAT_EAC_R11_UNORM_BLOCK */ {}, + /* DATA_FORMAT_EAC_R11_SNORM_BLOCK */ {}, + /* DATA_FORMAT_EAC_R11G11_UNORM_BLOCK */ {}, + /* DATA_FORMAT_EAC_R11G11_SNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_4x4_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_4x4_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_5x4_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_5x4_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_5x5_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_5x5_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_6x5_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_6x5_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_6x6_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_6x6_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_8x5_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_8x5_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_8x6_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_8x6_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_8x8_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_8x8_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_10x5_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_10x5_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_10x6_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_10x6_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_10x8_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_10x8_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_10x10_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_10x10_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_12x10_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_12x10_SRGB_BLOCK */ {}, + /* DATA_FORMAT_ASTC_12x12_UNORM_BLOCK */ {}, + /* DATA_FORMAT_ASTC_12x12_SRGB_BLOCK */ {}, + /* DATA_FORMAT_G8B8G8R8_422_UNORM */ {}, + /* DATA_FORMAT_B8G8R8G8_422_UNORM */ {}, + /* DATA_FORMAT_G8_B8_R8_3PLANE_420_UNORM */ {}, + /* DATA_FORMAT_G8_B8R8_2PLANE_420_UNORM */ {}, + /* DATA_FORMAT_G8_B8_R8_3PLANE_422_UNORM */ {}, + /* DATA_FORMAT_G8_B8R8_2PLANE_422_UNORM */ {}, + /* DATA_FORMAT_G8_B8_R8_3PLANE_444_UNORM */ {}, + /* DATA_FORMAT_R10X6_UNORM_PACK16 */ {}, + /* DATA_FORMAT_R10X6G10X6_UNORM_2PACK16 */ {}, + /* DATA_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 */ {}, + /* DATA_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 */ {}, + /* DATA_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 */ {}, + /* DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 */ {}, + /* DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 */ {}, + /* DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 */ {}, + /* DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 */ {}, + /* DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 */ {}, + /* DATA_FORMAT_R12X4_UNORM_PACK16 */ {}, + /* DATA_FORMAT_R12X4G12X4_UNORM_2PACK16 */ {}, + /* DATA_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 */ {}, + /* DATA_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 */ {}, + /* DATA_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 */ {}, + /* DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 */ {}, + /* DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 */ {}, + /* DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 */ {}, + /* DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 */ {}, + /* DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 */ {}, + /* DATA_FORMAT_G16B16G16R16_422_UNORM */ {}, + /* DATA_FORMAT_B16G16R16G16_422_UNORM */ {}, + /* DATA_FORMAT_G16_B16_R16_3PLANE_420_UNORM */ {}, + /* DATA_FORMAT_G16_B16R16_2PLANE_420_UNORM */ {}, + /* DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM */ {}, + /* DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM */ {}, + /* DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM */ {}, +}; + +Error RenderingDeviceDriverD3D12::DescriptorsHeap::allocate(ID3D12Device *p_device, D3D12_DESCRIPTOR_HEAP_TYPE p_type, uint32_t p_descriptor_count, bool p_for_gpu) { + ERR_FAIL_COND_V(heap, ERR_ALREADY_EXISTS); + ERR_FAIL_COND_V(p_descriptor_count == 0, ERR_INVALID_PARAMETER); + + handle_size = p_device->GetDescriptorHandleIncrementSize(p_type); + + desc.Type = p_type; + desc.NumDescriptors = p_descriptor_count; + desc.Flags = p_for_gpu ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + HRESULT res = p_device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(heap.GetAddressOf())); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ERR_CANT_CREATE, "CreateDescriptorHeap failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + + return OK; +} + +RenderingDeviceDriverD3D12::DescriptorsHeap::Walker RenderingDeviceDriverD3D12::DescriptorsHeap::make_walker() const { + Walker walker; + walker.handle_size = handle_size; + walker.handle_count = desc.NumDescriptors; + if (heap) { +#if defined(_MSC_VER) || !defined(_WIN32) + walker.first_cpu_handle = heap->GetCPUDescriptorHandleForHeapStart(); + if ((desc.Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)) { + walker.first_gpu_handle = heap->GetGPUDescriptorHandleForHeapStart(); + } +#else + heap->GetCPUDescriptorHandleForHeapStart(&walker.first_cpu_handle); + if ((desc.Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)) { + heap->GetGPUDescriptorHandleForHeapStart(&walker.first_gpu_handle); + } +#endif + } + return walker; +} + +void RenderingDeviceDriverD3D12::DescriptorsHeap::Walker::advance(uint32_t p_count) { + ERR_FAIL_COND_MSG(handle_index + p_count > handle_count, "Would advance past EOF."); + handle_index += p_count; +} + +D3D12_CPU_DESCRIPTOR_HANDLE RenderingDeviceDriverD3D12::DescriptorsHeap::Walker::get_curr_cpu_handle() { + ERR_FAIL_COND_V_MSG(is_at_eof(), D3D12_CPU_DESCRIPTOR_HANDLE(), "Heap walker is at EOF."); + return D3D12_CPU_DESCRIPTOR_HANDLE{ first_cpu_handle.ptr + handle_index * handle_size }; +} + +D3D12_GPU_DESCRIPTOR_HANDLE RenderingDeviceDriverD3D12::DescriptorsHeap::Walker::get_curr_gpu_handle() { + ERR_FAIL_COND_V_MSG(!first_gpu_handle.ptr, D3D12_GPU_DESCRIPTOR_HANDLE(), "Can't provide a GPU handle from a non-GPU descriptors heap."); + ERR_FAIL_COND_V_MSG(is_at_eof(), D3D12_GPU_DESCRIPTOR_HANDLE(), "Heap walker is at EOF."); + return D3D12_GPU_DESCRIPTOR_HANDLE{ first_gpu_handle.ptr + handle_index * handle_size }; +} + +static const D3D12_COMPARISON_FUNC RD_TO_D3D12_COMPARE_OP[RD::COMPARE_OP_MAX] = { + D3D12_COMPARISON_FUNC_NEVER, + D3D12_COMPARISON_FUNC_LESS, + D3D12_COMPARISON_FUNC_EQUAL, + D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_COMPARISON_FUNC_GREATER, + D3D12_COMPARISON_FUNC_NOT_EQUAL, + D3D12_COMPARISON_FUNC_GREATER_EQUAL, + D3D12_COMPARISON_FUNC_ALWAYS, +}; + +/****************/ +/**** MEMORY ****/ +/****************/ + +static const uint32_t SMALL_ALLOCATION_MAX_SIZE = 4096; + +#ifdef USE_SMALL_ALLOCS_POOL +D3D12MA::Pool *RenderingDeviceDriverD3D12::_find_or_create_small_allocs_pool(D3D12_HEAP_TYPE p_heap_type, D3D12_HEAP_FLAGS p_heap_flags) { + D3D12_HEAP_FLAGS effective_heap_flags = p_heap_flags; + if (allocator->GetD3D12Options().ResourceHeapTier != D3D12_RESOURCE_HEAP_TIER_1) { + // Heap tier 2 allows mixing resource types liberally. + effective_heap_flags &= ~(D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS | D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES | D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES); + } + + AllocPoolKey pool_key; + pool_key.heap_type = p_heap_type; + pool_key.heap_flags = effective_heap_flags; + if (small_allocs_pools.has(pool_key.key)) { + return small_allocs_pools[pool_key.key].Get(); + } + +#ifdef DEV_ENABLED + print_verbose("Creating D3D12MA small objects pool for heap type " + itos(p_heap_type) + " and heap flags " + itos(p_heap_flags)); +#endif + + D3D12MA::POOL_DESC poolDesc = {}; + poolDesc.HeapProperties.Type = p_heap_type; + poolDesc.HeapFlags = effective_heap_flags; + + ComPtr<D3D12MA::Pool> pool; + HRESULT res = allocator->CreatePool(&poolDesc, pool.GetAddressOf()); + small_allocs_pools[pool_key.key] = pool; // Don't try to create it again if failed the first time. + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), nullptr, "CreatePool failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + + return pool.Get(); +} +#endif + +/******************/ +/**** RESOURCE ****/ +/******************/ + +static const D3D12_RESOURCE_DIMENSION RD_TEXTURE_TYPE_TO_D3D12_RESOURCE_DIMENSION[RD::TEXTURE_TYPE_MAX] = { + D3D12_RESOURCE_DIMENSION_TEXTURE1D, + D3D12_RESOURCE_DIMENSION_TEXTURE2D, + D3D12_RESOURCE_DIMENSION_TEXTURE3D, + D3D12_RESOURCE_DIMENSION_TEXTURE2D, + D3D12_RESOURCE_DIMENSION_TEXTURE1D, + D3D12_RESOURCE_DIMENSION_TEXTURE2D, + D3D12_RESOURCE_DIMENSION_TEXTURE2D, +}; + +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(); +#endif + + ResourceInfo::States *res_states = p_resource->states_ptr; + D3D12_RESOURCE_STATES *curr_state = &res_states->subresource_states[p_subresource]; + + ID3D12Resource *res_to_transition = p_resource_override ? p_resource_override : p_resource->resource; + + bool redundant_transition = ((*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; + if (needs_uav_barrier) { + if (res_barriers.size() < res_barriers_count + 1) { + res_barriers.resize(res_barriers_count + 1); + } + res_barriers[res_barriers_count] = CD3DX12_RESOURCE_BARRIER::UAV(res_to_transition); + res_barriers_count++; + res_states->last_batch_with_uav_barrier = res_barriers_batch; + } + } else { + uint64_t subres_mask_piece = ((uint64_t)1 << (p_subresource & 0b111111)); + uint8_t subres_qword = p_subresource >> 6; + + if (res_barriers_requests.has(res_states)) { + BarrierRequest &br = res_barriers_requests.get(res_states); + DEV_ASSERT(br.dx_resource == res_to_transition); + DEV_ASSERT(br.subres_mask_qwords == STEPIFY(res_states->subresource_states.size(), 64) / 64); + DEV_ASSERT(br.planes == p_num_planes); + + // First, find if the subresource already has a barrier scheduled. + uint8_t curr_group_idx = 0; + bool same_transition_scheduled = false; + for (curr_group_idx = 0; curr_group_idx < br.groups_count; curr_group_idx++) { + if (unlikely(br.groups[curr_group_idx].states == BarrierRequest::DELETED_GROUP)) { + continue; + } + if ((br.groups[curr_group_idx].subres_mask[subres_qword] & subres_mask_piece)) { + uint32_t state_mask = br.groups[curr_group_idx].states; + same_transition_scheduled = (state_mask & (uint32_t)p_new_state) == (uint32_t)p_new_state; + break; + } + } + if (!same_transition_scheduled) { + bool subres_already_there = curr_group_idx != br.groups_count; + D3D12_RESOURCE_STATES final_states = {}; + if (subres_already_there) { + final_states = br.groups[curr_group_idx].states; + final_states |= p_new_state; + bool subres_alone = true; + for (uint8_t i = 0; i < br.subres_mask_qwords; i++) { + if (i == subres_qword) { + if (br.groups[curr_group_idx].subres_mask[i] != subres_mask_piece) { + subres_alone = false; + break; + } + } else { + if (br.groups[curr_group_idx].subres_mask[i] != 0) { + subres_alone = false; + break; + } + } + } + bool relocated = false; + if (subres_alone) { + // Subresource is there by itself. + for (uint8_t i = 0; i < br.groups_count; i++) { + if (unlikely(i == curr_group_idx)) { + continue; + } + if (unlikely(br.groups[i].states == BarrierRequest::DELETED_GROUP)) { + continue; + } + // There's another group with the final states; relocate to it. + if (br.groups[i].states == final_states) { + br.groups[curr_group_idx].subres_mask[subres_qword] &= ~subres_mask_piece; + relocated = true; + break; + } + } + if (relocated) { + // Let's delete the group where it used to be by itself. + if (curr_group_idx == br.groups_count - 1) { + br.groups_count--; + } else { + br.groups[curr_group_idx].states = BarrierRequest::DELETED_GROUP; + } + } else { + // Its current group, where it's alone, can extend its states. + br.groups[curr_group_idx].states = final_states; + } + } else { + // Already there, but not by itself and the state mask is different, so it now belongs to a different group. + br.groups[curr_group_idx].subres_mask[subres_qword] &= ~subres_mask_piece; + subres_already_there = false; + } + } else { + final_states = p_new_state; + } + if (!subres_already_there) { + // See if it fits exactly the states of some of the groups to fit it there. + for (uint8_t i = 0; i < br.groups_count; i++) { + if (unlikely(i == curr_group_idx)) { + continue; + } + if (unlikely(br.groups[i].states == BarrierRequest::DELETED_GROUP)) { + continue; + } + if (br.groups[i].states == final_states) { + br.groups[i].subres_mask[subres_qword] |= subres_mask_piece; + subres_already_there = true; + break; + } + } + if (!subres_already_there) { + // Add a new group to accommodate this subresource. + uint8_t group_to_fill = 0; + if (br.groups_count < BarrierRequest::MAX_GROUPS) { + // There are still free groups. + group_to_fill = br.groups_count; + br.groups_count++; + } else { + // Let's try to take over a deleted one. + for (; group_to_fill < br.groups_count; group_to_fill++) { + if (unlikely(br.groups[group_to_fill].states == BarrierRequest::DELETED_GROUP)) { + break; + } + } + CRASH_COND(group_to_fill == br.groups_count); + } + + br.groups[group_to_fill].states = final_states; + for (uint8_t i = 0; i < br.subres_mask_qwords; i++) { + if (unlikely(i == subres_qword)) { + br.groups[group_to_fill].subres_mask[i] = subres_mask_piece; + } else { + br.groups[group_to_fill].subres_mask[i] = 0; + } + } + } + } + } + } else { + BarrierRequest &br = res_barriers_requests[res_states]; + br.dx_resource = res_to_transition; + br.subres_mask_qwords = STEPIFY(p_resource->states_ptr->subresource_states.size(), 64) / 64; + CRASH_COND(p_resource->states_ptr->subresource_states.size() > BarrierRequest::MAX_SUBRESOURCES); + br.planes = p_num_planes; + br.groups[0].states = p_new_state; + for (uint8_t i = 0; i < br.subres_mask_qwords; i++) { + if (unlikely(i == subres_qword)) { + br.groups[0].subres_mask[i] = subres_mask_piece; + } else { + br.groups[0].subres_mask[i] = 0; + } + } + br.groups_count = 1; + } + } + + if (p_new_state == D3D12_RESOURCE_STATE_UNORDERED_ACCESS) { + res_states->last_batch_transitioned_to_uav = res_barriers_batch; + } + +#ifdef DEBUG_COUNT_BARRIERS + frame_barriers_cpu_time += OS::get_singleton()->get_ticks_usec() - start; +#endif +} + +void RenderingDeviceDriverD3D12::_resource_transitions_flush(ID3D12GraphicsCommandList *p_cmd_list) { +#ifdef DEBUG_COUNT_BARRIERS + uint64_t start = OS::get_singleton()->get_ticks_usec(); +#endif + + for (const KeyValue<ResourceInfo::States *, BarrierRequest> &E : res_barriers_requests) { + ResourceInfo::States *res_states = E.key; + const BarrierRequest &br = E.value; + + uint32_t num_subresources = res_states->subresource_states.size(); + + // When there's not a lot of subresources, the empirical finding is that it's better + // to avoid attempting the single-barrier optimization. + static const uint32_t SINGLE_BARRIER_ATTEMPT_MAX_NUM_SUBRESOURCES = 48; + + bool may_do_single_barrier = br.groups_count == 1 && num_subresources * br.planes >= SINGLE_BARRIER_ATTEMPT_MAX_NUM_SUBRESOURCES; + if (may_do_single_barrier) { + // A single group means we may be able to do a single all-subresources barrier. + + { + // First requisite is that all subresources are involved. + + uint8_t subres_mask_full_qwords = num_subresources / 64; + for (uint32_t i = 0; i < subres_mask_full_qwords; i++) { + if (br.groups[0].subres_mask[i] != UINT64_MAX) { + may_do_single_barrier = false; + break; + } + } + if (may_do_single_barrier) { + if (num_subresources % 64) { + DEV_ASSERT(br.subres_mask_qwords == subres_mask_full_qwords + 1); + uint64_t mask_tail_qword = 0; + for (uint8_t i = 0; i < num_subresources % 64; i++) { + mask_tail_qword |= ((uint64_t)1 << i); + } + if ((br.groups[0].subres_mask[subres_mask_full_qwords] & mask_tail_qword) != mask_tail_qword) { + may_do_single_barrier = false; + } + } + } + } + + if (may_do_single_barrier) { + // Second requisite is that the source state is the same for all. + + for (uint32_t i = 1; i < num_subresources; i++) { + if (res_states->subresource_states[i] != res_states->subresource_states[0]) { + may_do_single_barrier = false; + break; + } + } + + if (may_do_single_barrier) { + // Hurray!, we can do a single barrier (plus maybe a UAV one, too). + + bool just_written = res_states->subresource_states[0] == D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + bool needs_uav_barrier = just_written && res_states->last_batch_with_uav_barrier != res_barriers_batch; + + uint32_t needed_barriers = (needs_uav_barrier ? 1 : 0) + 1; + if (res_barriers.size() < res_barriers_count + needed_barriers) { + res_barriers.resize(res_barriers_count + needed_barriers); + } + + if (needs_uav_barrier) { + res_barriers[res_barriers_count] = CD3DX12_RESOURCE_BARRIER::UAV(br.dx_resource); + res_barriers_count++; + res_states->last_batch_with_uav_barrier = res_barriers_batch; + } + + if (res_states->subresource_states[0] != br.groups[0].states) { + res_barriers[res_barriers_count] = CD3DX12_RESOURCE_BARRIER::Transition(br.dx_resource, res_states->subresource_states[0], br.groups[0].states, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES); + res_barriers_count++; + } + + for (uint32_t i = 0; i < num_subresources; i++) { + res_states->subresource_states[i] = br.groups[0].states; + } + } + } + } + + if (!may_do_single_barrier) { + for (uint8_t i = 0; i < br.groups_count; i++) { + const BarrierRequest::Group &g = E.value.groups[i]; + + if (unlikely(g.states == BarrierRequest::DELETED_GROUP)) { + continue; + } + + uint32_t subresource = 0; + do { + uint64_t subres_mask_piece = ((uint64_t)1 << (subresource % 64)); + uint8_t subres_qword = subresource / 64; + + if (likely(g.subres_mask[subres_qword] == 0)) { + subresource += 64; + continue; + } + + if (likely(!(g.subres_mask[subres_qword] & subres_mask_piece))) { + subresource++; + continue; + } + + D3D12_RESOURCE_STATES *curr_state = &res_states->subresource_states[subresource]; + + 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; + + uint32_t needed_barriers = (needs_uav_barrier ? 1 : 0) + br.planes; + if (res_barriers.size() < res_barriers_count + needed_barriers) { + res_barriers.resize(res_barriers_count + needed_barriers); + } + + if (needs_uav_barrier) { + res_barriers[res_barriers_count] = CD3DX12_RESOURCE_BARRIER::UAV(br.dx_resource); + res_barriers_count++; + res_states->last_batch_with_uav_barrier = res_barriers_batch; + } + + if (*curr_state != g.states) { + for (uint8_t k = 0; k < br.planes; k++) { + res_barriers[res_barriers_count] = CD3DX12_RESOURCE_BARRIER::Transition(br.dx_resource, *curr_state, g.states, subresource + k * num_subresources); + res_barriers_count++; + } + } + + *curr_state = g.states; + + subresource++; + } while (subresource < num_subresources); + } + } + } + + if (res_barriers_count) { + p_cmd_list->ResourceBarrier(res_barriers_count, res_barriers.ptr()); + res_barriers_requests.clear(); + } + +#ifdef DEBUG_COUNT_BARRIERS + frame_barriers_count += res_barriers_count; + frame_barriers_batches_count++; + frame_barriers_cpu_time += OS::get_singleton()->get_ticks_usec() - start; +#endif + + res_barriers_count = 0; + res_barriers_batch++; +} + +/*****************/ +/**** BUFFERS ****/ +/*****************/ + +RDD::BufferID RenderingDeviceDriverD3D12::buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type) { + // D3D12 debug layers complain at CBV creation time if the size is not multiple of the value per the spec + // but also if you give a rounded size at that point because it will extend beyond the + // memory of the resource. Therefore, it seems the only way is to create it with a + // rounded size. + CD3DX12_RESOURCE_DESC resource_desc = CD3DX12_RESOURCE_DESC::Buffer(STEPIFY(p_size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT)); + if (p_usage.has_flag(RDD::BUFFER_USAGE_STORAGE_BIT)) { + resource_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + } else { + resource_desc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + } + + D3D12MA::ALLOCATION_DESC allocation_desc = {}; + allocation_desc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + D3D12_RESOURCE_STATES initial_state = D3D12_RESOURCE_STATE_COPY_DEST; + switch (p_allocation_type) { + case MEMORY_ALLOCATION_TYPE_CPU: { + bool is_src = p_usage.has_flag(BUFFER_USAGE_TRANSFER_FROM_BIT); + bool is_dst = p_usage.has_flag(BUFFER_USAGE_TRANSFER_TO_BIT); + if (is_src && !is_dst) { + // Looks like a staging buffer: CPU maps, writes sequentially, then GPU copies to VRAM. + allocation_desc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + initial_state = D3D12_RESOURCE_STATE_GENERIC_READ; + } + if (is_dst && !is_src) { + // Looks like a readback buffer: GPU copies from VRAM, then CPU maps and reads. + allocation_desc.HeapType = D3D12_HEAP_TYPE_READBACK; + } + } break; + case MEMORY_ALLOCATION_TYPE_GPU: { +#ifdef USE_SMALL_ALLOCS_POOL + if (p_size <= SMALL_ALLOCATION_MAX_SIZE) { + allocation_desc.CustomPool = _find_or_create_small_allocs_pool(allocation_desc.HeapType, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS); + } +#endif + } break; + } + + ComPtr<ID3D12Resource> buffer; + ComPtr<D3D12MA::Allocation> allocation; + HRESULT res = allocator->CreateResource( + &allocation_desc, + &resource_desc, + initial_state, + nullptr, + allocation.GetAddressOf(), + IID_PPV_ARGS(buffer.GetAddressOf())); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), BufferID(), "Can't create buffer of size: " + itos(p_size) + ", error " + vformat("0x%08ux", (uint64_t)res) + "."); + + // Bookkeep. + + BufferInfo *buf_info = VersatileResource::allocate<BufferInfo>(resources_allocator); + buf_info->resource = buffer.Get(); + buf_info->owner_info.resource = buffer; + buf_info->owner_info.allocation = allocation; + buf_info->owner_info.states.subresource_states.push_back(initial_state); + buf_info->states_ptr = &buf_info->owner_info.states; + buf_info->size = p_size; + buf_info->flags.usable_as_uav = (resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS); + buf_info->flags.is_for_upload = allocation_desc.HeapType == D3D12_HEAP_TYPE_UPLOAD; + + return BufferID(buf_info); +} + +bool RenderingDeviceDriverD3D12::buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) { + BufferInfo *buf_info = (BufferInfo *)p_buffer.id; + buf_info->texel_format = p_format; + return true; +} + +void RenderingDeviceDriverD3D12::buffer_free(BufferID p_buffer) { + BufferInfo *buf_info = (BufferInfo *)p_buffer.id; + VersatileResource::free(resources_allocator, buf_info); +} + +uint64_t RenderingDeviceDriverD3D12::buffer_get_allocation_size(BufferID p_buffer) { + const BufferInfo *buf_info = (const BufferInfo *)p_buffer.id; + return buf_info->owner_info.allocation ? buf_info->owner_info.allocation->GetSize() : 0; +} + +uint8_t *RenderingDeviceDriverD3D12::buffer_map(BufferID p_buffer) { + const BufferInfo *buf_info = (const BufferInfo *)p_buffer.id; + void *data_ptr = nullptr; + HRESULT res = buf_info->resource->Map(0, &VOID_RANGE, &data_ptr); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), nullptr, "Map failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + return (uint8_t *)data_ptr; +} + +void RenderingDeviceDriverD3D12::buffer_unmap(BufferID p_buffer) { + const BufferInfo *buf_info = (const BufferInfo *)p_buffer.id; + buf_info->resource->Unmap(0, &VOID_RANGE); +} + +/*****************/ +/**** TEXTURE ****/ +/*****************/ + +static const D3D12_SRV_DIMENSION RD_TEXTURE_TYPE_TO_D3D12_VIEW_DIMENSION_FOR_SRV[RD::TEXTURE_TYPE_MAX] = { + D3D12_SRV_DIMENSION_TEXTURE1D, + D3D12_SRV_DIMENSION_TEXTURE2D, + D3D12_SRV_DIMENSION_TEXTURE3D, + D3D12_SRV_DIMENSION_TEXTURECUBE, + D3D12_SRV_DIMENSION_TEXTURE1DARRAY, + D3D12_SRV_DIMENSION_TEXTURE2DARRAY, + D3D12_SRV_DIMENSION_TEXTURECUBEARRAY, +}; + +static const D3D12_SRV_DIMENSION RD_TEXTURE_TYPE_TO_D3D12_VIEW_DIMENSION_FOR_SRV_MS[RD::TEXTURE_TYPE_MAX] = { + D3D12_SRV_DIMENSION_UNKNOWN, + D3D12_SRV_DIMENSION_TEXTURE2DMS, + D3D12_SRV_DIMENSION_UNKNOWN, + D3D12_SRV_DIMENSION_UNKNOWN, + D3D12_SRV_DIMENSION_UNKNOWN, + D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY, + D3D12_SRV_DIMENSION_UNKNOWN, +}; + +static const D3D12_UAV_DIMENSION RD_TEXTURE_TYPE_TO_D3D12_VIEW_DIMENSION_FOR_UAV[RD::TEXTURE_TYPE_MAX] = { + D3D12_UAV_DIMENSION_TEXTURE1D, + D3D12_UAV_DIMENSION_TEXTURE2D, + D3D12_UAV_DIMENSION_TEXTURE3D, + D3D12_UAV_DIMENSION_TEXTURE2DARRAY, + D3D12_UAV_DIMENSION_TEXTURE1DARRAY, + D3D12_UAV_DIMENSION_TEXTURE2DARRAY, + D3D12_UAV_DIMENSION_TEXTURE2DARRAY, +}; + +uint32_t RenderingDeviceDriverD3D12::_find_max_common_supported_sample_count(VectorView<DXGI_FORMAT> p_formats) { + uint32_t common = UINT32_MAX; + + for (uint32_t i = 0; i < p_formats.size(); i++) { + if (format_sample_counts_mask_cache.has(p_formats[i])) { + common &= format_sample_counts_mask_cache[p_formats[i]]; + } else { + D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msql = {}; + msql.Format = p_formats[i]; + uint32_t mask = 0; + for (int samples = 1 << (TEXTURE_SAMPLES_MAX - 1); samples >= 1; samples /= 2) { + msql.SampleCount = (UINT)samples; + HRESULT res = device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msql, sizeof(msql)); + if (SUCCEEDED(res) && msql.NumQualityLevels) { + int bit = get_shift_from_power_of_2(samples); + ERR_FAIL_COND_V(bit == -1, 1); + mask |= (uint32_t)(1 << bit); + } + } + format_sample_counts_mask_cache.insert(p_formats[i], mask); + common &= mask; + } + } + if (common == UINT32_MAX) { + return 1; + } else { + return ((uint32_t)1 << nearest_shift(common)); + } +} + +UINT RenderingDeviceDriverD3D12::_compute_component_mapping(const RDD::TextureView &p_view) { + UINT base_swizzle = RD_TO_D3D12_FORMAT[p_view.format].swizzle; + + D3D12_SHADER_COMPONENT_MAPPING component_swizzles[TEXTURE_SWIZZLE_MAX] = { + D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0, // Unused. + D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0, + D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1, + // These will be D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_*. + D3D12_DECODE_SHADER_4_COMPONENT_MAPPING(0, base_swizzle), + D3D12_DECODE_SHADER_4_COMPONENT_MAPPING(1, base_swizzle), + D3D12_DECODE_SHADER_4_COMPONENT_MAPPING(2, base_swizzle), + D3D12_DECODE_SHADER_4_COMPONENT_MAPPING(3, base_swizzle), + }; + + return D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING( + p_view.swizzle_r == TEXTURE_SWIZZLE_IDENTITY ? component_swizzles[TEXTURE_SWIZZLE_R] : component_swizzles[p_view.swizzle_r], + p_view.swizzle_g == TEXTURE_SWIZZLE_IDENTITY ? component_swizzles[TEXTURE_SWIZZLE_G] : component_swizzles[p_view.swizzle_g], + p_view.swizzle_b == TEXTURE_SWIZZLE_IDENTITY ? component_swizzles[TEXTURE_SWIZZLE_B] : component_swizzles[p_view.swizzle_b], + p_view.swizzle_a == TEXTURE_SWIZZLE_IDENTITY ? component_swizzles[TEXTURE_SWIZZLE_A] : component_swizzles[p_view.swizzle_a]); +} + +UINT RenderingDeviceDriverD3D12::_compute_plane_slice(DataFormat p_format, BitField<TextureAspectBits> p_aspect_bits) { + TextureAspect aspect = TEXTURE_ASPECT_MAX; + + if (p_aspect_bits.has_flag(TEXTURE_ASPECT_COLOR_BIT)) { + DEV_ASSERT(aspect == TEXTURE_ASPECT_MAX); + aspect = TEXTURE_ASPECT_COLOR; + } + if (p_aspect_bits.has_flag(TEXTURE_ASPECT_DEPTH_BIT)) { + DEV_ASSERT(aspect == TEXTURE_ASPECT_MAX); + aspect = TEXTURE_ASPECT_DEPTH; + } + if (p_aspect_bits.has_flag(TEXTURE_ASPECT_STENCIL_BIT)) { + DEV_ASSERT(aspect == TEXTURE_ASPECT_MAX); + aspect = TEXTURE_ASPECT_STENCIL; + } + + DEV_ASSERT(aspect != TEXTURE_ASPECT_MAX); + + return _compute_plane_slice(p_format, aspect); +} + +UINT RenderingDeviceDriverD3D12::_compute_plane_slice(DataFormat p_format, TextureAspect p_aspect) { + switch (p_aspect) { + case TEXTURE_ASPECT_COLOR: + // The plane must be 0 for the color aspect (assuming the format is a regular color one, which must be the case). + return 0; + case TEXTURE_ASPECT_DEPTH: + // The plane must be 0 for the color or depth aspect + return 0; + case TEXTURE_ASPECT_STENCIL: + // The plane may be 0 for the stencil aspect (if the format is stencil-only), or 1 (if the format is depth-stencil; other cases are ill). + return format_get_plane_count(p_format) == 2 ? 1 : 0; + default: + DEV_ASSERT(false); + return 0; + } +} + +void RenderingDeviceDriverD3D12::_discard_texture_subresources(const TextureInfo *p_tex_info, const CommandBufferInfo *p_cmd_buf_info) { + uint32_t planes = 1; + if ((p_tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { + planes = format_get_plane_count(p_tex_info->format); + } + D3D12_DISCARD_REGION dr = {}; + dr.NumRects = p_cmd_buf_info->render_pass_state.region_is_all ? 0 : 1; + dr.pRects = p_cmd_buf_info->render_pass_state.region_is_all ? nullptr : &p_cmd_buf_info->render_pass_state.region_rect; + dr.FirstSubresource = UINT_MAX; + dr.NumSubresources = 0; + for (uint32_t u = 0; u < planes; u++) { + for (uint32_t v = 0; v < p_tex_info->layers; v++) { + for (uint32_t w = 0; w < p_tex_info->mipmaps; w++) { + UINT subresource = D3D12CalcSubresource( + p_tex_info->base_mip + w, + p_tex_info->base_layer + v, + u, + p_tex_info->desc.MipLevels, + p_tex_info->desc.ArraySize()); + if (dr.NumSubresources == 0) { + dr.FirstSubresource = subresource; + dr.NumSubresources = 1; + } else if (dr.FirstSubresource + dr.NumSubresources == subresource) { + dr.NumSubresources++; + } else { + p_cmd_buf_info->cmd_list->DiscardResource(p_tex_info->resource, &dr); + dr.FirstSubresource = subresource; + dr.NumSubresources = 1; + } + } + } + } + if (dr.NumSubresources) { + p_cmd_buf_info->cmd_list->DiscardResource(p_tex_info->resource, &dr); + } +} + +RDD::TextureID RenderingDeviceDriverD3D12::texture_create(const TextureFormat &p_format, const TextureView &p_view) { + // Using D3D12_RESOURCE_DESC1. Thanks to the layout, it's sliceable down to D3D12_RESOURCE_DESC if needed. + CD3DX12_RESOURCE_DESC1 resource_desc = {}; + resource_desc.Dimension = RD_TEXTURE_TYPE_TO_D3D12_RESOURCE_DIMENSION[p_format.texture_type]; + resource_desc.Alignment = 0; // D3D12MA will override this to use a smaller alignment than the default if possible. + + resource_desc.Width = p_format.width; + resource_desc.Height = p_format.height; + resource_desc.DepthOrArraySize = p_format.depth * p_format.array_layers; + resource_desc.MipLevels = p_format.mipmaps; + + // Format. + bool cross_family_sharing = false; + bool relaxed_casting_available = false; + DXGI_FORMAT *relaxed_casting_formats = nullptr; + uint32_t relaxed_casting_format_count = 0; + { + resource_desc.Format = RD_TO_D3D12_FORMAT[p_format.format].family; + + // If views of different families are wanted, special setup is needed for proper sharing among them. + // Two options here: + // 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) { + ComPtr<ID3D12Device10> device_10; + device->QueryInterface(device_10.GetAddressOf()); + if (device_10) { + relaxed_casting_available = true; + relaxed_casting_formats = ALLOCA_ARRAY(DXGI_FORMAT, p_format.shareable_formats.size()); + relaxed_casting_formats[0] = RD_TO_D3D12_FORMAT[p_format.format].general_format; + relaxed_casting_format_count++; + } + } + } + + HashMap<DataFormat, D3D12_RESOURCE_FLAGS> aliases_forbidden_flags; + for (int i = 0; i < p_format.shareable_formats.size(); i++) { + DataFormat curr_format = p_format.shareable_formats[i]; + String format_text = "'" + String(FORMAT_NAMES[p_format.format]) + "'"; + + ERR_FAIL_COND_V_MSG(RD_TO_D3D12_FORMAT[curr_format].family == DXGI_FORMAT_UNKNOWN, TextureID(), "Format " + format_text + " is not supported."); + + if (RD_TO_D3D12_FORMAT[curr_format].family != RD_TO_D3D12_FORMAT[p_format.format].family) { + cross_family_sharing = true; + if (!relaxed_casting_available) { + break; + } + relaxed_casting_formats[relaxed_casting_format_count] = RD_TO_D3D12_FORMAT[curr_format].general_format; + relaxed_casting_format_count++; + } + } + + if (cross_family_sharing && !relaxed_casting_available) { + // At least guarantee the same layout among aliases. + resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE; + + // Per https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_texture_layout. + if (p_format.texture_type == TEXTURE_TYPE_1D) { + ERR_FAIL_V_MSG(TextureID(), "This texture's views require aliasing, but that's not supported for a 1D texture."); + } + if (p_format.samples != TEXTURE_SAMPLES_1) { + ERR_FAIL_V_MSG(TextureID(), "This texture's views require aliasing, but that's not supported for a multi-sample texture."); + } + if ((p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + ERR_FAIL_V_MSG(TextureID(), "This texture's views require aliasing, but that's not supported for a depth-stencil texture."); + } + if (RD_TO_D3D12_FORMAT[p_format.format].family == DXGI_FORMAT_R32G32B32_TYPELESS) { + ERR_FAIL_V_MSG(TextureID(), "This texture's views require aliasing, but that's not supported for an R32G32B32 texture."); + } + } + } + + // Usage. + if ((p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + resource_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + } else { + if ((p_format.usage_bits & TEXTURE_USAGE_CAN_COPY_TO_BIT)) { + resource_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; // For clearing via UAV. + } + } + if ((p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + resource_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + } + if ((p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT)) { + resource_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + } + if ((p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) { + // For VRS images we can't use the typeless format. + resource_desc.Format = DXGI_FORMAT_R8_UINT; + } + + resource_desc.SampleDesc = {}; + DXGI_FORMAT format_to_test = (resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) ? RD_TO_D3D12_FORMAT[p_format.format].dsv_format : RD_TO_D3D12_FORMAT[p_format.format].general_format; + if (!(resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)) { + resource_desc.SampleDesc.Count = MIN( + _find_max_common_supported_sample_count(format_to_test), + TEXTURE_SAMPLES_COUNT[p_format.samples]); + } else { + // No MSAA in D3D12 if storage. May have become possible recently where supported, though. + resource_desc.SampleDesc.Count = 1; + } + resource_desc.SampleDesc.Quality = resource_desc.SampleDesc.Count == 1 ? 0 : DXGI_STANDARD_MULTISAMPLE_QUALITY_PATTERN; + + // Create. + + D3D12MA::ALLOCATION_DESC allocation_desc = {}; + if (cross_family_sharing && !relaxed_casting_available) { + allocation_desc.Flags = D3D12MA::ALLOCATION_FLAG_CAN_ALIAS; + } + allocation_desc.HeapType = (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) ? D3D12_HEAP_TYPE_READBACK : D3D12_HEAP_TYPE_DEFAULT; + if ((resource_desc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))) { + allocation_desc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES; + } else { + allocation_desc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES; + } + if ((resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)) { + allocation_desc.ExtraHeapFlags |= D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS; + } + +#ifdef USE_SMALL_ALLOCS_POOL + uint32_t width = 0, height = 0; + uint32_t image_size = get_image_format_required_size(p_format.format, p_format.width, p_format.height, p_format.depth, p_format.mipmaps, &width, &height); + if (image_size <= SMALL_ALLOCATION_MAX_SIZE) { + allocation_desc.CustomPool = _find_or_create_small_allocs_pool(allocation_desc.HeapType, allocation_desc.ExtraHeapFlags); + } +#endif + + D3D12_RESOURCE_STATES initial_state = {}; + ID3D12Resource *texture = nullptr; + ComPtr<ID3D12Resource> main_texture; + ComPtr<D3D12MA::Allocation> allocation; + static const FLOAT black[4] = {}; + D3D12_CLEAR_VALUE clear_value = CD3DX12_CLEAR_VALUE(RD_TO_D3D12_FORMAT[p_format.format].general_format, black); + D3D12_CLEAR_VALUE *clear_value_ptr = (resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) ? &clear_value : nullptr; + { + HRESULT res = E_FAIL; + if (cross_family_sharing && relaxed_casting_available) { + res = allocator->CreateResource3( + &allocation_desc, + &resource_desc, + D3D12_BARRIER_LAYOUT_COMMON, // Needed for barrier interop. + clear_value_ptr, + relaxed_casting_format_count, + relaxed_casting_formats, + allocation.GetAddressOf(), + IID_PPV_ARGS(main_texture.GetAddressOf())); + initial_state = D3D12_RESOURCE_STATE_COMMON; // Needed for barrier interop. + } else { + res = allocator->CreateResource( + &allocation_desc, + (D3D12_RESOURCE_DESC *)&resource_desc, + D3D12_RESOURCE_STATE_COPY_DEST, + clear_value_ptr, + allocation.GetAddressOf(), + IID_PPV_ARGS(main_texture.GetAddressOf())); + initial_state = D3D12_RESOURCE_STATE_COPY_DEST; + } + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), TextureID(), "CreateResource failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + texture = main_texture.Get(); + } + + // Describe views. + + D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {}; + { + srv_desc.Format = RD_TO_D3D12_FORMAT[p_view.format].general_format; + srv_desc.ViewDimension = p_format.samples == TEXTURE_SAMPLES_1 ? RD_TEXTURE_TYPE_TO_D3D12_VIEW_DIMENSION_FOR_SRV[p_format.texture_type] : RD_TEXTURE_TYPE_TO_D3D12_VIEW_DIMENSION_FOR_SRV_MS[p_format.texture_type]; + srv_desc.Shader4ComponentMapping = _compute_component_mapping(p_view); + + switch (srv_desc.ViewDimension) { + case D3D12_SRV_DIMENSION_TEXTURE1D: { + srv_desc.Texture1D.MipLevels = p_format.mipmaps; + } break; + case D3D12_SRV_DIMENSION_TEXTURE1DARRAY: { + srv_desc.Texture1DArray.MipLevels = p_format.mipmaps; + srv_desc.Texture1DArray.ArraySize = p_format.array_layers; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2D: { + srv_desc.Texture2D.MipLevels = p_format.mipmaps; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2DMS: { + } break; + case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: { + srv_desc.Texture2DArray.MipLevels = p_format.mipmaps; + srv_desc.Texture2DArray.ArraySize = p_format.array_layers; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY: { + srv_desc.Texture2DMSArray.ArraySize = p_format.array_layers; + } break; + case D3D12_SRV_DIMENSION_TEXTURECUBEARRAY: { + srv_desc.TextureCubeArray.MipLevels = p_format.mipmaps; + srv_desc.TextureCubeArray.NumCubes = p_format.array_layers / 6; + } break; + case D3D12_SRV_DIMENSION_TEXTURE3D: { + srv_desc.Texture3D.MipLevels = p_format.mipmaps; + } break; + case D3D12_SRV_DIMENSION_TEXTURECUBE: { + srv_desc.TextureCube.MipLevels = p_format.mipmaps; + } break; + default: { + } + } + } + + D3D12_UNORDERED_ACCESS_VIEW_DESC main_uav_desc = {}; + { + main_uav_desc.Format = RD_TO_D3D12_FORMAT[p_format.format].general_format; + main_uav_desc.ViewDimension = p_format.samples == TEXTURE_SAMPLES_1 ? RD_TEXTURE_TYPE_TO_D3D12_VIEW_DIMENSION_FOR_UAV[p_format.texture_type] : D3D12_UAV_DIMENSION_UNKNOWN; + + switch (main_uav_desc.ViewDimension) { + case D3D12_UAV_DIMENSION_TEXTURE1DARRAY: { + main_uav_desc.Texture1DArray.ArraySize = p_format.array_layers; + } break; + case D3D12_UAV_DIMENSION_TEXTURE2DARRAY: { + // Either for an actual 2D texture array, cubemap or cubemap array. + main_uav_desc.Texture2DArray.ArraySize = p_format.array_layers; + } break; + case D3D12_UAV_DIMENSION_TEXTURE3D: { + main_uav_desc.Texture3D.WSize = p_format.depth; + } break; + default: { + } + } + } + + D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = main_uav_desc; + uav_desc.Format = RD_TO_D3D12_FORMAT[p_view.format].general_format; + + // Create aliases if needed. [[CROSS_FAMILY_ALIASING]] + + using AliasEntry = Pair<DXGI_FORMAT, ID3D12Resource *>; + AliasEntry *aliases = nullptr; + uint32_t alias_count = 0; + if (cross_family_sharing && !relaxed_casting_available) { + aliases = ALLOCA_ARRAY(AliasEntry, p_format.shareable_formats.size()); + + for (int i = 0; i < p_format.shareable_formats.size(); i++) { + DataFormat curr_format = p_format.shareable_formats[i]; + + DXGI_FORMAT format_family = RD_TO_D3D12_FORMAT[curr_format].family; + if (format_family == RD_TO_D3D12_FORMAT[p_format.format].family) { + continue; + } + + D3D12_RESOURCE_DESC alias_resource_desc = *(D3D12_RESOURCE_DESC *)&resource_desc; + alias_resource_desc.Format = format_family; + clear_value.Format = format_family; + if ((alias_resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)) { + if (!texture_get_usages_supported_by_format(curr_format, false).has_flag(TEXTURE_USAGE_STORAGE_BIT)) { + alias_resource_desc.Flags &= ~D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + } + } + ID3D12Resource *alias = nullptr; + HRESULT res = allocator->CreateAliasingResource( + allocation.Get(), + 0, + &alias_resource_desc, + initial_state, + (alias_resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) ? clear_value_ptr : nullptr, + IID_PPV_ARGS(&alias)); + if (!SUCCEEDED(res)) { + for (uint32_t j = 0; j < alias_count; j++) { + aliases[j].second->Release(); + } + ERR_FAIL_V_MSG(TextureID(), "CreateAliasingResource failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + } + aliases[alias_count] = AliasEntry(format_family, alias); + alias_count++; + + if (curr_format == p_view.format) { + texture = alias; + } + } + } + + // Bookkeep. + + TextureInfo *tex_info = VersatileResource::allocate<TextureInfo>(resources_allocator); + tex_info->resource = texture; + tex_info->owner_info.resource = main_texture; + tex_info->owner_info.allocation = allocation; + tex_info->owner_info.states.subresource_states.resize(p_format.mipmaps * p_format.array_layers); + for (uint32_t i = 0; i < tex_info->owner_info.states.subresource_states.size(); i++) { + tex_info->owner_info.states.subresource_states[i] = initial_state; + } + tex_info->states_ptr = &tex_info->owner_info.states; + tex_info->format = p_format.format; + tex_info->desc = *(CD3DX12_RESOURCE_DESC *)&resource_desc; + tex_info->base_layer = 0; + tex_info->layers = resource_desc.ArraySize(); + tex_info->base_mip = 0; + tex_info->mipmaps = resource_desc.MipLevels; + tex_info->view_descs.srv = srv_desc; + tex_info->view_descs.uav = uav_desc; + tex_info->main_texture = main_texture.Get(); + tex_info->aliasing_hack.main_uav_desc = main_uav_desc; + if (alias_count) { + for (uint32_t i = 0; i < alias_count; i++) { + tex_info->aliasing_hack.owner_info.aliases.insert(aliases[i].first, aliases[i].second); + } + } + + return TextureID(tex_info); +} + +RDD::TextureID RenderingDeviceDriverD3D12::texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) { + ERR_FAIL_V_MSG(TextureID(), "Unimplemented!"); +} + +RDD::TextureID RenderingDeviceDriverD3D12::texture_create_shared(TextureID p_original_texture, const TextureView &p_view) { + const TextureInfo *owner_tex_info = (const TextureInfo *)p_original_texture.id; +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V(!owner_tex_info->owner_info.allocation, TextureID()); +#endif + + ID3D12Resource *texture = nullptr; + if (owner_tex_info->aliasing_hack.owner_info.aliases.is_empty()) { + texture = owner_tex_info->resource; + } else { + texture = owner_tex_info->main_texture; + for (const KeyValue<DXGI_FORMAT, ComPtr<ID3D12Resource>> &E : owner_tex_info->aliasing_hack.owner_info.aliases) { + if (E.key == RD_TO_D3D12_FORMAT[p_view.format].family) { + texture = E.value.Get(); + break; + } + } + } + + // Describe views. + + D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = owner_tex_info->view_descs.srv; + { + srv_desc.Format = RD_TO_D3D12_FORMAT[p_view.format].general_format; + srv_desc.Shader4ComponentMapping = _compute_component_mapping(p_view); + } + + D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = owner_tex_info->view_descs.uav; + { + uav_desc.Format = RD_TO_D3D12_FORMAT[p_view.format].general_format; + } + + // Bookkeep. + + TextureInfo *tex_info = VersatileResource::allocate<TextureInfo>(resources_allocator); + tex_info->resource = texture; + tex_info->states_ptr = owner_tex_info->states_ptr; + tex_info->format = p_view.format; + tex_info->desc = owner_tex_info->desc; + tex_info->base_layer = owner_tex_info->base_layer; + tex_info->layers = owner_tex_info->layers; + tex_info->base_mip = owner_tex_info->base_mip; + tex_info->mipmaps = owner_tex_info->mipmaps; + tex_info->view_descs.srv = srv_desc; + tex_info->view_descs.uav = uav_desc; + tex_info->main_texture = owner_tex_info->main_texture; + tex_info->aliasing_hack.main_uav_desc = owner_tex_info->aliasing_hack.main_uav_desc; + + return TextureID(tex_info); +} + +RDD::TextureID RenderingDeviceDriverD3D12::texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) { + const TextureInfo *owner_tex_info = (const TextureInfo *)p_original_texture.id; +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V(!owner_tex_info->owner_info.allocation, TextureID()); +#endif + + // Find appropriate resource instance. + + ID3D12Resource *texture = nullptr; + if (owner_tex_info->aliasing_hack.owner_info.aliases.is_empty()) { + texture = owner_tex_info->resource; + } else { + texture = owner_tex_info->main_texture; + for (const KeyValue<DXGI_FORMAT, ComPtr<ID3D12Resource>> &E : owner_tex_info->aliasing_hack.owner_info.aliases) { + if (E.key == RD_TO_D3D12_FORMAT[p_view.format].family) { + texture = E.value.Get(); + break; + } + } + } + + // Describe views. + + D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = owner_tex_info->view_descs.srv; + { + srv_desc.Format = RD_TO_D3D12_FORMAT[p_view.format].general_format; + srv_desc.Shader4ComponentMapping = _compute_component_mapping(p_view); + } + + D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = owner_tex_info->view_descs.uav; + { + uav_desc.Format = RD_TO_D3D12_FORMAT[p_view.format].general_format; + } + + // Complete description with slicing. + // (Leveraging aliasing in members of the union as much as possible.) + + srv_desc.Texture1D.MostDetailedMip = p_mipmap; + srv_desc.Texture1D.MipLevels = 1; + + uav_desc.Texture1D.MipSlice = p_mipmap; + + switch (p_slice_type) { + case TEXTURE_SLICE_2D: { + if (srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2D && p_layer == 0) { + DEV_ASSERT(uav_desc.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE2D); + } else if (srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DMS && p_layer == 0) { + DEV_ASSERT(uav_desc.ViewDimension == D3D12_UAV_DIMENSION_UNKNOWN); + } else if ((srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DARRAY || (srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2D && p_layer)) || srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBE || srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBEARRAY) { + srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; + srv_desc.Texture2DArray.FirstArraySlice = p_layer; + srv_desc.Texture2DArray.ArraySize = 1; + srv_desc.Texture2DArray.PlaneSlice = 0; + srv_desc.Texture2DArray.ResourceMinLODClamp = 0.0f; + + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + uav_desc.Texture2DArray.FirstArraySlice = p_layer; + uav_desc.Texture2DArray.ArraySize = 1; + uav_desc.Texture2DArray.PlaneSlice = 0; + } else if ((srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY || (srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DMS && p_layer))) { + srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; + srv_desc.Texture2DMSArray.FirstArraySlice = p_layer; + srv_desc.Texture2DMSArray.ArraySize = 1; + + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_UNKNOWN; + } else { + DEV_ASSERT(false); + } + } break; + case TEXTURE_SLICE_CUBEMAP: { + if (srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBE) { + DEV_ASSERT(uav_desc.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE2DARRAY); + } else if (srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBE || p_layer == 0) { + srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; + + DEV_ASSERT(uav_desc.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE2DARRAY); + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + uav_desc.Texture2DArray.FirstArraySlice = 0; + uav_desc.Texture2DArray.ArraySize = 6; + uav_desc.Texture2DArray.PlaneSlice = 0; + } else if (srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBEARRAY || p_layer != 0) { + srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; + srv_desc.TextureCubeArray.First2DArrayFace = p_layer; + srv_desc.TextureCubeArray.NumCubes = 1; + srv_desc.TextureCubeArray.ResourceMinLODClamp = 0.0f; + + DEV_ASSERT(uav_desc.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE2DARRAY); + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + uav_desc.Texture2DArray.FirstArraySlice = p_layer; + uav_desc.Texture2DArray.ArraySize = 6; + uav_desc.Texture2DArray.PlaneSlice = 0; + } else { + DEV_ASSERT(false); + } + } break; + case TEXTURE_SLICE_3D: { + DEV_ASSERT(srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE3D); + + DEV_ASSERT(uav_desc.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE3D); + uav_desc.Texture3D.WSize = -1; + } break; + case TEXTURE_SLICE_2D_ARRAY: { + DEV_ASSERT(srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DARRAY); + srv_desc.Texture2DArray.FirstArraySlice = p_layer; + srv_desc.Texture2DArray.ArraySize = p_layers; + + DEV_ASSERT(uav_desc.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE2DARRAY); + uav_desc.Texture2DArray.FirstArraySlice = p_layer; + uav_desc.Texture2DArray.ArraySize = p_layers; + } break; + } + + // Bookkeep. + + TextureInfo *tex_info = VersatileResource::allocate<TextureInfo>(resources_allocator); + tex_info->resource = texture; + tex_info->states_ptr = owner_tex_info->states_ptr; + tex_info->format = p_view.format; + tex_info->desc = owner_tex_info->desc; + tex_info->base_layer = p_layer; + tex_info->layers = p_layers; + tex_info->base_mip = p_mipmap; + tex_info->mipmaps = p_mipmaps; + tex_info->view_descs.srv = srv_desc; + tex_info->view_descs.uav = uav_desc; + tex_info->main_texture = owner_tex_info->main_texture; + tex_info->aliasing_hack.main_uav_desc = owner_tex_info->aliasing_hack.main_uav_desc; + + return TextureID(tex_info); +} + +void RenderingDeviceDriverD3D12::texture_free(TextureID p_texture) { + TextureInfo *tex_info = (TextureInfo *)p_texture.id; + VersatileResource::free(resources_allocator, tex_info); +} + +uint64_t RenderingDeviceDriverD3D12::texture_get_allocation_size(TextureID p_texture) { + const TextureInfo *tex_info = (const TextureInfo *)p_texture.id; + return tex_info->owner_info.allocation ? tex_info->owner_info.allocation->GetSize() : 0; +} + +void RenderingDeviceDriverD3D12::texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) { + TextureInfo *tex_info = (TextureInfo *)p_texture.id; + + UINT subresource = tex_info->desc.CalcSubresource(p_subresource.mipmap, p_subresource.layer, 0); + + D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint = {}; + UINT64 subresource_total_size = 0; + device->GetCopyableFootprints( + &tex_info->desc, + subresource, + 1, + 0, + &footprint, + nullptr, + nullptr, + &subresource_total_size); + + *r_layout = {}; + r_layout->offset = footprint.Offset; + r_layout->size = subresource_total_size; + r_layout->row_pitch = footprint.Footprint.RowPitch; + r_layout->depth_pitch = subresource_total_size / tex_info->desc.Depth(); + r_layout->layer_pitch = subresource_total_size / tex_info->desc.ArraySize(); +} + +uint8_t *RenderingDeviceDriverD3D12::texture_map(TextureID p_texture, const TextureSubresource &p_subresource) { + TextureInfo *tex_info = (TextureInfo *)p_texture.id; +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V(tex_info->mapped_subresource != UINT_MAX, nullptr); +#endif + + UINT plane = _compute_plane_slice(tex_info->format, p_subresource.aspect); + UINT subresource = tex_info->desc.CalcSubresource(p_subresource.mipmap, p_subresource.layer, plane); + + void *data_ptr = nullptr; + HRESULT res = tex_info->resource->Map(subresource, &VOID_RANGE, &data_ptr); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), nullptr, "Map failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + tex_info->mapped_subresource = subresource; + return (uint8_t *)data_ptr; +} + +void RenderingDeviceDriverD3D12::texture_unmap(TextureID p_texture) { + TextureInfo *tex_info = (TextureInfo *)p_texture.id; +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(tex_info->mapped_subresource == UINT_MAX); +#endif + tex_info->resource->Unmap(tex_info->mapped_subresource, &VOID_RANGE); + tex_info->mapped_subresource = UINT_MAX; +} + +BitField<RDD::TextureUsageBits> RenderingDeviceDriverD3D12::texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) { + D3D12_FEATURE_DATA_FORMAT_SUPPORT srv_rtv_support = {}; + srv_rtv_support.Format = RD_TO_D3D12_FORMAT[p_format].general_format; + HRESULT res = device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &srv_rtv_support, sizeof(srv_rtv_support)); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), false, "CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + + D3D12_FEATURE_DATA_FORMAT_SUPPORT &uav_support = srv_rtv_support; // Fine for now. + + D3D12_FEATURE_DATA_FORMAT_SUPPORT dsv_support = {}; + dsv_support.Format = RD_TO_D3D12_FORMAT[p_format].dsv_format; + res = device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &dsv_support, sizeof(dsv_support)); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), false, "CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + + // Everything supported by default makes an all-or-nothing check easier for the caller. + BitField<RDD::TextureUsageBits> supported = INT64_MAX; + + // Per https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_format_support1, + // as long as the resource can be used as a texture, Sample() will work with point filter at least. + // However, we've empirically found that checking for at least D3D12_FORMAT_SUPPORT1_SHADER_LOAD is needed. + // That's almost good for integer formats. The problem is that theoretically there may be + // float formats that support LOAD but not SAMPLE fully, so this check will not detect + // such a flaw in the format. Linearly interpolated sampling would just not work on them. + if (!(srv_rtv_support.Support1 & (D3D12_FORMAT_SUPPORT1_SHADER_LOAD | D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE)) || + RD_TO_D3D12_FORMAT[p_format].general_format == DXGI_FORMAT_UNKNOWN) { + supported.clear_flag(TEXTURE_USAGE_SAMPLING_BIT); + } + + if (!(srv_rtv_support.Support1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET)) { + supported.clear_flag(TEXTURE_USAGE_COLOR_ATTACHMENT_BIT); + } + if (!(dsv_support.Support1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL)) { + supported.clear_flag(TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + } + if (!(uav_support.Support1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW)) { // Maybe check LOAD/STORE, too? + supported.clear_flag(TEXTURE_USAGE_STORAGE_BIT); + } + if (!(uav_support.Support2 & D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_ADD)) { // Check a basic atomic at least. + supported.clear_flag(TEXTURE_USAGE_STORAGE_ATOMIC_BIT); + } + if (RD_TO_D3D12_FORMAT[p_format].general_format != DXGI_FORMAT_R8_UINT) { + supported.clear_flag(TEXTURE_USAGE_VRS_ATTACHMENT_BIT); + } + + return supported; +} + +/*****************/ +/**** SAMPLER ****/ +/*****************/ + +static const D3D12_TEXTURE_ADDRESS_MODE RD_REPEAT_MODE_TO_D3D12_ADDRES_MODE[RDD::SAMPLER_REPEAT_MODE_MAX] = { + D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE_MIRROR, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_BORDER, + D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE, +}; + +static const FLOAT RD_TO_D3D12_SAMPLER_BORDER_COLOR[RDD::SAMPLER_BORDER_COLOR_MAX][4] = { + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 1 }, + { 0, 0, 0, 1 }, + { 1, 1, 1, 1 }, + { 1, 1, 1, 1 }, +}; + +RDD::SamplerID RenderingDeviceDriverD3D12::sampler_create(const SamplerState &p_state) { + uint32_t slot = UINT32_MAX; + + if (samplers.is_empty()) { + // Adding a seemigly busy slot 0 makes things easier elsewhere. + samplers.push_back({}); + samplers.push_back({}); + slot = 1; + } else { + for (uint32_t i = 1; i < samplers.size(); i++) { + if (samplers[i].Filter == INT_MAX) { + slot = i; + break; + } + } + if (slot == UINT32_MAX) { + slot = samplers.size(); + samplers.push_back({}); + } + } + + D3D12_SAMPLER_DESC &sampler_desc = samplers[slot]; + + if (p_state.use_anisotropy) { + sampler_desc.Filter = D3D12_ENCODE_ANISOTROPIC_FILTER(D3D12_FILTER_REDUCTION_TYPE_STANDARD); + sampler_desc.MaxAnisotropy = p_state.anisotropy_max; + } else { + static const D3D12_FILTER_TYPE RD_FILTER_TYPE_TO_D3D12[] = { + D3D12_FILTER_TYPE_POINT, // SAMPLER_FILTER_NEAREST. + D3D12_FILTER_TYPE_LINEAR, // SAMPLER_FILTER_LINEAR. + }; + sampler_desc.Filter = D3D12_ENCODE_BASIC_FILTER( + RD_FILTER_TYPE_TO_D3D12[p_state.min_filter], + RD_FILTER_TYPE_TO_D3D12[p_state.mag_filter], + RD_FILTER_TYPE_TO_D3D12[p_state.mip_filter], + p_state.enable_compare ? D3D12_FILTER_REDUCTION_TYPE_COMPARISON : D3D12_FILTER_REDUCTION_TYPE_STANDARD); + } + + sampler_desc.AddressU = RD_REPEAT_MODE_TO_D3D12_ADDRES_MODE[p_state.repeat_u]; + sampler_desc.AddressV = RD_REPEAT_MODE_TO_D3D12_ADDRES_MODE[p_state.repeat_v]; + sampler_desc.AddressW = RD_REPEAT_MODE_TO_D3D12_ADDRES_MODE[p_state.repeat_w]; + + for (int i = 0; i < 4; i++) { + sampler_desc.BorderColor[i] = RD_TO_D3D12_SAMPLER_BORDER_COLOR[p_state.border_color][i]; + } + + sampler_desc.MinLOD = p_state.min_lod; + sampler_desc.MaxLOD = p_state.max_lod; + sampler_desc.MipLODBias = p_state.lod_bias; + + sampler_desc.ComparisonFunc = p_state.enable_compare ? RD_TO_D3D12_COMPARE_OP[p_state.compare_op] : D3D12_COMPARISON_FUNC_NEVER; + + // TODO: Emulate somehow? + if (p_state.unnormalized_uvw) { + WARN_PRINT("Creating a sampler with unnormalized UVW, which is not supported."); + } + + return SamplerID(slot); +} + +void RenderingDeviceDriverD3D12::sampler_free(SamplerID p_sampler) { + samplers[p_sampler.id].Filter = (D3D12_FILTER)INT_MAX; +} + +bool RenderingDeviceDriverD3D12::sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) { + D3D12_FEATURE_DATA_FORMAT_SUPPORT srv_rtv_support = {}; + srv_rtv_support.Format = RD_TO_D3D12_FORMAT[p_format].general_format; + HRESULT res = device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &srv_rtv_support, sizeof(srv_rtv_support)); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), false, "CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + return (srv_rtv_support.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE); +} + +/**********************/ +/**** VERTEX ARRAY ****/ +/**********************/ + +RDD::VertexFormatID RenderingDeviceDriverD3D12::vertex_format_create(VectorView<VertexAttribute> p_vertex_attribs) { + VertexFormatInfo *vf_info = VersatileResource::allocate<VertexFormatInfo>(resources_allocator); + + vf_info->input_elem_descs.resize(p_vertex_attribs.size()); + vf_info->vertex_buffer_strides.resize(p_vertex_attribs.size()); + for (uint32_t i = 0; i < p_vertex_attribs.size(); i++) { + vf_info->input_elem_descs[i] = {}; + vf_info->input_elem_descs[i].SemanticName = "TEXCOORD"; + vf_info->input_elem_descs[i].SemanticIndex = p_vertex_attribs[i].location; + vf_info->input_elem_descs[i].Format = RD_TO_D3D12_FORMAT[p_vertex_attribs[i].format].general_format; + vf_info->input_elem_descs[i].InputSlot = i; // TODO: Can the same slot be used if data comes from the same buffer (regardless format)? + vf_info->input_elem_descs[i].AlignedByteOffset = p_vertex_attribs[i].offset; + if (p_vertex_attribs[i].frequency == VERTEX_FREQUENCY_INSTANCE) { + vf_info->input_elem_descs[i].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA; + vf_info->input_elem_descs[i].InstanceDataStepRate = 1; + } else { + vf_info->input_elem_descs[i].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; + vf_info->input_elem_descs[i].InstanceDataStepRate = 0; + } + + vf_info->vertex_buffer_strides[i] = p_vertex_attribs[i].stride; + } + + return VertexFormatID(vf_info); +} + +void RenderingDeviceDriverD3D12::vertex_format_free(VertexFormatID p_vertex_format) { + VertexFormatInfo *vf_info = (VertexFormatInfo *)p_vertex_format.id; + VersatileResource::free(resources_allocator, vf_info); +} + +/******************/ +/**** BARRIERS ****/ +/******************/ + +void RenderingDeviceDriverD3D12::command_pipeline_barrier( + CommandBufferID p_cmd_buffer, + BitField<RDD::PipelineStageBits> p_src_stages, + BitField<RDD::PipelineStageBits> p_dst_stages, + VectorView<RDD::MemoryBarrier> p_memory_barriers, + VectorView<RDD::BufferBarrier> p_buffer_barriers, + VectorView<RDD::TextureBarrier> p_texture_barriers) { + if (p_src_stages.has_flag(PIPELINE_STAGE_ALL_COMMANDS_BIT) && p_dst_stages.has_flag(PIPELINE_STAGE_ALL_COMMANDS_BIT)) { + // Looks like the intent is a a full barrier. + // In the resource barriers world, we can force a full barrier by discarding some resource, as per + // https://microsoft.github.io/DirectX-Specs/d3d/D3D12EnhancedBarriers.html#synchronous-copy-discard-and-resolve. + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + cmd_buf_info->cmd_list->DiscardResource(frames[frame_idx].aux_resource->GetResource(), nullptr); + } +} + +/*************************/ +/**** COMMAND BUFFERS ****/ +/*************************/ + +// ----- POOL ----- + +RDD::CommandPoolID RenderingDeviceDriverD3D12::command_pool_create(CommandBufferType p_cmd_buffer_type) { + last_command_pool_id.id++; + return last_command_pool_id; +} + +void RenderingDeviceDriverD3D12::command_pool_free(CommandPoolID p_cmd_pool) { + pools_command_buffers.erase(p_cmd_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; + + ID3D12CommandAllocator *cmd_allocator = nullptr; + { + HRESULT res = device->CreateCommandAllocator(list_type, IID_PPV_ARGS(&cmd_allocator)); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), CommandBufferID(), "CreateCommandAllocator failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + } + + ID3D12GraphicsCommandList *cmd_list = nullptr; + { + ComPtr<ID3D12Device4> device_4; + 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)); + } else { + res = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, 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) { + cmd_list->Close(); + } + } + + // Bookkeep + + CommandBufferInfo *cmd_buf_info = VersatileResource::allocate<CommandBufferInfo>(resources_allocator); + cmd_buf_info->cmd_allocator = cmd_allocator; + cmd_buf_info->cmd_list = cmd_list; + + return CommandBufferID(cmd_buf_info); +} + +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_list->Reset(cmd_buf_info->cmd_allocator.Get(), nullptr); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), false, "Reset failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + return true; +} + +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_list->Reset(cmd_buf_info->cmd_allocator.Get(), nullptr); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), false, "Reset failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + return true; +} + +void RenderingDeviceDriverD3D12::command_buffer_end(CommandBufferID p_cmd_buffer) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + HRESULT res = cmd_buf_info->cmd_list->Close(); + + ERR_FAIL_COND_MSG(!SUCCEEDED(res), "Close failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + cmd_buf_info->graphics_pso = nullptr; + cmd_buf_info->graphics_root_signature_crc = 0; + cmd_buf_info->compute_pso = nullptr; + cmd_buf_info->compute_root_signature_crc = 0; +} + +void RenderingDeviceDriverD3D12::command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> 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()); + } +} + +/*********************/ +/**** FRAMEBUFFER ****/ +/*********************/ + +D3D12_RENDER_TARGET_VIEW_DESC RenderingDeviceDriverD3D12::_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) { + D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = {}; + rtv_desc.Format = p_texture_info->view_descs.srv.Format; + + switch (p_texture_info->view_descs.srv.ViewDimension) { + case D3D12_SRV_DIMENSION_TEXTURE1D: { + rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D; + rtv_desc.Texture1D.MipSlice = p_texture_info->base_mip + p_mipmap_offset; + } break; + case D3D12_SRV_DIMENSION_TEXTURE1DARRAY: { + rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1DARRAY; + rtv_desc.Texture1DArray.MipSlice = (p_add_bases ? p_texture_info->base_mip : 0) + p_mipmap_offset; + rtv_desc.Texture1DArray.FirstArraySlice = (p_add_bases ? p_texture_info->base_layer : 0) + p_layer_offset; + rtv_desc.Texture1DArray.ArraySize = p_layers == UINT32_MAX ? p_texture_info->view_descs.srv.Texture1DArray.ArraySize : p_layers; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2D: { + rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + rtv_desc.Texture2D.MipSlice = (p_add_bases ? p_texture_info->base_mip : 0) + p_mipmap_offset; + rtv_desc.Texture2D.PlaneSlice = p_texture_info->view_descs.srv.Texture2D.PlaneSlice; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: { + rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; + rtv_desc.Texture2DArray.MipSlice = (p_add_bases ? p_texture_info->base_mip : 0) + p_mipmap_offset; + rtv_desc.Texture2DArray.FirstArraySlice = (p_add_bases ? p_texture_info->base_layer : 0) + p_layer_offset; + rtv_desc.Texture2DArray.ArraySize = p_layers == UINT32_MAX ? p_texture_info->view_descs.srv.Texture2DArray.ArraySize : p_layers; + rtv_desc.Texture2DArray.PlaneSlice = p_texture_info->view_descs.srv.Texture2DArray.PlaneSlice; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2DMS: { + rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY: { + rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; + rtv_desc.Texture2DMSArray.FirstArraySlice = (p_add_bases ? p_texture_info->base_layer : 0) + p_layer_offset; + rtv_desc.Texture2DMSArray.ArraySize = p_layers == UINT32_MAX ? p_texture_info->view_descs.srv.Texture2DMSArray.ArraySize : p_layers; + } break; + case D3D12_SRV_DIMENSION_TEXTURE3D: { + rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D; + rtv_desc.Texture3D.MipSlice = p_texture_info->view_descs.srv.Texture3D.MostDetailedMip + p_mipmap_offset; + rtv_desc.Texture3D.FirstWSlice = 0; + rtv_desc.Texture3D.WSize = -1; + } break; + default: { + DEV_ASSERT(false); + } + } + + return rtv_desc; +} + +D3D12_DEPTH_STENCIL_VIEW_DESC RenderingDeviceDriverD3D12::_make_dsv_for_texture(const TextureInfo *p_texture_info) { + D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc = {}; + dsv_desc.Format = RD_TO_D3D12_FORMAT[p_texture_info->format].dsv_format; + dsv_desc.Flags = D3D12_DSV_FLAG_NONE; + + switch (p_texture_info->view_descs.srv.ViewDimension) { + case D3D12_SRV_DIMENSION_TEXTURE1D: { + dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1D; + dsv_desc.Texture1D.MipSlice = p_texture_info->base_mip; + } break; + case D3D12_SRV_DIMENSION_TEXTURE1DARRAY: { + dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1DARRAY; + dsv_desc.Texture1DArray.MipSlice = p_texture_info->base_mip; + dsv_desc.Texture1DArray.FirstArraySlice = p_texture_info->base_layer; + dsv_desc.Texture1DArray.ArraySize = p_texture_info->view_descs.srv.Texture1DArray.ArraySize; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2D: { + dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + dsv_desc.Texture2D.MipSlice = p_texture_info->view_descs.srv.Texture2D.MostDetailedMip; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: { + dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY; + dsv_desc.Texture2DArray.MipSlice = p_texture_info->base_mip; + dsv_desc.Texture2DArray.FirstArraySlice = p_texture_info->base_layer; + dsv_desc.Texture2DArray.ArraySize = p_texture_info->view_descs.srv.Texture2DArray.ArraySize; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2DMS: { + dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; + dsv_desc.Texture2DMS.UnusedField_NothingToDefine = p_texture_info->view_descs.srv.Texture2DMS.UnusedField_NothingToDefine; + } break; + case D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY: { + dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY; + dsv_desc.Texture2DMSArray.FirstArraySlice = p_texture_info->base_layer; + dsv_desc.Texture2DMSArray.ArraySize = p_texture_info->view_descs.srv.Texture2DMSArray.ArraySize; + } break; + default: { + DEV_ASSERT(false); + } + } + + return dsv_desc; +} + +RDD::FramebufferID RenderingDeviceDriverD3D12::framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) { + // Pre-bookkeep. + FramebufferInfo *fb_info = VersatileResource::allocate<FramebufferInfo>(resources_allocator); + + const RenderPassInfo *pass_info = (const RenderPassInfo *)p_render_pass.id; + + uint32_t num_color = 0; + uint32_t num_depth_stencil = 0; + for (uint32_t i = 0; i < p_attachments.size(); i++) { + const TextureInfo *tex_info = (const TextureInfo *)p_attachments[i].id; + if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { + num_color++; + } else if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { + num_depth_stencil++; + } + } + + uint32_t vrs_index = UINT32_MAX; + for (const Subpass &E : pass_info->subpasses) { + if (E.vrs_reference.attachment != AttachmentReference::UNUSED) { + vrs_index = E.vrs_reference.attachment; + } + } + + if (num_color) { + Error err = fb_info->rtv_heap.allocate(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, num_color, false); + if (err) { + VersatileResource::free(resources_allocator, fb_info); + ERR_FAIL_V(FramebufferID()); + } + } + 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); + if (err) { + VersatileResource::free(resources_allocator, fb_info); + ERR_FAIL_V(FramebufferID()); + } + } + DescriptorsHeap::Walker dsv_heap_walker = fb_info->dsv_heap.make_walker(); + + fb_info->attachments_handle_inds.resize(p_attachments.size()); + fb_info->attachments.reserve(num_color + num_depth_stencil); + + uint32_t color_idx = 0; + uint32_t depth_stencil_idx = 0; + for (uint32_t i = 0; i < p_attachments.size(); i++) { + const TextureInfo *tex_info = (const TextureInfo *)p_attachments[i].id; + + if (fb_info->size.x == 0) { + fb_info->size = Size2i(tex_info->desc.Width, tex_info->desc.Height); + } + + if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { + D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = _make_rtv_for_texture(tex_info, 0, 0, UINT32_MAX); + device->CreateRenderTargetView(tex_info->resource, &rtv_desc, rtv_heap_walker.get_curr_cpu_handle()); + rtv_heap_walker.advance(); + + fb_info->attachments_handle_inds[i] = color_idx; + fb_info->attachments.push_back(p_attachments[i]); + color_idx++; + } else if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { + D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc = _make_dsv_for_texture(tex_info); + device->CreateDepthStencilView(tex_info->resource, &dsv_desc, dsv_heap_walker.get_curr_cpu_handle()); + dsv_heap_walker.advance(); + + fb_info->attachments_handle_inds[i] = depth_stencil_idx; + fb_info->attachments.push_back(p_attachments[i]); + depth_stencil_idx++; + } else if (i == vrs_index) { + fb_info->vrs_attachment = p_attachments[i]; + } else { + DEV_ASSERT(false); + } + } + + DEV_ASSERT(fb_info->attachments.size() == color_idx + depth_stencil_idx); + DEV_ASSERT((fb_info->vrs_attachment.id != 0) == (vrs_index != UINT32_MAX)); + + DEV_ASSERT(rtv_heap_walker.is_at_eof()); + DEV_ASSERT(dsv_heap_walker.is_at_eof()); + + return FramebufferID(fb_info); +} + +void RenderingDeviceDriverD3D12::framebuffer_free(FramebufferID p_framebuffer) { + FramebufferInfo *fb_info = (FramebufferInfo *)p_framebuffer.id; + VersatileResource::free(resources_allocator, fb_info); +} + +/****************/ +/**** SHADER ****/ +/****************/ + +static uint32_t SHADER_STAGES_BIT_OFFSET_INDICES[RenderingDevice::SHADER_STAGE_MAX] = { + /* SHADER_STAGE_VERTEX */ 0, + /* SHADER_STAGE_FRAGMENT */ 1, + /* SHADER_STAGE_TESSELATION_CONTROL */ UINT32_MAX, + /* SHADER_STAGE_TESSELATION_EVALUATION */ UINT32_MAX, + /* SHADER_STAGE_COMPUTE */ 2, +}; + +dxil_validator *RenderingDeviceDriverD3D12::_get_dxil_validator_for_current_thread() { + MutexLock lock(dxil_mutex); + + int thread_idx = WorkerThreadPool::get_singleton()->get_thread_index(); + if (dxil_validators.has(thread_idx)) { + return dxil_validators[thread_idx]; + } + +#ifdef DEV_ENABLED + print_verbose("Creating DXIL validator for worker thread index " + itos(thread_idx)); +#endif + + dxil_validator *dxil_validator = dxil_create_validator(nullptr); + CRASH_COND(!dxil_validator); + + dxil_validators.insert(thread_idx, dxil_validator); + return dxil_validator; +} + +uint32_t RenderingDeviceDriverD3D12::_shader_patch_dxil_specialization_constant( + PipelineSpecializationConstantType p_type, + const void *p_value, + const uint64_t (&p_stages_bit_offsets)[D3D12_BITCODE_OFFSETS_NUM_STAGES], + HashMap<ShaderStage, Vector<uint8_t>> &r_stages_bytecodes, + bool p_is_first_patch) { + uint32_t patch_val = 0; + switch (p_type) { + case PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT: { + uint32_t int_value = *((const int *)p_value); + ERR_FAIL_COND_V(int_value & (1 << 31), 0); + patch_val = int_value; + } break; + case PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL: { + bool bool_value = *((const bool *)p_value); + patch_val = (uint32_t)bool_value; + } break; + case PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT: { + uint32_t int_value = *((const int *)p_value); + ERR_FAIL_COND_V(int_value & (1 << 31), 0); + patch_val = (int_value >> 1); + } break; + } + // For VBR encoding to encode the number of bits we expect (32), we need to set the MSB unconditionally. + // However, signed VBR moves the MSB to the LSB, so setting the MSB to 1 wouldn't help. Therefore, + // the bit we set to 1 is the one at index 30. + patch_val |= (1 << 30); + patch_val <<= 1; // What signed VBR does. + + auto tamper_bits = [](uint8_t *p_start, uint64_t p_bit_offset, uint64_t p_tb_value) -> uint64_t { + uint64_t original = 0; + uint32_t curr_input_byte = p_bit_offset / 8; + uint8_t curr_input_bit = p_bit_offset % 8; + auto get_curr_input_bit = [&]() -> bool { + return ((p_start[curr_input_byte] >> curr_input_bit) & 1); + }; + auto move_to_next_input_bit = [&]() { + if (curr_input_bit == 7) { + curr_input_bit = 0; + curr_input_byte++; + } else { + curr_input_bit++; + } + }; + auto tamper_input_bit = [&](bool p_new_bit) { + p_start[curr_input_byte] &= ~((uint8_t)1 << curr_input_bit); + if (p_new_bit) { + p_start[curr_input_byte] |= (uint8_t)1 << curr_input_bit; + } + }; + uint8_t value_bit_idx = 0; + for (uint32_t i = 0; i < 5; i++) { // 32 bits take 5 full bytes in VBR. + for (uint32_t j = 0; j < 7; j++) { + bool input_bit = get_curr_input_bit(); + original |= (uint64_t)(input_bit ? 1 : 0) << value_bit_idx; + tamper_input_bit((p_tb_value >> value_bit_idx) & 1); + move_to_next_input_bit(); + value_bit_idx++; + } +#ifdef DEV_ENABLED + bool input_bit = get_curr_input_bit(); + DEV_ASSERT((i < 4 && input_bit) || (i == 4 && !input_bit)); +#endif + move_to_next_input_bit(); + } + return original; + }; + uint32_t stages_patched_mask = 0; + for (int stage = 0; stage < SHADER_STAGE_MAX; stage++) { + if (!r_stages_bytecodes.has((ShaderStage)stage)) { + continue; + } + + uint64_t offset = p_stages_bit_offsets[SHADER_STAGES_BIT_OFFSET_INDICES[stage]]; + if (offset == 0) { + // This constant does not appear at this stage. + continue; + } + + Vector<uint8_t> &bytecode = r_stages_bytecodes[(ShaderStage)stage]; +#ifdef DEV_ENABLED + uint64_t orig_patch_val = tamper_bits(bytecode.ptrw(), offset, patch_val); + // Checking against the value the NIR patch should have set. + DEV_ASSERT(!p_is_first_patch || ((orig_patch_val >> 1) & GODOT_NIR_SC_SENTINEL_MAGIC_MASK) == GODOT_NIR_SC_SENTINEL_MAGIC); + uint64_t readback_patch_val = tamper_bits(bytecode.ptrw(), offset, patch_val); + DEV_ASSERT(readback_patch_val == patch_val); +#else + tamper_bits(bytecode.ptrw(), offset, patch_val); +#endif + + stages_patched_mask |= (1 << stage); + } + return stages_patched_mask; +} + +bool RenderingDeviceDriverD3D12::_shader_apply_specialization_constants( + const ShaderInfo *p_shader_info, + VectorView<PipelineSpecializationConstant> p_specialization_constants, + HashMap<ShaderStage, Vector<uint8_t>> &r_final_stages_bytecode) { + // If something needs to be patched, COW will do the trick. + r_final_stages_bytecode = p_shader_info->stages_bytecode; + uint32_t stages_re_sign_mask = 0; + for (uint32_t i = 0; i < p_specialization_constants.size(); i++) { + const PipelineSpecializationConstant &psc = p_specialization_constants[i]; + if (!(p_shader_info->spirv_specialization_constants_ids_mask & (1 << psc.constant_id))) { + // This SC wasn't even in the original SPIR-V shader. + continue; + } + for (const ShaderInfo::SpecializationConstant &sc : p_shader_info->specialization_constants) { + if (psc.constant_id == sc.constant_id) { + if (psc.int_value != sc.int_value) { + stages_re_sign_mask |= _shader_patch_dxil_specialization_constant(psc.type, &psc.int_value, sc.stages_bit_offsets, r_final_stages_bytecode, false); + } + break; + } + } + } + // Re-sign patched stages. + for (KeyValue<ShaderStage, Vector<uint8_t>> &E : r_final_stages_bytecode) { + ShaderStage stage = E.key; + if ((stages_re_sign_mask & (1 << stage))) { + Vector<uint8_t> &bytecode = E.value; + bool sign_ok = _shader_sign_dxil_bytecode(stage, bytecode); + ERR_FAIL_COND_V(!sign_ok, false); + } + } + + return true; +} + +bool RenderingDeviceDriverD3D12::_shader_sign_dxil_bytecode(ShaderStage p_stage, Vector<uint8_t> &r_dxil_blob) { + dxil_validator *validator = _get_dxil_validator_for_current_thread(); + + char *err = nullptr; + bool res = dxil_validate_module(validator, r_dxil_blob.ptrw(), r_dxil_blob.size(), &err); + if (!res) { + if (err) { + ERR_FAIL_COND_V_MSG(!res, false, "Shader signing invocation at stage " + String(SHADER_STAGE_NAMES[p_stage]) + " failed:\n" + String(err)); + } else { + ERR_FAIL_COND_V_MSG(!res, false, "Shader signing invocation at stage " + String(SHADER_STAGE_NAMES[p_stage]) + " failed."); + } + } + + return true; +} + +String RenderingDeviceDriverD3D12::shader_get_binary_cache_key() { + return "D3D12-SV" + uitos(ShaderBinary::VERSION) + "-" + itos(context->get_shader_capabilities().shader_model); +} + +Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(VectorView<ShaderStageSPIRVData> p_spirv, const String &p_shader_name) { + ShaderReflection shader_refl; + if (_reflect_spirv(p_spirv, shader_refl) != OK) { + return Vector<uint8_t>(); + } + + // Collect reflection data into binary data. + ShaderBinary::Data binary_data; + Vector<Vector<ShaderBinary::DataBinding>> sets_bindings; + Vector<ShaderBinary::SpecializationConstant> specialization_constants; + { + binary_data.vertex_input_mask = shader_refl.vertex_input_mask; + binary_data.fragment_output_mask = shader_refl.fragment_output_mask; + binary_data.specialization_constants_count = shader_refl.specialization_constants.size(); + binary_data.is_compute = shader_refl.is_compute; + binary_data.compute_local_size[0] = shader_refl.compute_local_size[0]; + binary_data.compute_local_size[1] = shader_refl.compute_local_size[1]; + binary_data.compute_local_size[2] = shader_refl.compute_local_size[2]; + binary_data.set_count = shader_refl.uniform_sets.size(); + binary_data.push_constant_size = shader_refl.push_constant_size; + binary_data.nir_runtime_data_root_param_idx = UINT32_MAX; + binary_data.stage_count = p_spirv.size(); + + for (const Vector<ShaderUniform> &spirv_set : shader_refl.uniform_sets) { + Vector<ShaderBinary::DataBinding> bindings; + for (const ShaderUniform &spirv_uniform : spirv_set) { + ShaderBinary::DataBinding binding; + binding.type = (uint32_t)spirv_uniform.type; + binding.binding = spirv_uniform.binding; + binding.stages = (uint32_t)spirv_uniform.stages; + binding.length = spirv_uniform.length; + binding.writable = (uint32_t)spirv_uniform.writable; + bindings.push_back(binding); + } + sets_bindings.push_back(bindings); + } + + for (const ShaderSpecializationConstant &spirv_sc : shader_refl.specialization_constants) { + ShaderBinary::SpecializationConstant spec_constant; + spec_constant.type = (uint32_t)spirv_sc.type; + spec_constant.constant_id = spirv_sc.constant_id; + spec_constant.int_value = spirv_sc.int_value; + spec_constant.stage_flags = spirv_sc.stages; + specialization_constants.push_back(spec_constant); + + binary_data.spirv_specialization_constants_ids_mask |= (1 << spirv_sc.constant_id); + } + } + + // Translate SPIR-V shaders to DXIL, and collect shader info from the new representation. + HashMap<ShaderStage, Vector<uint8_t>> dxil_blobs; + BitField<ShaderStage> stages_processed; + { + HashMap<int, nir_shader *> stages_nir_shaders; + + auto free_nir_shaders = [&]() { + for (KeyValue<int, nir_shader *> &E : stages_nir_shaders) { + ralloc_free(E.value); + } + stages_nir_shaders.clear(); + }; + + // This is based on spirv2dxil.c. May need updates when it changes. + // Also, this has to stay around until after linking. + nir_shader_compiler_options nir_options = *dxil_get_nir_compiler_options(); + nir_options.lower_base_vertex = false; + + dxil_spirv_runtime_conf dxil_runtime_conf = {}; + dxil_runtime_conf.runtime_data_cbv.register_space = RUNTIME_DATA_SPACE; + dxil_runtime_conf.runtime_data_cbv.base_shader_register = RUNTIME_DATA_REGISTER; + dxil_runtime_conf.push_constant_cbv.register_space = ROOT_CONSTANT_SPACE; + dxil_runtime_conf.push_constant_cbv.base_shader_register = ROOT_CONSTANT_REGISTER; + dxil_runtime_conf.zero_based_vertex_instance_id = true; + dxil_runtime_conf.zero_based_compute_workgroup_id = true; + dxil_runtime_conf.declared_read_only_images_as_srvs = true; + // Making this explicit to let maintainers know that in practice this didn't improve performance, + // probably because data generated by one shader and consumed by another one forces the resource + // to transition from UAV to SRV, and back, instead of being an UAV all the time. + // In case someone wants to try, care must be taken so in case of incompatible bindings across stages + // happen as a result, all the stages are re-translated. That can happen if, for instance, a stage only + // uses an allegedly writable resource only for reading but the next stage doesn't. + dxil_runtime_conf.inferred_read_only_images_as_srvs = false; + + // - Translate SPIR-V to NIR. + for (uint32_t i = 0; i < p_spirv.size(); i++) { + ShaderStage stage = (ShaderStage)p_spirv[i].shader_stage; + ShaderStage stage_flag = (ShaderStage)(1 << p_spirv[i].shader_stage); + + stages_processed.set_flag(stage_flag); + + { + const char *entry_point = "main"; + + static const gl_shader_stage SPIRV_TO_MESA_STAGES[SHADER_STAGE_MAX] = { + /* SHADER_STAGE_VERTEX */ MESA_SHADER_VERTEX, + /* SHADER_STAGE_FRAGMENT */ MESA_SHADER_FRAGMENT, + /* SHADER_STAGE_TESSELATION_CONTROL */ MESA_SHADER_TESS_CTRL, + /* SHADER_STAGE_TESSELATION_EVALUATION */ MESA_SHADER_TESS_EVAL, + /* SHADER_STAGE_COMPUTE */ MESA_SHADER_COMPUTE, + }; + + nir_shader *shader = spirv_to_nir( + (const uint32_t *)p_spirv[i].spirv.ptr(), + p_spirv[i].spirv.size() / sizeof(uint32_t), + nullptr, + 0, + SPIRV_TO_MESA_STAGES[stage], + entry_point, + dxil_spirv_nir_get_spirv_options(), &nir_options); + if (!shader) { + free_nir_shaders(); + ERR_FAIL_V_MSG(Vector<uint8_t>(), "Shader translation (step 1) at stage " + String(SHADER_STAGE_NAMES[stage]) + " failed."); + } + +#ifdef DEV_ENABLED + nir_validate_shader(shader, "Validate before feeding NIR to the DXIL compiler"); +#endif + + if (stage == SHADER_STAGE_VERTEX) { + dxil_runtime_conf.yz_flip.y_mask = 0xffff; + dxil_runtime_conf.yz_flip.mode = DXIL_SPIRV_Y_FLIP_UNCONDITIONAL; + } else { + dxil_runtime_conf.yz_flip.y_mask = 0; + dxil_runtime_conf.yz_flip.mode = DXIL_SPIRV_YZ_FLIP_NONE; + } + + // This is based on spirv2dxil.c. May need updates when it changes. + dxil_spirv_nir_prep(shader); + bool requires_runtime_data = {}; + dxil_spirv_nir_passes(shader, &dxil_runtime_conf, &requires_runtime_data); + + stages_nir_shaders[stage] = shader; + } + } + + // - Link NIR shaders. + for (int i = SHADER_STAGE_MAX - 1; i >= 0; i--) { + if (!stages_nir_shaders.has(i)) { + continue; + } + nir_shader *shader = stages_nir_shaders[i]; + nir_shader *prev_shader = nullptr; + for (int j = i - 1; j >= 0; j--) { + if (stages_nir_shaders.has(j)) { + prev_shader = stages_nir_shaders[j]; + break; + } + } + if (prev_shader) { + bool requires_runtime_data = {}; + dxil_spirv_nir_link(shader, prev_shader, &dxil_runtime_conf, &requires_runtime_data); + } + } + + // - Translate NIR to DXIL. + for (uint32_t i = 0; i < p_spirv.size(); i++) { + ShaderStage stage = (ShaderStage)p_spirv[i].shader_stage; + + struct ShaderData { + ShaderStage stage; + ShaderBinary::Data &binary_data; + Vector<Vector<ShaderBinary::DataBinding>> &sets_bindings; + Vector<ShaderBinary::SpecializationConstant> &specialization_constants; + } shader_data{ stage, binary_data, sets_bindings, specialization_constants }; + + GodotNirCallbacks godot_nir_callbacks = {}; + godot_nir_callbacks.data = &shader_data; + + godot_nir_callbacks.report_resource = [](uint32_t p_register, uint32_t p_space, uint32_t p_dxil_type, void *p_data) { + ShaderData &shader_data_in = *(ShaderData *)p_data; + + // Types based on Mesa's dxil_container.h. + static const uint32_t DXIL_RES_SAMPLER = 1; + static const ResourceClass DXIL_TYPE_TO_CLASS[] = { + /* DXIL_RES_INVALID */ RES_CLASS_INVALID, + /* DXIL_RES_SAMPLER */ RES_CLASS_INVALID, // Handling sampler as a flag. + /* DXIL_RES_CBV */ RES_CLASS_CBV, + /* DXIL_RES_SRV_TYPED */ RES_CLASS_SRV, + /* DXIL_RES_SRV_RAW */ RES_CLASS_SRV, + /* DXIL_RES_SRV_STRUCTURED */ RES_CLASS_SRV, + /* DXIL_RES_UAV_TYPED */ RES_CLASS_UAV, + /* DXIL_RES_UAV_RAW */ RES_CLASS_UAV, + /* DXIL_RES_UAV_STRUCTURED */ RES_CLASS_UAV, + /* DXIL_RES_UAV_STRUCTURED_WITH_COUNTER */ RES_CLASS_INVALID, + }; + DEV_ASSERT(p_dxil_type < ARRAY_SIZE(DXIL_TYPE_TO_CLASS)); + ResourceClass res_class = DXIL_TYPE_TO_CLASS[p_dxil_type]; + + if (p_register == ROOT_CONSTANT_REGISTER && p_space == ROOT_CONSTANT_SPACE) { + DEV_ASSERT(res_class == RES_CLASS_CBV); + shader_data_in.binary_data.dxil_push_constant_stages |= (1 << shader_data_in.stage); + } else if (p_register == RUNTIME_DATA_REGISTER && p_space == RUNTIME_DATA_SPACE) { + DEV_ASSERT(res_class == RES_CLASS_CBV); + shader_data_in.binary_data.nir_runtime_data_root_param_idx = 1; // Temporary, to be determined later. + } else { + DEV_ASSERT(p_space == 0); + + uint32_t set = p_register / GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER; + uint32_t binding = (p_register % GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER) / GODOT_NIR_BINDING_MULTIPLIER; + + DEV_ASSERT(set < (uint32_t)shader_data_in.sets_bindings.size()); + bool found = false; + for (int j = 0; j < shader_data_in.sets_bindings[set].size(); j++) { + if (shader_data_in.sets_bindings[set][j].binding != binding) { + continue; + } + + ShaderBinary::DataBinding &binding_info = shader_data_in.sets_bindings.write[set].write[j]; + + binding_info.dxil_stages |= (1 << shader_data_in.stage); + + if (res_class != RES_CLASS_INVALID) { + DEV_ASSERT(binding_info.res_class == (uint32_t)RES_CLASS_INVALID || binding_info.res_class == (uint32_t)res_class); + binding_info.res_class = res_class; + } else if (p_dxil_type == DXIL_RES_SAMPLER) { + binding_info.has_sampler = (uint32_t) true; + } else { + CRASH_NOW(); + } + + found = true; + break; + } + DEV_ASSERT(found); + } + }; + + godot_nir_callbacks.report_sc_bit_offset_fn = [](uint32_t p_sc_id, uint64_t p_bit_offset, void *p_data) { + ShaderData &shader_data_in = *(ShaderData *)p_data; + + bool found = false; + for (int j = 0; j < shader_data_in.specialization_constants.size(); j++) { + if (shader_data_in.specialization_constants[j].constant_id != p_sc_id) { + continue; + } + + uint32_t offset_idx = SHADER_STAGES_BIT_OFFSET_INDICES[shader_data_in.stage]; + DEV_ASSERT(shader_data_in.specialization_constants.write[j].stages_bit_offsets[offset_idx] == 0); + shader_data_in.specialization_constants.write[j].stages_bit_offsets[offset_idx] = p_bit_offset; + + found = true; + break; + } + DEV_ASSERT(found); + }; + + godot_nir_callbacks.report_bitcode_bit_offset_fn = [](uint64_t p_bit_offset, void *p_data) { + DEV_ASSERT(p_bit_offset % 8 == 0); + ShaderData &shader_data_in = *(ShaderData *)p_data; + uint32_t offset_idx = SHADER_STAGES_BIT_OFFSET_INDICES[shader_data_in.stage]; + for (int j = 0; j < shader_data_in.specialization_constants.size(); j++) { + if (shader_data_in.specialization_constants.write[j].stages_bit_offsets[offset_idx] == 0) { + // This SC has been optimized out from this stage. + continue; + } + shader_data_in.specialization_constants.write[j].stages_bit_offsets[offset_idx] += p_bit_offset; + } + }; + + auto shader_model_d3d_to_dxil = [](D3D_SHADER_MODEL p_d3d_shader_model) -> dxil_shader_model { + static_assert(SHADER_MODEL_6_0 == 0x60000); + static_assert(SHADER_MODEL_6_3 == 0x60003); + static_assert(D3D_SHADER_MODEL_6_0 == 0x60); + static_assert(D3D_SHADER_MODEL_6_3 == 0x63); + return (dxil_shader_model)((p_d3d_shader_model >> 4) * 0x10000 + (p_d3d_shader_model & 0xf)); + }; + + 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.validator_version_max = dxil_get_validator_version(_get_dxil_validator_for_current_thread()); + nir_to_dxil_options.godot_nir_callbacks = &godot_nir_callbacks; + + dxil_logger logger = {}; + logger.log = [](void *p_priv, const char *p_msg) { +#ifdef DEBUG_ENABLED + print_verbose(p_msg); +#endif + }; + + blob dxil_blob = {}; + bool ok = nir_to_dxil(stages_nir_shaders[stage], &nir_to_dxil_options, &logger, &dxil_blob); + ralloc_free(stages_nir_shaders[stage]); + stages_nir_shaders.erase(stage); + if (!ok) { + free_nir_shaders(); + ERR_FAIL_V_MSG(Vector<uint8_t>(), "Shader translation at stage " + String(SHADER_STAGE_NAMES[stage]) + " failed."); + } + + Vector<uint8_t> blob_copy; + blob_copy.resize(dxil_blob.size); + memcpy(blob_copy.ptrw(), dxil_blob.data, dxil_blob.size); + blob_finish(&dxil_blob); + dxil_blobs.insert(stage, blob_copy); + } + } + +#if 0 + if (dxil_blobs.has(SHADER_STAGE_FRAGMENT)) { + Ref<FileAccess> f = FileAccess::open("res://1.dxil", FileAccess::WRITE); + f->store_buffer(dxil_blobs[SHADER_STAGE_FRAGMENT].ptr(), dxil_blobs[SHADER_STAGE_FRAGMENT].size()); + } +#endif + + // Patch with default values of specialization constants. + if (specialization_constants.size()) { + for (const ShaderBinary::SpecializationConstant &sc : specialization_constants) { + _shader_patch_dxil_specialization_constant((PipelineSpecializationConstantType)sc.type, &sc.int_value, sc.stages_bit_offsets, dxil_blobs, true); + } +#if 0 + if (dxil_blobs.has(SHADER_STAGE_FRAGMENT)) { + Ref<FileAccess> f = FileAccess::open("res://2.dxil", FileAccess::WRITE); + f->store_buffer(dxil_blobs[SHADER_STAGE_FRAGMENT].ptr(), dxil_blobs[SHADER_STAGE_FRAGMENT].size()); + } +#endif + } + + // Sign. + for (KeyValue<ShaderStage, Vector<uint8_t>> &E : dxil_blobs) { + ShaderStage stage = E.key; + Vector<uint8_t> &dxil_blob = E.value; + bool sign_ok = _shader_sign_dxil_bytecode(stage, dxil_blob); + ERR_FAIL_COND_V(!sign_ok, Vector<uint8_t>()); + } + + // Build the root signature. + ComPtr<ID3DBlob> root_sig_blob; + { + auto stages_to_d3d12_visibility = [](uint32_t p_stages_mask) -> D3D12_SHADER_VISIBILITY { + switch (p_stages_mask) { + case SHADER_STAGE_VERTEX_BIT: { + return D3D12_SHADER_VISIBILITY_VERTEX; + } + case SHADER_STAGE_FRAGMENT_BIT: { + return D3D12_SHADER_VISIBILITY_PIXEL; + } + default: { + return D3D12_SHADER_VISIBILITY_ALL; + } + } + }; + + LocalVector<D3D12_ROOT_PARAMETER1> root_params; + + // Root (push) constants. + if (binary_data.dxil_push_constant_stages) { + CD3DX12_ROOT_PARAMETER1 push_constant; + push_constant.InitAsConstants( + binary_data.push_constant_size / sizeof(uint32_t), + ROOT_CONSTANT_REGISTER, + ROOT_CONSTANT_SPACE, + stages_to_d3d12_visibility(binary_data.dxil_push_constant_stages)); + root_params.push_back(push_constant); + } + + // NIR-DXIL runtime data. + if (binary_data.nir_runtime_data_root_param_idx == 1) { // Set above to 1 when discovering runtime data is needed. + DEV_ASSERT(!binary_data.is_compute); // Could be supported if needed, but it's pointless as of now. + binary_data.nir_runtime_data_root_param_idx = root_params.size(); + CD3DX12_ROOT_PARAMETER1 nir_runtime_data; + nir_runtime_data.InitAsConstants( + sizeof(dxil_spirv_vertex_runtime_data) / sizeof(uint32_t), + RUNTIME_DATA_REGISTER, + RUNTIME_DATA_SPACE, + D3D12_SHADER_VISIBILITY_VERTEX); + root_params.push_back(nir_runtime_data); + } + + // Descriptor tables (up to two per uniform set, for resources and/or samplers). + + // These have to stay around until serialization! + struct TraceableDescriptorTable { + uint32_t stages_mask = {}; + Vector<D3D12_DESCRIPTOR_RANGE1> ranges; + Vector<ShaderBinary::DataBinding::RootSignatureLocation *> root_sig_locations; + }; + Vector<TraceableDescriptorTable> resource_tables_maps; + Vector<TraceableDescriptorTable> sampler_tables_maps; + + for (int set = 0; set < sets_bindings.size(); set++) { + bool first_resource_in_set = true; + bool first_sampler_in_set = true; + sets_bindings.write[set].sort(); + for (int i = 0; i < sets_bindings[set].size(); i++) { + const ShaderBinary::DataBinding &binding = sets_bindings[set][i]; + + bool really_used = binding.dxil_stages != 0; +#ifdef DEV_ENABLED + bool anybody_home = (ResourceClass)binding.res_class != RES_CLASS_INVALID || binding.has_sampler; + DEV_ASSERT(anybody_home == really_used); +#endif + if (!really_used) { + continue; // Existed in SPIR-V; went away in DXIL. + } + + auto insert_range = [](D3D12_DESCRIPTOR_RANGE_TYPE p_range_type, + uint32_t p_num_descriptors, + uint32_t p_dxil_register, + uint32_t p_dxil_stages_mask, + ShaderBinary::DataBinding::RootSignatureLocation(&p_root_sig_locations), + Vector<TraceableDescriptorTable> &r_tables, + bool &r_first_in_set) { + if (r_first_in_set) { + r_tables.resize(r_tables.size() + 1); + r_first_in_set = false; + } + TraceableDescriptorTable &table = r_tables.write[r_tables.size() - 1]; + table.stages_mask |= p_dxil_stages_mask; + + CD3DX12_DESCRIPTOR_RANGE1 range; + // Due to the aliasing hack for SRV-UAV of different families, + // we can be causing an unintended change of data (sometimes the validation layers catch it). + D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE; + if (p_range_type == D3D12_DESCRIPTOR_RANGE_TYPE_SRV || p_range_type == D3D12_DESCRIPTOR_RANGE_TYPE_UAV) { + flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE; + } else if (p_range_type == D3D12_DESCRIPTOR_RANGE_TYPE_CBV) { + flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE; + } + range.Init(p_range_type, p_num_descriptors, p_dxil_register, 0, flags); + + table.ranges.push_back(range); + table.root_sig_locations.push_back(&p_root_sig_locations); + }; + + uint32_t num_descriptors = 1; + + D3D12_DESCRIPTOR_RANGE_TYPE resource_range_type = {}; + switch ((ResourceClass)binding.res_class) { + case RES_CLASS_INVALID: { + num_descriptors = binding.length; + DEV_ASSERT(binding.has_sampler); + } break; + case RES_CLASS_CBV: { + resource_range_type = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; + DEV_ASSERT(!binding.has_sampler); + } break; + case RES_CLASS_SRV: { + resource_range_type = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + num_descriptors = MAX(1u, binding.length); // An unbound R/O buffer is reflected as zero-size. + } break; + case RES_CLASS_UAV: { + resource_range_type = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + num_descriptors = MAX(1u, binding.length); // An unbound R/W buffer is reflected as zero-size. + DEV_ASSERT(!binding.has_sampler); + } break; + } + + uint32_t dxil_register = set * GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER + binding.binding * GODOT_NIR_BINDING_MULTIPLIER; + + if (binding.res_class != RES_CLASS_INVALID) { + insert_range( + resource_range_type, + num_descriptors, + dxil_register, + sets_bindings[set][i].dxil_stages, + sets_bindings.write[set].write[i].root_sig_locations[RS_LOC_TYPE_RESOURCE], + resource_tables_maps, + first_resource_in_set); + } + if (binding.has_sampler) { + insert_range( + D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, + num_descriptors, + dxil_register, + sets_bindings[set][i].dxil_stages, + sets_bindings.write[set].write[i].root_sig_locations[RS_LOC_TYPE_SAMPLER], + sampler_tables_maps, + first_sampler_in_set); + } + } + } + + auto make_descriptor_tables = [&root_params, &stages_to_d3d12_visibility](const Vector<TraceableDescriptorTable> &p_tables) { + for (const TraceableDescriptorTable &table : p_tables) { + D3D12_SHADER_VISIBILITY visibility = stages_to_d3d12_visibility(table.stages_mask); + DEV_ASSERT(table.ranges.size() == table.root_sig_locations.size()); + for (int i = 0; i < table.ranges.size(); i++) { + // By now we know very well which root signature location corresponds to the pointed uniform. + table.root_sig_locations[i]->root_param_idx = root_params.size(); + table.root_sig_locations[i]->range_idx = i; + } + + CD3DX12_ROOT_PARAMETER1 root_table; + root_table.InitAsDescriptorTable(table.ranges.size(), table.ranges.ptr(), visibility); + root_params.push_back(root_table); + } + }; + + make_descriptor_tables(resource_tables_maps); + make_descriptor_tables(sampler_tables_maps); + + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC root_sig_desc = {}; + D3D12_ROOT_SIGNATURE_FLAGS root_sig_flags = + D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | + D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS | + D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS | + D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS | + D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS; + if (!stages_processed.has_flag(SHADER_STAGE_VERTEX_BIT)) { + root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS; + } + if (!stages_processed.has_flag(SHADER_STAGE_FRAGMENT_BIT)) { + root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS; + } + if (binary_data.vertex_input_mask) { + root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + } + root_sig_desc.Init_1_1(root_params.size(), root_params.ptr(), 0, nullptr, root_sig_flags); + + ComPtr<ID3DBlob> error_blob; + HRESULT res = D3DX12SerializeVersionedRootSignature(&root_sig_desc, D3D_ROOT_SIGNATURE_VERSION_1_1, root_sig_blob.GetAddressOf(), error_blob.GetAddressOf()); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), Vector<uint8_t>(), + "Serialization of root signature failed with error " + vformat("0x%08ux", (uint64_t)res) + " and the following message:\n" + String((char *)error_blob->GetBufferPointer(), error_blob->GetBufferSize())); + + binary_data.root_signature_crc = crc32(0, nullptr, 0); + binary_data.root_signature_crc = crc32(binary_data.root_signature_crc, (const Bytef *)root_sig_blob->GetBufferPointer(), root_sig_blob->GetBufferSize()); + } + + Vector<Vector<uint8_t>> compressed_stages; + Vector<uint32_t> zstd_size; + + uint32_t stages_binary_size = 0; + + for (uint32_t i = 0; i < p_spirv.size(); i++) { + Vector<uint8_t> zstd; + Vector<uint8_t> &dxil_blob = dxil_blobs[p_spirv[i].shader_stage]; + zstd.resize(Compression::get_max_compressed_buffer_size(dxil_blob.size(), Compression::MODE_ZSTD)); + int dst_size = Compression::compress(zstd.ptrw(), dxil_blob.ptr(), dxil_blob.size(), Compression::MODE_ZSTD); + + zstd_size.push_back(dst_size); + zstd.resize(dst_size); + compressed_stages.push_back(zstd); + + uint32_t s = compressed_stages[i].size(); + if (s % 4 != 0) { + s += 4 - (s % 4); + } + stages_binary_size += s; + } + + CharString shader_name_utf = p_shader_name.utf8(); + + binary_data.shader_name_len = shader_name_utf.length(); + + uint32_t total_size = sizeof(uint32_t) * 3; // Header + version + main datasize;. + total_size += sizeof(ShaderBinary::Data); + + total_size += binary_data.shader_name_len; + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. + total_size += 4 - (binary_data.shader_name_len % 4); + } + + for (int i = 0; i < sets_bindings.size(); i++) { + total_size += sizeof(uint32_t); + total_size += sets_bindings[i].size() * sizeof(ShaderBinary::DataBinding); + } + + total_size += sizeof(ShaderBinary::SpecializationConstant) * specialization_constants.size(); + + total_size += compressed_stages.size() * sizeof(uint32_t) * 3; // Sizes. + total_size += stages_binary_size; + + binary_data.root_signature_len = root_sig_blob->GetBufferSize(); + total_size += binary_data.root_signature_len; + + Vector<uint8_t> ret; + ret.resize(total_size); + { + uint32_t offset = 0; + uint8_t *binptr = ret.ptrw(); + binptr[0] = 'G'; + binptr[1] = 'S'; + binptr[2] = 'B'; + binptr[3] = 'D'; // Godot shader binary data. + offset += 4; + encode_uint32(ShaderBinary::VERSION, binptr + offset); + offset += sizeof(uint32_t); + encode_uint32(sizeof(ShaderBinary::Data), binptr + offset); + offset += sizeof(uint32_t); + memcpy(binptr + offset, &binary_data, sizeof(ShaderBinary::Data)); + offset += sizeof(ShaderBinary::Data); + + if (binary_data.shader_name_len > 0) { + memcpy(binptr + offset, shader_name_utf.ptr(), binary_data.shader_name_len); + offset += binary_data.shader_name_len; + + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. + offset += 4 - (binary_data.shader_name_len % 4); + } + } + + for (int i = 0; i < sets_bindings.size(); i++) { + int count = sets_bindings[i].size(); + encode_uint32(count, binptr + offset); + offset += sizeof(uint32_t); + if (count > 0) { + memcpy(binptr + offset, sets_bindings[i].ptr(), sizeof(ShaderBinary::DataBinding) * count); + offset += sizeof(ShaderBinary::DataBinding) * count; + } + } + + if (specialization_constants.size()) { + memcpy(binptr + offset, specialization_constants.ptr(), sizeof(ShaderBinary::SpecializationConstant) * specialization_constants.size()); + offset += sizeof(ShaderBinary::SpecializationConstant) * specialization_constants.size(); + } + + for (int i = 0; i < compressed_stages.size(); i++) { + encode_uint32(p_spirv[i].shader_stage, binptr + offset); + offset += sizeof(uint32_t); + encode_uint32(dxil_blobs[p_spirv[i].shader_stage].size(), binptr + offset); + offset += sizeof(uint32_t); + encode_uint32(zstd_size[i], binptr + offset); + offset += sizeof(uint32_t); + memcpy(binptr + offset, compressed_stages[i].ptr(), compressed_stages[i].size()); + + uint32_t s = compressed_stages[i].size(); + + if (s % 4 != 0) { + s += 4 - (s % 4); + } + + offset += s; + } + + memcpy(binptr + offset, root_sig_blob->GetBufferPointer(), root_sig_blob->GetBufferSize()); + offset += root_sig_blob->GetBufferSize(); + + ERR_FAIL_COND_V(offset != (uint32_t)ret.size(), Vector<uint8_t>()); + } + + return ret; +} + +RDD::ShaderID RenderingDeviceDriverD3D12::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name) { + r_shader_desc = {}; // Driver-agnostic. + ShaderInfo shader_info_in; // Driver-specific. + + const uint8_t *binptr = p_shader_binary.ptr(); + uint32_t binsize = p_shader_binary.size(); + + uint32_t read_offset = 0; + + // Consistency check. + ERR_FAIL_COND_V(binsize < sizeof(uint32_t) * 3 + sizeof(ShaderBinary::Data), ShaderID()); + ERR_FAIL_COND_V(binptr[0] != 'G' || binptr[1] != 'S' || binptr[2] != 'B' || binptr[3] != 'D', ShaderID()); + + uint32_t bin_version = decode_uint32(binptr + 4); + ERR_FAIL_COND_V(bin_version != ShaderBinary::VERSION, ShaderID()); + + uint32_t bin_data_size = decode_uint32(binptr + 8); + + const ShaderBinary::Data &binary_data = *(reinterpret_cast<const ShaderBinary::Data *>(binptr + 12)); + + r_shader_desc.push_constant_size = binary_data.push_constant_size; + shader_info_in.dxil_push_constant_size = binary_data.dxil_push_constant_stages ? binary_data.push_constant_size : 0; + shader_info_in.nir_runtime_data_root_param_idx = binary_data.nir_runtime_data_root_param_idx; + + r_shader_desc.vertex_input_mask = binary_data.vertex_input_mask; + r_shader_desc.fragment_output_mask = binary_data.fragment_output_mask; + + r_shader_desc.is_compute = binary_data.is_compute; + shader_info_in.is_compute = binary_data.is_compute; + r_shader_desc.compute_local_size[0] = binary_data.compute_local_size[0]; + r_shader_desc.compute_local_size[1] = binary_data.compute_local_size[1]; + r_shader_desc.compute_local_size[2] = binary_data.compute_local_size[2]; + + read_offset += sizeof(uint32_t) * 3 + bin_data_size; + + if (binary_data.shader_name_len) { + r_name.parse_utf8((const char *)(binptr + read_offset), binary_data.shader_name_len); + read_offset += binary_data.shader_name_len; + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. + read_offset += 4 - (binary_data.shader_name_len % 4); + } + } + + r_shader_desc.uniform_sets.resize(binary_data.set_count); + shader_info_in.sets.resize(binary_data.set_count); + + for (uint32_t i = 0; i < binary_data.set_count; i++) { + ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) >= binsize, ShaderID()); + uint32_t set_count = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + const ShaderBinary::DataBinding *set_ptr = reinterpret_cast<const ShaderBinary::DataBinding *>(binptr + read_offset); + uint32_t set_size = set_count * sizeof(ShaderBinary::DataBinding); + ERR_FAIL_COND_V(read_offset + set_size >= binsize, ShaderID()); + + shader_info_in.sets[i].bindings.reserve(set_count); + + for (uint32_t j = 0; j < set_count; j++) { + ShaderUniform info; + info.type = UniformType(set_ptr[j].type); + info.writable = set_ptr[j].writable; + info.length = set_ptr[j].length; + info.binding = set_ptr[j].binding; + + ShaderInfo::UniformBindingInfo binding; + binding.stages = set_ptr[j].dxil_stages; + binding.res_class = (ResourceClass)set_ptr[j].res_class; + binding.type = info.type; + binding.length = info.length; +#ifdef DEV_ENABLED + binding.writable = set_ptr[j].writable; +#endif + static_assert(sizeof(ShaderInfo::UniformBindingInfo::root_sig_locations) == sizeof(ShaderBinary::DataBinding::root_sig_locations)); + memcpy((void *)&binding.root_sig_locations, (void *)&set_ptr[j].root_sig_locations, sizeof(ShaderInfo::UniformBindingInfo::root_sig_locations)); + + if (binding.root_sig_locations.resource.root_param_idx != UINT32_MAX) { + shader_info_in.sets[i].num_root_params.resources++; + } + if (binding.root_sig_locations.sampler.root_param_idx != UINT32_MAX) { + shader_info_in.sets[i].num_root_params.samplers++; + } + + r_shader_desc.uniform_sets.write[i].push_back(info); + shader_info_in.sets[i].bindings.push_back(binding); + } + + read_offset += set_size; + } + + ERR_FAIL_COND_V(read_offset + binary_data.specialization_constants_count * sizeof(ShaderBinary::SpecializationConstant) >= binsize, ShaderID()); + + r_shader_desc.specialization_constants.resize(binary_data.specialization_constants_count); + shader_info_in.specialization_constants.resize(binary_data.specialization_constants_count); + for (uint32_t i = 0; i < binary_data.specialization_constants_count; i++) { + const ShaderBinary::SpecializationConstant &src_sc = *(reinterpret_cast<const ShaderBinary::SpecializationConstant *>(binptr + read_offset)); + ShaderSpecializationConstant sc; + sc.type = PipelineSpecializationConstantType(src_sc.type); + sc.constant_id = src_sc.constant_id; + sc.int_value = src_sc.int_value; + sc.stages = src_sc.stage_flags; + r_shader_desc.specialization_constants.write[i] = sc; + + ShaderInfo::SpecializationConstant ssc; + ssc.constant_id = src_sc.constant_id; + ssc.int_value = src_sc.int_value; + memcpy(ssc.stages_bit_offsets, src_sc.stages_bit_offsets, sizeof(ssc.stages_bit_offsets)); + shader_info_in.specialization_constants[i] = ssc; + + read_offset += sizeof(ShaderBinary::SpecializationConstant); + } + shader_info_in.spirv_specialization_constants_ids_mask = binary_data.spirv_specialization_constants_ids_mask; + + for (uint32_t i = 0; i < binary_data.stage_count; i++) { + ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) * 3 >= binsize, ShaderID()); + uint32_t stage = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + uint32_t dxil_size = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + uint32_t zstd_size = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + + // Decompress. + Vector<uint8_t> dxil; + dxil.resize(dxil_size); + int dec_dxil_size = Compression::decompress(dxil.ptrw(), dxil.size(), binptr + read_offset, zstd_size, Compression::MODE_ZSTD); + ERR_FAIL_COND_V(dec_dxil_size != (int32_t)dxil_size, ShaderID()); + shader_info_in.stages_bytecode[ShaderStage(stage)] = dxil; + + if (zstd_size % 4 != 0) { + zstd_size += 4 - (zstd_size % 4); + } + + ERR_FAIL_COND_V(read_offset + zstd_size > binsize, ShaderID()); + + read_offset += zstd_size; + } + + const uint8_t *root_sig_data_ptr = binptr + read_offset; + + HRESULT res = D3D12CreateRootSignatureDeserializer(root_sig_data_ptr, binary_data.root_signature_len, IID_PPV_ARGS(shader_info_in.root_signature_deserializer.GetAddressOf())); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ShaderID(), "D3D12CreateRootSignatureDeserializer failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + read_offset += binary_data.root_signature_len; + + ERR_FAIL_COND_V(read_offset != binsize, ShaderID()); + + ComPtr<ID3D12RootSignature> root_signature; + res = device->CreateRootSignature(0, root_sig_data_ptr, binary_data.root_signature_len, IID_PPV_ARGS(shader_info_in.root_signature.GetAddressOf())); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ShaderID(), "CreateRootSignature failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + shader_info_in.root_signature_desc = shader_info_in.root_signature_deserializer->GetRootSignatureDesc(); + shader_info_in.root_signature_crc = binary_data.root_signature_crc; + + // Bookkeep. + + ShaderInfo *shader_info_ptr = VersatileResource::allocate<ShaderInfo>(resources_allocator); + *shader_info_ptr = shader_info_in; + return ShaderID(shader_info_ptr); +} + +uint32_t RenderingDeviceDriverD3D12::shader_get_layout_hash(ShaderID p_shader) { + const ShaderInfo *shader_info_in = (const ShaderInfo *)p_shader.id; + return shader_info_in->root_signature_crc; +} + +void RenderingDeviceDriverD3D12::shader_free(ShaderID p_shader) { + ShaderInfo *shader_info_in = (ShaderInfo *)p_shader.id; + VersatileResource::free(resources_allocator, shader_info_in); +} + +/*********************/ +/**** UNIFORM SET ****/ +/*********************/ + +static void _add_descriptor_count_for_uniform(RenderingDevice::UniformType p_type, uint32_t p_binding_length, bool p_dobule_srv_uav_ambiguous, uint32_t &r_num_resources, uint32_t &r_num_samplers, bool &r_srv_uav_ambiguity) { + r_srv_uav_ambiguity = false; + + // Some resource types can be SRV or UAV, depending on what NIR-DXIL decided for a specific shader variant. + // The goal is to generate both SRV and UAV for the descriptor sets' heaps and copy only the relevant one + // to the frame descriptor heap at binding time. + // [[SRV_UAV_AMBIGUITY]] + + switch (p_type) { + case RenderingDevice::UNIFORM_TYPE_SAMPLER: { + r_num_samplers += p_binding_length; + } break; + case RenderingDevice::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: + case RenderingDevice::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: { + r_num_resources += p_binding_length; + r_num_samplers += p_binding_length; + } break; + case RenderingDevice::UNIFORM_TYPE_UNIFORM_BUFFER: { + r_num_resources += 1; + } break; + case RenderingDevice::UNIFORM_TYPE_STORAGE_BUFFER: { + r_num_resources += p_dobule_srv_uav_ambiguous ? 2 : 1; + r_srv_uav_ambiguity = true; + } break; + case RenderingDevice::UNIFORM_TYPE_IMAGE: { + r_num_resources += p_binding_length * (p_dobule_srv_uav_ambiguous ? 2 : 1); + r_srv_uav_ambiguity = true; + } break; + default: { + r_num_resources += p_binding_length; + } + } +} + +RDD::UniformSetID RenderingDeviceDriverD3D12::uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index) { + // Pre-bookkeep. + UniformSetInfo *uniform_set_info = VersatileResource::allocate<UniformSetInfo>(resources_allocator); + + // Do a first pass to count resources and samplers. + uint32_t num_resource_descs = 0; + uint32_t num_sampler_descs = 0; + for (uint32_t i = 0; i < p_uniforms.size(); i++) { + const BoundUniform &uniform = p_uniforms[i]; + + // Since the uniform set may be created for a shader different than the one that will be actually bound, + // which may have a different set of uniforms optimized out, the stages mask we can check now is not reliable. + // Therefore, we can't make any assumptions here about descriptors that we may not need to create, + // pixel or vertex-only shader resource states, etc. + + bool srv_uav_ambiguity = false; + uint32_t binding_length = uniform.ids.size(); + if (uniform.type == UNIFORM_TYPE_SAMPLER_WITH_TEXTURE || uniform.type == UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER) { + binding_length /= 2; + } + _add_descriptor_count_for_uniform(uniform.type, binding_length, true, num_resource_descs, num_sampler_descs, srv_uav_ambiguity); + } +#ifdef DEV_ENABLED + uniform_set_info->resources_desc_info.reserve(num_resource_descs); +#endif + + if (num_resource_descs) { + Error err = uniform_set_info->desc_heaps.resources.allocate(device, 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); + if (err) { + VersatileResource::free(resources_allocator, uniform_set_info); + ERR_FAIL_V(UniformSetID()); + } + } + struct { + DescriptorsHeap::Walker resources; + DescriptorsHeap::Walker samplers; + } desc_heap_walkers; + desc_heap_walkers.resources = uniform_set_info->desc_heaps.resources.make_walker(); + desc_heap_walkers.samplers = uniform_set_info->desc_heaps.samplers.make_walker(); + + struct NeededState { + bool is_buffer = false; + uint64_t shader_uniform_idx_mask = 0; + D3D12_RESOURCE_STATES states = {}; + }; + HashMap<ResourceInfo *, NeededState> resource_states; + + for (uint32_t i = 0; i < p_uniforms.size(); i++) { + const BoundUniform &uniform = p_uniforms[i]; + +#ifdef DEV_ENABLED + const ShaderInfo *shader_info_in = (const ShaderInfo *)p_shader.id; + const ShaderInfo::UniformBindingInfo &shader_uniform = shader_info_in->sets[p_set_index].bindings[i]; + bool is_compute = shader_info_in->stages_bytecode.has(SHADER_STAGE_COMPUTE); + DEV_ASSERT(!(is_compute && (shader_uniform.stages & (SHADER_STAGE_VERTEX_BIT | SHADER_STAGE_FRAGMENT_BIT)))); + DEV_ASSERT(!(!is_compute && (shader_uniform.stages & SHADER_STAGE_COMPUTE_BIT))); +#endif + + switch (uniform.type) { + case UNIFORM_TYPE_SAMPLER: { + for (uint32_t j = 0; j < uniform.ids.size(); j++) { + const D3D12_SAMPLER_DESC &sampler_desc = samplers[uniform.ids[j].id]; + device->CreateSampler(&sampler_desc, desc_heap_walkers.samplers.get_curr_cpu_handle()); + desc_heap_walkers.samplers.advance(); + } + } break; + case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { + for (uint32_t j = 0; j < uniform.ids.size(); j += 2) { + const D3D12_SAMPLER_DESC &sampler_desc = samplers[uniform.ids[j].id]; + TextureInfo *texture_info = (TextureInfo *)uniform.ids[j + 1].id; + + device->CreateSampler(&sampler_desc, desc_heap_walkers.samplers.get_curr_cpu_handle()); + desc_heap_walkers.samplers.advance(); + device->CreateShaderResourceView(texture_info->resource, &texture_info->view_descs.srv, desc_heap_walkers.resources.get_curr_cpu_handle()); +#ifdef DEV_ENABLED + uniform_set_info->resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_SRV, texture_info->view_descs.srv.ViewDimension }); +#endif + desc_heap_walkers.resources.advance(); + + NeededState &ns = resource_states[texture_info]; + ns.shader_uniform_idx_mask |= ((uint64_t)1 << i); + ns.states |= D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE; + } + } break; + case UNIFORM_TYPE_TEXTURE: { + for (uint32_t j = 0; j < uniform.ids.size(); j++) { + TextureInfo *texture_info = (TextureInfo *)uniform.ids[j].id; + device->CreateShaderResourceView(texture_info->resource, &texture_info->view_descs.srv, desc_heap_walkers.resources.get_curr_cpu_handle()); +#ifdef DEV_ENABLED + uniform_set_info->resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_SRV, texture_info->view_descs.srv.ViewDimension }); +#endif + desc_heap_walkers.resources.advance(); + + NeededState &ns = resource_states[texture_info]; + ns.shader_uniform_idx_mask |= ((uint64_t)1 << i); + ns.states |= D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE; + } + } break; + case UNIFORM_TYPE_IMAGE: { + for (uint32_t j = 0; j < uniform.ids.size(); j++) { + TextureInfo *texture_info = (TextureInfo *)uniform.ids[j].id; + + NeededState &ns = resource_states[texture_info]; + ns.shader_uniform_idx_mask |= ((uint64_t)1 << i); + ns.states |= (D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + } + + // SRVs first. [[SRV_UAV_AMBIGUITY]] + for (uint32_t j = 0; j < uniform.ids.size(); j++) { + TextureInfo *texture_info = (TextureInfo *)uniform.ids[j].id; + + device->CreateShaderResourceView(texture_info->resource, &texture_info->view_descs.srv, desc_heap_walkers.resources.get_curr_cpu_handle()); +#ifdef DEV_ENABLED + uniform_set_info->resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_SRV, texture_info->view_descs.srv.ViewDimension }); +#endif + desc_heap_walkers.resources.advance(); + } + + // UAVs then. [[SRV_UAV_AMBIGUITY]] + for (uint32_t j = 0; j < uniform.ids.size(); j++) { + TextureInfo *texture_info = (TextureInfo *)uniform.ids[j].id; + + device->CreateUnorderedAccessView(texture_info->resource, nullptr, &texture_info->view_descs.uav, desc_heap_walkers.resources.get_curr_cpu_handle()); +#ifdef DEV_ENABLED + uniform_set_info->resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_UAV, {} }); +#endif + desc_heap_walkers.resources.advance(); + } + } break; + case UNIFORM_TYPE_TEXTURE_BUFFER: + case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: { + CRASH_NOW_MSG("Unimplemented!"); + } break; + case UNIFORM_TYPE_IMAGE_BUFFER: { + CRASH_NOW_MSG("Unimplemented!"); + } break; + case UNIFORM_TYPE_UNIFORM_BUFFER: { + BufferInfo *buf_info = (BufferInfo *)uniform.ids[0].id; + + D3D12_CONSTANT_BUFFER_VIEW_DESC cbv_desc = {}; + cbv_desc.BufferLocation = buf_info->resource->GetGPUVirtualAddress(); + cbv_desc.SizeInBytes = STEPIFY(buf_info->size, 256); + device->CreateConstantBufferView(&cbv_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); + desc_heap_walkers.resources.advance(); +#ifdef DEV_ENABLED + uniform_set_info->resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_CBV, {} }); +#endif + + NeededState &ns = resource_states[buf_info]; + ns.is_buffer = true; + ns.shader_uniform_idx_mask |= ((uint64_t)1 << i); + ns.states |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; + } break; + case UNIFORM_TYPE_STORAGE_BUFFER: { + BufferInfo *buf_info = (BufferInfo *)uniform.ids[0].id; + + // SRV first. [[SRV_UAV_AMBIGUITY]] + { + D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {}; + srv_desc.Format = DXGI_FORMAT_R32_TYPELESS; + srv_desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srv_desc.Buffer.FirstElement = 0; + srv_desc.Buffer.NumElements = (buf_info->size + 3) / 4; + srv_desc.Buffer.StructureByteStride = 0; + srv_desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; + device->CreateShaderResourceView(buf_info->resource, &srv_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); +#ifdef DEV_ENABLED + uniform_set_info->resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_SRV, srv_desc.ViewDimension }); +#endif + desc_heap_walkers.resources.advance(); + } + + // UAV then. [[SRV_UAV_AMBIGUITY]] + { + if (buf_info->flags.usable_as_uav) { + D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = {}; + uav_desc.Format = DXGI_FORMAT_R32_TYPELESS; + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uav_desc.Buffer.FirstElement = 0; + uav_desc.Buffer.NumElements = (buf_info->size + 3) / 4; + uav_desc.Buffer.StructureByteStride = 0; + uav_desc.Buffer.CounterOffsetInBytes = 0; + uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW; + device->CreateUnorderedAccessView(buf_info->resource, nullptr, &uav_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); +#ifdef DEV_ENABLED + uniform_set_info->resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_UAV, {} }); +#endif + } else { + // If can't transition to UAV, leave this one empty since it won't be + // used, and trying to create an UAV view would trigger a validation error. + } + + desc_heap_walkers.resources.advance(); + } + + NeededState &ns = resource_states[buf_info]; + ns.shader_uniform_idx_mask |= ((uint64_t)1 << i); + ns.is_buffer = true; + ns.states |= (D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + } break; + case UNIFORM_TYPE_INPUT_ATTACHMENT: { + for (uint32_t j = 0; j < uniform.ids.size(); j++) { + TextureInfo *texture_info = (TextureInfo *)uniform.ids[j].id; + + device->CreateShaderResourceView(texture_info->resource, &texture_info->view_descs.srv, desc_heap_walkers.resources.get_curr_cpu_handle()); +#ifdef DEV_ENABLED + uniform_set_info->resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_SRV, texture_info->view_descs.srv.ViewDimension }); +#endif + desc_heap_walkers.resources.advance(); + + NeededState &ns = resource_states[texture_info]; + ns.shader_uniform_idx_mask |= ((uint64_t)1 << i); + ns.states |= D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + } + } break; + default: { + DEV_ASSERT(false); + } + } + } + + DEV_ASSERT(desc_heap_walkers.resources.is_at_eof()); + DEV_ASSERT(desc_heap_walkers.samplers.is_at_eof()); + + { + uniform_set_info->resource_states.reserve(resource_states.size()); + uint32_t i = 0; + for (const KeyValue<ResourceInfo *, NeededState> &E : resource_states) { + UniformSetInfo::StateRequirement sr; + sr.resource = E.key; + sr.is_buffer = E.value.is_buffer; + sr.states = E.value.states; + sr.shader_uniform_idx_mask = E.value.shader_uniform_idx_mask; + uniform_set_info->resource_states.push_back(sr); + i++; + } + } + + return UniformSetID(uniform_set_info); +} + +void RenderingDeviceDriverD3D12::uniform_set_free(UniformSetID p_uniform_set) { + UniformSetInfo *uniform_set_info = (UniformSetInfo *)p_uniform_set.id; + VersatileResource::free(resources_allocator, uniform_set_info); +} + +// ----- COMMANDS ----- + +void RenderingDeviceDriverD3D12::command_uniform_set_prepare_for_use(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) { + const UniformSetInfo *uniform_set_info = (const UniformSetInfo *)p_uniform_set.id; + const ShaderInfo *shader_info_in = (const ShaderInfo *)p_shader.id; + const ShaderInfo::UniformSet &shader_set = shader_info_in->sets[p_set_index]; + + for (const UniformSetInfo::StateRequirement &sr : uniform_set_info->resource_states) { +#ifdef DEV_ENABLED + { + uint32_t stages = 0; + D3D12_RESOURCE_STATES wanted_state = {}; + bool writable = false; + // Doing the full loop for debugging since the real one below may break early, + // but we want an exhaustive check + uint64_t inv_uniforms_mask = ~sr.shader_uniform_idx_mask; // Inverting the mask saves operations. + for (uint8_t bit = 0; inv_uniforms_mask != UINT64_MAX; bit++) { + uint64_t bit_mask = ((uint64_t)1 << bit); + if (likely((inv_uniforms_mask & bit_mask))) { + continue; + } + inv_uniforms_mask |= bit_mask; + + const ShaderInfo::UniformBindingInfo &binding = shader_set.bindings[bit]; + if (unlikely(!binding.stages)) { + continue; + } + + D3D12_RESOURCE_STATES required_states = sr.states; + + // Resolve a case of SRV/UAV ambiguity now. [[SRV_UAV_AMBIGUITY]] + if ((required_states & D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE) && (required_states & D3D12_RESOURCE_STATE_UNORDERED_ACCESS)) { + if (binding.res_class == RES_CLASS_SRV) { + required_states &= ~D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + } else { + required_states = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + } + } + + if (stages) { // Second occurrence at least? + CRASH_COND_MSG(binding.writable != writable, "A resource is used in the same uniform set both as R/O and R/W. That's not supported and shouldn't happen."); + CRASH_COND_MSG(required_states != wanted_state, "A resource is used in the same uniform set with different resource states. The code needs to be enhanced to support that."); + } else { + wanted_state = required_states; + stages |= binding.stages; + writable = binding.writable; + } + + DEV_ASSERT((wanted_state == D3D12_RESOURCE_STATE_UNORDERED_ACCESS) == (bool)(wanted_state & D3D12_RESOURCE_STATE_UNORDERED_ACCESS)); + + if (wanted_state == D3D12_RESOURCE_STATE_UNORDERED_ACCESS || wanted_state == D3D12_RESOURCE_STATE_RENDER_TARGET) { + if (!sr.is_buffer) { + TextureInfo *tex_info = (TextureInfo *)sr.resource; + CRASH_COND_MSG(tex_info->resource != tex_info->main_texture, "The texture format used for UAV or RTV must be the main one."); + } + } + } + } +#endif + + // We may have assumed D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE for a resource, + // because at uniform set creation time we couldn't know for sure which stages + // it would be used in (due to the fact that a set can be created against a different, + // albeit compatible, shader, which may make a different usage in the end). + // However, now we know and can exclude up to one unneeded states. + + // TODO: If subresources involved already in the needed states, or scheduled for it, + // maybe it's more optimal not to do anything here + + uint32_t stages = 0; + D3D12_RESOURCE_STATES wanted_state = {}; + uint64_t inv_uniforms_mask = ~sr.shader_uniform_idx_mask; // Inverting the mask saves operations. + for (uint8_t bit = 0; inv_uniforms_mask != UINT64_MAX; bit++) { + uint64_t bit_mask = ((uint64_t)1 << bit); + if (likely((inv_uniforms_mask & bit_mask))) { + continue; + } + inv_uniforms_mask |= bit_mask; + + const ShaderInfo::UniformBindingInfo &binding = shader_set.bindings[bit]; + if (unlikely(!binding.stages)) { + continue; + } + + if (!stages) { + D3D12_RESOURCE_STATES required_states = sr.states; + + // Resolve a case of SRV/UAV ambiguity now. [[SRV_UAV_AMBIGUITY]] + if ((required_states & D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE) && (required_states & D3D12_RESOURCE_STATE_UNORDERED_ACCESS)) { + if (binding.res_class == RES_CLASS_SRV) { + required_states &= ~D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + } else { + required_states = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + } + } + + wanted_state = required_states; + + if (!(wanted_state & D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE)) { + // By now, we already know the resource is used, and with no PS/NON_PS disjuntive; no need to check further. + break; + } + } + + stages |= binding.stages; + + if (stages == (SHADER_STAGE_VERTEX_BIT | SHADER_STAGE_FRAGMENT_BIT) || stages == SHADER_STAGE_COMPUTE_BIT) { + // By now, we already know the resource is used, and as both PS/NON_PS; no need to check further. + break; + } + } + + if (likely(wanted_state)) { + if ((wanted_state & D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE)) { + if (stages == SHADER_STAGE_VERTEX_BIT || stages == SHADER_STAGE_COMPUTE_BIT) { + D3D12_RESOURCE_STATES unneeded_states = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + wanted_state &= ~unneeded_states; + } else if (stages == SHADER_STAGE_FRAGMENT_BIT) { + D3D12_RESOURCE_STATES unneeded_states = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + wanted_state &= ~unneeded_states; + } + } + + if (likely(wanted_state)) { + if (sr.is_buffer) { + _resource_transition_batch(sr.resource, 0, 1, wanted_state); + } else { + TextureInfo *tex_info = (TextureInfo *)sr.resource; + uint32_t planes = 1; + if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { + planes = format_get_plane_count(tex_info->format); + } + for (uint32_t i = 0; i < tex_info->layers; i++) { + for (uint32_t j = 0; j < tex_info->mipmaps; j++) { + uint32_t subresource = D3D12CalcSubresource(tex_info->base_mip + j, tex_info->base_layer + i, 0, tex_info->desc.MipLevels, tex_info->desc.ArraySize()); + _resource_transition_batch(tex_info, subresource, planes, wanted_state, tex_info->main_texture); + } + } + } + } + } + } + + if (p_set_index == shader_info_in->sets.size() - 1) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + } +} + +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); + } + + UniformSetInfo *uniform_set_info = (UniformSetInfo *)p_uniform_set.id; + const ShaderInfo *shader_info_in = (const ShaderInfo *)p_shader.id; + const ShaderInfo::UniformSet &shader_set = shader_info_in->sets[p_set_index]; + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + + using SetRootDescriptorTableFn = void (STDMETHODCALLTYPE ID3D12GraphicsCommandList::*)(UINT, D3D12_GPU_DESCRIPTOR_HANDLE); + SetRootDescriptorTableFn set_root_desc_table_fn = p_for_compute ? &ID3D12GraphicsCommandList::SetComputeRootDescriptorTable : &ID3D12GraphicsCommandList1::SetGraphicsRootDescriptorTable; + + // If this set's descriptors have already been set for the current execution and a compatible root signature, reuse! + uint32_t root_sig_crc = p_for_compute ? cmd_buf_info->compute_root_signature_crc : cmd_buf_info->graphics_root_signature_crc; + UniformSetInfo::RecentBind *last_bind = nullptr; + for (int i = 0; i < (int)ARRAY_SIZE(uniform_set_info->recent_binds); i++) { + if (uniform_set_info->recent_binds[i].segment_serial == frames[frame_idx].segment_serial) { + if (uniform_set_info->recent_binds[i].root_signature_crc == root_sig_crc) { + for (const RootDescriptorTable &table : uniform_set_info->recent_binds[i].root_tables.resources) { + (cmd_buf_info->cmd_list.Get()->*set_root_desc_table_fn)(table.root_param_idx, table.start_gpu_handle); + } + for (const RootDescriptorTable &table : uniform_set_info->recent_binds[i].root_tables.samplers) { + (cmd_buf_info->cmd_list.Get()->*set_root_desc_table_fn)(table.root_param_idx, table.start_gpu_handle); + } +#ifdef DEV_ENABLED + uniform_set_info->recent_binds[i].uses++; + frames[frame_idx].uniform_set_reused++; +#endif + return; + } else { + if (!last_bind || uniform_set_info->recent_binds[i].uses < last_bind->uses) { + // Prefer this one since it's been used less or we still haven't a better option. + last_bind = &uniform_set_info->recent_binds[i]; + } + } + } else { + // Prefer this one since it's unused. + last_bind = &uniform_set_info->recent_binds[i]; + last_bind->uses = 0; + } + } + + struct { + DescriptorsHeap::Walker *resources = nullptr; + DescriptorsHeap::Walker *samplers = nullptr; + } frame_heap_walkers; + frame_heap_walkers.resources = &frames[frame_idx].desc_heap_walkers.resources; + frame_heap_walkers.samplers = &frames[frame_idx].desc_heap_walkers.samplers; + + struct { + DescriptorsHeap::Walker resources; + DescriptorsHeap::Walker samplers; + } set_heap_walkers; + set_heap_walkers.resources = uniform_set_info->desc_heaps.resources.make_walker(); + set_heap_walkers.samplers = uniform_set_info->desc_heaps.samplers.make_walker(); + +#ifdef DEV_ENABLED + // Whether we have stages where the uniform is actually used should match + // whether we have any root signature locations for it. + for (uint32_t i = 0; i < shader_set.bindings.size(); i++) { + bool has_rs_locations = false; + if (shader_set.bindings[i].root_sig_locations.resource.root_param_idx != UINT32_MAX || + shader_set.bindings[i].root_sig_locations.sampler.root_param_idx != UINT32_MAX) { + has_rs_locations = true; + break; + } + + bool has_stages = shader_set.bindings[i].stages; + + DEV_ASSERT(has_rs_locations == has_stages); + } +#endif + + last_bind->root_tables.resources.reserve(shader_set.num_root_params.resources); + last_bind->root_tables.resources.clear(); + last_bind->root_tables.samplers.reserve(shader_set.num_root_params.samplers); + last_bind->root_tables.samplers.clear(); + last_bind->uses++; + + struct { + RootDescriptorTable *resources = nullptr; + RootDescriptorTable *samplers = nullptr; + } tables; + for (uint32_t i = 0; i < shader_set.bindings.size(); i++) { + const ShaderInfo::UniformBindingInfo &binding = shader_set.bindings[i]; + + uint32_t num_resource_descs = 0; + uint32_t num_sampler_descs = 0; + bool srv_uav_ambiguity = false; + _add_descriptor_count_for_uniform(binding.type, binding.length, false, num_resource_descs, num_sampler_descs, srv_uav_ambiguity); + + bool resource_used = false; + if (shader_set.bindings[i].stages) { + { + const ShaderInfo::UniformBindingInfo::RootSignatureLocation &rs_loc_resource = shader_set.bindings[i].root_sig_locations.resource; + if (rs_loc_resource.root_param_idx != UINT32_MAX) { // Location used? + DEV_ASSERT(num_resource_descs); + DEV_ASSERT(!(srv_uav_ambiguity && (shader_set.bindings[i].res_class != RES_CLASS_SRV && shader_set.bindings[i].res_class != RES_CLASS_UAV))); // [[SRV_UAV_AMBIGUITY]] + + bool must_flush_table = tables.resources && rs_loc_resource.root_param_idx != tables.resources->root_param_idx; + if (must_flush_table) { + // Check the root signature data has been filled ordered. + DEV_ASSERT(rs_loc_resource.root_param_idx > tables.resources->root_param_idx); + + (cmd_buf_info->cmd_list.Get()->*set_root_desc_table_fn)(tables.resources->root_param_idx, tables.resources->start_gpu_handle); + tables.resources = nullptr; + } + + if (unlikely(frame_heap_walkers.resources->get_free_handles() < num_resource_descs)) { + if (!frames[frame_idx].desc_heaps_exhausted_reported.resources) { + frames[frame_idx].desc_heaps_exhausted_reported.resources = true; + ERR_FAIL_MSG("Cannot bind uniform set because there's no enough room in current frame's RESOURCES descriptor heap.\n" + "Please increase the value of the rendering/rendering_device/d3d12/max_resource_descriptors_per_frame project setting."); + } else { + return; + } + } + + if (!tables.resources) { + DEV_ASSERT(last_bind->root_tables.resources.size() < last_bind->root_tables.resources.get_capacity()); + last_bind->root_tables.resources.resize(last_bind->root_tables.resources.size() + 1); + tables.resources = &last_bind->root_tables.resources[last_bind->root_tables.resources.size() - 1]; + tables.resources->root_param_idx = rs_loc_resource.root_param_idx; + tables.resources->start_gpu_handle = frame_heap_walkers.resources->get_curr_gpu_handle(); + } + + // If there is ambiguity and it didn't clarify as SRVs, skip them, which come first. [[SRV_UAV_AMBIGUITY]] + if (srv_uav_ambiguity && shader_set.bindings[i].res_class != RES_CLASS_SRV) { + set_heap_walkers.resources.advance(num_resource_descs); + } + + // TODO: Batch to avoid multiple calls where possible (in any case, flush before setting root descriptor tables, or even batch that as well). + device->CopyDescriptorsSimple( + num_resource_descs, + frame_heap_walkers.resources->get_curr_cpu_handle(), + set_heap_walkers.resources.get_curr_cpu_handle(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + frame_heap_walkers.resources->advance(num_resource_descs); + + // If there is ambiguity and it didn't clarify as UAVs, skip them, which come later. [[SRV_UAV_AMBIGUITY]] + if (srv_uav_ambiguity && shader_set.bindings[i].res_class != RES_CLASS_UAV) { + set_heap_walkers.resources.advance(num_resource_descs); + } + + resource_used = true; + } + } + + { + const ShaderInfo::UniformBindingInfo::RootSignatureLocation &rs_loc_sampler = shader_set.bindings[i].root_sig_locations.sampler; + if (rs_loc_sampler.root_param_idx != UINT32_MAX) { // Location used? + DEV_ASSERT(num_sampler_descs); + DEV_ASSERT(!srv_uav_ambiguity); // [[SRV_UAV_AMBIGUITY]] + + bool must_flush_table = tables.samplers && rs_loc_sampler.root_param_idx != tables.samplers->root_param_idx; + if (must_flush_table) { + // Check the root signature data has been filled ordered. + DEV_ASSERT(rs_loc_sampler.root_param_idx > tables.samplers->root_param_idx); + + (cmd_buf_info->cmd_list.Get()->*set_root_desc_table_fn)(tables.samplers->root_param_idx, tables.samplers->start_gpu_handle); + tables.samplers = nullptr; + } + + if (unlikely(frame_heap_walkers.samplers->get_free_handles() < num_sampler_descs)) { + if (!frames[frame_idx].desc_heaps_exhausted_reported.samplers) { + frames[frame_idx].desc_heaps_exhausted_reported.samplers = true; + ERR_FAIL_MSG("Cannot bind uniform set because there's no enough room in current frame's SAMPLERS descriptors heap.\n" + "Please increase the value of the rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame project setting."); + } else { + return; + } + } + + if (!tables.samplers) { + DEV_ASSERT(last_bind->root_tables.samplers.size() < last_bind->root_tables.samplers.get_capacity()); + last_bind->root_tables.samplers.resize(last_bind->root_tables.samplers.size() + 1); + tables.samplers = &last_bind->root_tables.samplers[last_bind->root_tables.samplers.size() - 1]; + tables.samplers->root_param_idx = rs_loc_sampler.root_param_idx; + tables.samplers->start_gpu_handle = frame_heap_walkers.samplers->get_curr_gpu_handle(); + } + + // TODO: Batch to avoid multiple calls where possible (in any case, flush before setting root descriptor tables, or even batch that as well). + device->CopyDescriptorsSimple( + num_sampler_descs, + frame_heap_walkers.samplers->get_curr_cpu_handle(), + set_heap_walkers.samplers.get_curr_cpu_handle(), + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + frame_heap_walkers.samplers->advance(num_sampler_descs); + } + } + } + + // Uniform set descriptor heaps are always full (descriptors are created for every uniform in them) despite + // the shader variant a given set is created upon may not need all of them due to DXC optimizations. + // Therefore, at this point we have to advance through the descriptor set descriptor's heap unconditionally. + + set_heap_walkers.resources.advance(num_resource_descs); + if (srv_uav_ambiguity) { + DEV_ASSERT(num_resource_descs); + if (!resource_used) { + set_heap_walkers.resources.advance(num_resource_descs); // Additional skip, since both SRVs and UAVs have to be bypassed. + } + } + + set_heap_walkers.samplers.advance(num_sampler_descs); + } + + DEV_ASSERT(set_heap_walkers.resources.is_at_eof()); + DEV_ASSERT(set_heap_walkers.samplers.is_at_eof()); + + { + bool must_flush_table = tables.resources; + if (must_flush_table) { + (cmd_buf_info->cmd_list.Get()->*set_root_desc_table_fn)(tables.resources->root_param_idx, tables.resources->start_gpu_handle); + } + } + { + bool must_flush_table = tables.samplers; + if (must_flush_table) { + (cmd_buf_info->cmd_list.Get()->*set_root_desc_table_fn)(tables.samplers->root_param_idx, tables.samplers->start_gpu_handle); + } + } + + last_bind->root_signature_crc = root_sig_crc; + last_bind->segment_serial = frames[frame_idx].segment_serial; +} + +/******************/ +/**** TRANSFER ****/ +/******************/ + +void RenderingDeviceDriverD3D12::command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + BufferInfo *buf_info = (BufferInfo *)p_buffer.id; + + if (frames[frame_idx].desc_heap_walkers.resources.is_at_eof()) { + if (!frames[frame_idx].desc_heaps_exhausted_reported.resources) { + frames[frame_idx].desc_heaps_exhausted_reported.resources = true; + ERR_FAIL_MSG( + "Cannot clear buffer because there's no enough room in current frame's RESOURCE descriptors heap.\n" + "Please increase the value of the rendering/rendering_device/d3d12/max_resource_descriptors_per_frame project setting."); + } else { + return; + } + } + if (frames[frame_idx].desc_heap_walkers.aux.is_at_eof()) { + if (!frames[frame_idx].desc_heaps_exhausted_reported.aux) { + frames[frame_idx].desc_heaps_exhausted_reported.aux = true; + ERR_FAIL_MSG( + "Cannot clear buffer because there's no enough room in current frame's AUX descriptors heap.\n" + "Please increase the value of the rendering/rendering_device/d3d12/max_misc_descriptors_per_frame project setting."); + } else { + return; + } + } + + _resource_transition_batch(buf_info, 0, 1, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = {}; + uav_desc.Format = DXGI_FORMAT_R32_TYPELESS; + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uav_desc.Buffer.FirstElement = 0; + uav_desc.Buffer.NumElements = (buf_info->size + 3) / 4; + uav_desc.Buffer.StructureByteStride = 0; + uav_desc.Buffer.CounterOffsetInBytes = 0; + uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW; + device->CreateUnorderedAccessView( + buf_info->resource, + nullptr, + &uav_desc, + frames[frame_idx].desc_heap_walkers.aux.get_curr_cpu_handle()); + + device->CopyDescriptorsSimple( + 1, + frames[frame_idx].desc_heap_walkers.resources.get_curr_cpu_handle(), + frames[frame_idx].desc_heap_walkers.aux.get_curr_cpu_handle(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + static const UINT values[4] = {}; + cmd_buf_info->cmd_list->ClearUnorderedAccessViewUint( + frames[frame_idx].desc_heap_walkers.resources.get_curr_gpu_handle(), + frames[frame_idx].desc_heap_walkers.aux.get_curr_cpu_handle(), + buf_info->resource, + values, + 0, + nullptr); + + frames[frame_idx].desc_heap_walkers.resources.advance(); + frames[frame_idx].desc_heap_walkers.aux.advance(); +} + +void RenderingDeviceDriverD3D12::command_copy_buffer(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, BufferID p_dst_buffer, VectorView<BufferCopyRegion> p_regions) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + BufferInfo *src_buf_info = (BufferInfo *)p_src_buffer.id; + BufferInfo *dst_buf_info = (BufferInfo *)p_dst_buffer.id; + + _resource_transition_batch(src_buf_info, 0, 1, D3D12_RESOURCE_STATE_COPY_SOURCE); + _resource_transition_batch(dst_buf_info, 0, 1, D3D12_RESOURCE_STATE_COPY_DEST); + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + for (uint32_t i = 0; i < p_regions.size(); i++) { + cmd_buf_info->cmd_list->CopyBufferRegion(dst_buf_info->resource, p_regions[i].dst_offset, src_buf_info->resource, p_regions[i].src_offset, p_regions[i].size); + } +} + +void RenderingDeviceDriverD3D12::command_copy_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<TextureCopyRegion> p_regions) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + TextureInfo *src_tex_info = (TextureInfo *)p_src_texture.id; + TextureInfo *dst_tex_info = (TextureInfo *)p_dst_texture.id; + + for (uint32_t i = 0; i < p_regions.size(); i++) { + UINT src_subresource = D3D12CalcSubresource( + p_regions[i].src_subresources.mipmap, + p_regions[i].src_subresources.base_layer, + _compute_plane_slice(src_tex_info->format, p_regions[i].src_subresources.aspect), + src_tex_info->desc.MipLevels, + src_tex_info->desc.ArraySize()); + _resource_transition_batch(src_tex_info, src_subresource, 1, D3D12_RESOURCE_STATE_COPY_SOURCE); + + UINT dst_subresource = D3D12CalcSubresource( + p_regions[i].dst_subresources.mipmap, + p_regions[i].dst_subresources.base_layer, + _compute_plane_slice(dst_tex_info->format, p_regions[i].dst_subresources.aspect), + dst_tex_info->desc.MipLevels, + dst_tex_info->desc.ArraySize()); + _resource_transition_batch(dst_tex_info, dst_subresource, 1, D3D12_RESOURCE_STATE_COPY_DEST); + + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + CD3DX12_TEXTURE_COPY_LOCATION src_location(src_tex_info->resource, src_subresource); + CD3DX12_TEXTURE_COPY_LOCATION dst_location(dst_tex_info->resource, dst_subresource); + + CD3DX12_BOX src_box( + p_regions[i].src_offset.x, + p_regions[i].src_offset.y, + p_regions[i].src_offset.z, + p_regions[i].src_offset.x + p_regions[i].size.x, + p_regions[i].src_offset.y + p_regions[i].size.y, + p_regions[i].src_offset.z + p_regions[i].size.z); + + cmd_buf_info->cmd_list->CopyTextureRegion( + &dst_location, + p_regions[i].dst_offset.x, + p_regions[i].dst_offset.y, + p_regions[i].dst_offset.z, + &src_location, + &src_box); + } +} + +void RenderingDeviceDriverD3D12::command_resolve_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + TextureInfo *src_tex_info = (TextureInfo *)p_src_texture.id; + TextureInfo *dst_tex_info = (TextureInfo *)p_dst_texture.id; + + UINT src_subresource = D3D12CalcSubresource(p_src_mipmap, p_src_layer, 0, src_tex_info->desc.MipLevels, src_tex_info->desc.ArraySize()); + _resource_transition_batch(src_tex_info, src_subresource, 1, D3D12_RESOURCE_STATE_RESOLVE_SOURCE); + + UINT dst_subresource = D3D12CalcSubresource(p_dst_mipmap, p_dst_layer, 0, dst_tex_info->desc.MipLevels, dst_tex_info->desc.ArraySize()); + _resource_transition_batch(dst_tex_info, dst_subresource, 1, D3D12_RESOURCE_STATE_RESOLVE_DEST); + + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + cmd_buf_info->cmd_list->ResolveSubresource(dst_tex_info->resource, dst_subresource, src_tex_info->resource, src_subresource, RD_TO_D3D12_FORMAT[src_tex_info->format].general_format); +} + +void RenderingDeviceDriverD3D12::command_clear_color_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, const Color &p_color, const TextureSubresourceRange &p_subresources) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + TextureInfo *tex_info = (TextureInfo *)p_texture.id; + + ID3D12Resource *res_to_clear = tex_info->main_texture ? tex_info->main_texture : tex_info->resource; + if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { + // Clear via RTV. + + if (frames[frame_idx].desc_heap_walkers.rtv.is_at_eof()) { + if (!frames[frame_idx].desc_heaps_exhausted_reported.rtv) { + frames[frame_idx].desc_heaps_exhausted_reported.rtv = true; + ERR_FAIL_MSG( + "Cannot clear texture because there's no enough room in current frame's RENDER TARGET descriptors heap.\n" + "Please increase the value of the rendering/rendering_device/d3d12/max_misc_descriptors_per_frame project setting."); + } else { + return; + } + } + + D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = _make_rtv_for_texture(tex_info, p_subresources.base_mipmap, p_subresources.base_layer, p_subresources.layer_count, false); + rtv_desc.Format = tex_info->aliasing_hack.main_uav_desc.Format; + + for (uint32_t i = 0; i < p_subresources.layer_count; i++) { + for (uint32_t j = 0; j < p_subresources.mipmap_count; j++) { + UINT subresource = D3D12CalcSubresource( + p_subresources.base_mipmap + j, + p_subresources.base_layer + i, + 0, + tex_info->desc.MipLevels, + tex_info->desc.ArraySize()); + _resource_transition_batch(tex_info, subresource, 1, D3D12_RESOURCE_STATE_RENDER_TARGET, tex_info->main_texture); + } + } + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + device->CreateRenderTargetView( + res_to_clear, + &rtv_desc, + frames[frame_idx].desc_heap_walkers.rtv.get_curr_cpu_handle()); + cmd_buf_info->cmd_list->ClearRenderTargetView( + frames[frame_idx].desc_heap_walkers.rtv.get_curr_cpu_handle(), + p_color.components, + 0, + nullptr); + frames[frame_idx].desc_heap_walkers.rtv.advance(); + } else { + // Clear via UAV. + + if (frames[frame_idx].desc_heap_walkers.resources.is_at_eof()) { + if (!frames[frame_idx].desc_heaps_exhausted_reported.resources) { + frames[frame_idx].desc_heaps_exhausted_reported.resources = true; + ERR_FAIL_MSG( + "Cannot clear texture because there's no enough room in current frame's RESOURCE descriptors heap.\n" + "Please increase the value of the rendering/rendering_device/d3d12/max_resource_descriptors_per_frame project setting."); + } else { + return; + } + } + if (frames[frame_idx].desc_heap_walkers.aux.is_at_eof()) { + if (!frames[frame_idx].desc_heaps_exhausted_reported.aux) { + frames[frame_idx].desc_heaps_exhausted_reported.aux = true; + ERR_FAIL_MSG( + "Cannot clear texture because there's no enough room in current frame's AUX descriptors heap.\n" + "Please increase the value of the rendering/rendering_device/d3d12/max_misc_descriptors_per_frame project setting."); + } else { + return; + } + } + + for (uint32_t i = 0; i < p_subresources.layer_count; i++) { + for (uint32_t j = 0; j < p_subresources.mipmap_count; j++) { + UINT subresource = D3D12CalcSubresource( + p_subresources.base_mipmap + j, + p_subresources.base_layer + i, + 0, + tex_info->desc.MipLevels, + tex_info->desc.ArraySize()); + _resource_transition_batch(tex_info, subresource, 1, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, tex_info->main_texture); + } + } + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + device->CreateUnorderedAccessView( + res_to_clear, + nullptr, + &tex_info->aliasing_hack.main_uav_desc, + frames[frame_idx].desc_heap_walkers.aux.get_curr_cpu_handle()); + + device->CopyDescriptorsSimple( + 1, + frames[frame_idx].desc_heap_walkers.resources.get_curr_cpu_handle(), + frames[frame_idx].desc_heap_walkers.aux.get_curr_cpu_handle(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + UINT values[4] = { + (UINT)p_color.get_r8(), + (UINT)p_color.get_g8(), + (UINT)p_color.get_b8(), + (UINT)p_color.get_a8(), + }; + cmd_buf_info->cmd_list->ClearUnorderedAccessViewUint( + frames[frame_idx].desc_heap_walkers.resources.get_curr_gpu_handle(), + frames[frame_idx].desc_heap_walkers.aux.get_curr_cpu_handle(), + res_to_clear, + values, + 0, + nullptr); + + frames[frame_idx].desc_heap_walkers.resources.advance(); + frames[frame_idx].desc_heap_walkers.aux.advance(); + } +} + +void RenderingDeviceDriverD3D12::command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<BufferTextureCopyRegion> p_regions) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + BufferInfo *buf_info = (BufferInfo *)p_src_buffer.id; + TextureInfo *tex_info = (TextureInfo *)p_dst_texture.id; + + if (buf_info->flags.is_for_upload) { + _resource_transition_batch(buf_info, 0, 1, D3D12_RESOURCE_STATE_COPY_SOURCE, nullptr); + } + + uint32_t pixel_size = get_image_format_pixel_size(tex_info->format); + uint32_t block_w = 0, block_h = 0; + get_compressed_image_format_block_dimensions(tex_info->format, block_w, block_h); + + for (uint32_t i = 0; i < p_regions.size(); i++) { + uint32_t region_pitch = (p_regions[i].texture_region_size.x * pixel_size * block_w) >> get_compressed_image_format_pixel_rshift(tex_info->format); + region_pitch = STEPIFY(region_pitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); + + D3D12_PLACED_SUBRESOURCE_FOOTPRINT src_footprint = {}; + src_footprint.Offset = p_regions[i].buffer_offset; + src_footprint.Footprint = CD3DX12_SUBRESOURCE_FOOTPRINT( + RD_TO_D3D12_FORMAT[tex_info->format].family, + STEPIFY(p_regions[i].texture_region_size.x, block_w), + STEPIFY(p_regions[i].texture_region_size.y, block_h), + p_regions[i].texture_region_size.z, + region_pitch); + CD3DX12_TEXTURE_COPY_LOCATION copy_src(buf_info->resource, src_footprint); + + CD3DX12_BOX src_box( + 0, 0, 0, + STEPIFY(p_regions[i].texture_region_size.x, block_w), + STEPIFY(p_regions[i].texture_region_size.y, block_h), + p_regions[i].texture_region_size.z); + + for (uint32_t j = 0; j < p_regions[i].texture_subresources.layer_count; j++) { + UINT dst_subresource = D3D12CalcSubresource( + p_regions[i].texture_subresources.mipmap, + p_regions[i].texture_subresources.base_layer + j, + _compute_plane_slice(tex_info->format, p_regions[i].texture_subresources.aspect), + tex_info->desc.MipLevels, + tex_info->desc.ArraySize()); + CD3DX12_TEXTURE_COPY_LOCATION copy_dst(tex_info->resource, dst_subresource); + + _resource_transition_batch(tex_info, dst_subresource, 1, D3D12_RESOURCE_STATE_COPY_DEST, tex_info->main_texture); + } + + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + for (uint32_t j = 0; j < p_regions[i].texture_subresources.layer_count; j++) { + UINT dst_subresource = D3D12CalcSubresource( + p_regions[i].texture_subresources.mipmap, + p_regions[i].texture_subresources.base_layer + j, + _compute_plane_slice(tex_info->format, p_regions[i].texture_subresources.aspect), + tex_info->desc.MipLevels, + tex_info->desc.ArraySize()); + CD3DX12_TEXTURE_COPY_LOCATION copy_dst(tex_info->resource, dst_subresource); + + cmd_buf_info->cmd_list->CopyTextureRegion( + ©_dst, + p_regions[i].texture_offset.x, + p_regions[i].texture_offset.y, + p_regions[i].texture_offset.z, + ©_src, + &src_box); + } + } +} + +void RenderingDeviceDriverD3D12::command_copy_texture_to_buffer(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, BufferID p_dst_buffer, VectorView<BufferTextureCopyRegion> p_regions) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + TextureInfo *tex_info = (TextureInfo *)p_src_texture.id; + BufferInfo *buf_info = (BufferInfo *)p_dst_buffer.id; + + _resource_transition_batch(buf_info, 0, 1, D3D12_RESOURCE_STATE_COPY_DEST, nullptr); + + uint32_t block_w = 0, block_h = 0; + get_compressed_image_format_block_dimensions(tex_info->format, block_w, block_h); + + for (uint32_t i = 0; i < p_regions.size(); i++) { + for (uint32_t j = 0; j < p_regions[i].texture_subresources.layer_count; j++) { + UINT src_subresource = D3D12CalcSubresource( + p_regions[i].texture_subresources.mipmap, + p_regions[i].texture_subresources.base_layer + j, + _compute_plane_slice(tex_info->format, p_regions[i].texture_subresources.aspect), + tex_info->desc.MipLevels, + tex_info->desc.ArraySize()); + + _resource_transition_batch(tex_info, src_subresource, 1, D3D12_RESOURCE_STATE_COPY_SOURCE, tex_info->main_texture); + } + + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + for (uint32_t j = 0; j < p_regions[i].texture_subresources.layer_count; j++) { + UINT src_subresource = D3D12CalcSubresource( + p_regions[i].texture_subresources.mipmap, + p_regions[i].texture_subresources.base_layer + j, + _compute_plane_slice(tex_info->format, p_regions[i].texture_subresources.aspect), + tex_info->desc.MipLevels, + tex_info->desc.ArraySize()); + + CD3DX12_TEXTURE_COPY_LOCATION copy_src(tex_info->resource, src_subresource); + + uint32_t computed_d = MAX(1, tex_info->desc.DepthOrArraySize >> p_regions[i].texture_subresources.mipmap); + uint32_t image_size = get_image_format_required_size( + tex_info->format, + MAX(1u, tex_info->desc.Width >> p_regions[i].texture_subresources.mipmap), + MAX(1u, tex_info->desc.Height >> p_regions[i].texture_subresources.mipmap), + computed_d, + 1); + uint32_t row_pitch = image_size / (p_regions[i].texture_region_size.y * computed_d) * block_h; + row_pitch = STEPIFY(row_pitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); + + D3D12_PLACED_SUBRESOURCE_FOOTPRINT dst_footprint = {}; + dst_footprint.Offset = p_regions[i].buffer_offset; + dst_footprint.Footprint.Width = STEPIFY(p_regions[i].texture_region_size.x, block_w); + dst_footprint.Footprint.Height = STEPIFY(p_regions[i].texture_region_size.y, block_h); + dst_footprint.Footprint.Depth = p_regions[i].texture_region_size.z; + dst_footprint.Footprint.RowPitch = row_pitch; + dst_footprint.Footprint.Format = RD_TO_D3D12_FORMAT[tex_info->format].family; + + CD3DX12_TEXTURE_COPY_LOCATION copy_dst(buf_info->resource, dst_footprint); + + cmd_buf_info->cmd_list->CopyTextureRegion(©_dst, 0, 0, 0, ©_src, nullptr); + } + } +} + +/******************/ +/**** PIPELINE ****/ +/******************/ + +void RenderingDeviceDriverD3D12::pipeline_free(PipelineID p_pipeline) { + ID3D12PipelineState *pso = (ID3D12PipelineState *)p_pipeline.id; + pso->Release(); + pipelines_shaders.erase(pso); + render_psos_extra_info.erase(pso); +} + +// ----- BINDING ----- + +void RenderingDeviceDriverD3D12::command_bind_push_constants(CommandBufferID p_cmd_buffer, ShaderID p_shader, uint32_t p_dst_first_index, VectorView<uint32_t> p_data) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + const ShaderInfo *shader_info_in = (const ShaderInfo *)p_shader.id; + if (!shader_info_in->dxil_push_constant_size) { + return; + } + if (shader_info_in->is_compute) { + cmd_buf_info->cmd_list->SetComputeRoot32BitConstants(0, p_data.size(), p_data.ptr(), p_dst_first_index); + } else { + cmd_buf_info->cmd_list->SetGraphicsRoot32BitConstants(0, p_data.size(), p_data.ptr(), p_dst_first_index); + } +} + +// ----- CACHE ----- + +bool RenderingDeviceDriverD3D12::pipeline_cache_create(const Vector<uint8_t> &p_data) { + WARN_PRINT("PSO caching is not implemented yet in the Direct3D 12 driver."); + return false; +} + +void RenderingDeviceDriverD3D12::pipeline_cache_free() { + ERR_FAIL_MSG("Not implemented."); +} + +size_t RenderingDeviceDriverD3D12::pipeline_cache_query_size() { + ERR_FAIL_V_MSG(0, "Not implemented."); +} + +Vector<uint8_t> RenderingDeviceDriverD3D12::pipeline_cache_serialize() { + ERR_FAIL_V_MSG(Vector<uint8_t>(), "Not implemented."); +} + +/*******************/ +/**** RENDERING ****/ +/*******************/ + +// ----- SUBPASS ----- + +RDD::RenderPassID RenderingDeviceDriverD3D12::render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) { + // Pre-bookkeep. + RenderPassInfo *pass_info = VersatileResource::allocate<RenderPassInfo>(resources_allocator); + + pass_info->attachments.resize(p_attachments.size()); + for (uint32_t i = 0; i < p_attachments.size(); i++) { + pass_info->attachments[i] = p_attachments[i]; + } + + pass_info->subpasses.resize(p_subpasses.size()); + for (uint32_t i = 0; i < p_subpasses.size(); i++) { + pass_info->subpasses[i] = p_subpasses[i]; + } + + pass_info->view_count = p_view_count; + + DXGI_FORMAT *formats = ALLOCA_ARRAY(DXGI_FORMAT, p_attachments.size()); + for (uint32_t i = 0; i < p_attachments.size(); i++) { + const D3D12Format &format = RD_TO_D3D12_FORMAT[p_attachments[i].format]; + if (format.dsv_format != DXGI_FORMAT_UNKNOWN) { + formats[i] = format.dsv_format; + } else { + formats[i] = format.general_format; + } + } + pass_info->max_supported_sample_count = _find_max_common_supported_sample_count(VectorView(formats, p_attachments.size())); + + return RenderPassID(pass_info); +} + +void RenderingDeviceDriverD3D12::render_pass_free(RenderPassID p_render_pass) { + RenderPassInfo *pass_info = (RenderPassInfo *)p_render_pass.id; + VersatileResource::free(resources_allocator, pass_info); +} + +// ----- COMMANDS ----- + +void RenderingDeviceDriverD3D12::command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_attachment_clears) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + const RenderPassInfo *pass_info = (const RenderPassInfo *)p_render_pass.id; + const FramebufferInfo *fb_info = (const FramebufferInfo *)p_framebuffer.id; + + DEV_ASSERT(cmd_buf_info->render_pass_state.current_subpass == UINT32_MAX); + + auto _transition_subresources = [&](TextureInfo *p_texture_info, D3D12_RESOURCE_STATES p_states) { + uint32_t planes = 1; + if ((p_texture_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { + planes = format_get_plane_count(p_texture_info->format); + } + for (uint32_t i = 0; i < p_texture_info->layers; i++) { + for (uint32_t j = 0; j < p_texture_info->mipmaps; j++) { + uint32_t subresource = D3D12CalcSubresource( + p_texture_info->base_mip + j, + p_texture_info->base_layer + i, + 0, + p_texture_info->desc.MipLevels, + p_texture_info->desc.ArraySize()); + _resource_transition_batch(p_texture_info, subresource, planes, p_states, nullptr); + } + } + }; + + // 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)) { + _transition_subresources(tex_info, D3D12_RESOURCE_STATE_RENDER_TARGET); + } else if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { + _transition_subresources(tex_info, D3D12_RESOURCE_STATE_DEPTH_WRITE); + } else { + DEV_ASSERT(false); + } + } + if (fb_info->vrs_attachment) { + TextureInfo *tex_info = (TextureInfo *)fb_info->vrs_attachment.id; + _transition_subresources(tex_info, D3D12_RESOURCE_STATE_SHADING_RATE_SOURCE); + } + + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + cmd_buf_info->render_pass_state.region_rect = CD3DX12_RECT( + p_rect.position.x, + p_rect.position.y, + p_rect.position.x + p_rect.size.x, + p_rect.position.y + p_rect.size.y); + cmd_buf_info->render_pass_state.region_is_all = !( + cmd_buf_info->render_pass_state.region_rect.left == 0 && + cmd_buf_info->render_pass_state.region_rect.top == 0 && + 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); + } + } + } + + if (fb_info->vrs_attachment && context->get_vrs_capabilities().ss_image_supported) { + ComPtr<ID3D12GraphicsCommandList5> cmd_list_5; + cmd_buf_info->cmd_list->QueryInterface(cmd_list_5.GetAddressOf()); + if (cmd_list_5) { + static const D3D12_SHADING_RATE_COMBINER COMBINERS[D3D12_RS_SET_SHADING_RATE_COMBINER_COUNT] = { + D3D12_SHADING_RATE_COMBINER_PASSTHROUGH, + D3D12_SHADING_RATE_COMBINER_OVERRIDE, + }; + cmd_list_5->RSSetShadingRate(D3D12_SHADING_RATE_1X1, COMBINERS); + } + } + + cmd_buf_info->render_pass_state.current_subpass = UINT32_MAX; + cmd_buf_info->render_pass_state.fb_info = fb_info; + 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()); + 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; + } + + 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); + } + } + 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) { + command_render_clear_attachments(p_cmd_buffer, VectorView(clears, num_clears), VectorView(clear_rects, num_clears)); + } +} + +void RenderingDeviceDriverD3D12::_end_render_pass(CommandBufferID p_cmd_buffer) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + + DEV_ASSERT(cmd_buf_info->render_pass_state.current_subpass != UINT32_MAX); + + const FramebufferInfo *fb_info = cmd_buf_info->render_pass_state.fb_info; + 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]; + + struct Resolve { + ID3D12Resource *src_res = nullptr; + uint32_t src_subres = 0; + ID3D12Resource *dst_res = nullptr; + uint32_t dst_subres = 0; + DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; + }; + Resolve *resolves = ALLOCA_ARRAY(Resolve, subpass.resolve_references.size()); + uint32_t num_resolves = 0; + + for (uint32_t i = 0; i < subpass.resolve_references.size(); i++) { + uint32_t color_index = subpass.color_references[i].attachment; + uint32_t resolve_index = subpass.resolve_references[i].attachment; + DEV_ASSERT((color_index == AttachmentReference::UNUSED) == (resolve_index == AttachmentReference::UNUSED)); + if (color_index == AttachmentReference::UNUSED || !fb_info->attachments[color_index]) { + continue; + } + + TextureInfo *src_tex_info = (TextureInfo *)fb_info->attachments[color_index].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_RESOLVE_SOURCE); + + TextureInfo *dst_tex_info = (TextureInfo *)fb_info->attachments[resolve_index].id; + uint32_t dst_subresource = D3D12CalcSubresource(dst_tex_info->base_mip, dst_tex_info->base_layer, 0, dst_tex_info->desc.MipLevels, dst_tex_info->desc.ArraySize()); + _resource_transition_batch(dst_tex_info, dst_subresource, 1, D3D12_RESOURCE_STATE_RESOLVE_DEST); + + resolves[num_resolves].src_res = src_tex_info->resource; + resolves[num_resolves].src_subres = src_subresource; + resolves[num_resolves].dst_res = dst_tex_info->resource; + resolves[num_resolves].dst_subres = dst_subresource; + resolves[num_resolves].format = RD_TO_D3D12_FORMAT[src_tex_info->format].general_format; + num_resolves++; + } + + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + for (uint32_t i = 0; i < num_resolves; i++) { + cmd_buf_info->cmd_list->ResolveSubresource(resolves[i].dst_res, resolves[i].dst_subres, resolves[i].src_res, resolves[i].src_subres, resolves[i].format); + } +} + +void RenderingDeviceDriverD3D12::command_end_render_pass(CommandBufferID p_cmd_buffer) { + _end_render_pass(p_cmd_buffer); + + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + DEV_ASSERT(cmd_buf_info->render_pass_state.current_subpass != UINT32_MAX); + + 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) { + ComPtr<ID3D12GraphicsCommandList5> cmd_list_5; + cmd_buf_info->cmd_list->QueryInterface(cmd_list_5.GetAddressOf()); + if (cmd_list_5) { + cmd_list_5->RSSetShadingRateImage(nullptr); + } + } + + 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); + } + } + } + + cmd_buf_info->render_pass_state.current_subpass = UINT32_MAX; +} + +void RenderingDeviceDriverD3D12::command_next_render_subpass(CommandBufferID p_cmd_buffer, CommandBufferType p_cmd_buffer_type) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + + if (cmd_buf_info->render_pass_state.current_subpass == UINT32_MAX) { + cmd_buf_info->render_pass_state.current_subpass = 0; + } else { + _end_render_pass(p_cmd_buffer); + cmd_buf_info->render_pass_state.current_subpass++; + } + + const FramebufferInfo *fb_info = cmd_buf_info->render_pass_state.fb_info; + 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]; + + D3D12_CPU_DESCRIPTOR_HANDLE *rtv_handles = ALLOCA_ARRAY(D3D12_CPU_DESCRIPTOR_HANDLE, subpass.color_references.size()); + DescriptorsHeap::Walker rtv_heap_walker = fb_info->rtv_heap.make_walker(); + for (uint32_t i = 0; i < subpass.color_references.size(); i++) { + uint32_t attachment = subpass.color_references[i].attachment; + if (attachment == AttachmentReference::UNUSED) { + if (!frames[frame_idx].null_rtv_handle.ptr) { + // No null descriptor-handle created for this frame yet. + + if (frames[frame_idx].desc_heap_walkers.rtv.is_at_eof()) { + if (!frames[frame_idx].desc_heaps_exhausted_reported.rtv) { + frames[frame_idx].desc_heaps_exhausted_reported.rtv = true; + ERR_FAIL_MSG("Cannot begin subpass because there's no enough room in current frame's RENDER TARGET descriptors heap.\n" + "Please increase the value of the rendering/rendering_device/d3d12/max_misc_descriptors_per_frame project setting."); + } else { + return; + } + } + + D3D12_RENDER_TARGET_VIEW_DESC rtv_desc_null = {}; + rtv_desc_null.Format = DXGI_FORMAT_R8_UINT; + rtv_desc_null.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + frames[frame_idx].null_rtv_handle = frames[frame_idx].desc_heap_walkers.rtv.get_curr_cpu_handle(); + device->CreateRenderTargetView(nullptr, &rtv_desc_null, frames[frame_idx].null_rtv_handle); + frames[frame_idx].desc_heap_walkers.rtv.advance(); + } + rtv_handles[i] = frames[frame_idx].null_rtv_handle; + } else { + uint32_t rt_index = fb_info->attachments_handle_inds[attachment]; + rtv_heap_walker.rewind(); + rtv_heap_walker.advance(rt_index); + rtv_handles[i] = rtv_heap_walker.get_curr_cpu_handle(); + } + } + + D3D12_CPU_DESCRIPTOR_HANDLE dsv_handle = {}; + { + DescriptorsHeap::Walker dsv_heap_walker = fb_info->dsv_heap.make_walker(); + if (subpass.depth_stencil_reference.attachment != AttachmentReference::UNUSED) { + uint32_t ds_index = fb_info->attachments_handle_inds[subpass.depth_stencil_reference.attachment]; + dsv_heap_walker.rewind(); + dsv_heap_walker.advance(ds_index); + dsv_handle = dsv_heap_walker.get_curr_cpu_handle(); + } + } + + cmd_buf_info->cmd_list->OMSetRenderTargets(subpass.color_references.size(), rtv_handles, false, dsv_handle.ptr ? &dsv_handle : nullptr); +} + +void RenderingDeviceDriverD3D12::command_render_set_viewport(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_viewports) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + + D3D12_VIEWPORT *d3d12_viewports = ALLOCA_ARRAY(D3D12_VIEWPORT, p_viewports.size()); + for (uint32_t i = 0; i < p_viewports.size(); i++) { + d3d12_viewports[i] = CD3DX12_VIEWPORT( + p_viewports[i].position.x, + p_viewports[i].position.y, + p_viewports[i].size.x, + p_viewports[i].size.y); + } + + cmd_buf_info->cmd_list->RSSetViewports(p_viewports.size(), d3d12_viewports); +} + +void RenderingDeviceDriverD3D12::command_render_set_scissor(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_scissors) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + + D3D12_RECT *d3d12_scissors = ALLOCA_ARRAY(D3D12_RECT, p_scissors.size()); + for (uint32_t i = 0; i < p_scissors.size(); i++) { + d3d12_scissors[i] = CD3DX12_RECT( + p_scissors[i].position.x, + p_scissors[i].position.y, + p_scissors[i].position.x + p_scissors[i].size.x, + p_scissors[i].position.y + p_scissors[i].size.y); + } + + cmd_buf_info->cmd_list->RSSetScissorRects(p_scissors.size(), d3d12_scissors); +} + +void RenderingDeviceDriverD3D12::command_render_clear_attachments(CommandBufferID p_cmd_buffer, VectorView<AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + + DEV_ASSERT(cmd_buf_info->render_pass_state.current_subpass != UINT32_MAX); + const FramebufferInfo *fb_info = cmd_buf_info->render_pass_state.fb_info; + const RenderPassInfo *pass_info = cmd_buf_info->render_pass_state.pass_info; + + DescriptorsHeap::Walker rtv_heap_walker = fb_info->rtv_heap.make_walker(); + DescriptorsHeap::Walker dsv_heap_walker = fb_info->dsv_heap.make_walker(); + + for (uint32_t i = 0; i < p_attachment_clears.size(); i++) { + uint32_t attachment = UINT32_MAX; + bool is_render_target = false; + if (p_attachment_clears[i].aspect.has_flag(TEXTURE_ASPECT_COLOR_BIT)) { + attachment = p_attachment_clears[i].color_attachment; + is_render_target = true; + } else { + attachment = pass_info->subpasses[cmd_buf_info->render_pass_state.current_subpass].depth_stencil_reference.attachment; + } + + for (uint32_t j = 0; j < p_rects.size(); j++) { + D3D12_RECT rect = CD3DX12_RECT( + p_rects[j].position.x, + p_rects[j].position.y, + p_rects[j].position.x + p_rects[j].size.x, + p_rects[j].position.y + p_rects[j].size.y); + const D3D12_RECT *rect_ptr = cmd_buf_info->render_pass_state.region_is_all ? nullptr : ▭ + + if (is_render_target) { + uint32_t color_idx = fb_info->attachments_handle_inds[attachment]; + rtv_heap_walker.rewind(); + rtv_heap_walker.advance(color_idx); + cmd_buf_info->cmd_list->ClearRenderTargetView( + rtv_heap_walker.get_curr_cpu_handle(), + p_attachment_clears[i].value.color.components, + rect_ptr ? 1 : 0, + rect_ptr); + } else { + uint32_t depth_stencil_idx = fb_info->attachments_handle_inds[attachment]; + dsv_heap_walker.rewind(); + dsv_heap_walker.advance(depth_stencil_idx); + D3D12_CLEAR_FLAGS flags = {}; + if (p_attachment_clears[i].aspect.has_flag(TEXTURE_ASPECT_DEPTH_BIT)) { + flags |= D3D12_CLEAR_FLAG_DEPTH; + } + if (p_attachment_clears[i].aspect.has_flag(TEXTURE_ASPECT_STENCIL_BIT)) { + flags |= D3D12_CLEAR_FLAG_STENCIL; + } + cmd_buf_info->cmd_list->ClearDepthStencilView( + dsv_heap_walker.get_curr_cpu_handle(), + flags, + p_attachment_clears[i].value.depth, + p_attachment_clears[i].value.stencil, + rect_ptr ? 1 : 0, + rect_ptr); + } + } + } +} + +void RenderingDeviceDriverD3D12::command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + ID3D12PipelineState *pso = (ID3D12PipelineState *)p_pipeline.id; + + if (cmd_buf_info->graphics_pso == pso) { + return; + } + + const ShaderInfo *shader_info_in = pipelines_shaders[pso]; + const RenderPipelineExtraInfo &pso_extra_info = render_psos_extra_info[pso]; + + cmd_buf_info->cmd_list->SetPipelineState(pso); + if (cmd_buf_info->graphics_root_signature_crc != shader_info_in->root_signature_crc) { + cmd_buf_info->cmd_list->SetGraphicsRootSignature(shader_info_in->root_signature.Get()); + cmd_buf_info->graphics_root_signature_crc = shader_info_in->root_signature_crc; + } + + cmd_buf_info->cmd_list->IASetPrimitiveTopology(pso_extra_info.dyn_params.primitive_topology); + cmd_buf_info->cmd_list->OMSetBlendFactor(pso_extra_info.dyn_params.blend_constant.components); + cmd_buf_info->cmd_list->OMSetStencilRef(pso_extra_info.dyn_params.stencil_reference); + + ComPtr<ID3D12GraphicsCommandList1> command_list_1; + cmd_buf_info->cmd_list->QueryInterface(command_list_1.GetAddressOf()); + if (command_list_1) { + command_list_1->OMSetDepthBounds(pso_extra_info.dyn_params.depth_bounds_min, pso_extra_info.dyn_params.depth_bounds_max); + } + + cmd_buf_info->render_pass_state.vf_info = pso_extra_info.vf_info; + + cmd_buf_info->graphics_pso = pso; + cmd_buf_info->compute_pso = nullptr; +} + +void RenderingDeviceDriverD3D12::command_bind_render_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) { + _command_bind_uniform_set(p_cmd_buffer, p_uniform_set, p_shader, p_set_index, false); +} + +void RenderingDeviceDriverD3D12::command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + _bind_vertex_buffers(cmd_buf_info); + cmd_buf_info->cmd_list->DrawInstanced(p_vertex_count, p_instance_count, p_base_vertex, p_first_instance); +} + +void RenderingDeviceDriverD3D12::command_render_draw_indexed(CommandBufferID p_cmd_buffer, uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index, int32_t p_vertex_offset, uint32_t p_first_instance) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + _bind_vertex_buffers(cmd_buf_info); + cmd_buf_info->cmd_list->DrawIndexedInstanced(p_index_count, p_instance_count, p_first_index, p_vertex_offset, p_first_instance); +} + +void RenderingDeviceDriverD3D12::command_render_draw_indexed_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + _bind_vertex_buffers(cmd_buf_info); + BufferInfo *indirect_buf_info = (BufferInfo *)p_indirect_buffer.id; + _resource_transition_batch(indirect_buf_info, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + cmd_buf_info->cmd_list->ExecuteIndirect(indirect_cmd_signatures.draw_indexed.Get(), p_draw_count, indirect_buf_info->resource, p_offset, nullptr, 0); +} + +void RenderingDeviceDriverD3D12::command_render_draw_indexed_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + _bind_vertex_buffers(cmd_buf_info); + BufferInfo *indirect_buf_info = (BufferInfo *)p_indirect_buffer.id; + BufferInfo *count_buf_info = (BufferInfo *)p_count_buffer.id; + _resource_transition_batch(indirect_buf_info, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); + _resource_transition_batch(count_buf_info, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + cmd_buf_info->cmd_list->ExecuteIndirect(indirect_cmd_signatures.draw_indexed.Get(), p_max_draw_count, indirect_buf_info->resource, p_offset, count_buf_info->resource, p_count_buffer_offset); +} + +void RenderingDeviceDriverD3D12::command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + _bind_vertex_buffers(cmd_buf_info); + BufferInfo *indirect_buf_info = (BufferInfo *)p_indirect_buffer.id; + _resource_transition_batch(indirect_buf_info, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + cmd_buf_info->cmd_list->ExecuteIndirect(indirect_cmd_signatures.draw.Get(), p_draw_count, indirect_buf_info->resource, p_offset, nullptr, 0); +} + +void RenderingDeviceDriverD3D12::command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + _bind_vertex_buffers(cmd_buf_info); + BufferInfo *indirect_buf_info = (BufferInfo *)p_indirect_buffer.id; + BufferInfo *count_buf_info = (BufferInfo *)p_count_buffer.id; + _resource_transition_batch(indirect_buf_info, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); + _resource_transition_batch(count_buf_info, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + cmd_buf_info->cmd_list->ExecuteIndirect(indirect_cmd_signatures.draw.Get(), p_max_draw_count, indirect_buf_info->resource, p_offset, count_buf_info->resource, p_count_buffer_offset); +} + +void RenderingDeviceDriverD3D12::command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + + DEV_ASSERT(cmd_buf_info->render_pass_state.current_subpass != UINT32_MAX); + + // Vertex buffer views are set deferredly, to be sure we already know the strides by then, + // which is only true once the pipeline has been bound. Otherwise, we'd need that the pipeline + // is always bound first, which would be not kind of us. [[DEFERRED_VERTEX_BUFFERS]] + DEV_ASSERT(p_binding_count <= ARRAY_SIZE(cmd_buf_info->render_pass_state.vertex_buffer_views)); + for (uint32_t i = 0; i < p_binding_count; i++) { + BufferInfo *buffer_info = (BufferInfo *)p_buffers[i].id; + + cmd_buf_info->render_pass_state.vertex_buffer_views[i] = {}; + cmd_buf_info->render_pass_state.vertex_buffer_views[i].BufferLocation = buffer_info->resource->GetGPUVirtualAddress() + p_offsets[i]; + cmd_buf_info->render_pass_state.vertex_buffer_views[i].SizeInBytes = buffer_info->size - p_offsets[i]; + + _resource_transition_batch(buffer_info, 0, 1, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); + } + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + cmd_buf_info->render_pass_state.vertex_buffer_count = p_binding_count; +} + +void RenderingDeviceDriverD3D12::command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + BufferInfo *buffer_info = (BufferInfo *)p_buffer.id; + + D3D12_INDEX_BUFFER_VIEW d3d12_ib_view = {}; + d3d12_ib_view.BufferLocation = buffer_info->resource->GetGPUVirtualAddress() + p_offset; + d3d12_ib_view.SizeInBytes = buffer_info->size - p_offset; + d3d12_ib_view.Format = p_format == INDEX_BUFFER_FORMAT_UINT16 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; + + _resource_transition_batch(buffer_info, 0, 1, D3D12_RESOURCE_STATE_INDEX_BUFFER); + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + + cmd_buf_info->cmd_list->IASetIndexBuffer(&d3d12_ib_view); +} + +// [[DEFERRED_VERTEX_BUFFERS]] +void RenderingDeviceDriverD3D12::_bind_vertex_buffers(CommandBufferInfo *p_cmd_buf_info) { + RenderPassState &render_pass_state = p_cmd_buf_info->render_pass_state; + if (render_pass_state.vertex_buffer_count && render_pass_state.vf_info) { + for (uint32_t i = 0; i < render_pass_state.vertex_buffer_count; i++) { + render_pass_state.vertex_buffer_views[i].StrideInBytes = render_pass_state.vf_info->vertex_buffer_strides[i]; + } + p_cmd_buf_info->cmd_list->IASetVertexBuffers(0, render_pass_state.vertex_buffer_count, render_pass_state.vertex_buffer_views); + render_pass_state.vertex_buffer_count = 0; + } +} + +void RenderingDeviceDriverD3D12::command_render_set_blend_constants(CommandBufferID p_cmd_buffer, const Color &p_constants) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + cmd_buf_info->cmd_list->OMSetBlendFactor(p_constants.components); +} + +void RenderingDeviceDriverD3D12::command_render_set_line_width(CommandBufferID p_cmd_buffer, float p_width) { + if (!Math::is_equal_approx(p_width, 1.0f)) { + ERR_FAIL_MSG("Setting line widths other than 1.0 is not supported by the Direct3D 12 rendering driver."); + } +} + +// ----- PIPELINE ----- + +static const D3D12_PRIMITIVE_TOPOLOGY_TYPE RD_PRIMITIVE_TO_D3D12_TOPOLOGY_TYPE[RDD::RENDER_PRIMITIVE_MAX] = { + D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH, +}; + +static const D3D12_PRIMITIVE_TOPOLOGY RD_PRIMITIVE_TO_D3D12_TOPOLOGY[RDD::RENDER_PRIMITIVE_MAX] = { + D3D_PRIMITIVE_TOPOLOGY_POINTLIST, + D3D_PRIMITIVE_TOPOLOGY_LINELIST, + D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ, + D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, + D3D_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST, +}; + +static const D3D12_CULL_MODE RD_POLYGON_CULL_TO_D3D12_CULL_MODE[RDD::POLYGON_CULL_MAX] = { + D3D12_CULL_MODE_NONE, + D3D12_CULL_MODE_FRONT, + D3D12_CULL_MODE_BACK, +}; + +static const D3D12_STENCIL_OP RD_TO_D3D12_STENCIL_OP[RDD::STENCIL_OP_MAX] = { + D3D12_STENCIL_OP_KEEP, + D3D12_STENCIL_OP_ZERO, + D3D12_STENCIL_OP_REPLACE, + D3D12_STENCIL_OP_INCR_SAT, + D3D12_STENCIL_OP_DECR_SAT, + D3D12_STENCIL_OP_INVERT, + D3D12_STENCIL_OP_INCR, + D3D12_STENCIL_OP_DECR, +}; + +static const D3D12_LOGIC_OP RD_TO_D3D12_LOGIC_OP[RDD::LOGIC_OP_MAX] = { + D3D12_LOGIC_OP_CLEAR, + D3D12_LOGIC_OP_AND, + D3D12_LOGIC_OP_AND_REVERSE, + D3D12_LOGIC_OP_COPY, + D3D12_LOGIC_OP_AND_INVERTED, + D3D12_LOGIC_OP_NOOP, + D3D12_LOGIC_OP_XOR, + D3D12_LOGIC_OP_OR, + D3D12_LOGIC_OP_NOR, + D3D12_LOGIC_OP_EQUIV, + D3D12_LOGIC_OP_INVERT, + D3D12_LOGIC_OP_OR_REVERSE, + D3D12_LOGIC_OP_COPY_INVERTED, + D3D12_LOGIC_OP_OR_INVERTED, + D3D12_LOGIC_OP_NAND, + D3D12_LOGIC_OP_SET, +}; + +static const D3D12_BLEND RD_TO_D3D12_BLEND_FACTOR[RDD::BLEND_FACTOR_MAX] = { + D3D12_BLEND_ZERO, + D3D12_BLEND_ONE, + D3D12_BLEND_SRC_COLOR, + D3D12_BLEND_INV_SRC_COLOR, + D3D12_BLEND_DEST_COLOR, + D3D12_BLEND_INV_DEST_COLOR, + D3D12_BLEND_SRC_ALPHA, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_DEST_ALPHA, + D3D12_BLEND_INV_DEST_ALPHA, + D3D12_BLEND_BLEND_FACTOR, + D3D12_BLEND_INV_BLEND_FACTOR, + D3D12_BLEND_BLEND_FACTOR, + D3D12_BLEND_INV_BLEND_FACTOR, + D3D12_BLEND_SRC_ALPHA_SAT, + D3D12_BLEND_SRC1_COLOR, + D3D12_BLEND_INV_SRC1_COLOR, + D3D12_BLEND_SRC1_ALPHA, + D3D12_BLEND_INV_SRC1_ALPHA, +}; + +static const D3D12_BLEND_OP RD_TO_D3D12_BLEND_OP[RDD::BLEND_OP_MAX] = { + D3D12_BLEND_OP_ADD, + D3D12_BLEND_OP_SUBTRACT, + D3D12_BLEND_OP_REV_SUBTRACT, + D3D12_BLEND_OP_MIN, + D3D12_BLEND_OP_MAX, +}; + +RDD::PipelineID RenderingDeviceDriverD3D12::render_pipeline_create( + ShaderID p_shader, + VertexFormatID p_vertex_format, + RenderPrimitive p_render_primitive, + PipelineRasterizationState p_rasterization_state, + PipelineMultisampleState p_multisample_state, + PipelineDepthStencilState p_depth_stencil_state, + PipelineColorBlendState p_blend_state, + VectorView<int32_t> p_color_attachments, + BitField<PipelineDynamicStateFlags> p_dynamic_state, + RenderPassID p_render_pass, + uint32_t p_render_subpass, + VectorView<PipelineSpecializationConstant> p_specialization_constants) { + const ShaderInfo *shader_info_in = (const ShaderInfo *)p_shader.id; + + CD3DX12_PIPELINE_STATE_STREAM pipeline_desc = {}; + RenderPipelineExtraInfo pso_extra_info; + + const RenderPassInfo *pass_info = (const RenderPassInfo *)p_render_pass.id; + + // Attachments. + LocalVector<uint32_t> color_attachments; + { + const Subpass &subpass = pass_info->subpasses[p_render_subpass]; + + for (uint32_t i = 0; i < ARRAY_SIZE((&pipeline_desc.RTVFormats)->RTFormats); i++) { + (&pipeline_desc.RTVFormats)->RTFormats[i] = DXGI_FORMAT_UNKNOWN; + } + + for (uint32_t i = 0; i < subpass.color_references.size(); i++) { + const AttachmentReference &ref = subpass.color_references[i]; + if (ref.attachment != AttachmentReference::UNUSED) { + const Attachment &attachment = pass_info->attachments[ref.attachment]; + DEV_ASSERT((&pipeline_desc.RTVFormats)->RTFormats[i] == DXGI_FORMAT_UNKNOWN); + (&pipeline_desc.RTVFormats)->RTFormats[i] = RD_TO_D3D12_FORMAT[attachment.format].general_format; + } + } + (&pipeline_desc.RTVFormats)->NumRenderTargets = p_color_attachments.size(); + + if (subpass.depth_stencil_reference.attachment != AttachmentReference::UNUSED) { + const Attachment &attachment = pass_info->attachments[subpass.depth_stencil_reference.attachment]; + pipeline_desc.DSVFormat = RD_TO_D3D12_FORMAT[attachment.format].dsv_format; + } else { + pipeline_desc.DSVFormat = DXGI_FORMAT_UNKNOWN; + } + } + + // Vertex. + if (p_vertex_format) { + const VertexFormatInfo *vf_info = (const VertexFormatInfo *)p_vertex_format.id; + (&pipeline_desc.InputLayout)->pInputElementDescs = vf_info->input_elem_descs.ptr(); + (&pipeline_desc.InputLayout)->NumElements = vf_info->input_elem_descs.size(); + pso_extra_info.vf_info = vf_info; + } + + // Input assembly & tessellation. + + pipeline_desc.PrimitiveTopologyType = RD_PRIMITIVE_TO_D3D12_TOPOLOGY_TYPE[p_render_primitive]; + if (p_render_primitive == RENDER_PRIMITIVE_TESSELATION_PATCH) { + // Is there any way to get the true point count limit? + ERR_FAIL_COND_V(p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > 32, PipelineID()); + pso_extra_info.dyn_params.primitive_topology = (D3D12_PRIMITIVE_TOPOLOGY)((int)D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + p_rasterization_state.patch_control_points); + } else { + pso_extra_info.dyn_params.primitive_topology = RD_PRIMITIVE_TO_D3D12_TOPOLOGY[p_render_primitive]; + } + if (p_render_primitive == RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_RESTART_INDEX) { + // TODO: This is right for 16-bit indices; for 32-bit there's a different enum value to set, but we don't know at this point. + pipeline_desc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF; + } else { + pipeline_desc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED; + } + + // Rasterization. + (&pipeline_desc.RasterizerState)->DepthClipEnable = !p_rasterization_state.enable_depth_clamp; + // In D3D12, discard can be supported with some extra effort (empty pixel shader + disable depth/stencil test); that said, unsupported by now. + ERR_FAIL_COND_V(p_rasterization_state.discard_primitives, PipelineID()); + (&pipeline_desc.RasterizerState)->FillMode = p_rasterization_state.wireframe ? D3D12_FILL_MODE_WIREFRAME : D3D12_FILL_MODE_SOLID; + (&pipeline_desc.RasterizerState)->CullMode = RD_POLYGON_CULL_TO_D3D12_CULL_MODE[p_rasterization_state.cull_mode]; + (&pipeline_desc.RasterizerState)->FrontCounterClockwise = p_rasterization_state.front_face == POLYGON_FRONT_FACE_COUNTER_CLOCKWISE; + // In D3D12, there's still a point in setting up depth bias with no depth buffer, but just zeroing (disabling) it all in such case is closer to Vulkan. + if (p_rasterization_state.depth_bias_enabled && pipeline_desc.DSVFormat != DXGI_FORMAT_UNKNOWN) { + (&pipeline_desc.RasterizerState)->DepthBias = p_rasterization_state.depth_bias_constant_factor; + (&pipeline_desc.RasterizerState)->DepthBiasClamp = p_rasterization_state.depth_bias_clamp; + (&pipeline_desc.RasterizerState)->SlopeScaledDepthBias = p_rasterization_state.depth_bias_slope_factor; + (&pipeline_desc.RasterizerState)->DepthBias = 0; + (&pipeline_desc.RasterizerState)->DepthBiasClamp = 0.0f; + (&pipeline_desc.RasterizerState)->SlopeScaledDepthBias = 0.0f; + } + (&pipeline_desc.RasterizerState)->ForcedSampleCount = 0; + (&pipeline_desc.RasterizerState)->ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + (&pipeline_desc.RasterizerState)->MultisampleEnable = TEXTURE_SAMPLES_COUNT[p_multisample_state.sample_count] != 1; + (&pipeline_desc.RasterizerState)->AntialiasedLineEnable = true; + + // In D3D12, there's no line width. + ERR_FAIL_COND_V(!Math::is_equal_approx(p_rasterization_state.line_width, 1.0f), PipelineID()); + + // Multisample. + ERR_FAIL_COND_V(p_multisample_state.enable_sample_shading, PipelineID()); // How one enables this in D3D12? + if ((&pipeline_desc.RTVFormats)->NumRenderTargets || pipeline_desc.DSVFormat != DXGI_FORMAT_UNKNOWN) { + uint32_t sample_count = MIN( + pass_info->max_supported_sample_count, + TEXTURE_SAMPLES_COUNT[p_multisample_state.sample_count]); + (&pipeline_desc.SampleDesc)->Count = sample_count; + } else { + (&pipeline_desc.SampleDesc)->Count = 1; + } + if ((&pipeline_desc.SampleDesc)->Count > 1) { + (&pipeline_desc.SampleDesc)->Quality = DXGI_STANDARD_MULTISAMPLE_QUALITY_PATTERN; + } else { + (&pipeline_desc.SampleDesc)->Quality = 0; + } + if (p_multisample_state.sample_mask.size()) { + for (int i = 1; i < p_multisample_state.sample_mask.size(); i++) { + // In D3D12 there's a single sample mask for every pixel. + ERR_FAIL_COND_V(p_multisample_state.sample_mask[i] != p_multisample_state.sample_mask[0], PipelineID()); + } + pipeline_desc.SampleMask = p_multisample_state.sample_mask[0]; + } else { + pipeline_desc.SampleMask = 0xffffffff; + } + + // Depth stencil. + + if (pipeline_desc.DSVFormat == DXGI_FORMAT_UNKNOWN) { + (&pipeline_desc.DepthStencilState)->DepthEnable = false; + (&pipeline_desc.DepthStencilState)->StencilEnable = false; + } else { + (&pipeline_desc.DepthStencilState)->DepthEnable = p_depth_stencil_state.enable_depth_test; + (&pipeline_desc.DepthStencilState)->DepthWriteMask = p_depth_stencil_state.enable_depth_write ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; + (&pipeline_desc.DepthStencilState)->DepthFunc = RD_TO_D3D12_COMPARE_OP[p_depth_stencil_state.depth_compare_operator]; + (&pipeline_desc.DepthStencilState)->DepthBoundsTestEnable = p_depth_stencil_state.enable_depth_range; + (&pipeline_desc.DepthStencilState)->StencilEnable = p_depth_stencil_state.enable_stencil; + + // In D3D12 some elements can't be different across front and back. + ERR_FAIL_COND_V(p_depth_stencil_state.front_op.compare_mask != p_depth_stencil_state.back_op.compare_mask, PipelineID()); + ERR_FAIL_COND_V(p_depth_stencil_state.front_op.write_mask != p_depth_stencil_state.back_op.write_mask, PipelineID()); + ERR_FAIL_COND_V(p_depth_stencil_state.front_op.reference != p_depth_stencil_state.back_op.reference, PipelineID()); + (&pipeline_desc.DepthStencilState)->StencilReadMask = p_depth_stencil_state.front_op.compare_mask; + (&pipeline_desc.DepthStencilState)->StencilWriteMask = p_depth_stencil_state.front_op.write_mask; + + (&pipeline_desc.DepthStencilState)->FrontFace.StencilFailOp = RD_TO_D3D12_STENCIL_OP[p_depth_stencil_state.front_op.fail]; + (&pipeline_desc.DepthStencilState)->FrontFace.StencilPassOp = RD_TO_D3D12_STENCIL_OP[p_depth_stencil_state.front_op.pass]; + (&pipeline_desc.DepthStencilState)->FrontFace.StencilDepthFailOp = RD_TO_D3D12_STENCIL_OP[p_depth_stencil_state.front_op.depth_fail]; + (&pipeline_desc.DepthStencilState)->FrontFace.StencilFunc = RD_TO_D3D12_COMPARE_OP[p_depth_stencil_state.front_op.compare]; + + (&pipeline_desc.DepthStencilState)->BackFace.StencilFailOp = RD_TO_D3D12_STENCIL_OP[p_depth_stencil_state.back_op.fail]; + (&pipeline_desc.DepthStencilState)->BackFace.StencilPassOp = RD_TO_D3D12_STENCIL_OP[p_depth_stencil_state.back_op.pass]; + (&pipeline_desc.DepthStencilState)->BackFace.StencilDepthFailOp = RD_TO_D3D12_STENCIL_OP[p_depth_stencil_state.back_op.depth_fail]; + (&pipeline_desc.DepthStencilState)->BackFace.StencilFunc = RD_TO_D3D12_COMPARE_OP[p_depth_stencil_state.back_op.compare]; + + pso_extra_info.dyn_params.depth_bounds_min = p_depth_stencil_state.enable_depth_range ? p_depth_stencil_state.depth_range_min : 0.0f; + pso_extra_info.dyn_params.depth_bounds_max = p_depth_stencil_state.enable_depth_range ? p_depth_stencil_state.depth_range_max : 1.0f; + pso_extra_info.dyn_params.stencil_reference = p_depth_stencil_state.front_op.reference; + } + + // Blend states. + (&pipeline_desc.BlendState)->AlphaToCoverageEnable = p_multisample_state.enable_alpha_to_coverage; + { + bool all_attachments_same_blend = true; + for (int i = 0; i < p_blend_state.attachments.size(); i++) { + const PipelineColorBlendState::Attachment &bs = p_blend_state.attachments[i]; + D3D12_RENDER_TARGET_BLEND_DESC &bd = (&pipeline_desc.BlendState)->RenderTarget[i]; + + bd.BlendEnable = bs.enable_blend; + bd.LogicOpEnable = p_blend_state.enable_logic_op; + bd.LogicOp = RD_TO_D3D12_LOGIC_OP[p_blend_state.logic_op]; + + bd.SrcBlend = RD_TO_D3D12_BLEND_FACTOR[bs.src_color_blend_factor]; + bd.DestBlend = RD_TO_D3D12_BLEND_FACTOR[bs.dst_color_blend_factor]; + bd.BlendOp = RD_TO_D3D12_BLEND_OP[bs.color_blend_op]; + + bd.SrcBlendAlpha = RD_TO_D3D12_BLEND_FACTOR[bs.src_alpha_blend_factor]; + bd.DestBlendAlpha = RD_TO_D3D12_BLEND_FACTOR[bs.dst_alpha_blend_factor]; + bd.BlendOpAlpha = RD_TO_D3D12_BLEND_OP[bs.alpha_blend_op]; + + if (bs.write_r) { + bd.RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_RED; + } + if (bs.write_g) { + bd.RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_GREEN; + } + if (bs.write_b) { + bd.RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_BLUE; + } + if (bs.write_a) { + bd.RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_ALPHA; + } + + if (i > 0 && all_attachments_same_blend) { + all_attachments_same_blend = &(&pipeline_desc.BlendState)->RenderTarget[i] == &(&pipeline_desc.BlendState)->RenderTarget[0]; + } + } + + // Per D3D12 docs, if logic op used, independent blending is not supported. + ERR_FAIL_COND_V(p_blend_state.enable_logic_op && !all_attachments_same_blend, PipelineID()); + + (&pipeline_desc.BlendState)->IndependentBlendEnable = !all_attachments_same_blend; + } + + pso_extra_info.dyn_params.blend_constant = p_blend_state.blend_constant; + + // Stages bytecodes + specialization constants. + + pipeline_desc.pRootSignature = shader_info_in->root_signature.Get(); + + HashMap<ShaderStage, Vector<uint8_t>> final_stages_bytecode; + bool ok = _shader_apply_specialization_constants(shader_info_in, p_specialization_constants, final_stages_bytecode); + ERR_FAIL_COND_V(!ok, PipelineID()); + + pipeline_desc.VS = D3D12_SHADER_BYTECODE{ + final_stages_bytecode[SHADER_STAGE_VERTEX].ptr(), + (SIZE_T)final_stages_bytecode[SHADER_STAGE_VERTEX].size() + }; + pipeline_desc.PS = D3D12_SHADER_BYTECODE{ + final_stages_bytecode[SHADER_STAGE_FRAGMENT].ptr(), + (SIZE_T)final_stages_bytecode[SHADER_STAGE_FRAGMENT].size() + }; + + ComPtr<ID3D12Device2> device_2; + device->QueryInterface(device_2.GetAddressOf()); + ID3D12PipelineState *pso = nullptr; + HRESULT res = E_FAIL; + if (device_2) { + D3D12_PIPELINE_STATE_STREAM_DESC pssd = {}; + pssd.pPipelineStateSubobjectStream = &pipeline_desc; + pssd.SizeInBytes = sizeof(pipeline_desc); + res = device_2->CreatePipelineState(&pssd, IID_PPV_ARGS(&pso)); + } else { + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = pipeline_desc.GraphicsDescV0(); + res = device->CreateGraphicsPipelineState(&desc, IID_PPV_ARGS(&pso)); + } + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), PipelineID(), "Create(Graphics)PipelineState failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + + // Bookkeep ancillary info. + + pipelines_shaders[pso] = shader_info_in; + render_psos_extra_info[pso] = pso_extra_info; + + return PipelineID(pso); +} + +/*****************/ +/**** COMPUTE ****/ +/*****************/ + +// ----- COMMANDS ----- + +void RenderingDeviceDriverD3D12::command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) { + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + ID3D12PipelineState *pso = (ID3D12PipelineState *)p_pipeline.id; + const ShaderInfo *shader_info_in = pipelines_shaders[pso]; + + if (cmd_buf_info->compute_pso == pso) { + return; + } + + cmd_buf_info->cmd_list->SetPipelineState(pso); + if (cmd_buf_info->compute_root_signature_crc != shader_info_in->root_signature_crc) { + cmd_buf_info->cmd_list->SetComputeRootSignature(shader_info_in->root_signature.Get()); + cmd_buf_info->compute_root_signature_crc = shader_info_in->root_signature_crc; + } + + cmd_buf_info->compute_pso = pso; + cmd_buf_info->graphics_pso = nullptr; +} + +void RenderingDeviceDriverD3D12::command_bind_compute_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) { + _command_bind_uniform_set(p_cmd_buffer, p_uniform_set, p_shader, p_set_index, true); +} + +void RenderingDeviceDriverD3D12::command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + cmd_buf_info->cmd_list->Dispatch(p_x_groups, p_y_groups, p_z_groups); +} + +void RenderingDeviceDriverD3D12::command_compute_dispatch_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + BufferInfo *indirect_buf_info = (BufferInfo *)p_indirect_buffer.id; + _resource_transition_batch(indirect_buf_info, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); + _resource_transitions_flush(cmd_buf_info->cmd_list.Get()); + cmd_buf_info->cmd_list->ExecuteIndirect(indirect_cmd_signatures.dispatch.Get(), 1, indirect_buf_info->resource, p_offset, nullptr, 0); +} + +// ----- PIPELINE ----- + +RDD::PipelineID RenderingDeviceDriverD3D12::compute_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) { + const ShaderInfo *shader_info_in = (const ShaderInfo *)p_shader.id; + + CD3DX12_PIPELINE_STATE_STREAM pipeline_desc = {}; + + // Stages bytecodes + specialization constants. + + pipeline_desc.pRootSignature = shader_info_in->root_signature.Get(); + + HashMap<ShaderStage, Vector<uint8_t>> final_stages_bytecode; + bool ok = _shader_apply_specialization_constants(shader_info_in, p_specialization_constants, final_stages_bytecode); + ERR_FAIL_COND_V(!ok, PipelineID()); + + pipeline_desc.CS = D3D12_SHADER_BYTECODE{ + final_stages_bytecode[SHADER_STAGE_COMPUTE].ptr(), + (SIZE_T)final_stages_bytecode[SHADER_STAGE_COMPUTE].size() + }; + + ComPtr<ID3D12Device2> device_2; + device->QueryInterface(device_2.GetAddressOf()); + ID3D12PipelineState *pso = nullptr; + HRESULT res = E_FAIL; + if (device_2) { + D3D12_PIPELINE_STATE_STREAM_DESC pssd = {}; + pssd.pPipelineStateSubobjectStream = &pipeline_desc; + pssd.SizeInBytes = sizeof(pipeline_desc); + res = device_2->CreatePipelineState(&pssd, IID_PPV_ARGS(&pso)); + } else { + D3D12_COMPUTE_PIPELINE_STATE_DESC desc = pipeline_desc.ComputeDescV0(); + res = device->CreateComputePipelineState(&desc, IID_PPV_ARGS(&pso)); + } + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), PipelineID(), "Create(Compute)PipelineState failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + + // Bookkeep ancillary info. + + pipelines_shaders[pso] = shader_info_in; + + return PipelineID(pso); +} + +/*****************/ +/**** QUERIES ****/ +/*****************/ + +// ----- TIMESTAMP ----- + +RDD::QueryPoolID RenderingDeviceDriverD3D12::timestamp_query_pool_create(uint32_t p_query_count) { + ComPtr<ID3D12QueryHeap> query_heap; + { + D3D12_QUERY_HEAP_DESC qh_desc = {}; + qh_desc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; + qh_desc.Count = p_query_count; + qh_desc.NodeMask = 0; + HRESULT res = device->CreateQueryHeap(&qh_desc, IID_PPV_ARGS(query_heap.GetAddressOf())); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), QueryPoolID(), "CreateQueryHeap failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + } + + ComPtr<D3D12MA::Allocation> results_buffer_allocation; + { + D3D12MA::ALLOCATION_DESC allocation_desc = {}; + allocation_desc.HeapType = D3D12_HEAP_TYPE_READBACK; + + CD3DX12_RESOURCE_DESC resource_desc = CD3DX12_RESOURCE_DESC::Buffer(sizeof(uint64_t) * p_query_count); + + ComPtr<ID3D12Resource> results_buffer; + HRESULT res = allocator->CreateResource( + &allocation_desc, + &resource_desc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + results_buffer_allocation.GetAddressOf(), + IID_PPV_ARGS(results_buffer.GetAddressOf())); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), QueryPoolID(), "D3D12MA::CreateResource failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + } + + // Bookkeep. + + TimestampQueryPoolInfo *tqp_info = VersatileResource::allocate<TimestampQueryPoolInfo>(resources_allocator); + tqp_info->query_heap = query_heap; + tqp_info->query_count = p_query_count; + tqp_info->results_buffer_allocation = results_buffer_allocation; + + return RDD::QueryPoolID(tqp_info); +} + +void RenderingDeviceDriverD3D12::timestamp_query_pool_free(QueryPoolID p_pool_id) { + TimestampQueryPoolInfo *tqp_info = (TimestampQueryPoolInfo *)p_pool_id.id; + VersatileResource::free(resources_allocator, tqp_info); +} + +void RenderingDeviceDriverD3D12::timestamp_query_pool_get_results(QueryPoolID p_pool_id, uint32_t p_query_count, uint64_t *r_results) { + TimestampQueryPoolInfo *tqp_info = (TimestampQueryPoolInfo *)p_pool_id.id; + + ID3D12Resource *results_buffer = tqp_info->results_buffer_allocation->GetResource(); + + void *results_buffer_data = nullptr; + results_buffer->Map(0, &VOID_RANGE, &results_buffer_data); + memcpy(r_results, results_buffer_data, sizeof(uint64_t) * p_query_count); + results_buffer->Unmap(0, &VOID_RANGE); +} + +uint64_t RenderingDeviceDriverD3D12::timestamp_query_result_to_time(uint64_t p_result) { + return p_result / (double)context->get_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) { +} + +void RenderingDeviceDriverD3D12::command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + TimestampQueryPoolInfo *tqp_info = (TimestampQueryPoolInfo *)p_pool_id.id; + ID3D12Resource *results_buffer = tqp_info->results_buffer_allocation->GetResource(); + cmd_buf_info->cmd_list->EndQuery(tqp_info->query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, p_index); + cmd_buf_info->cmd_list->ResolveQueryData(tqp_info->query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, p_index, 1, results_buffer, p_index * sizeof(uint64_t)); +} + +void RenderingDeviceDriverD3D12::command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) { +#ifdef PIX_ENABLED + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + PIXBeginEvent(cmd_buf_info->cmd_list.Get(), p_color.to_argb32(), p_label_name); +#endif +} + +void RenderingDeviceDriverD3D12::command_end_label(CommandBufferID p_cmd_buffer) { +#ifdef PIX_ENABLED + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + PIXEndEvent(cmd_buf_info->cmd_list.Get()); +#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) { + frame_idx = p_frame_index; + + frames_drawn = p_frames_drawn; + allocator->SetCurrentFrameIndex(p_frames_drawn); + + frames[frame_idx].desc_heap_walkers.resources.rewind(); + frames[frame_idx].desc_heap_walkers.samplers.rewind(); + 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].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; +} + +void RenderingDeviceDriverD3D12::end_segment() { + segment_serial++; + segment_begun = false; +} + +/**************/ +/**** MISC ****/ +/**************/ + +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); + } + } 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); + } 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); + } 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"); + } + 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"); + } + } break; + case OBJECT_TYPE_PIPELINE: { + ID3D12PipelineState *pso = (ID3D12PipelineState *)p_driver_id.id; + context->set_object_name(pso, p_name); + } break; + default: { + DEV_ASSERT(false); + } + } +} + +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; + } + case DRIVER_RESOURCE_PHYSICAL_DEVICE: { + return (uint64_t)context->get_adapter(); + } + case DRIVER_RESOURCE_TOPMOST_OBJECT: { + return 0; + } + case DRIVER_RESOURCE_COMMAND_QUEUE: { + return (uint64_t)p_driver_id.id; + } + case DRIVER_RESOURCE_QUEUE_FAMILY: { + return 0; + } + case DRIVER_RESOURCE_TEXTURE: { + const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id; + return (uint64_t)tex_info->main_texture; + } break; + case DRIVER_RESOURCE_TEXTURE_VIEW: { + const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id; + return (uint64_t)tex_info->resource; + } + case DRIVER_RESOURCE_TEXTURE_DATA_FORMAT: { + const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id; + return (uint64_t)tex_info->desc.Format; + } + case DRIVER_RESOURCE_SAMPLER: + case DRIVER_RESOURCE_UNIFORM_SET: + return 0; + case DRIVER_RESOURCE_BUFFER: { + const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id; + return (uint64_t)tex_info->resource; + } break; + case DRIVER_RESOURCE_COMPUTE_PIPELINE: + case DRIVER_RESOURCE_RENDER_PIPELINE: { + return p_driver_id.id; + } + default: { + return 0; + } + } +} + +uint64_t RenderingDeviceDriverD3D12::get_total_memory_used() { + D3D12MA::TotalStatistics stats; + allocator->CalculateStatistics(&stats); + return stats.Total.Stats.BlockBytes; +} + +uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) { + uint64_t safe_unbounded = ((uint64_t)1 << 30); + switch (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; + case LIMIT_MAX_UNIFORM_BUFFER_SIZE: + return 65536; + case LIMIT_MAX_VIEWPORT_DIMENSIONS_X: + case LIMIT_MAX_VIEWPORT_DIMENSIONS_Y: + return 16384; // Based on max. texture size. Maybe not correct. + case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X: + return D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION; + case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y: + return D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION; + case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z: + return D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION; + case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X: + return D3D12_CS_THREAD_GROUP_MAX_X; + case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y: + return D3D12_CS_THREAD_GROUP_MAX_Y; + case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z: + return D3D12_CS_THREAD_GROUP_MAX_Z; + case LIMIT_SUBGROUP_SIZE: + // 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(); + return subgroup_capabilities.size; + } + case LIMIT_SUBGROUP_IN_SHADERS: { + const D3D12Context::SubgroupCapabilities &subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.supported_stages_flags_rd(); + } + case LIMIT_SUBGROUP_OPERATIONS: { + const D3D12Context::SubgroupCapabilities &subgroup_capabilities = context->get_subgroup_capabilities(); + 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; + } + default: { +#ifdef DEV_ENABLED + WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + "."); +#endif + return safe_unbounded; + } + } +} + +uint64_t RenderingDeviceDriverD3D12::api_trait_get(ApiTrait p_trait) { + switch (p_trait) { + case API_TRAIT_HONORS_PIPELINE_BARRIERS: + // TODO: + // 1. Map fine/Vulkan/enhanced barriers to legacy barriers as closely as possible + // so there's still some advantage even without enhanced barriers available. + // 2. Implement enhanced barriers and return true where available. + return 0; + case API_TRAIT_SHADER_CHANGE_INVALIDATION: + return (uint64_t)SHADER_CHANGE_INVALIDATION_ALL_OR_NONE_ACCORDING_TO_LAYOUT_HASH; + case API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT: + return D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT; + case API_TRAIT_TEXTURE_DATA_ROW_PITCH_STEP: + return D3D12_TEXTURE_DATA_PITCH_ALIGNMENT; + case API_TRAIT_SECONDARY_VIEWPORT_SCISSOR: + return false; + default: + return RenderingDeviceDriver::api_trait_get(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(); + return vrs_capabilities.ss_image_supported; + } break; + case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: { + return true; + } break; + default: { + return false; + } + } +} + +const RDD::MultiviewCapabilities &RenderingDeviceDriverD3D12::get_multiview_capabilities() { + return context->get_multiview_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(); + + 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<ID3D12CommandSignature> *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(); +} + +RenderingDeviceDriverD3D12::~RenderingDeviceDriverD3D12() { + { + MutexLock lock(dxil_mutex); + for (const KeyValue<int, dxil_validator *> &E : dxil_validators) { + dxil_destroy_validator(E.value); + } + } + + glsl_type_singleton_decref(); +} diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h new file mode 100644 index 0000000000..0da339c6fd --- /dev/null +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -0,0 +1,865 @@ +/**************************************************************************/ +/* rendering_device_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_DEVICE_DRIVER_D3D12_H +#define RENDERING_DEVICE_DRIVER_D3D12_H + +#include "core/templates/hash_map.h" +#include "core/templates/paged_allocator.h" +#include "servers/rendering/rendering_device_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 + +#include "d3dx12.h" +#include <dxgi1_6.h> +#define D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED +#include "D3D12MemAlloc.h" + +#include <wrl/client.h> + +#if defined(_MSC_VER) && defined(MemoryBarrier) +// Annoying define from winnt.h. Reintroduced by some of the headers above. +#undef MemoryBarrier +#endif + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + +using Microsoft::WRL::ComPtr; + +#define D3D12_BITCODE_OFFSETS_NUM_STAGES 3 + +struct dxil_validator; + +class D3D12Context; + +// 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 ****/ + /*****************/ + + struct D3D12Format { + DXGI_FORMAT family = DXGI_FORMAT_UNKNOWN; + DXGI_FORMAT general_format = DXGI_FORMAT_UNKNOWN; + UINT swizzle = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + DXGI_FORMAT dsv_format = DXGI_FORMAT_UNKNOWN; + }; + + static const D3D12Format RD_TO_D3D12_FORMAT[RDD::DATA_FORMAT_MAX]; + + D3D12Context *context = nullptr; + ID3D12Device *device = nullptr; // Owned by the context. + + class DescriptorsHeap { + D3D12_DESCRIPTOR_HEAP_DESC desc = {}; + ComPtr<ID3D12DescriptorHeap> heap; + uint32_t handle_size = 0; + + public: + class Walker { // Texas Ranger. + friend class DescriptorsHeap; + + uint32_t handle_size = 0; + uint32_t handle_count = 0; + D3D12_CPU_DESCRIPTOR_HANDLE first_cpu_handle = {}; + D3D12_GPU_DESCRIPTOR_HANDLE first_gpu_handle = {}; + uint32_t handle_index = 0; + + public: + D3D12_CPU_DESCRIPTOR_HANDLE get_curr_cpu_handle(); + D3D12_GPU_DESCRIPTOR_HANDLE get_curr_gpu_handle(); + _FORCE_INLINE_ void rewind() { handle_index = 0; } + void advance(uint32_t p_count = 1); + uint32_t get_current_handle_index() const { return handle_index; } + uint32_t get_free_handles() { return handle_count - handle_index; } + bool is_at_eof() { return handle_index == handle_count; } + }; + + Error allocate(ID3D12Device *m_device, D3D12_DESCRIPTOR_HEAP_TYPE m_type, uint32_t m_descriptor_count, bool p_for_gpu); + uint32_t get_descriptor_count() const { return desc.NumDescriptors; } + ID3D12DescriptorHeap *get_heap() const { return heap.Get(); } + + Walker make_walker() const; + }; + + struct { + ComPtr<ID3D12CommandSignature> draw; + ComPtr<ID3D12CommandSignature> draw_indexed; + ComPtr<ID3D12CommandSignature> dispatch; + } indirect_cmd_signatures; + + /****************/ + /**** MEMORY ****/ + /****************/ + + ComPtr<D3D12MA::Allocator> allocator; + +#define USE_SMALL_ALLOCS_POOL // Disabled by now; seems not to be beneficial as it is in Vulkan. +#ifdef USE_SMALL_ALLOCS_POOL + union AllocPoolKey { + struct { + D3D12_HEAP_TYPE heap_type; + D3D12_HEAP_FLAGS heap_flags; + }; + uint64_t key = 0; + }; + HashMap<uint64_t, ComPtr<D3D12MA::Pool>> small_allocs_pools; + + D3D12MA::Pool *_find_or_create_small_allocs_pool(D3D12_HEAP_TYPE p_heap_type, D3D12_HEAP_FLAGS p_heap_flags); +#endif + + /******************/ + /**** RESOURCE ****/ + /******************/ + + struct ResourceInfo { + struct States { + // As many subresources as mipmaps * layers; planes (for depth-stencil) are tracked together. + TightLocalVector<D3D12_RESOURCE_STATES> subresource_states; // Used only if not a view. + uint32_t last_batch_transitioned_to_uav = 0; + uint32_t last_batch_with_uav_barrier = 0; + }; + + ID3D12Resource *resource = nullptr; // Non-null even if a view. + struct { + ComPtr<ID3D12Resource> resource; + ComPtr<D3D12MA::Allocation> allocation; + States states; + } owner_info; // All empty if a view. + States *states_ptr = nullptr; // Own or from another if a view. + }; + + struct BarrierRequest { + static const uint32_t MAX_GROUPS = 4; + // Maybe this is too much data to have it locally. Benchmarking may reveal that + // cache would be used better by having a maximum of local subresource masks and beyond + // that have an allocated vector with the rest. + static const uint32_t MAX_SUBRESOURCES = 4096; + ID3D12Resource *dx_resource = nullptr; + uint8_t subres_mask_qwords = 0; + uint8_t planes = 0; + struct Group { + D3D12_RESOURCE_STATES states = {}; + static_assert(MAX_SUBRESOURCES % 64 == 0); + 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; + }; + PagedAllocator<HashMapElement<ResourceInfo::States *, BarrierRequest>> res_barriers_requests_allocator; + HashMap<ResourceInfo::States *, BarrierRequest, HashMapHasherDefault, HashMapComparatorDefault<ResourceInfo::States *>, decltype(res_barriers_requests_allocator)> res_barriers_requests; + + LocalVector<D3D12_RESOURCE_BARRIER> res_barriers; + uint32_t res_barriers_count = 0; + uint32_t res_barriers_batch = 0; +#ifdef DEV_ENABLED + int frame_barriers_count = 0; + int frame_barriers_batches_count = 0; + uint64_t frame_barriers_cpu_time = 0; +#endif + + void _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 = nullptr); + void _resource_transitions_flush(ID3D12GraphicsCommandList *p_cmd_list); + + /*****************/ + /**** BUFFERS ****/ + /*****************/ + + struct BufferInfo : public ResourceInfo { + DataFormat texel_format = DATA_FORMAT_MAX; + uint64_t size = 0; + struct { + bool usable_as_uav : 1; + bool is_for_upload : 1; + } flags = {}; + }; + +public: + virtual BufferID buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type) override final; + virtual bool buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) override final; + virtual void buffer_free(BufferID p_buffer) override final; + virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final; + virtual uint8_t *buffer_map(BufferID p_buffer) override final; + virtual void buffer_unmap(BufferID p_buffer) override final; + + /*****************/ + /**** TEXTURE ****/ + /*****************/ +private: + struct TextureInfo : public ResourceInfo { + DataFormat format = DATA_FORMAT_MAX; + CD3DX12_RESOURCE_DESC desc = {}; + uint32_t base_layer = 0; + uint32_t layers = 0; + uint32_t base_mip = 0; + uint32_t mipmaps = 0; + + struct { + D3D12_SHADER_RESOURCE_VIEW_DESC srv; + D3D12_UNORDERED_ACCESS_VIEW_DESC uav; + } view_descs = {}; + + ID3D12Resource *main_texture = nullptr; + struct { + D3D12_UNORDERED_ACCESS_VIEW_DESC main_uav_desc; + struct { + HashMap<DXGI_FORMAT, ComPtr<ID3D12Resource>> aliases; // Key is the DXGI format family. + } owner_info = {}; + } aliasing_hack = {}; // [[CROSS_FAMILY_ALIASING]] + + UINT mapped_subresource = UINT_MAX; + }; + + HashMap<DXGI_FORMAT, uint32_t> format_sample_counts_mask_cache; + + uint32_t _find_max_common_supported_sample_count(VectorView<DXGI_FORMAT> p_formats); + UINT _compute_component_mapping(const TextureView &p_view); + UINT _compute_plane_slice(DataFormat p_format, BitField<TextureAspectBits> p_aspect_bits); + UINT _compute_plane_slice(DataFormat p_format, TextureAspect p_aspect); + + struct CommandBufferInfo; + void _discard_texture_subresources(const TextureInfo *p_tex_info, const CommandBufferInfo *p_cmd_buf_info); + +public: + virtual TextureID texture_create(const TextureFormat &p_format, const TextureView &p_view) override final; + virtual TextureID texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) override final; + virtual TextureID texture_create_shared(TextureID p_original_texture, const TextureView &p_view) override final; + virtual TextureID texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) override final; + virtual void texture_free(TextureID p_texture) override final; + virtual uint64_t texture_get_allocation_size(TextureID p_texture) override final; + virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) override final; + virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) override final; + virtual void texture_unmap(TextureID p_texture) override final; + virtual BitField<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final; + + /*****************/ + /**** SAMPLER ****/ + /*****************/ +private: + LocalVector<D3D12_SAMPLER_DESC> samplers; + +public: + virtual SamplerID sampler_create(const SamplerState &p_state) final override; + virtual void sampler_free(SamplerID p_sampler) final override; + virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) override final; + + /**********************/ + /**** VERTEX ARRAY ****/ + /**********************/ +private: + struct VertexFormatInfo { + TightLocalVector<D3D12_INPUT_ELEMENT_DESC> input_elem_descs; + TightLocalVector<UINT> vertex_buffer_strides; + }; + +public: + virtual VertexFormatID vertex_format_create(VectorView<VertexAttribute> p_vertex_attribs) override final; + virtual void vertex_format_free(VertexFormatID p_vertex_format) override final; + + /******************/ + /**** BARRIERS ****/ + /******************/ + + virtual void command_pipeline_barrier( + CommandBufferID p_cmd_buffer, + BitField<RDD::PipelineStageBits> p_src_stages, + BitField<RDD::PipelineStageBits> p_dst_stages, + VectorView<RDD::MemoryBarrier> p_memory_barriers, + VectorView<RDD::BufferBarrier> p_buffer_barriers, + VectorView<RDD::TextureBarrier> p_texture_barriers) override final; + + /*************************/ + /**** COMMAND BUFFERS ****/ + /*************************/ + + // ----- POOL ----- + + virtual CommandPoolID command_pool_create(CommandBufferType p_cmd_buffer_type) override final; + virtual void command_pool_free(CommandPoolID p_cmd_pool) override final; + + // ----- BUFFER ----- + +private: + // Belongs to RENDERING-SUBPASS, but needed here. + struct FramebufferInfo; + struct RenderPassInfo; + struct RenderPassState { + uint32_t current_subpass = UINT32_MAX; + const FramebufferInfo *fb_info = nullptr; + const RenderPassInfo *pass_info = nullptr; + CD3DX12_RECT region_rect = {}; + bool region_is_all = false; + + const VertexFormatInfo *vf_info = nullptr; + D3D12_VERTEX_BUFFER_VIEW vertex_buffer_views[8] = {}; + uint32_t vertex_buffer_count = 0; + }; + + // Leveraging knowledge of actual usage and D3D12 specifics (namely, command lists from the same allocator + // can't be freely begun and ended), an allocator per list works better. + struct CommandBufferInfo { + ComPtr<ID3D12CommandAllocator> cmd_allocator; + ComPtr<ID3D12GraphicsCommandList> cmd_list; + + ID3D12PipelineState *graphics_pso = nullptr; + ID3D12PipelineState *compute_pso = nullptr; + + uint32_t graphics_root_signature_crc = 0; + uint32_t compute_root_signature_crc = 0; + + RenderPassState render_pass_state; + }; + RBMap<CommandPoolID, LocalVector<CommandBufferInfo *>> 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 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<CommandBufferID> p_secondary_cmd_buffers) override final; + + /*********************/ + /**** FRAMEBUFFER ****/ + /*********************/ +private: + struct FramebufferInfo { + bool is_screen = false; + Size2i size; + TightLocalVector<uint32_t> attachments_handle_inds; // RTV heap index for color; DSV heap index for DSV. + DescriptorsHeap rtv_heap; + DescriptorsHeap dsv_heap; // Used only if not for screen and some depth-stencil attachments. + + TightLocalVector<TextureID> attachments; // Color and depth-stencil. Used if not screen. + TextureID vrs_attachment; + }; + + 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); + +public: + virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) override final; + virtual void framebuffer_free(FramebufferID p_framebuffer) override final; + + /****************/ + /**** SHADER ****/ + /****************/ +private: + static const uint32_t ROOT_SIGNATURE_SIZE = 256; + static const uint32_t PUSH_CONSTANT_SIZE = 128; // Mimicking Vulkan. + + enum { + // We can only aim to set a maximum here, since depending on the shader + // there may be more or less root signature free for descriptor tables. + // Therefore, we'll have to rely on the final check at runtime, when building + // the root signature structure for a given shader. + // To be precise, these may be present or not, and their size vary statically: + // - Push constant (we'll assume this is always present to avoid reserving much + // more space for descriptor sets than needed for almost any imaginable case, + // given that most shader templates feature push constants). + // - NIR-DXIL runtime data. + MAX_UNIFORM_SETS = (ROOT_SIGNATURE_SIZE - PUSH_CONSTANT_SIZE) / sizeof(uint32_t), + }; + + enum RootSignatureLocationType { + RS_LOC_TYPE_RESOURCE, + RS_LOC_TYPE_SAMPLER, + }; + + enum ResourceClass { + RES_CLASS_INVALID, + RES_CLASS_CBV, + RES_CLASS_SRV, + RES_CLASS_UAV, + }; + + struct ShaderBinary { + // Version 1: Initial. + // Version 2: 64-bit vertex input mask. + // Version 3: Added SC stage mask. + static const uint32_t VERSION = 3; + + // Phase 1: SPIR-V reflection, where the Vulkan/RD interface of the shader is discovered. + // Phase 2: SPIR-V to DXIL translation, where the DXIL interface is discovered, which may have gaps due to optimizations. + + struct DataBinding { + // - Phase 1. + uint32_t type = 0; + uint32_t binding = 0; + uint32_t stages = 0; + uint32_t length = 0; // Size of arrays (in total elements), or ubos (in bytes * total elements). + uint32_t writable = 0; + // - Phase 2. + uint32_t res_class = 0; + uint32_t has_sampler = 0; + uint32_t dxil_stages = 0; + struct RootSignatureLocation { + uint32_t root_param_idx = UINT32_MAX; // UINT32_MAX if unused. + uint32_t range_idx = UINT32_MAX; // UINT32_MAX if unused. + }; + RootSignatureLocation root_sig_locations[2]; // Index is RootSignatureLocationType. + + // We need to sort these to fill the root signature locations properly. + bool operator<(const DataBinding &p_other) const { + return binding < p_other.binding; + } + }; + + struct SpecializationConstant { + // - Phase 1. + uint32_t type = 0; + uint32_t constant_id = 0; + union { + uint32_t int_value = 0; + float float_value; + bool bool_value; + }; + uint32_t stage_flags = 0; + // - Phase 2. + uint64_t stages_bit_offsets[D3D12_BITCODE_OFFSETS_NUM_STAGES] = {}; + }; + + struct Data { + uint64_t vertex_input_mask = 0; + uint32_t fragment_output_mask = 0; + uint32_t specialization_constants_count = 0; + uint32_t spirv_specialization_constants_ids_mask = 0; + uint32_t is_compute = 0; + uint32_t compute_local_size[3] = {}; + uint32_t set_count = 0; + uint32_t push_constant_size = 0; + uint32_t dxil_push_constant_stages = 0; // Phase 2. + uint32_t nir_runtime_data_root_param_idx = 0; // Phase 2. + uint32_t stage_count = 0; + uint32_t shader_name_len = 0; + uint32_t root_signature_len = 0; + uint32_t root_signature_crc = 0; + }; + }; + + struct ShaderInfo { + uint32_t dxil_push_constant_size = 0; + uint32_t nir_runtime_data_root_param_idx = UINT32_MAX; + bool is_compute = false; + + struct UniformBindingInfo { + uint32_t stages = 0; // Actual shader stages using the uniform (0 if totally optimized out). + ResourceClass res_class = RES_CLASS_INVALID; + UniformType type = UNIFORM_TYPE_MAX; + uint32_t length = UINT32_MAX; +#ifdef DEV_ENABLED + bool writable = false; +#endif + struct RootSignatureLocation { + uint32_t root_param_idx = UINT32_MAX; + uint32_t range_idx = UINT32_MAX; + }; + struct { + RootSignatureLocation resource; + RootSignatureLocation sampler; + } root_sig_locations; + }; + + struct UniformSet { + TightLocalVector<UniformBindingInfo> bindings; + struct { + uint32_t resources = 0; + uint32_t samplers = 0; + } num_root_params; + }; + + TightLocalVector<UniformSet> sets; + + struct SpecializationConstant { + uint32_t constant_id = UINT32_MAX; + uint32_t int_value = UINT32_MAX; + uint64_t stages_bit_offsets[D3D12_BITCODE_OFFSETS_NUM_STAGES] = {}; + }; + + TightLocalVector<SpecializationConstant> specialization_constants; + uint32_t spirv_specialization_constants_ids_mask = 0; + + HashMap<ShaderStage, Vector<uint8_t>> stages_bytecode; + + ComPtr<ID3D12RootSignature> root_signature; + ComPtr<ID3D12RootSignatureDeserializer> root_signature_deserializer; + const D3D12_ROOT_SIGNATURE_DESC *root_signature_desc = nullptr; // Owned by the deserializer. + uint32_t root_signature_crc = 0; + }; + + Mutex dxil_mutex; + HashMap<int, dxil_validator *> dxil_validators; // One per WorkerThreadPool thread used for shader compilation, plus one (-1) for all the other. + + dxil_validator *_get_dxil_validator_for_current_thread(); + uint32_t _shader_patch_dxil_specialization_constant( + PipelineSpecializationConstantType p_type, + const void *p_value, + const uint64_t (&p_stages_bit_offsets)[D3D12_BITCODE_OFFSETS_NUM_STAGES], + HashMap<ShaderStage, Vector<uint8_t>> &r_stages_bytecodes, + bool p_is_first_patch); + bool _shader_apply_specialization_constants( + const ShaderInfo *p_shader_info, + VectorView<PipelineSpecializationConstant> p_specialization_constants, + HashMap<ShaderStage, Vector<uint8_t>> &r_final_stages_bytecode); + bool _shader_sign_dxil_bytecode(ShaderStage p_stage, Vector<uint8_t> &r_dxil_blob); + +public: + virtual String shader_get_binary_cache_key() override final; + virtual Vector<uint8_t> shader_compile_binary_from_spirv(VectorView<ShaderStageSPIRVData> p_spirv, const String &p_shader_name) override final; + virtual ShaderID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name) override final; + virtual uint32_t shader_get_layout_hash(ShaderID p_shader) override final; + virtual void shader_free(ShaderID p_shader) override final; + + /*********************/ + /**** UNIFORM SET ****/ + /*********************/ + +private: + struct RootDescriptorTable { + uint32_t root_param_idx = UINT32_MAX; + D3D12_GPU_DESCRIPTOR_HANDLE start_gpu_handle = {}; + }; + + struct UniformSetInfo { + struct { + DescriptorsHeap resources; + DescriptorsHeap samplers; + } desc_heaps; + + struct StateRequirement { + ResourceInfo *resource = nullptr; + bool is_buffer = false; + D3D12_RESOURCE_STATES states = {}; + uint64_t shader_uniform_idx_mask = 0; + }; + TightLocalVector<StateRequirement> resource_states; + + struct RecentBind { + uint64_t segment_serial = 0; + uint32_t root_signature_crc = 0; + struct { + TightLocalVector<RootDescriptorTable> resources; + TightLocalVector<RootDescriptorTable> samplers; + } root_tables; + int uses = 0; + } recent_binds[4]; // A better amount may be empirically found. + +#ifdef DEV_ENABLED + // Filthy, but useful for dev. + struct ResourceDescInfo { + D3D12_DESCRIPTOR_RANGE_TYPE type; + D3D12_SRV_DIMENSION srv_dimension; + }; + TightLocalVector<ResourceDescInfo> resources_desc_info; +#endif + }; + +public: + virtual UniformSetID uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index) override final; + virtual void uniform_set_free(UniformSetID p_uniform_set) override final; + + // ----- COMMANDS ----- + + 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_bind_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index, bool p_for_compute); + +public: + /******************/ + /**** TRANSFER ****/ + /******************/ + + virtual void command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) override final; + virtual void command_copy_buffer(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, BufferID p_dst_buffer, VectorView<BufferCopyRegion> p_regions) override final; + + virtual void command_copy_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<TextureCopyRegion> p_regions) override final; + virtual void command_resolve_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap) override final; + virtual void command_clear_color_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, const Color &p_color, const TextureSubresourceRange &p_subresources) override final; + + virtual void command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<BufferTextureCopyRegion> p_regions) override final; + virtual void command_copy_texture_to_buffer(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, BufferID p_dst_buffer, VectorView<BufferTextureCopyRegion> p_regions) override final; + + /******************/ + /**** PIPELINE ****/ + /******************/ + + virtual void pipeline_free(PipelineID p_pipeline) override final; + +private: + HashMap<ID3D12PipelineState *, const ShaderInfo *> pipelines_shaders; + +public: + // ----- BINDING ----- + + virtual void command_bind_push_constants(CommandBufferID p_cmd_buffer, ShaderID p_shader, uint32_t p_dst_first_index, VectorView<uint32_t> p_data) override final; + + // ----- CACHE ----- + + virtual bool pipeline_cache_create(const Vector<uint8_t> &p_data) override final; + virtual void pipeline_cache_free() override final; + virtual size_t pipeline_cache_query_size() override final; + virtual Vector<uint8_t> pipeline_cache_serialize() override final; + + /*******************/ + /**** RENDERING ****/ + /*******************/ + + // ----- SUBPASS ----- + +private: + struct RenderPassInfo { + TightLocalVector<Attachment> attachments; + TightLocalVector<Subpass> subpasses; + uint32_t view_count = 0; + uint32_t max_supported_sample_count = 0; + }; + +public: + virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) override final; + virtual void render_pass_free(RenderPassID p_render_pass) override final; + + // ----- COMMANDS ----- + + virtual void command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_clear_values) override final; + +private: + void _end_render_pass(CommandBufferID p_cmd_buffer); + +public: + virtual void command_end_render_pass(CommandBufferID p_cmd_buffer) override final; + virtual void command_next_render_subpass(CommandBufferID p_cmd_buffer, CommandBufferType p_cmd_buffer_type) override final; + virtual void command_render_set_viewport(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_viewports) override final; + virtual void command_render_set_scissor(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_scissors) override final; + + virtual void command_render_clear_attachments(CommandBufferID p_cmd_buffer, VectorView<AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) override final; + + // Binding. + virtual void command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final; + virtual void command_bind_render_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final; + + // Drawing. + virtual void command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) override final; + virtual void command_render_draw_indexed(CommandBufferID p_cmd_buffer, uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index, int32_t p_vertex_offset, uint32_t p_first_instance) override final; + virtual void command_render_draw_indexed_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final; + virtual void command_render_draw_indexed_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final; + virtual void command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final; + virtual void command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final; + + // Buffer binding. + virtual void command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) override final; + virtual void command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) override final; + +private: + void _bind_vertex_buffers(CommandBufferInfo *p_cmd_buf_info); + +public: + // Dynamic state. + virtual void command_render_set_blend_constants(CommandBufferID p_cmd_buffer, const Color &p_constants) override final; + virtual void command_render_set_line_width(CommandBufferID p_cmd_buffer, float p_width) override final; + + // ----- PIPELINE ----- + +private: + struct RenderPipelineExtraInfo { + struct { + D3D12_PRIMITIVE_TOPOLOGY primitive_topology = {}; + Color blend_constant; + float depth_bounds_min = 0.0f; + float depth_bounds_max = 0.0f; + uint32_t stencil_reference = 0; + } dyn_params; + + const VertexFormatInfo *vf_info = nullptr; + }; + HashMap<ID3D12PipelineState *, RenderPipelineExtraInfo> render_psos_extra_info; + +public: + virtual PipelineID render_pipeline_create( + ShaderID p_shader, + VertexFormatID p_vertex_format, + RenderPrimitive p_render_primitive, + PipelineRasterizationState p_rasterization_state, + PipelineMultisampleState p_multisample_state, + PipelineDepthStencilState p_depth_stencil_state, + PipelineColorBlendState p_blend_state, + VectorView<int32_t> p_color_attachments, + BitField<PipelineDynamicStateFlags> p_dynamic_state, + RenderPassID p_render_pass, + uint32_t p_render_subpass, + VectorView<PipelineSpecializationConstant> p_specialization_constants) override final; + + /*****************/ + /**** COMPUTE ****/ + /*****************/ + + // ----- COMMANDS ----- + + // Binding. + virtual void command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final; + virtual void command_bind_compute_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final; + + // Dispatching. + virtual void command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override final; + virtual void command_compute_dispatch_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset) override final; + + // ----- PIPELINE ----- + + virtual PipelineID compute_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) override final; + + /*****************/ + /**** QUERIES ****/ + /*****************/ + + // ----- TIMESTAMP ----- + +private: + struct TimestampQueryPoolInfo { + ComPtr<ID3D12QueryHeap> query_heap; + uint32_t query_count = 0; + ComPtr<D3D12MA::Allocation> results_buffer_allocation; + }; + +public: + // Basic. + virtual QueryPoolID timestamp_query_pool_create(uint32_t p_query_count) override final; + virtual void timestamp_query_pool_free(QueryPoolID p_pool_id) override final; + virtual void timestamp_query_pool_get_results(QueryPoolID p_pool_id, uint32_t p_query_count, uint64_t *r_results) override final; + virtual uint64_t timestamp_query_result_to_time(uint64_t p_result) override final; + + // Commands. + virtual void command_timestamp_query_pool_reset(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_query_count) override final; + virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) override final; + + /****************/ + /**** LABELS ****/ + /****************/ + + 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 ****/ + /********************/ +private: + struct FrameInfo { + struct { + DescriptorsHeap resources; + DescriptorsHeap samplers; + DescriptorsHeap aux; + DescriptorsHeap rtv; + } desc_heaps; + struct { + DescriptorsHeap::Walker resources; + DescriptorsHeap::Walker samplers; + DescriptorsHeap::Walker aux; + DescriptorsHeap::Walker rtv; + } desc_heap_walkers; + struct { + bool resources = false; + bool samplers = false; + bool aux = false; + bool rtv = false; + } desc_heaps_exhausted_reported; + CD3DX12_CPU_DESCRIPTOR_HANDLE null_rtv_handle = {}; // For [[MANUAL_SUBPASSES]]. + ComPtr<D3D12MA::Allocation> aux_resource; + uint32_t segment_serial = 0; + +#ifdef DEV_ENABLED + uint32_t uniform_set_reused = 0; +#endif + }; + TightLocalVector<FrameInfo> frames; + uint32_t frame_idx = 0; + uint32_t frames_drawn = 0; + uint32_t segment_serial = 0; + 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 end_segment() override final; + + /**************/ + /**** MISC ****/ + /**************/ + + 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; + virtual uint64_t limit_get(Limit p_limit) override final; + 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; + +private: + /*********************/ + /**** BOOKKEEPING ****/ + /*********************/ + + using VersatileResource = VersatileResourceTemplate< + BufferInfo, + TextureInfo, + TextureInfo, + TextureInfo, + VertexFormatInfo, + CommandBufferInfo, + FramebufferInfo, + ShaderInfo, + UniformSetInfo, + RenderPassInfo, + TimestampQueryPoolInfo>; + PagedAllocator<VersatileResource> resources_allocator; + + /******************/ + +public: + RenderingDeviceDriverD3D12(D3D12Context *p_context, ID3D12Device *p_device, uint32_t p_frame_count); + virtual ~RenderingDeviceDriverD3D12(); +}; + +#endif // RENDERING_DEVICE_DRIVER_D3D12_H diff --git a/drivers/egl/egl_manager.h b/drivers/egl/egl_manager.h index c3a749cd19..61d3289b2d 100644 --- a/drivers/egl/egl_manager.h +++ b/drivers/egl/egl_manager.h @@ -45,7 +45,7 @@ class EGLManager { private: - // An EGL-side rappresentation of a display with its own rendering + // An EGL-side representation of a display with its own rendering // context. struct GLDisplay { void *display = nullptr; diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp index 996e7eee7f..c6fb6ca70b 100644 --- a/drivers/gles3/effects/copy_effects.cpp +++ b/drivers/gles3/effects/copy_effects.cpp @@ -125,6 +125,18 @@ void CopyEffects::copy_to_rect(const Rect2 &p_rect) { draw_screen_quad(); } +void CopyEffects::copy_to_and_from_rect(const Rect2 &p_rect) { + bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE); + if (!success) { + return; + } + + copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE); + copy.shader.version_set_uniform(CopyShaderGLES3::SOURCE_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE); + + draw_screen_quad(); +} + void CopyEffects::copy_screen() { bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_DEFAULT); if (!success) { diff --git a/drivers/gles3/effects/copy_effects.h b/drivers/gles3/effects/copy_effects.h index 6e2cb07382..509d07b955 100644 --- a/drivers/gles3/effects/copy_effects.h +++ b/drivers/gles3/effects/copy_effects.h @@ -62,6 +62,7 @@ public: // These functions assume that a framebuffer and texture are bound already. They only manage the shader, uniforms, and vertex array. void copy_to_rect(const Rect2 &p_rect); + void copy_to_and_from_rect(const Rect2 &p_rect); void copy_screen(); void copy_cube_to_rect(const Rect2 &p_rect); void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region); diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 2db17e96f7..fe16d8838b 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -104,7 +104,7 @@ void RasterizerCanvasGLES3::_update_transform_to_mat4(const Transform3D &p_trans p_mat4[15] = 1; } -void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) { +void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); @@ -459,7 +459,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ update_skeletons = false; } // Canvas group begins here, render until before this item - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); item_count = 0; if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { @@ -490,7 +490,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ mesh_storage->update_mesh_instances(); update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info); item_count = 0; if (ci->canvas_group->blur_mipmaps) { @@ -514,7 +514,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } //render anything pending, including clearing if no items - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); item_count = 0; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps); @@ -544,7 +544,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ mesh_storage->update_mesh_instances(); update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info); //then reset item_count = 0; } @@ -564,7 +564,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state.current_instance_buffer_index = 0; } -void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer) { +void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer, RenderingMethod::RenderInfo *r_render_info) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); canvas_begin(p_to_render_target, p_to_backbuffer); @@ -661,6 +661,11 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou state.current_tex = RID(); for (uint32_t i = 0; i <= state.current_batch_index; i++) { + // Skipping when there is no instances. + if (state.canvas_instance_batches[i].instance_count == 0) { + continue; + } + //setup clip if (current_clip != state.canvas_instance_batches[i].clip) { current_clip = state.canvas_instance_batches[i].clip; @@ -767,7 +772,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou last_blend_color = blend_color; } - _render_batch(p_lights, i); + _render_batch(p_lights, i, r_render_info); } state.current_batch_index = 0; @@ -1235,7 +1240,13 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend } } -void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { +_FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primitive, uint32_t p_indices) { + static const uint32_t divisor[RS::PRIMITIVE_MAX] = { 1, 2, 1, 3, 1 }; + static const uint32_t subtractor[RS::PRIMITIVE_MAX] = { 0, 0, 1, 0, 1 }; + return (p_indices - subtractor[p_primitive]) / divisor[p_primitive]; +} + +void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index, RenderingMethod::RenderInfo *r_render_info) { ERR_FAIL_NULL(state.canvas_instance_batches[state.current_batch_index].command); // Used by Polygon and Mesh. @@ -1254,6 +1265,12 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, state.canvas_instance_batches[p_index].instance_count); glBindVertexArray(0); + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] += state.canvas_instance_batches[p_index].instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += 2 * state.canvas_instance_batches[p_index].instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } break; case Item::Command::TYPE_POLYGON: { @@ -1283,6 +1300,12 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { // Reset so this doesn't pollute other draw calls. glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0); } + + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(polygon->primitive, pb->count); + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } } break; case Item::Command::TYPE_PRIMITIVE: { @@ -1297,6 +1320,12 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { if (instance_count >= 1) { glDrawArraysInstanced(primitive[state.canvas_instance_batches[p_index].primitive_points], 0, state.canvas_instance_batches[p_index].primitive_points, instance_count); } + if (r_render_info) { + const RenderingServer::PrimitiveType rs_primitive[5] = { RS::PRIMITIVE_POINTS, RS::PRIMITIVE_POINTS, RS::PRIMITIVE_LINES, RS::PRIMITIVE_TRIANGLES, RS::PRIMITIVE_TRIANGLES }; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] += instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(rs_primitive[state.canvas_instance_batches[p_index].primitive_points], state.canvas_instance_batches[p_index].primitive_points) * instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } } break; @@ -1383,7 +1412,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { GLuint vertex_array_gl = 0; GLuint index_array_gl = 0; - uint64_t input_mask = 0; // 2D meshes always use the same vertex format. + uint64_t input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_COLOR | RS::ARRAY_FORMAT_TEX_UV; // 2D meshes always use the same vertex format. if (mesh_instance.is_valid()) { mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl); } else { @@ -1432,10 +1461,12 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { GLenum primitive_gl = prim[int(primitive)]; + uint32_t vertex_count = mesh_storage->mesh_surface_get_vertices_drawn_count(surface); + if (use_index_buffer) { - glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count); + glDrawElementsInstanced(primitive_gl, vertex_count, mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count); } else { - glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count); + glDrawArraysInstanced(primitive_gl, 0, vertex_count, instance_count); } glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -1445,6 +1476,12 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { glDisableVertexAttribArray(7); glDisableVertexAttribArray(8); } + if (r_render_info) { + // Meshes, Particles, and MultiMesh are always just one object with one draw call. + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(primitive, vertex_count) * instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } } } break; @@ -2258,14 +2295,18 @@ void RasterizerCanvasGLES3::_prepare_canvas_texture(RID p_texture, RS::CanvasIte GLES3::Texture *texture = texture_storage->get_texture(ct->diffuse); Size2i size_cache; + + // Cache default white resource ID. + const RID default_texture_id = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); + + // If no texture is assigned, assign default white. if (!texture) { - ct->diffuse = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); - GLES3::Texture *tex = texture_storage->get_texture(ct->diffuse); - size_cache = Size2i(tex->width, tex->height); - } else { - size_cache = Size2i(texture->width, texture->height); + ct->diffuse = default_texture_id; } + // Enforce a 1x1 size if default white texture. + size_cache = ct->diffuse == default_texture_id ? Size2i(1, 1) : Size2i(texture->width, texture->height); + GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map); if (ct->specular_color.a < 0.999) { diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 94c771cde7..552b2afd6b 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -354,10 +354,10 @@ public: void _bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat); void _prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size); - void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override; - void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false); + void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override; + void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr); void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used); - void _render_batch(Light *p_lights, uint32_t p_index); + void _render_batch(Light *p_lights, uint32_t p_index, RenderingMethod::RenderInfo *r_render_info = nullptr); bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization); void _new_batch(bool &r_batch_broken); void _add_to_batch(uint32_t &r_index, bool &r_batch_broken); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index c048e9bf37..6ab98a9473 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -103,6 +103,11 @@ void RasterizerGLES3::begin_frame(double frame_step) { } void RasterizerGLES3::end_frame(bool p_swap_buffers) { + GLES3::Utilities *utils = GLES3::Utilities::get_singleton(); + utils->capture_timestamps_end(); +} + +void RasterizerGLES3::end_viewport(bool p_swap_buffers) { if (p_swap_buffers) { DisplayServer::get_singleton()->swap_buffers(); } else { @@ -352,13 +357,6 @@ RasterizerGLES3::~RasterizerGLES3() { } void RasterizerGLES3::prepare_for_blitting_render_targets() { - // This is a hack, but this function is called one time after all viewports have been updated. - // So it marks the end of the frame for all viewports - // In the OpenGL renderer we have to call end_frame for each viewport so we can swap the - // buffers for each window before proceeding to the next. - // This allows us to only increment the frame after all viewports are done. - GLES3::Utilities *utils = GLES3::Utilities::get_singleton(); - utils->capture_timestamps_end(); } 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) { @@ -474,7 +472,7 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c copy_effects->copy_to_rect(screenrect); glBindTexture(GL_TEXTURE_2D, 0); - end_frame(true); + end_viewport(true); texture_storage->texture_free(texture); } diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index b19ca0e9c9..d9f436a2ec 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -93,6 +93,7 @@ public: 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); void end_frame(bool p_swap_buffers); void finalize(); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 205f9a552e..a646b1ec7d 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -130,9 +130,27 @@ void RasterizerSceneGLES3::GeometryInstanceGLES3::_mark_dirty() { } void RasterizerSceneGLES3::GeometryInstanceGLES3::set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) { + lightmap_instance = p_lightmap_instance; + lightmap_uv_scale = p_lightmap_uv_scale; + lightmap_slice_index = p_lightmap_slice_index; + + _mark_dirty(); } void RasterizerSceneGLES3::GeometryInstanceGLES3::set_lightmap_capture(const Color *p_sh9) { + if (p_sh9) { + if (lightmap_sh == nullptr) { + lightmap_sh = memnew(GeometryInstanceLightmapSH); + } + + memcpy(lightmap_sh->sh, p_sh9, sizeof(Color) * 9); + } else { + if (lightmap_sh != nullptr) { + memdelete(lightmap_sh); + lightmap_sh = nullptr; + } + } + _mark_dirty(); } void RasterizerSceneGLES3::_update_dirty_geometry_instances() { @@ -958,10 +976,10 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p glDisable(GL_BLEND); glDepthMask(GL_FALSE); glDisable(GL_DEPTH_TEST); + scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_DISABLED; glDisable(GL_SCISSOR_TEST); - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); - scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; + glDisable(GL_CULL_FACE); + scene_state.cull_mode = GLES3::SceneShaderData::CULL_DISABLED; for (int i = 0; i < 6; i++) { Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); @@ -982,6 +1000,14 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p sky->reflection_dirty = false; } else { if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) { + glDisable(GL_BLEND); + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_DISABLED; + glDisable(GL_SCISSOR_TEST); + glDisable(GL_CULL_FACE); + scene_state.cull_mode = GLES3::SceneShaderData::CULL_DISABLED; + _filter_sky_radiance(sky, sky->processing_layer); sky->processing_layer++; } @@ -1271,12 +1297,15 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance); if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) { - GeometryInstanceGLES3::LightPass pass; - pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); - pass.shadow_id = shadow_id; - pass.light_instance_rid = light_instance; - pass.is_omni = true; - inst->light_passes.push_back(pass); + // Skip static lights when a lightmap is used. + if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) { + GeometryInstanceGLES3::LightPass pass; + pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); + pass.shadow_id = shadow_id; + pass.light_instance_rid = light_instance; + pass.is_omni = true; + inst->light_passes.push_back(pass); + } } else { // Lights without shadow can all go in base pass. inst->omni_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance)); @@ -1294,11 +1323,14 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance); if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) { - GeometryInstanceGLES3::LightPass pass; - pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); - pass.shadow_id = shadow_id; - pass.light_instance_rid = light_instance; - inst->light_passes.push_back(pass); + // Skip static lights when a lightmap is used. + if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) { + GeometryInstanceGLES3::LightPass pass; + pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); + pass.shadow_id = shadow_id; + pass.light_instance_rid = light_instance; + inst->light_passes.push_back(pass); + } } else { // Lights without shadow can all go in base pass. inst->spot_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance)); @@ -1610,6 +1642,8 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b light_data.direction[1] = direction.y; light_data.direction[2] = direction.z; + light_data.bake_mode = light_storage->light_get_bake_mode(base); + float sign = light_storage->light_is_negative(base) ? -1 : 1; light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); @@ -1758,6 +1792,8 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b light_data.position[1] = pos.y; light_data.position[2] = pos.z; + light_data.bake_mode = light_storage->light_get_bake_mode(base); + float radius = MAX(0.001, light_storage->light_get_param(base, RS::LIGHT_PARAM_RANGE)); light_data.inv_radius = 1.0 / radius; @@ -2238,9 +2274,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ bool fb_cleared = false; - Size2i screen_size; - screen_size.x = rb->width; - screen_size.y = rb->height; + Size2i screen_size = rb->internal_size; bool use_wireframe = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME; @@ -2360,8 +2394,10 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ } } - glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); - glViewport(0, 0, rb->width, rb->height); + GLuint fbo = rb->get_render_fbo(); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glViewport(0, 0, rb->internal_size.x, rb->internal_size.y); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); @@ -2463,25 +2499,48 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ } if (scene_state.used_screen_texture || scene_state.used_depth_texture) { - texture_storage->copy_scene_to_backbuffer(rt, scene_state.used_screen_texture, scene_state.used_depth_texture); - glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); - glReadBuffer(GL_COLOR_ATTACHMENT0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rt->backbuffer_fbo); - if (scene_state.used_screen_texture) { - glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, - 0, 0, rt->size.x, rt->size.y, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5); - glBindTexture(GL_TEXTURE_2D, rt->backbuffer); - } - if (scene_state.used_depth_texture) { - glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, - 0, 0, rt->size.x, rt->size.y, - GL_DEPTH_BUFFER_BIT, GL_NEAREST); - glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); - glBindTexture(GL_TEXTURE_2D, rt->backbuffer_depth); - } - glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); + Size2i size; + GLuint backbuffer_fbo = 0; + GLuint backbuffer = 0; + GLuint backbuffer_depth = 0; + + if (rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_OFF) { + texture_storage->check_backbuffer(rt, scene_state.used_screen_texture, scene_state.used_depth_texture); // note, badly names, this just allocates! + + size = rt->size; + backbuffer_fbo = rt->backbuffer_fbo; + backbuffer = rt->backbuffer; + backbuffer_depth = rt->backbuffer_depth; + } else { + rb->check_backbuffer(scene_state.used_screen_texture, scene_state.used_depth_texture); + size = rb->get_internal_size(); + backbuffer_fbo = rb->get_backbuffer_fbo(); + backbuffer = rb->get_backbuffer(); + backbuffer_depth = rb->get_backbuffer_depth(); + } + + if (backbuffer_fbo != 0) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, backbuffer_fbo); + if (scene_state.used_screen_texture) { + glBlitFramebuffer(0, 0, size.x, size.y, + 0, 0, size.x, size.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5); + glBindTexture(GL_TEXTURE_2D, backbuffer); + } + if (scene_state.used_depth_texture) { + glBlitFramebuffer(0, 0, size.x, size.y, + 0, 0, size.x, size.y, + GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); + glBindTexture(GL_TEXTURE_2D, backbuffer_depth); + } + } + + // Bound framebuffer may have changed, so change it back + glBindFramebuffer(GL_FRAMEBUFFER, fbo); } RENDER_TIMESTAMP("Render 3D Transparent Pass"); @@ -2498,14 +2557,110 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ } if (rb.is_valid()) { - _render_buffers_debug_draw(rb, p_shadow_atlas); + _render_buffers_debug_draw(rb, p_shadow_atlas, fbo); } glDisable(GL_BLEND); + + _render_post_processing(&render_data); + texture_storage->render_target_disable_clear_request(rb->render_target); glActiveTexture(GL_TEXTURE0); } +void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_render_data) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + Ref<RenderSceneBuffersGLES3> rb = p_render_data->render_buffers; + ERR_FAIL_COND(rb.is_null()); + + RID render_target = rb->get_render_target(); + Size2i internal_size = rb->get_internal_size(); + Size2i target_size = rb->get_target_size(); + uint32_t view_count = rb->get_view_count(); + + // bool msaa2d_needs_resolve = texture_storage->render_target_get_msaa(render_target) != RS::VIEWPORT_MSAA_DISABLED && !GLES3::Config::get_singleton()->rt_msaa_supported; + bool msaa3d_needs_resolve = rb->get_msaa_needs_resolve(); + GLuint fbo_msaa_3d = rb->get_msaa3d_fbo(); + GLuint fbo_int = rb->get_internal_fbo(); + GLuint fbo_rt = texture_storage->render_target_get_fbo(render_target); // TODO if MSAA 2D is enabled and we're not using rt_msaa, get 2D render target here. + + if (view_count == 1) { + // Resolve if needed. + if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) { + // We can use blit to copy things over + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_msaa_3d); + + if (fbo_int != 0) { + // We can't combine resolve and scaling, so resolve into our internal buffer + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_int); + } else { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_rt); + } + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + + if (fbo_int != 0) { + // TODO If we have glow or other post processing, we upscale only depth here, post processing will also do scaling. + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_rt); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + + glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); + } else if ((fbo_msaa_3d != 0 && msaa3d_needs_resolve) || (fbo_int != 0)) { + // TODO investigate if it's smarter to cache these FBOs + GLuint fbos[2]; // read and write + glGenFramebuffers(2, fbos); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); + + if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) { + GLuint read_color = rb->get_msaa3d_color(); + GLuint read_depth = rb->get_msaa3d_depth(); + GLuint write_color = 0; + GLuint write_depth = 0; + + if (fbo_int != 0) { + write_color = rb->get_internal_color(); + write_depth = rb->get_internal_depth(); + } else { + write_color = texture_storage->render_target_get_color(render_target); + write_depth = texture_storage->render_target_get_depth(render_target); + } + + for (uint32_t v = 0; v < view_count; v++) { + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + } + + if (fbo_int != 0) { + GLuint read_color = rb->get_internal_color(); + GLuint read_depth = rb->get_internal_depth(); + GLuint write_color = texture_storage->render_target_get_color(render_target); + GLuint write_depth = texture_storage->render_target_get_depth(render_target); + + for (uint32_t v = 0; v < view_count; v++) { + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); + + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); + glDeleteFramebuffers(2, fbos); + } +} + template <PassMode p_pass_mode> void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass) { GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); @@ -2621,6 +2776,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, bool uses_additive_lighting = (inst->light_passes.size() + p_render_data->directional_shadow_count) > 0; uses_additive_lighting = uses_additive_lighting && !shader->unshaded; + // TODOS /* * Still a bug when atlas space is limited. Somehow need to evict light when it doesn't have a spot on the atlas, current check isn't enough @@ -2650,6 +2806,12 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, // Shadow wasn't able to get a spot on the atlas. So skip it. continue; } + } else if (pass > 0) { + uint32_t shadow_id = MAX_DIRECTIONAL_LIGHTS - 1 - (pass - int32_t(inst->light_passes.size())); + if (inst->lightmap_instance.is_valid() && scene_state.directional_lights[shadow_id].bake_mode == RenderingServer::LIGHT_BAKE_STATIC) { + // Skip shadows for static lights on meshes with a lightmap. + continue; + } } } @@ -2738,12 +2900,16 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, GLuint vertex_array_gl = 0; GLuint index_array_gl = 0; + uint64_t vertex_input_mask = shader->vertex_input_mask; + if (inst->lightmap_instance.is_valid()) { + vertex_input_mask |= 1 << RS::ARRAY_TEX_UV2; + } - //skeleton and blend shape + // Skeleton and blend shapes. if (surf->owner->mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, shader->vertex_input_mask, vertex_array_gl); + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, vertex_input_mask, vertex_array_gl); } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, shader->vertex_input_mask, vertex_array_gl); + mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, vertex_input_mask, vertex_array_gl); } index_array_gl = mesh_storage->mesh_surface_get_index_buffer(mesh_surface, surf->lod_index); @@ -2758,6 +2924,15 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, prev_index_array_gl = 0; } + bool use_wireframe = false; + if (p_params->force_wireframe) { + GLuint wireframe_index_array_gl = mesh_storage->mesh_surface_get_index_buffer_wireframe(mesh_surface); + if (wireframe_index_array_gl) { + index_array_gl = wireframe_index_array_gl; + use_wireframe = true; + } + } + bool use_index_buffer = index_array_gl != 0; if (prev_index_array_gl != index_array_gl) { if (index_array_gl != 0) { @@ -2803,12 +2978,28 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) { spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; } + + if (inst->lightmap_instance.is_valid()) { + spec_constants |= SceneShaderGLES3::USE_LIGHTMAP; + + GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance); + GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap); + + if (lm->uses_spherical_harmonics) { + spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP; + } + } else if (inst->lightmap_sh) { + spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE; + } else { + spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; + } } else { // Only base pass uses the radiance map. spec_constants &= ~SceneShaderGLES3::USE_RADIANCE_MAP; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; + spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; } if (uses_additive_lighting) { @@ -2831,6 +3022,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, // Render directional lights. uint32_t shadow_id = MAX_DIRECTIONAL_LIGHTS - 1 - (pass - int32_t(inst->light_passes.size())); + if (pass == 0 && inst->lightmap_instance.is_valid() && scene_state.directional_lights[shadow_id].bake_mode == RenderingServer::LIGHT_BAKE_STATIC) { + // Disable additive lighting with a static light and a lightmap. + spec_constants &= ~SceneShaderGLES3::USE_ADDITIVE_LIGHTING; + } if (scene_state.directional_shadows[shadow_id].shadow_split_offsets[0] == scene_state.directional_shadows[shadow_id].shadow_split_offsets[1]) { // Orthogonal, do nothing. } else if (scene_state.directional_shadows[shadow_id].shadow_split_offsets[1] == scene_state.directional_shadows[shadow_id].shadow_split_offsets[2]) { @@ -2920,6 +3115,46 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant, spec_constants), inst->spot_light_gl_cache.size(), inst->spot_light_gl_cache.ptr()); } + if (inst->lightmap_instance.is_valid()) { + GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance); + GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap); + + GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lm->light_texture); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4); + glBindTexture(GL_TEXTURE_2D_ARRAY, tex); + + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_SLICE, inst->lightmap_slice_index, shader->version, instance_variant, spec_constants); + + Vector4 uv_scale(inst->lightmap_uv_scale.position.x, inst->lightmap_uv_scale.position.y, inst->lightmap_uv_scale.size.x, inst->lightmap_uv_scale.size.y); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_UV_SCALE, uv_scale, shader->version, instance_variant, spec_constants); + + float exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + exposure_normalization = enf / lm->baked_exposure; + } + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_EXPOSURE_NORMALIZATION, exposure_normalization, shader->version, instance_variant, spec_constants); + + if (lm->uses_spherical_harmonics) { + Basis to_lm = li->transform.basis.inverse() * p_render_data->cam_transform.basis; + to_lm = to_lm.inverse().transposed(); + GLfloat matrix[9] = { + (GLfloat)to_lm.rows[0][0], + (GLfloat)to_lm.rows[1][0], + (GLfloat)to_lm.rows[2][0], + (GLfloat)to_lm.rows[0][1], + (GLfloat)to_lm.rows[1][1], + (GLfloat)to_lm.rows[2][1], + (GLfloat)to_lm.rows[0][2], + (GLfloat)to_lm.rows[1][2], + (GLfloat)to_lm.rows[2][2], + }; + glUniformMatrix3fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_NORMAL_XFORM, shader->version, instance_variant, spec_constants), 1, GL_FALSE, matrix); + } + } else if (inst->lightmap_sh) { + glUniform4fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURES, shader->version, instance_variant, spec_constants), 9, reinterpret_cast<const GLfloat *>(inst->lightmap_sh->sh)); + } + prev_inst = inst; } } @@ -2938,6 +3173,8 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } } + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::MODEL_FLAGS, inst->flags_cache, shader->version, instance_variant, spec_constants); + // Can be index count or vertex count uint32_t count = 0; if (surf->lod_index > 0) { @@ -2946,6 +3183,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, count = mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface); } + if (use_wireframe) { + // In this case we are using index count, and we need double the indices for the wireframe mesh. + count = count * 2; + } + if constexpr (p_pass_mode != PASS_MODE_DEPTH) { // Don't count draw calls during depth pre-pass to match the RD renderers. if (p_render_data->render_info) { @@ -3000,17 +3242,25 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, glVertexAttribI4ui(15, default_color, default_color, default_custom, default_custom); } - if (use_index_buffer) { - glDrawElementsInstanced(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0, inst->instance_count); + if (use_wireframe) { + glDrawElementsInstanced(GL_LINES, count, GL_UNSIGNED_INT, 0, inst->instance_count); } else { - glDrawArraysInstanced(primitive_gl, 0, count, inst->instance_count); + if (use_index_buffer) { + glDrawElementsInstanced(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0, inst->instance_count); + } else { + glDrawArraysInstanced(primitive_gl, 0, count, inst->instance_count); + } } } else { // Using regular Mesh. - if (use_index_buffer) { - glDrawElements(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0); + if (use_wireframe) { + glDrawElements(GL_LINES, count, GL_UNSIGNED_INT, 0); } else { - glDrawArrays(primitive_gl, 0, count); + if (use_index_buffer) { + glDrawElements(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0); + } else { + glDrawArrays(primitive_gl, 0, count); + } } } @@ -3123,7 +3373,7 @@ Ref<RenderSceneBuffers> RasterizerSceneGLES3::render_buffers_create() { return rb; } -void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas) { +void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, GLuint p_fbo) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); GLES3::CopyEffects *copy_effects = GLES3::CopyEffects::get_singleton(); @@ -3200,8 +3450,11 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES } } } - glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); - glViewport(0, 0, rt->size.width, rt->size.height); + + // Set back to FBO + glBindFramebuffer(GL_FRAMEBUFFER, p_fbo); + Size2i size = p_render_buffers->get_internal_size(); + glViewport(0, 0, size.width, size.height); glBindTexture(GL_TEXTURE_2D, shadow_atlas_texture); copy_effects->copy_to_rect(Rect2(Vector2(), Vector2(0.5, 0.5))); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 7d3c8896da..045511321a 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -170,6 +170,9 @@ private: float cos_spot_angle; float specular_amount; float shadow_opacity; + + float pad[3]; + uint32_t bake_mode; }; static_assert(sizeof(LightData) % 16 == 0, "LightData size must be a multiple of 16 bytes"); @@ -181,7 +184,7 @@ private: float size; uint32_t enabled; // For use by SkyShaders - float pad; + uint32_t bake_mode; float shadow_opacity; float specular; }; @@ -269,6 +272,10 @@ private: GeometryInstanceGLES3 *owner = nullptr; }; + struct GeometryInstanceLightmapSH { + Color sh[9]; + }; + class GeometryInstanceGLES3 : public RenderGeometryInstanceBase { public: //used during rendering @@ -296,6 +303,11 @@ private: LocalVector<uint32_t> omni_light_gl_cache; LocalVector<uint32_t> spot_light_gl_cache; + RID lightmap_instance; + Rect2 lightmap_uv_scale; + uint32_t lightmap_slice_index; + GeometryInstanceLightmapSH *lightmap_sh = nullptr; + // Used during setup. GeometryInstanceSurface *surface_caches = nullptr; SelfList<GeometryInstanceGLES3> dirty_list_element; @@ -316,6 +328,7 @@ private: }; enum { + INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3, INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4, INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5, INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 7, @@ -518,6 +531,7 @@ private: void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false); void _render_shadows(const RenderDataGLES3 *p_render_data, const Size2i &p_viewport_size = Size2i(1, 1)); void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1)); + void _render_post_processing(const RenderDataGLES3 *p_render_data); template <PassMode p_pass_mode> _FORCE_INLINE_ void _render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass = false); @@ -530,7 +544,7 @@ protected: float screen_space_roughness_limiter_amount = 0.25; float screen_space_roughness_limiter_limit = 0.18; - void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas); + void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, GLuint p_fbo); /* Camera Attributes */ diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 75b2662a1c..27402da7c8 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -49,7 +49,7 @@ void ShaderGLES3::_add_stage(const char *p_code, StageType p_stage_type) { String text; for (int i = 0; i < lines.size(); i++) { - String l = lines[i]; + const String &l = lines[i]; bool push_chunk = false; StageTemplate::Chunk chunk; @@ -611,6 +611,7 @@ void ShaderGLES3::_save_to_cache(Version *p_version) { #ifdef WEB_ENABLED // not supported in webgl return; #else + ERR_FAIL_COND(!shader_cache_dir_valid); #if !defined(ANDROID_ENABLED) && !defined(IOS_ENABLED) if (RasterizerGLES3::is_gles_over_gl() && (glGetProgramBinary == NULL)) { // ARB_get_program_binary extension not available. return; diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index 0cb53da316..8968e76c12 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -209,7 +209,9 @@ protected: _compile_specialization(s, p_variant, version, p_specialization); version->variants[p_variant].insert(p_specialization, s); spec = version->variants[p_variant].lookup_ptr(p_specialization); - _save_to_cache(version); + if (shader_cache_dir_valid) { + _save_to_cache(version); + } } } else if (spec->build_queued) { // Still queued, wait diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl index 265acc1430..f37968a4fd 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/copy.glsl @@ -3,6 +3,7 @@ mode_default = #define MODE_SIMPLE_COPY mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY +mode_copy_section_source = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define MODE_COPY_FROM mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR mode_mipmap = #define MODE_MIPMAP mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION @@ -21,7 +22,7 @@ out vec2 uv_interp; // Defined in 0-1 coords. uniform highp vec4 copy_section; #endif -#ifdef MODE_GAUSSIAN_BLUR +#if defined(MODE_GAUSSIAN_BLUR) || defined(MODE_COPY_FROM) uniform highp vec4 source_section; #endif @@ -32,7 +33,7 @@ void main() { #if defined(USE_COPY_SECTION) || defined(MODE_GAUSSIAN_BLUR) gl_Position.xy = (copy_section.xy + uv_interp.xy * copy_section.zw) * 2.0 - 1.0; #endif -#ifdef MODE_GAUSSIAN_BLUR +#if defined(MODE_GAUSSIAN_BLUR) || defined(MODE_COPY_FROM) uv_interp = source_section.xy + uv_interp * source_section.zw; #endif } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index e0f8e83373..e95d684763 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1,7 +1,7 @@ /* clang-format off */ #[modes] -mode_color = +mode_color = mode_color_instancing = \n#define USE_INSTANCING mode_depth = #define MODE_RENDER_DEPTH mode_depth_instancing = #define MODE_RENDER_DEPTH \n#define USE_INSTANCING @@ -14,6 +14,9 @@ DISABLE_LIGHT_OMNI = false DISABLE_LIGHT_SPOT = false DISABLE_FOG = false USE_RADIANCE_MAP = true +USE_LIGHTMAP = false +USE_SH_LIGHTMAP = false +USE_LIGHTMAP_CAPTURE = false USE_MULTIVIEW = false RENDER_SHADOWS = false RENDER_SHADOWS_LINEAR = false @@ -139,6 +142,8 @@ layout(location = 14) in highp vec4 instance_xform2; layout(location = 15) in highp uvec4 instance_color_custom_data; // Color packed into xy, Custom data into zw. #endif +#define FLAGS_NON_UNIFORM_SCALE (1 << 4) + layout(std140) uniform GlobalShaderUniformData { //ubo:1 vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; @@ -242,6 +247,8 @@ uniform highp vec3 compressed_aabb_position; uniform highp vec3 compressed_aabb_size; uniform highp vec4 uv_scale; +uniform highp uint model_flags; + /* Varyings */ out highp vec3 vertex_interp; @@ -310,7 +317,14 @@ void main() { #ifdef NORMAL_USED vec3 normal = oct_to_vec3(axis_tangent_attrib.xy * 2.0 - 1.0); #endif - highp mat3 model_normal_matrix = mat3(model_matrix); + + highp mat3 model_normal_matrix; + + if (bool(model_flags & uint(FLAGS_NON_UNIFORM_SCALE))) { + model_normal_matrix = transpose(inverse(mat3(model_matrix))); + } else { + model_normal_matrix = mat3(model_matrix); + } #if defined(NORMAL_USED) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) @@ -593,8 +607,7 @@ layout(std140) uniform GlobalShaderUniformData { //ubo:1 vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; - /* Material Uniforms */ - +/* Material Uniforms */ #ifdef MATERIAL_UNIFORMS_USED /* clang-format off */ @@ -665,6 +678,10 @@ multiview_data; /* clang-format on */ +#define LIGHT_BAKE_DISABLED 0u +#define LIGHT_BAKE_STATIC 1u +#define LIGHT_BAKE_DYNAMIC 2u + #ifndef MODE_RENDER_DEPTH // Directional light data. #if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)) @@ -674,7 +691,8 @@ struct DirectionalLightData { mediump float energy; mediump vec3 color; mediump float size; - mediump vec2 pad; + lowp uint unused; + lowp uint bake_mode; mediump float shadow_opacity; mediump float specular; }; @@ -707,6 +725,9 @@ struct LightData { // This structure needs to be as packed as possible. mediump float cone_angle; mediump float specular_amount; mediump float shadow_opacity; + + lowp vec3 pad; + lowp uint bake_mode; }; #if !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI) @@ -823,6 +844,23 @@ float sample_shadow(highp sampler2DShadow shadow, float shadow_pixel_size, vec4 #endif // !MODE_RENDER_DEPTH +#ifndef DISABLE_LIGHTMAP +#ifdef USE_LIGHTMAP +uniform mediump sampler2DArray lightmap_textures; //texunit:-4 +uniform lowp uint lightmap_slice; +uniform highp vec4 lightmap_uv_scale; +uniform float lightmap_exposure_normalization; + +#ifdef USE_SH_LIGHTMAP +uniform mediump mat3 lightmap_normal_xform; +#endif // USE_SH_LIGHTMAP +#endif // USE_LIGHTMAP + +#ifdef USE_LIGHTMAP_CAPTURE +uniform mediump vec4[9] lightmap_captures; +#endif // USE_LIGHTMAP_CAPTURE +#endif // !DISABLE_LIGHTMAP + #ifdef USE_MULTIVIEW uniform highp sampler2DArray depth_buffer; // texunit:-6 uniform highp sampler2DArray color_buffer; // texunit:-5 @@ -1395,7 +1433,6 @@ void main() { #endif // Calculate Reflection probes - // Calculate Lightmaps #if defined(CUSTOM_RADIANCE_USED) specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a); @@ -1420,6 +1457,61 @@ void main() { ambient_light = mix(ambient_light, custom_irradiance.rgb, custom_irradiance.a); #endif // CUSTOM_IRRADIANCE_USED +#ifndef DISABLE_LIGHTMAP +#ifdef USE_LIGHTMAP_CAPTURE + { + vec3 wnormal = mat3(scene_data.inv_view_matrix) * normal; + const float c1 = 0.429043; + const float c2 = 0.511664; + const float c3 = 0.743125; + const float c4 = 0.886227; + const float c5 = 0.247708; + ambient_light += (c1 * lightmap_captures[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + + c3 * lightmap_captures[6].rgb * wnormal.z * wnormal.z + + c4 * lightmap_captures[0].rgb - + c5 * lightmap_captures[6].rgb + + 2.0 * c1 * lightmap_captures[4].rgb * wnormal.x * wnormal.y + + 2.0 * c1 * lightmap_captures[7].rgb * wnormal.x * wnormal.z + + 2.0 * c1 * lightmap_captures[5].rgb * wnormal.y * wnormal.z + + 2.0 * c2 * lightmap_captures[3].rgb * wnormal.x + + 2.0 * c2 * lightmap_captures[1].rgb * wnormal.y + + 2.0 * c2 * lightmap_captures[2].rgb * wnormal.z) * + scene_data.emissive_exposure_normalization; + } +#else +#ifdef USE_LIGHTMAP + { + vec3 uvw; + uvw.xy = uv2 * lightmap_uv_scale.zw + lightmap_uv_scale.xy; + uvw.z = float(lightmap_slice); + +#ifdef USE_SH_LIGHTMAP + uvw.z *= 4.0; // SH textures use 4 times more data. + vec3 lm_light_l0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + vec3 lm_light_l1n1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + vec3 lm_light_l1_0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + vec3 lm_light_l1p1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + + vec3 n = normalize(lightmap_normal_xform * normal); + + ambient_light += lm_light_l0 * 0.282095f; + ambient_light += lm_light_l1n1 * 0.32573 * n.y * lightmap_exposure_normalization; + ambient_light += lm_light_l1_0 * 0.32573 * n.z * lightmap_exposure_normalization; + ambient_light += lm_light_l1p1 * 0.32573 * n.x * lightmap_exposure_normalization; + if (metallic > 0.01) { // Since the more direct bounced light is lost, we can kind of fake it with this trick. + vec3 r = reflect(normalize(-vertex), normal); + specular_light += lm_light_l1n1 * 0.32573 * r.y * lightmap_exposure_normalization; + specular_light += lm_light_l1_0 * 0.32573 * r.z * lightmap_exposure_normalization; + specular_light += lm_light_l1p1 * 0.32573 * r.x * lightmap_exposure_normalization; + } +#else + ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization; +#endif + } +#endif // USE_LIGHTMAP +#endif // USE_LIGHTMAP_CAPTURE +#endif // !DISABLE_LIGHTMAP + { #if defined(AMBIENT_LIGHT_DISABLED) ambient_light = vec3(0.0, 0.0, 0.0); @@ -1455,6 +1547,11 @@ void main() { #ifndef DISABLE_LIGHT_DIRECTIONAL for (uint i = uint(0); i < scene_data.directional_light_count; i++) { +#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) + if (directional_lights[i].bake_mode == LIGHT_BAKE_STATIC) { + continue; + } +#endif light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, true, 1.0, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1479,6 +1576,11 @@ void main() { if (i >= omni_light_count) { break; } +#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) + if (omni_lights[omni_light_indices[i]].bake_mode == LIGHT_BAKE_STATIC) { + continue; + } +#endif light_process_omni(omni_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1502,6 +1604,11 @@ void main() { if (i >= spot_light_count) { break; } +#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) + if (spot_lights[spot_light_indices[i]].bake_mode == LIGHT_BAKE_STATIC) { + continue; + } +#endif light_process_spot(spot_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1600,6 +1707,8 @@ void main() { #if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT) +#ifndef SHADOWS_DISABLED + // Orthogonal shadows #if !defined(LIGHT_USE_PSSM2) && !defined(LIGHT_USE_PSSM4) float directional_shadow = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord); @@ -1706,6 +1815,9 @@ void main() { directional_shadow = mix(directional_shadow, 1.0, smoothstep(directional_shadows[directional_shadow_index].fade_from, directional_shadows[directional_shadow_index].fade_to, vertex.z)); directional_shadow = mix(1.0, directional_shadow, directional_lights[directional_shadow_index].shadow_opacity); +#else + float directional_shadow = 1.0f; +#endif // SHADOWS_DISABLED light_compute(normal, normalize(directional_lights[directional_shadow_index].direction), normalize(view), directional_lights[directional_shadow_index].size, directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, directional_shadow, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1725,11 +1837,12 @@ void main() { #endif // !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT) #ifdef ADDITIVE_OMNI + float omni_shadow = 1.0f; +#ifndef SHADOWS_DISABLED vec3 light_ray = ((positional_shadows[positional_shadow_index].shadow_matrix * vec4(shadow_coord.xyz, 1.0))).xyz; - - float omni_shadow = texture(omni_shadow_texture, vec4(light_ray, length(light_ray) * omni_lights[omni_light_index].inv_radius)); + omni_shadow = texture(omni_shadow_texture, vec4(light_ray, length(light_ray) * omni_lights[omni_light_index].inv_radius)); omni_shadow = mix(1.0, omni_shadow, omni_lights[omni_light_index].shadow_opacity); - +#endif // SHADOWS_DISABLED light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1748,9 +1861,11 @@ void main() { #endif // ADDITIVE_OMNI #ifdef ADDITIVE_SPOT - float spot_shadow = sample_shadow(spot_shadow_texture, positional_shadows[positional_shadow_index].shadow_atlas_pixel_size, shadow_coord); + float spot_shadow = 1.0f; +#ifndef SHADOWS_DISABLED + spot_shadow = sample_shadow(spot_shadow_texture, positional_shadows[positional_shadow_index].shadow_atlas_pixel_size, shadow_coord); spot_shadow = mix(1.0, spot_shadow, spot_lights[spot_light_index].shadow_opacity); - +#endif // SHADOWS_DISABLED light_process_spot(spot_light_index, vertex, view, normal, f0, roughness, metallic, spot_shadow, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index 4bf6165fe9..5d01ab0346 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -92,14 +92,55 @@ Config::Config() { anisotropic_level = MIN(float(1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level); } - multiview_supported = extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview"); + glGetIntegerv(GL_MAX_SAMPLES, &msaa_max_samples); +#ifdef WEB_ENABLED + msaa_supported = (msaa_max_samples > 0); +#else + msaa_supported = extensions.has("GL_EXT_framebuffer_multisample"); +#endif +#ifndef IOS_ENABLED +#ifdef WEB_ENABLED + msaa_multiview_supported = extensions.has("OCULUS_multiview"); + rt_msaa_multiview_supported = msaa_multiview_supported; +#else + msaa_multiview_supported = extensions.has("GL_EXT_multiview_texture_multisample"); +#endif + + multiview_supported = extensions.has("OCULUS_multiview") || extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview"); +#endif + #ifdef ANDROID_ENABLED + // These are GLES only + rt_msaa_supported = extensions.has("GL_EXT_multisampled_render_to_texture"); + rt_msaa_multiview_supported = extensions.has("GL_OVR_multiview_multisampled_render_to_texture"); + if (multiview_supported) { eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR"); if (eglFramebufferTextureMultiviewOVR == nullptr) { multiview_supported = false; } } + + if (msaa_multiview_supported) { + eglTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)eglGetProcAddress("glTexStorage3DMultisample"); + if (eglTexStorage3DMultisample == nullptr) { + msaa_multiview_supported = false; + } + } + + if (rt_msaa_supported) { + eglFramebufferTexture2DMultisampleEXT = (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)eglGetProcAddress("glFramebufferTexture2DMultisampleEXT"); + if (eglFramebufferTexture2DMultisampleEXT == nullptr) { + rt_msaa_supported = false; + } + } + + if (rt_msaa_multiview_supported) { + eglFramebufferTextureMultisampleMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR"); + if (eglFramebufferTextureMultisampleMultiviewOVR == nullptr) { + rt_msaa_multiview_supported = false; + } + } #endif force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index b2dd98f025..1c0a5178bd 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -42,6 +42,9 @@ #ifdef ANDROID_ENABLED typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei); +typedef void (*PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean); +typedef void (*PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei); +typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei); #endif namespace GLES3 { @@ -63,8 +66,7 @@ public: int64_t max_renderable_lights = 0; int64_t max_lights_per_object = 0; - // TODO implement wireframe in OpenGL - // bool generate_wireframes; + bool generate_wireframes = false; HashSet<String> extensions; @@ -82,9 +84,18 @@ public: bool support_anisotropic_filter = false; float anisotropic_level = 0.0f; + GLint msaa_max_samples = 0; + bool msaa_supported = false; + bool msaa_multiview_supported = false; + bool rt_msaa_supported = false; + bool rt_msaa_multiview_supported = false; bool multiview_supported = false; + #ifdef ANDROID_ENABLED PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr; + PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr; + PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr; + PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr; #endif static Config *get_singleton() { return singleton; }; diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index 6d4d23bd10..2607a133d6 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -569,69 +569,171 @@ void LightStorage::lightmap_initialize(RID p_rid) { void LightStorage::lightmap_free(RID p_rid) { Lightmap *lightmap = lightmap_owner.get_or_null(p_rid); + ERR_FAIL_NULL(lightmap); lightmap->dependency.deleted_notify(p_rid); lightmap_owner.free(p_rid); } void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + lightmap->light_texture = p_light; + lightmap->uses_spherical_harmonics = p_uses_spherical_haromics; + + GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lightmap->light_texture); + glBindTexture(GL_TEXTURE_2D_ARRAY, tex); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); } void LightStorage::lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + lightmap->bounds = p_bounds; } void LightStorage::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + lightmap->interior = p_interior; } void LightStorage::lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + + if (p_points.size()) { + ERR_FAIL_COND(p_points.size() * 9 != p_point_sh.size()); + ERR_FAIL_COND((p_tetrahedra.size() % 4) != 0); + ERR_FAIL_COND((p_bsp_tree.size() % 6) != 0); + } + + lightmap->points = p_points; + lightmap->point_sh = p_point_sh; + lightmap->tetrahedra = p_tetrahedra; + lightmap->bsp_tree = p_bsp_tree; } void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + + lightmap->baked_exposure = p_exposure; } PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { - return PackedVector3Array(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedVector3Array()); + return lightmap->points; } PackedColorArray LightStorage::lightmap_get_probe_capture_sh(RID p_lightmap) const { - return PackedColorArray(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedColorArray()); + return lightmap->point_sh; } PackedInt32Array LightStorage::lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const { - return PackedInt32Array(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedInt32Array()); + return lightmap->tetrahedra; } PackedInt32Array LightStorage::lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const { - return PackedInt32Array(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedInt32Array()); + return lightmap->bsp_tree; } AABB LightStorage::lightmap_get_aabb(RID p_lightmap) const { - return AABB(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, AABB()); + return lightmap->bounds; } void LightStorage::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lm); + + for (int i = 0; i < 9; i++) { + r_sh[i] = Color(0, 0, 0, 0); + } + + if (!lm->points.size() || !lm->bsp_tree.size() || !lm->tetrahedra.size()) { + return; + } + + static_assert(sizeof(Lightmap::BSP) == 24); + + const Lightmap::BSP *bsp = (const Lightmap::BSP *)lm->bsp_tree.ptr(); + int32_t node = 0; + while (node >= 0) { + if (Plane(bsp[node].plane[0], bsp[node].plane[1], bsp[node].plane[2], bsp[node].plane[3]).is_point_over(p_point)) { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(bsp[node].over >= 0 && bsp[node].over < node); +#endif + + node = bsp[node].over; + } else { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(bsp[node].under >= 0 && bsp[node].under < node); +#endif + node = bsp[node].under; + } + } + + if (node == Lightmap::BSP::EMPTY_LEAF) { + return; // Nothing could be done. + } + + node = ABS(node) - 1; + + uint32_t *tetrahedron = (uint32_t *)&lm->tetrahedra[node * 4]; + Vector3 points[4] = { lm->points[tetrahedron[0]], lm->points[tetrahedron[1]], lm->points[tetrahedron[2]], lm->points[tetrahedron[3]] }; + const Color *sh_colors[4]{ &lm->point_sh[tetrahedron[0] * 9], &lm->point_sh[tetrahedron[1] * 9], &lm->point_sh[tetrahedron[2] * 9], &lm->point_sh[tetrahedron[3] * 9] }; + Color barycentric = Geometry3D::tetrahedron_get_barycentric_coords(points[0], points[1], points[2], points[3], p_point); + + for (int i = 0; i < 4; i++) { + float c = CLAMP(barycentric[i], 0.0, 1.0); + for (int j = 0; j < 9; j++) { + r_sh[j] += sh_colors[i][j] * c; + } + } } bool LightStorage::lightmap_is_interior(RID p_lightmap) const { - return false; + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, false); + return lightmap->interior; } void LightStorage::lightmap_set_probe_capture_update_speed(float p_speed) { + lightmap_probe_capture_update_speed = p_speed; } float LightStorage::lightmap_get_probe_capture_update_speed() const { - return 0; + return lightmap_probe_capture_update_speed; } /* LIGHTMAP INSTANCE */ RID LightStorage::lightmap_instance_create(RID p_lightmap) { - return RID(); + LightmapInstance li; + li.lightmap = p_lightmap; + return lightmap_instance_owner.make_rid(li); } void LightStorage::lightmap_instance_free(RID p_lightmap) { + lightmap_instance_owner.free(p_lightmap); } void LightStorage::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) { + LightmapInstance *li = lightmap_instance_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(li); + li->transform = p_transform; } /* SHADOW ATLAS API */ @@ -1019,6 +1121,7 @@ void LightStorage::update_directional_shadow_atlas() { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0); } + glUseProgram(0); glDepthMask(GL_TRUE); glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo); RasterizerGLES3::clear_depth(1.0); diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 2fb4dcaeca..7ab0286098 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -155,6 +155,11 @@ struct Lightmap { Dependency dependency; }; +struct LightmapInstance { + RID lightmap; + Transform3D transform; +}; + class LightStorage : public RendererLightStorage { public: enum ShadowAtlastQuadrant { @@ -179,9 +184,14 @@ private: /* LIGHTMAP */ Vector<RID> lightmap_textures; + float lightmap_probe_capture_update_speed = 4; mutable RID_Owner<Lightmap, true> lightmap_owner; + /* LIGHTMAP INSTANCE */ + + mutable RID_Owner<LightmapInstance> lightmap_instance_owner; + /* SHADOW ATLAS */ // Note: The ShadowAtlas in the OpenGL is virtual. Each light gets assigned its @@ -622,6 +632,9 @@ public: /* LIGHTMAP INSTANCE */ + LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); }; + bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); }; + virtual RID lightmap_instance_create(RID p_lightmap) override; virtual void lightmap_instance_free(RID p_lightmap) override; virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index b7a42141fd..13ab05c0a0 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -2968,17 +2968,17 @@ void SceneShaderData::set_code(const String &p_code) { depth_test = DepthTest(depth_testi); cull_mode = Cull(cull_modei); - vertex_input_mask = uint64_t(uses_normal); - vertex_input_mask |= uses_tangent << 1; - vertex_input_mask |= uses_color << 2; - vertex_input_mask |= uses_uv << 3; - vertex_input_mask |= uses_uv2 << 4; - vertex_input_mask |= uses_custom0 << 5; - vertex_input_mask |= uses_custom1 << 6; - vertex_input_mask |= uses_custom2 << 7; - vertex_input_mask |= uses_custom3 << 8; - vertex_input_mask |= uses_bones << 9; - vertex_input_mask |= uses_weights << 10; + vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL; // We can always read vertices and normals. + vertex_input_mask |= uses_tangent << RS::ARRAY_TANGENT; + vertex_input_mask |= uses_color << RS::ARRAY_COLOR; + vertex_input_mask |= uses_uv << RS::ARRAY_TEX_UV; + vertex_input_mask |= uses_uv2 << RS::ARRAY_TEX_UV2; + vertex_input_mask |= uses_custom0 << RS::ARRAY_CUSTOM0; + vertex_input_mask |= uses_custom1 << RS::ARRAY_CUSTOM1; + vertex_input_mask |= uses_custom2 << RS::ARRAY_CUSTOM2; + vertex_input_mask |= uses_custom3 << RS::ARRAY_CUSTOM3; + vertex_input_mask |= uses_bones << RS::ARRAY_BONES; + vertex_input_mask |= uses_weights << RS::ARRAY_WEIGHTS; uses_screen_texture = gen_code.uses_screen_texture; uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps; @@ -3000,10 +3000,6 @@ void SceneShaderData::set_code(const String &p_code) { WARN_PRINT_ONCE_ED("Transmittance is only available when using the Forward+ rendering backend."); } - if (uses_depth_texture) { - WARN_PRINT_ONCE_ED("Reading from the depth texture is not supported when using the GL Compatibility backend yet. Support will be added in a future release."); - } - if (uses_normal_texture) { WARN_PRINT_ONCE_ED("Reading from the normal-roughness texture is only available when using the Forward+ or Mobile rendering backends."); } diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 88ee749ed6..b4e266d976 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -31,6 +31,7 @@ #ifdef GLES3_ENABLED #include "mesh_storage.h" +#include "config.h" #include "material_storage.h" #include "utilities.h" @@ -285,8 +286,72 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) ERR_FAIL_COND_MSG(!new_surface.index_count && !new_surface.vertex_count, "Meshes must contain a vertex array, an index array, or both"); + if (GLES3::Config::get_singleton()->generate_wireframes && s->primitive == RS::PRIMITIVE_TRIANGLES) { + // Generate wireframes. This is mostly used by the editor. + s->wireframe = memnew(Mesh::Surface::Wireframe); + Vector<uint32_t> wf_indices; + uint32_t &wf_index_count = s->wireframe->index_count; + uint32_t *wr = nullptr; + + if (new_surface.format & RS::ARRAY_FORMAT_INDEX) { + wf_index_count = s->index_count * 2; + wf_indices.resize(wf_index_count); + + Vector<uint8_t> ir = new_surface.index_data; + wr = wf_indices.ptrw(); + + if (new_surface.vertex_count < (1 << 16)) { + // Read 16 bit indices. + const uint16_t *src_idx = (const uint16_t *)ir.ptr(); + for (uint32_t i = 0; i + 5 < wf_index_count; i += 6) { + // We use GL_LINES instead of GL_TRIANGLES for drawing these primitives later, + // so we need double the indices for each triangle. + wr[i + 0] = src_idx[i / 2]; + wr[i + 1] = src_idx[i / 2 + 1]; + wr[i + 2] = src_idx[i / 2 + 1]; + wr[i + 3] = src_idx[i / 2 + 2]; + wr[i + 4] = src_idx[i / 2 + 2]; + wr[i + 5] = src_idx[i / 2]; + } + + } else { + // Read 32 bit indices. + const uint32_t *src_idx = (const uint32_t *)ir.ptr(); + for (uint32_t i = 0; i + 5 < wf_index_count; i += 6) { + wr[i + 0] = src_idx[i / 2]; + wr[i + 1] = src_idx[i / 2 + 1]; + wr[i + 2] = src_idx[i / 2 + 1]; + wr[i + 3] = src_idx[i / 2 + 2]; + wr[i + 4] = src_idx[i / 2 + 2]; + wr[i + 5] = src_idx[i / 2]; + } + } + } else { + // Not using indices. + wf_index_count = s->vertex_count * 2; + wf_indices.resize(wf_index_count); + wr = wf_indices.ptrw(); + + for (uint32_t i = 0; i + 5 < wf_index_count; i += 6) { + wr[i + 0] = i / 2; + wr[i + 1] = i / 2 + 1; + wr[i + 2] = i / 2 + 1; + wr[i + 3] = i / 2 + 2; + wr[i + 4] = i / 2 + 2; + wr[i + 5] = i / 2; + } + } + + s->wireframe->index_buffer_size = wf_index_count * sizeof(uint32_t); + glGenBuffers(1, &s->wireframe->index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->wireframe->index_buffer); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ELEMENT_ARRAY_BUFFER, s->wireframe->index_buffer, s->wireframe->index_buffer_size, wr, GL_STATIC_DRAW, "Mesh wireframe index buffer"); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // unbind + } + s->aabb = new_surface.aabb; s->bone_aabbs = new_surface.bone_aabbs; //only really useful for returning them. + s->mesh_to_skeleton_xform = p_surface.mesh_to_skeleton_xform; s->uv_scale = new_surface.uv_scale; @@ -508,6 +573,7 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { } sd.bone_aabbs = s.bone_aabbs; + sd.mesh_to_skeleton_xform = s.mesh_to_skeleton_xform; if (mesh->blend_shape_count) { sd.blend_shape_data = Vector<uint8_t>(); @@ -561,15 +627,16 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { for (uint32_t i = 0; i < mesh->surface_count; i++) { AABB laabb; - if ((mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) && mesh->surfaces[i]->bone_aabbs.size()) { - int bs = mesh->surfaces[i]->bone_aabbs.size(); - const AABB *skbones = mesh->surfaces[i]->bone_aabbs.ptr(); + const Mesh::Surface &surface = *mesh->surfaces[i]; + if ((surface.format & RS::ARRAY_FORMAT_BONES) && surface.bone_aabbs.size()) { + int bs = surface.bone_aabbs.size(); + const AABB *skbones = surface.bone_aabbs.ptr(); int sbs = skeleton->size; ERR_CONTINUE(bs > sbs); const float *baseptr = skeleton->data.ptr(); - bool first = true; + bool found_bone_aabb = false; if (skeleton->use_2d) { for (int j = 0; j < bs; j++) { @@ -589,11 +656,13 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { mtx.basis.rows[1][1] = dataptr[5]; mtx.origin.y = dataptr[7]; - AABB baabb = mtx.xform(skbones[j]); + // Transform bounds to skeleton's space before applying animation data. + AABB baabb = surface.mesh_to_skeleton_xform.xform(skbones[j]); + baabb = mtx.xform(baabb); - if (first) { + if (!found_bone_aabb) { laabb = baabb; - first = false; + found_bone_aabb = true; } else { laabb.merge_with(baabb); } @@ -621,21 +690,29 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { mtx.basis.rows[2][2] = dataptr[10]; mtx.origin.z = dataptr[11]; - AABB baabb = mtx.xform(skbones[j]); - if (first) { + // Transform bounds to skeleton's space before applying animation data. + AABB baabb = surface.mesh_to_skeleton_xform.xform(skbones[j]); + baabb = mtx.xform(baabb); + + if (!found_bone_aabb) { laabb = baabb; - first = false; + found_bone_aabb = true; } else { laabb.merge_with(baabb); } } } + if (found_bone_aabb) { + // Transform skeleton bounds back to mesh's space if any animated AABB applied. + laabb = surface.mesh_to_skeleton_xform.affine_inverse().xform(laabb); + } + if (laabb.size == Vector3()) { - laabb = mesh->surfaces[i]->aabb; + laabb = surface.aabb; } } else { - laabb = mesh->surfaces[i]->aabb; + laabb = surface.aabb; } if (i == 0) { @@ -712,6 +789,11 @@ void MeshStorage::mesh_clear(RID p_mesh) { memfree(s.versions); //reallocs, so free with memfree. } + if (s.wireframe) { + GLES3::Utilities::get_singleton()->buffer_free_data(s.wireframe->index_buffer); + memdelete(s.wireframe); + } + if (s.lod_count) { for (uint32_t j = 0; j < s.lod_count; j++) { if (s.lods[j].index_buffer != 0) { @@ -764,14 +846,17 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V int skin_stride = 0; for (int i = 0; i < RS::ARRAY_INDEX; i++) { + attribs[i].enabled = false; + attribs[i].integer = false; if (!(s->format & (1ULL << i))) { - attribs[i].enabled = false; - attribs[i].integer = false; continue; } - attribs[i].enabled = true; - attribs[i].integer = false; + if ((p_input_mask & (1ULL << i))) { + // Only enable if it matches input mask. + // Iterate over all anyway, so we can calculate stride. + attribs[i].enabled = true; + } switch (i) { case RS::ARRAY_VERTEX: { @@ -1108,8 +1193,6 @@ void MeshStorage::_blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uin } void MeshStorage::_compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t p_surface) { - glBindBuffer(GL_ARRAY_BUFFER, 0); - // Add in the bones and weights. glBindBuffer(GL_ARRAY_BUFFER, p_mi->mesh->surfaces[p_surface]->skin_buffer); @@ -1200,9 +1283,8 @@ void MeshStorage::update_mesh_instances() { glBindBuffer(GL_ARRAY_BUFFER, 0); GLuint vertex_array_gl = 0; - uint64_t mask = ((1 << 10) - 1) << 3; // Mask from ARRAY_FORMAT_COLOR to ARRAY_FORMAT_INDEX. - mask = ~mask; - uint64_t format = mi->surfaces[i].format_cache & mask; // Format should only have vertex, normal, tangent (as necessary) + compressions. + uint64_t mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_VERTEX; + uint64_t format = mi->mesh->surfaces[i]->format & mask; // Format should only have vertex, normal, tangent (as necessary). mesh_surface_get_vertex_arrays_and_format(mi->mesh->surfaces[i], format, vertex_array_gl); glBindVertexArray(vertex_array_gl); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[0]); @@ -1315,9 +1397,8 @@ void MeshStorage::update_mesh_instances() { skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_OFFSET, inverse_transform[2], skeleton_shader.shader_version, variant, specialization); GLuint vertex_array_gl = 0; - uint64_t mask = ((1 << 10) - 1) << 3; // Mask from ARRAY_FORMAT_COLOR to ARRAY_FORMAT_INDEX. - mask = ~mask; - uint64_t format = mi->surfaces[i].format_cache & mask; // Format should only have vertex, normal, tangent (as necessary) + compressions. + uint64_t mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_VERTEX; + uint64_t format = mi->mesh->surfaces[i]->format & mask; // Format should only have vertex, normal, tangent (as necessary). mesh_surface_get_vertex_arrays_and_format(mi->mesh->surfaces[i], format, vertex_array_gl); glBindVertexArray(vertex_array_gl); _compute_skeleton(mi, sk, i); @@ -1457,7 +1538,7 @@ void MeshStorage::_multimesh_make_local(MultiMesh *multimesh) const { memset(w, 0, (size_t)multimesh->instances * multimesh->stride_cache * sizeof(float)); } } - uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, MULTIMESH_DIRTY_REGION_SIZE); multimesh->data_cache_dirty_regions = memnew_arr(bool, data_cache_dirty_region_count); for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { multimesh->data_cache_dirty_regions[i] = false; @@ -1468,7 +1549,7 @@ void MeshStorage::_multimesh_make_local(MultiMesh *multimesh) const { void MeshStorage::_multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb) { uint32_t region_index = p_index / MULTIMESH_DIRTY_REGION_SIZE; #ifdef DEBUG_ENABLED - uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, MULTIMESH_DIRTY_REGION_SIZE); ERR_FAIL_UNSIGNED_INDEX(region_index, data_cache_dirty_region_count); //bug #endif if (!multimesh->data_cache_dirty_regions[region_index]) { @@ -1489,7 +1570,7 @@ void MeshStorage::_multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool void MeshStorage::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb) { if (p_data) { - uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, MULTIMESH_DIRTY_REGION_SIZE); for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { if (!multimesh->data_cache_dirty_regions[i]) { @@ -1836,7 +1917,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b multimesh->data_cache = multimesh->data_cache; { //clear dirty since nothing will be dirty anymore - uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, MULTIMESH_DIRTY_REGION_SIZE); for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { multimesh->data_cache_dirty_regions[i] = false; } @@ -1963,8 +2044,8 @@ void MeshStorage::_update_dirty_multimeshes() { uint32_t visible_instances = multimesh->visible_instances >= 0 ? multimesh->visible_instances : multimesh->instances; if (multimesh->data_cache_used_dirty_regions) { - uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; - uint32_t visible_region_count = visible_instances == 0 ? 0 : (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, (int)MULTIMESH_DIRTY_REGION_SIZE); + uint32_t visible_region_count = visible_instances == 0 ? 0 : Math::division_round_up(visible_instances, (uint32_t)MULTIMESH_DIRTY_REGION_SIZE); GLint region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 25b15ab6a6..217c4dabf0 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -84,6 +84,14 @@ struct Mesh { uint32_t index_count = 0; uint32_t index_buffer_size = 0; + struct Wireframe { + GLuint index_buffer = 0; + uint32_t index_count = 0; + uint32_t index_buffer_size = 0; + }; + + Wireframe *wireframe = nullptr; + struct LOD { float edge_length = 0.0; uint32_t index_count = 0; @@ -98,6 +106,10 @@ struct Mesh { Vector<AABB> bone_aabbs; + // Transform used in runtime bone AABBs compute. + // As bone AABBs are saved in Mesh space, but bones animation is in Skeleton space. + Transform3D mesh_to_skeleton_xform; + Vector4 uv_scale; struct BlendShape { @@ -376,6 +388,16 @@ public: } } + _FORCE_INLINE_ GLuint mesh_surface_get_index_buffer_wireframe(void *p_surface) const { + Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); + + if (s->wireframe) { + return s->wireframe->index_buffer; + } + + return 0; + } + _FORCE_INLINE_ GLenum mesh_surface_get_index_type(void *p_surface) const { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index 829574cae0..33bb808856 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -31,30 +31,536 @@ #ifdef GLES3_ENABLED #include "render_scene_buffers_gles3.h" +#include "config.h" #include "texture_storage.h" +#include "utilities.h" + +#ifdef ANDROID_ENABLED +#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR +#define glTexStorage3DMultisample GLES3::Config::get_singleton()->eglTexStorage3DMultisample +#define glFramebufferTexture2DMultisampleEXT GLES3::Config::get_singleton()->eglFramebufferTexture2DMultisampleEXT +#define glFramebufferTextureMultisampleMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultisampleMultiviewOVR +#endif // ANDROID_ENABLED + +// Will only be defined if GLES 3.2 headers are included +#ifndef GL_TEXTURE_2D_MULTISAMPLE_ARRAY +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#endif RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { free_render_buffer_data(); } +void RenderSceneBuffersGLES3::_rt_attach_textures(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count) { + if (p_view_count > 1) { + if (p_samples > 1) { +#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED) + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, p_samples, 0, p_view_count); + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count); +#else + ERR_PRINT_ONCE("Multiview MSAA isn't supported on this platform."); +#endif + } else { +#ifndef IOS_ENABLED + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, 0, p_view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, 0, p_view_count); +#else + ERR_PRINT_ONCE("Multiview isn't supported on this platform."); +#endif + } + } else { + if (p_samples > 1) { +#ifdef ANDROID_ENABLED + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0, p_samples); + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples); +#else + ERR_PRINT_ONCE("MSAA via EXT_multisampled_render_to_texture isn't supported on this platform."); +#endif + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0); + } + } +} + +GLuint RenderSceneBuffersGLES3::_rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count) { + FBDEF new_fbo; + +#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED) + // There shouldn't be more then 3 entries in this... + for (const FBDEF &cached_fbo : msaa3d.cached_fbos) { + if (cached_fbo.color == p_color && cached_fbo.depth == p_depth) { + return cached_fbo.fbo; + } + } + + new_fbo.color = p_color; + new_fbo.depth = p_depth; + + glGenFramebuffers(1, &new_fbo.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, new_fbo.fbo); + + _rt_attach_textures(p_color, p_depth, p_samples, p_view_count); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); + + glDeleteFramebuffers(1, &new_fbo.fbo); + + new_fbo.fbo = 0; + } else { + // cache it! + msaa3d.cached_fbos.push_back(new_fbo); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + + return new_fbo.fbo; +} + void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p_config) { - //internal_size.x = p_config->get_internal_size().x; // ignore for now - //internal_size.y = p_config->get_internal_size().y; - width = p_config->get_target_size().x; - height = p_config->get_target_size().y; - //scaling_3d_mode = p_config->get_scaling_3d_mode() + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + + free_render_buffer_data(); + + internal_size = p_config->get_internal_size(); + target_size = p_config->get_target_size(); + scaling_3d_mode = p_config->get_scaling_3d_mode(); //fsr_sharpness = p_config->get_fsr_sharpness(); //texture_mipmap_bias = p_config->get_texture_mipmap_bias(); render_target = p_config->get_render_target(); - //msaa = p_config->get_msaa_3d(); + msaa3d.mode = p_config->get_msaa_3d(); //screen_space_aa = p_config->get_screen_space_aa(); //use_debanding = p_config->get_use_debanding(); - view_count = p_config->get_view_count(); + view_count = config->multiview_supported ? p_config->get_view_count() : 1; - free_render_buffer_data(); + ERR_FAIL_COND(view_count == 0); + bool use_multiview = view_count > 1; + + // Check our scaling mode + if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size.x == 0 && internal_size.y == 0) { + // Disable, no size set. + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; + } else if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size == target_size) { + // If size matches, we won't use scaling. + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; + } else if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_BILINEAR) { + // We only support bilinear scaling atm. + WARN_PRINT_ONCE("GLES only supports bilinear scaling."); + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; + } + + bool use_internal_buffer = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF; // TODO also need this if doing post processing like glow + if (use_internal_buffer) { + // Setup our internal buffer. + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + GLuint color_format = GL_RGBA; + GLuint color_type = is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; + + GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + + // Create our color buffer. + glGenTextures(1, &internal3d.color); + glBindTexture(texture_target, internal3d.color); + + if (use_multiview) { + glTexImage3D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, view_count, 0, color_format, color_type, nullptr); + } else { + glTexImage2D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, 0, color_format, color_type, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.color, internal_size.x * internal_size.y * view_count * 4, "3D color texture"); + + // Create our depth buffer. + glGenTextures(1, &internal3d.depth); + glBindTexture(texture_target, internal3d.depth); + + if (use_multiview) { + glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } else { + glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.depth, internal_size.x * internal_size.y * view_count * 3, "3D depth texture"); + + // Create our internal 3D FBO. + // Note that if MSAA is used and our rt_msaa_* extensions are available, this is only used for blitting and effects. + glGenFramebuffers(1, &internal3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, internal3d.fbo); + +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, internal3d.color, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, internal3d.depth, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, internal3d.color, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, internal3d.depth, 0); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_intermediate_buffers(); + WARN_PRINT("Could not create 3D buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindTexture(texture_target, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + // Check if we support MSAA. + if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && internal_size.x == 0 && internal_size.y == 0) { + // Disable, no size set. + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } else if (!use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_supported && !config->rt_msaa_supported) { + WARN_PRINT_ONCE("MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } else if (use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_multiview_supported && !config->rt_msaa_multiview_supported) { + WARN_PRINT_ONCE("Multiview MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } + + if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED) { + // Setup MSAA. + const GLsizei samples[] = { 1, 2, 4, 8 }; + msaa3d.samples = samples[msaa3d.mode]; + + // Constrain by limits of OpenGL driver. + if (msaa3d.samples > config->msaa_max_samples) { + msaa3d.samples = config->msaa_max_samples; + } + + if (!use_multiview && !config->rt_msaa_supported) { + // Render to texture extensions not supported? fall back to MSAA framebuffer through GL_EXT_framebuffer_multisample. + // Note, if 2D MSAA matches 3D MSAA and we're not scaling, it would be ideal if we reuse our 2D MSAA buffer here. + // We can't however because we don't trigger a change in configuration if 2D MSAA changes. + // We'll accept the overhead in this situation. + + msaa3d.needs_resolve = true; + msaa3d.check_fbo_cache = false; + + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + + // Create our color buffer. + glGenRenderbuffers(1, &msaa3d.color); + glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.color); + + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y); + GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * 4 * msaa3d.samples, "MSAA 3D color render buffer"); + + // Create our depth buffer. + glGenRenderbuffers(1, &msaa3d.depth); + glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.depth); + + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y); + GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * 3 * msaa3d.samples, "MSAA 3D depth render buffer"); + + // Create our MSAA 3D FBO. + glGenFramebuffers(1, &msaa3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaa3d.color); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaa3d.depth); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_msaa3d_buffers(); + WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#if !defined(IOS_ENABLED) && !defined(WEB_ENABLED) + } else if (use_multiview && !config->rt_msaa_multiview_supported) { + // Render to texture extensions not supported? fall back to MSAA textures through GL_EXT_multiview_texture_multisample. + msaa3d.needs_resolve = true; + msaa3d.check_fbo_cache = false; + + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + + // Create our color buffer. + glGenTextures(1, &msaa3d.color); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.color); + +#ifdef ANDROID_ENABLED + glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE); +#else + glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE); +#endif + + GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * 4 * msaa3d.samples, "MSAA 3D color texture"); + + // Create our depth buffer. + glGenTextures(1, &msaa3d.depth); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.depth); + +#ifdef ANDROID_ENABLED + glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); +#else + glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); +#endif + + GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * msaa3d.samples, "MSAA 3D depth texture"); + + // Create our MSAA 3D FBO. + glGenFramebuffers(1, &msaa3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); + + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, msaa3d.color, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, msaa3d.depth, 0, 0, view_count); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_msaa3d_buffers(); + WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif +#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED) // Only supported on OpenGLES! + } else if (!use_internal_buffer) { + // We are going to render directly into our render target textures, + // these can change from frame to frame as we cycle through swapchains, + // hence we'll use our FBO cache here. + msaa3d.needs_resolve = false; + msaa3d.check_fbo_cache = true; + } else if (use_internal_buffer) { + // We can combine MSAA and scaling/effects. + msaa3d.needs_resolve = false; + msaa3d.check_fbo_cache = false; + + // We render to our internal textures, MSAA is only done in tile memory only. + // On mobile this means MSAA never leaves tile memory = efficiency! + glGenFramebuffers(1, &msaa3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); + + _rt_attach_textures(internal3d.color, internal3d.depth, msaa3d.samples, view_count); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_msaa3d_buffers(); + WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + } else { + // HUH? how did we get here? + WARN_PRINT_ONCE("MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + msaa3d.samples = 1; + msaa3d.check_fbo_cache = false; + } + } else { + msaa3d.samples = 1; + msaa3d.check_fbo_cache = false; + } +} + +void RenderSceneBuffersGLES3::_clear_msaa3d_buffers() { + for (const FBDEF &cached_fbo : msaa3d.cached_fbos) { + GLuint fbo = cached_fbo.fbo; + glDeleteFramebuffers(1, &fbo); + } + msaa3d.cached_fbos.clear(); + + if (msaa3d.fbo) { + glDeleteFramebuffers(1, &msaa3d.fbo); + msaa3d.fbo = 0; + } + + if (msaa3d.color != 0) { + if (view_count == 1) { + GLES3::Utilities::get_singleton()->render_buffer_free_data(msaa3d.color); + } else { + GLES3::Utilities::get_singleton()->texture_free_data(msaa3d.color); + } + msaa3d.color = 0; + } + + if (msaa3d.depth != 0) { + if (view_count == 1) { + GLES3::Utilities::get_singleton()->render_buffer_free_data(msaa3d.depth); + } else { + GLES3::Utilities::get_singleton()->texture_free_data(msaa3d.depth); + } + msaa3d.depth = 0; + } +} + +void RenderSceneBuffersGLES3::_clear_intermediate_buffers() { + if (internal3d.fbo) { + glDeleteFramebuffers(1, &internal3d.fbo); + internal3d.fbo = 0; + } + + if (internal3d.color != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(internal3d.color); + internal3d.color = 0; + } + + if (internal3d.depth != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(internal3d.depth); + internal3d.depth = 0; + } +} + +void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_depth) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + // Setup our back buffer + + if (backbuffer3d.fbo == 0) { + glGenFramebuffers(1, &backbuffer3d.fbo); + } + + glBindFramebuffer(GL_FRAMEBUFFER, backbuffer3d.fbo); + + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + GLuint color_format = GL_RGBA; + GLuint color_type = is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; + + bool use_multiview = view_count > 1 && GLES3::Config::get_singleton()->multiview_supported; + GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + + if (backbuffer3d.color == 0 && p_need_color) { + glGenTextures(1, &backbuffer3d.color); + glBindTexture(texture_target, backbuffer3d.color); + + if (use_multiview) { + glTexImage3D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, view_count, 0, color_format, color_type, nullptr); + } else { + glTexImage2D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, 0, color_format, color_type, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.color, internal_size.x * internal_size.y * view_count * 4, "3D Back buffer color texture"); + +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, backbuffer3d.color, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, backbuffer3d.color, 0); + } + } + + if (backbuffer3d.depth == 0 && p_need_depth) { + glGenTextures(1, &backbuffer3d.depth); + glBindTexture(texture_target, backbuffer3d.depth); + + if (use_multiview) { + glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } else { + glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.depth, internal_size.x * internal_size.y * view_count * 3, "3D back buffer depth texture"); + +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, backbuffer3d.depth, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, backbuffer3d.depth, 0); + } + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_back_buffers(); + WARN_PRINT("Could not create 3D back buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindTexture(texture_target, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void RenderSceneBuffersGLES3::_clear_back_buffers() { + if (backbuffer3d.fbo) { + glDeleteFramebuffers(1, &backbuffer3d.fbo); + backbuffer3d.fbo = 0; + } + + if (backbuffer3d.color != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(backbuffer3d.color); + backbuffer3d.color = 0; + } + + if (backbuffer3d.depth != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(backbuffer3d.depth); + backbuffer3d.depth = 0; + } } void RenderSceneBuffersGLES3::free_render_buffer_data() { + _clear_msaa3d_buffers(); + _clear_intermediate_buffers(); + _clear_back_buffers(); +} + +GLuint RenderSceneBuffersGLES3::get_render_fbo() { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLuint rt_fbo = 0; + + if (msaa3d.check_fbo_cache) { + GLuint color = texture_storage->render_target_get_color(render_target); + GLuint depth = texture_storage->render_target_get_depth(render_target); + + rt_fbo = _rt_get_cached_fbo(color, depth, msaa3d.samples, view_count); + } else if (msaa3d.fbo != 0) { + // We have an MSAA fbo, render to our MSAA buffer + return msaa3d.fbo; + } else if (internal3d.fbo != 0) { + // We have an internal buffer, render to our internal buffer! + return internal3d.fbo; + } else { + rt_fbo = texture_storage->render_target_get_fbo(render_target); + } + + if (texture_storage->render_target_is_reattach_textures(render_target)) { + GLuint color = texture_storage->render_target_get_color(render_target); + GLuint depth = texture_storage->render_target_get_depth(render_target); + + glBindFramebuffer(GL_FRAMEBUFFER, rt_fbo); + _rt_attach_textures(color, depth, msaa3d.samples, view_count); + glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo); + } + + return rt_fbo; } #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h index 543e1aeb15..7a6811bf3c 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.h +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -41,21 +41,40 @@ class RenderSceneBuffersGLES3 : public RenderSceneBuffers { GDCLASS(RenderSceneBuffersGLES3, RenderSceneBuffers); public: - // Original implementation, need to investigate which ones we'll keep like this and what we'll change... - - int internal_width = 0; - int internal_height = 0; - int width = 0; - int height = 0; + Size2i internal_size; // Size of the buffer we render 3D content to. + Size2i target_size; // Size of our output buffer (render target). + RS::ViewportScaling3DMode scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; //float fsr_sharpness = 0.2f; - RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; //RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; + //bool use_taa = false; //bool use_debanding = false; uint32_t view_count = 1; RID render_target; - //built-in textures used for ping pong image processing and blurring + struct FBDEF { + GLuint color = 0; + GLuint depth = 0; + GLuint fbo = 0; + }; + + struct RTMSAA3D { + RS::ViewportMSAA mode = RS::VIEWPORT_MSAA_DISABLED; + bool needs_resolve = false; + GLsizei samples = 1; + GLuint color = 0; + GLuint depth = 0; + GLuint fbo = 0; + + bool check_fbo_cache = false; + Vector<FBDEF> cached_fbos; + } msaa3d; // MSAA buffers used to render 3D + + FBDEF internal3d; // buffers used to either render 3D (scaled/post) or to resolve MSAA into + + FBDEF backbuffer3d; // our back buffer + + // Built-in textures used for ping pong image processing and blurring. struct Blur { RID texture; @@ -72,6 +91,13 @@ public: Blur blur[2]; //the second one starts from the first mipmap private: + void _clear_msaa3d_buffers(); + void _clear_intermediate_buffers(); + void _clear_back_buffers(); + + void _rt_attach_textures(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count); + GLuint _rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count); + public: virtual ~RenderSceneBuffersGLES3(); virtual void configure(const RenderSceneBuffersConfiguration *p_config) override; @@ -81,6 +107,33 @@ public: virtual void set_use_debanding(bool p_use_debanding) override{}; void free_render_buffer_data(); + + void check_backbuffer(bool p_need_color, bool p_need_depth); // check if we need to initialise our backbuffer + + GLuint get_render_fbo(); + GLuint get_msaa3d_fbo() const { return msaa3d.fbo; } + GLuint get_msaa3d_color() const { return msaa3d.color; } + GLuint get_msaa3d_depth() const { return msaa3d.depth; } + bool get_msaa_needs_resolve() const { return msaa3d.needs_resolve; } + GLuint get_internal_fbo() const { return internal3d.fbo; } + GLuint get_internal_color() const { return internal3d.color; } + GLuint get_internal_depth() const { return internal3d.depth; } + GLuint get_backbuffer_fbo() const { return backbuffer3d.fbo; } + GLuint get_backbuffer() const { return backbuffer3d.color; } + GLuint get_backbuffer_depth() const { return backbuffer3d.depth; } + + // Getters + + _FORCE_INLINE_ RID get_render_target() const { return render_target; } + _FORCE_INLINE_ uint32_t get_view_count() const { return view_count; } + _FORCE_INLINE_ Size2i get_internal_size() const { return internal_size; } + _FORCE_INLINE_ Size2i get_target_size() const { return target_size; } + _FORCE_INLINE_ RS::ViewportScaling3DMode get_scaling_3d_mode() const { return scaling_3d_mode; } + //_FORCE_INLINE_ float get_fsr_sharpness() const { return fsr_sharpness; } + _FORCE_INLINE_ RS::ViewportMSAA get_msaa_3d() const { return msaa3d.mode; } + //_FORCE_INLINE_ RS::ViewportScreenSpaceAA get_screen_space_aa() const { return screen_space_aa; } + //_FORCE_INLINE_ bool get_use_taa() const { return use_taa; } + //_FORCE_INLINE_ bool get_use_debanding() const { return use_debanding; } }; #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index dec0a3f811..5a59f6c772 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -766,7 +766,7 @@ void TextureStorage::texture_2d_layered_initialize(RID p_texture, const Vector<R ERR_FAIL_COND(p_layered_type == RS::TEXTURE_LAYERED_CUBEMAP && p_layers.size() != 6); ERR_FAIL_COND_MSG(p_layered_type == RS::TEXTURE_LAYERED_CUBEMAP_ARRAY, "Cubemap Arrays are not supported in the GL Compatibility backend."); - Ref<Image> image = p_layers[0]; + const Ref<Image> &image = p_layers[0]; { int valid_width = 0; int valid_height = 0; @@ -1475,7 +1475,7 @@ void TextureStorage::update_texture_atlas() { //generate atlas Vector<TextureAtlas::SortItem> itemsv; itemsv.resize(texture_atlas.textures.size()); - int base_size = 8; + uint32_t base_size = 8; int idx = 0; @@ -1488,7 +1488,7 @@ void TextureStorage::update_texture_atlas() { si.size.height = (src_tex->height / border) + 1; si.pixel_size = Size2i(src_tex->width, src_tex->height); - if (base_size < si.size.width) { + if (base_size < (uint32_t)si.size.width) { base_size = nearest_power_of_2_templated(si.size.width); } @@ -1519,7 +1519,7 @@ void TextureStorage::update_texture_atlas() { TextureAtlas::SortItem &si = items[i]; int best_idx = -1; int best_height = 0x7FFFFFFF; - for (int j = 0; j <= base_size - si.size.width; j++) { + for (uint32_t j = 0; j <= base_size - si.size.width; j++) { int height = 0; for (int k = 0; k < si.size.width; k++) { int h = v_offsets[k + j]; @@ -1550,7 +1550,7 @@ void TextureStorage::update_texture_atlas() { } } - if (max_height <= base_size * 2) { + if ((uint32_t)max_height <= base_size * 2) { atlas_height = max_height; break; //good ratio, break; } @@ -1732,7 +1732,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, rt->color, 0); } // depth @@ -1765,7 +1765,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, rt->depth, 0); } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -1874,7 +1874,7 @@ void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } } -void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture) { +void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture) { if (rt->backbuffer != 0 && rt->backbuffer_depth != 0) { return; } @@ -1935,7 +1935,7 @@ void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const boo } } void TextureStorage::_clear_render_target(RenderTarget *rt) { - // there is nothing to clear when DIRECT_TO_SCREEN is used + // there is nothing else to clear when DIRECT_TO_SCREEN is used if (rt->direct_to_screen) { return; } @@ -2229,6 +2229,7 @@ void TextureStorage::render_target_clear_used(RID p_render_target) { void TextureStorage::render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); + ERR_FAIL_COND(rt->direct_to_screen); if (p_msaa == rt->msaa) { return; } @@ -2284,6 +2285,55 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) { glBindFramebuffer(GL_FRAMEBUFFER, system_fbo); } +GLuint TextureStorage::render_target_get_fbo(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, 0); + + return rt->fbo; +} + +GLuint TextureStorage::render_target_get_color(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, 0); + + if (rt->overridden.color.is_valid()) { + Texture *texture = get_texture(rt->overridden.color); + ERR_FAIL_NULL_V(texture, 0); + + return texture->tex_id; + } else { + return rt->color; + } +} + +GLuint TextureStorage::render_target_get_depth(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, 0); + + if (rt->overridden.depth.is_valid()) { + Texture *texture = get_texture(rt->overridden.depth); + ERR_FAIL_NULL_V(texture, 0); + + return texture->tex_id; + } else { + return rt->depth; + } +} + +void TextureStorage::render_target_set_reattach_textures(RID p_render_target, bool p_reattach_textures) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL(rt); + + rt->reattach_textures = p_reattach_textures; +} + +bool TextureStorage::render_target_is_reattach_textures(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, false); + + return rt->reattach_textures; +} + void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); @@ -2597,7 +2647,10 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, rt->color); - GLES3::CopyEffects::get_singleton()->copy_screen(); + Rect2 normalized_region = region; + normalized_region.position = normalized_region.position / Size2(rt->size); + normalized_region.size = normalized_region.size / Size2(rt->size); + GLES3::CopyEffects::get_singleton()->copy_to_and_from_rect(normalized_region); if (p_gen_mipmaps) { GLES3::CopyEffects::get_singleton()->gaussian_blur(rt->backbuffer, rt->mipmap_count, region, rt->size); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 87a07ebb36..a4e5eb260e 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -365,6 +365,7 @@ struct RenderTarget { bool used_in_frame = false; RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; + bool reattach_textures = false; struct RTOverridden { bool is_overridden = false; @@ -484,7 +485,7 @@ public: /* Texture API */ - Texture *get_texture(RID p_rid) { + Texture *get_texture(RID p_rid) const { Texture *texture = texture_owner.get_or_null(p_rid); if (texture && texture->is_proxy) { return texture_owner.get_or_null(texture->proxy_to); @@ -602,7 +603,7 @@ public: RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); }; bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); }; - void copy_scene_to_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture); + void check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture); virtual RID render_target_create() override; virtual void render_target_free(RID p_rid) override; @@ -619,6 +620,9 @@ public: void render_target_clear_used(RID p_render_target); virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override; virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override; + virtual void render_target_set_msaa_needs_resolve(RID p_render_target, bool p_needs_resolve) override {} + virtual bool render_target_get_msaa_needs_resolve(RID p_render_target) const override { return false; } + virtual void render_target_do_msaa_resolve(RID p_render_target) override {} virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override {} virtual bool render_target_is_using_hdr(RID p_render_target) const override { return false; } @@ -633,6 +637,12 @@ public: void render_target_disable_clear_request(RID p_render_target) override; void render_target_do_clear_request(RID p_render_target) override; + GLuint render_target_get_fbo(RID p_render_target) const; + GLuint render_target_get_color(RID p_render_target) const; + GLuint render_target_get_depth(RID p_render_target) const; + void render_target_set_reattach_textures(RID p_render_target, bool p_reattach_textures) const; + bool render_target_is_reattach_textures(RID p_render_target) const; + virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override; virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override; GLuint render_target_get_sdf_texture(RID p_render_target); diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 72bcbe879c..8a9e61c725 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -85,6 +85,21 @@ Utilities::~Utilities() { } } + if (render_buffer_mem_cache) { + uint32_t leaked_data_size = 0; + for (const KeyValue<GLuint, ResourceAllocation> &E : render_buffer_allocs_cache) { +#ifdef DEV_ENABLED + ERR_PRINT(E.value.name + ": leaked " + itos(E.value.size) + " bytes."); +#else + ERR_PRINT("Render buffer with GL ID of " + itos(E.key) + ": leaked " + itos(E.value.size) + " bytes."); +#endif + leaked_data_size += E.value.size; + } + if (leaked_data_size < render_buffer_mem_cache) { + ERR_PRINT("Render buffer cache is not empty. There may be an additional render buffer leak of " + itos(render_buffer_mem_cache - leaked_data_size) + " bytes."); + } + } + if (buffer_mem_cache) { uint32_t leaked_data_size = 0; @@ -327,6 +342,8 @@ void Utilities::update_dirty_resources() { } void Utilities::set_debug_generate_wireframes(bool p_generate) { + Config *config = Config::get_singleton(); + config->generate_wireframes = p_generate; } bool Utilities::has_os_feature(const String &p_feature) const { @@ -362,11 +379,11 @@ void Utilities::update_memory_info() { uint64_t Utilities::get_rendering_info(RS::RenderingInfo p_info) { if (p_info == RS::RENDERING_INFO_TEXTURE_MEM_USED) { - return texture_mem_cache; + return texture_mem_cache + render_buffer_mem_cache; // Add render buffer memory to our texture mem. } else if (p_info == RS::RENDERING_INFO_BUFFER_MEM_USED) { return buffer_mem_cache; } else if (p_info == RS::RENDERING_INFO_VIDEO_MEM_USED) { - return texture_mem_cache + buffer_mem_cache; + return texture_mem_cache + buffer_mem_cache + render_buffer_mem_cache; } return 0; } diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h index 76e4d510de..ea7bf4a4c2 100644 --- a/drivers/gles3/storage/utilities.h +++ b/drivers/gles3/storage/utilities.h @@ -50,9 +50,11 @@ private: uint32_t size = 0; }; HashMap<GLuint, ResourceAllocation> buffer_allocs_cache; + HashMap<GLuint, ResourceAllocation> render_buffer_allocs_cache; HashMap<GLuint, ResourceAllocation> texture_allocs_cache; uint64_t buffer_mem_cache = 0; + uint64_t render_buffer_mem_cache = 0; uint64_t texture_mem_cache = 0; public: @@ -88,6 +90,26 @@ public: buffer_allocs_cache.erase(p_id); } + _FORCE_INLINE_ void render_buffer_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") { + render_buffer_mem_cache += p_size; +#ifdef DEV_ENABLED + ERR_FAIL_COND_MSG(render_buffer_allocs_cache.has(p_id), "trying to allocate render buffer with name " + p_name + " but ID already used by " + render_buffer_allocs_cache[p_id].name); +#endif + ResourceAllocation resource_allocation; + resource_allocation.size = p_size; +#ifdef DEV_ENABLED + resource_allocation.name = p_name + ": " + itos((uint64_t)p_id); +#endif + render_buffer_allocs_cache[p_id] = resource_allocation; + } + + _FORCE_INLINE_ void render_buffer_free_data(GLuint p_id) { + ERR_FAIL_COND(!render_buffer_allocs_cache.has(p_id)); + glDeleteRenderbuffers(1, &p_id); + render_buffer_mem_cache -= render_buffer_allocs_cache[p_id].size; + render_buffer_allocs_cache.erase(p_id); + } + // Records that data was allocated for state tracking purposes. _FORCE_INLINE_ void texture_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") { texture_mem_cache += p_size; diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp index cbcb54bc11..6f98f072dd 100644 --- a/drivers/png/image_loader_png.cpp +++ b/drivers/png/image_loader_png.cpp @@ -66,12 +66,14 @@ Ref<Image> ImageLoaderPNG::load_mem_png(const uint8_t *p_png, int p_size) { return img; } +Ref<Image> ImageLoaderPNG::unpack_mem_png(const uint8_t *p_png, int p_size) { + ERR_FAIL_COND_V(p_size < 4, Ref<Image>()); + ERR_FAIL_COND_V(p_png[0] != 'P' || p_png[1] != 'N' || p_png[2] != 'G' || p_png[3] != ' ', Ref<Image>()); + return load_mem_png(&p_png[4], p_size - 4); +} + Ref<Image> ImageLoaderPNG::lossless_unpack_png(const Vector<uint8_t> &p_data) { - const int len = p_data.size(); - ERR_FAIL_COND_V(len < 4, Ref<Image>()); - const uint8_t *r = p_data.ptr(); - ERR_FAIL_COND_V(r[0] != 'P' || r[1] != 'N' || r[2] != 'G' || r[3] != ' ', Ref<Image>()); - return load_mem_png(&r[4], len - 4); + return unpack_mem_png(p_data.ptr(), p_data.size()); } Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) { @@ -99,6 +101,7 @@ Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) { ImageLoaderPNG::ImageLoaderPNG() { Image::_png_mem_loader_func = load_mem_png; + Image::_png_mem_unpacker_func = unpack_mem_png; Image::png_unpacker = lossless_unpack_png; Image::png_packer = lossless_pack_png; } diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h index d587672dd1..ecce9a405b 100644 --- a/drivers/png/image_loader_png.h +++ b/drivers/png/image_loader_png.h @@ -37,6 +37,7 @@ class ImageLoaderPNG : public ImageFormatLoader { private: static Vector<uint8_t> lossless_pack_png(const Ref<Image> &p_image); static Ref<Image> lossless_unpack_png(const Vector<uint8_t> &p_data); + static Ref<Image> unpack_mem_png(const uint8_t *p_png, int p_size); static Ref<Image> load_mem_png(const uint8_t *p_png, int p_size); public: diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index ca5a13799e..d1e4d207e7 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -228,6 +228,51 @@ uint8_t FileAccessUnix::get_8() const { return b; } +uint16_t FileAccessUnix::get_16() const { + ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use."); + + uint16_t b = 0; + if (fread(&b, 1, 2, f) != 2) { + check_errors(); + } + + if (big_endian) { + b = BSWAP16(b); + } + + return b; +} + +uint32_t FileAccessUnix::get_32() const { + ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use."); + + uint32_t b = 0; + if (fread(&b, 1, 4, f) != 4) { + check_errors(); + } + + if (big_endian) { + b = BSWAP32(b); + } + + return b; +} + +uint64_t FileAccessUnix::get_64() const { + ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use."); + + uint64_t b = 0; + if (fread(&b, 1, 8, f) != 8) { + check_errors(); + } + + if (big_endian) { + b = BSWAP64(b); + } + + return b; +} + uint64_t FileAccessUnix::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); ERR_FAIL_NULL_V_MSG(f, -1, "File must be opened before use."); @@ -251,6 +296,36 @@ void FileAccessUnix::store_8(uint8_t p_dest) { ERR_FAIL_COND(fwrite(&p_dest, 1, 1, f) != 1); } +void FileAccessUnix::store_16(uint16_t p_dest) { + ERR_FAIL_NULL_MSG(f, "File must be opened before use."); + + if (big_endian) { + p_dest = BSWAP16(p_dest); + } + + ERR_FAIL_COND(fwrite(&p_dest, 1, 2, f) != 2); +} + +void FileAccessUnix::store_32(uint32_t p_dest) { + ERR_FAIL_NULL_MSG(f, "File must be opened before use."); + + if (big_endian) { + p_dest = BSWAP32(p_dest); + } + + ERR_FAIL_COND(fwrite(&p_dest, 1, 4, f) != 4); +} + +void FileAccessUnix::store_64(uint64_t p_dest) { + ERR_FAIL_NULL_MSG(f, "File must be opened before use."); + + if (big_endian) { + p_dest = BSWAP64(p_dest); + } + + ERR_FAIL_COND(fwrite(&p_dest, 1, 8, f) != 8); +} + void FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_NULL_MSG(f, "File must be opened before use."); ERR_FAIL_COND(!p_src && p_length > 0); diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 2bfac27c4f..553fbcf355 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -68,12 +68,18 @@ public: virtual bool eof_reached() const override; ///< reading passed EOF virtual uint8_t get_8() const override; ///< get a byte + virtual uint16_t get_16() const override; + virtual uint32_t get_32() const override; + virtual uint64_t get_64() const override; virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; ///< get last error virtual void flush() override; virtual void store_8(uint8_t p_dest) override; ///< store a byte + virtual void store_16(uint16_t p_dest) override; + virtual void store_32(uint32_t p_dest) override; + virtual void store_64(uint64_t p_dest) override; virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_path) override; ///< return true if a file exists diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 9a77930d75..51ea9234d4 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -656,6 +656,8 @@ Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle path = get_executable_path().get_base_dir().path_join("../lib").path_join(p_path.get_file()); } + ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND); + p_library_handle = dlopen(path.utf8().get_data(), GODOT_DLOPEN_MODE); ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); @@ -751,12 +753,27 @@ String OS_Unix::get_executable_path() const { return OS::get_executable_path(); } return b; -#elif defined(__OpenBSD__) || defined(__NetBSD__) +#elif defined(__OpenBSD__) char resolved_path[MAXPATHLEN]; realpath(OS::get_executable_path().utf8().get_data(), resolved_path); return String(resolved_path); +#elif defined(__NetBSD__) + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; + char buf[MAXPATHLEN]; + size_t len = sizeof(buf); + if (sysctl(mib, 4, buf, &len, nullptr, 0) != 0) { + WARN_PRINT("Couldn't get executable path from sysctl"); + return OS::get_executable_path(); + } + + // NetBSD does not always return a normalized path. For example if argv[0] is "./a.out" then executable path is "/home/netbsd/./a.out". Normalize with realpath: + char resolved_path[MAXPATHLEN]; + + realpath(buf, resolved_path); + + return String(resolved_path); #elif defined(__FreeBSD__) int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; char buf[MAXPATHLEN]; diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp new file mode 100644 index 0000000000..8378e26666 --- /dev/null +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -0,0 +1,3371 @@ +/**************************************************************************/ +/* rendering_device_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. */ +/**************************************************************************/ + +#include "rendering_device_driver_vulkan.h" + +#include "core/config/project_settings.h" +#include "core/io/marshalls.h" +#include "thirdparty/misc/smolv.h" +#include "vulkan_context.h" + +/*****************/ +/**** GENERIC ****/ +/*****************/ + +static const VkFormat RD_TO_VK_FORMAT[RDD::DATA_FORMAT_MAX] = { + VK_FORMAT_R4G4_UNORM_PACK8, + VK_FORMAT_R4G4B4A4_UNORM_PACK16, + VK_FORMAT_B4G4R4A4_UNORM_PACK16, + VK_FORMAT_R5G6B5_UNORM_PACK16, + VK_FORMAT_B5G6R5_UNORM_PACK16, + VK_FORMAT_R5G5B5A1_UNORM_PACK16, + VK_FORMAT_B5G5R5A1_UNORM_PACK16, + VK_FORMAT_A1R5G5B5_UNORM_PACK16, + VK_FORMAT_R8_UNORM, + VK_FORMAT_R8_SNORM, + VK_FORMAT_R8_USCALED, + VK_FORMAT_R8_SSCALED, + VK_FORMAT_R8_UINT, + VK_FORMAT_R8_SINT, + VK_FORMAT_R8_SRGB, + VK_FORMAT_R8G8_UNORM, + VK_FORMAT_R8G8_SNORM, + VK_FORMAT_R8G8_USCALED, + VK_FORMAT_R8G8_SSCALED, + VK_FORMAT_R8G8_UINT, + VK_FORMAT_R8G8_SINT, + VK_FORMAT_R8G8_SRGB, + VK_FORMAT_R8G8B8_UNORM, + VK_FORMAT_R8G8B8_SNORM, + VK_FORMAT_R8G8B8_USCALED, + VK_FORMAT_R8G8B8_SSCALED, + VK_FORMAT_R8G8B8_UINT, + VK_FORMAT_R8G8B8_SINT, + VK_FORMAT_R8G8B8_SRGB, + VK_FORMAT_B8G8R8_UNORM, + VK_FORMAT_B8G8R8_SNORM, + VK_FORMAT_B8G8R8_USCALED, + VK_FORMAT_B8G8R8_SSCALED, + VK_FORMAT_B8G8R8_UINT, + VK_FORMAT_B8G8R8_SINT, + VK_FORMAT_B8G8R8_SRGB, + VK_FORMAT_R8G8B8A8_UNORM, + VK_FORMAT_R8G8B8A8_SNORM, + VK_FORMAT_R8G8B8A8_USCALED, + VK_FORMAT_R8G8B8A8_SSCALED, + VK_FORMAT_R8G8B8A8_UINT, + VK_FORMAT_R8G8B8A8_SINT, + VK_FORMAT_R8G8B8A8_SRGB, + VK_FORMAT_B8G8R8A8_UNORM, + VK_FORMAT_B8G8R8A8_SNORM, + VK_FORMAT_B8G8R8A8_USCALED, + VK_FORMAT_B8G8R8A8_SSCALED, + VK_FORMAT_B8G8R8A8_UINT, + VK_FORMAT_B8G8R8A8_SINT, + VK_FORMAT_B8G8R8A8_SRGB, + VK_FORMAT_A8B8G8R8_UNORM_PACK32, + VK_FORMAT_A8B8G8R8_SNORM_PACK32, + VK_FORMAT_A8B8G8R8_USCALED_PACK32, + VK_FORMAT_A8B8G8R8_SSCALED_PACK32, + VK_FORMAT_A8B8G8R8_UINT_PACK32, + VK_FORMAT_A8B8G8R8_SINT_PACK32, + VK_FORMAT_A8B8G8R8_SRGB_PACK32, + VK_FORMAT_A2R10G10B10_UNORM_PACK32, + VK_FORMAT_A2R10G10B10_SNORM_PACK32, + VK_FORMAT_A2R10G10B10_USCALED_PACK32, + VK_FORMAT_A2R10G10B10_SSCALED_PACK32, + VK_FORMAT_A2R10G10B10_UINT_PACK32, + VK_FORMAT_A2R10G10B10_SINT_PACK32, + VK_FORMAT_A2B10G10R10_UNORM_PACK32, + VK_FORMAT_A2B10G10R10_SNORM_PACK32, + VK_FORMAT_A2B10G10R10_USCALED_PACK32, + VK_FORMAT_A2B10G10R10_SSCALED_PACK32, + VK_FORMAT_A2B10G10R10_UINT_PACK32, + VK_FORMAT_A2B10G10R10_SINT_PACK32, + VK_FORMAT_R16_UNORM, + VK_FORMAT_R16_SNORM, + VK_FORMAT_R16_USCALED, + VK_FORMAT_R16_SSCALED, + VK_FORMAT_R16_UINT, + VK_FORMAT_R16_SINT, + VK_FORMAT_R16_SFLOAT, + VK_FORMAT_R16G16_UNORM, + VK_FORMAT_R16G16_SNORM, + VK_FORMAT_R16G16_USCALED, + VK_FORMAT_R16G16_SSCALED, + VK_FORMAT_R16G16_UINT, + VK_FORMAT_R16G16_SINT, + VK_FORMAT_R16G16_SFLOAT, + VK_FORMAT_R16G16B16_UNORM, + VK_FORMAT_R16G16B16_SNORM, + VK_FORMAT_R16G16B16_USCALED, + VK_FORMAT_R16G16B16_SSCALED, + VK_FORMAT_R16G16B16_UINT, + VK_FORMAT_R16G16B16_SINT, + VK_FORMAT_R16G16B16_SFLOAT, + VK_FORMAT_R16G16B16A16_UNORM, + VK_FORMAT_R16G16B16A16_SNORM, + VK_FORMAT_R16G16B16A16_USCALED, + VK_FORMAT_R16G16B16A16_SSCALED, + VK_FORMAT_R16G16B16A16_UINT, + VK_FORMAT_R16G16B16A16_SINT, + VK_FORMAT_R16G16B16A16_SFLOAT, + VK_FORMAT_R32_UINT, + VK_FORMAT_R32_SINT, + VK_FORMAT_R32_SFLOAT, + VK_FORMAT_R32G32_UINT, + VK_FORMAT_R32G32_SINT, + VK_FORMAT_R32G32_SFLOAT, + VK_FORMAT_R32G32B32_UINT, + VK_FORMAT_R32G32B32_SINT, + VK_FORMAT_R32G32B32_SFLOAT, + VK_FORMAT_R32G32B32A32_UINT, + VK_FORMAT_R32G32B32A32_SINT, + VK_FORMAT_R32G32B32A32_SFLOAT, + VK_FORMAT_R64_UINT, + VK_FORMAT_R64_SINT, + VK_FORMAT_R64_SFLOAT, + VK_FORMAT_R64G64_UINT, + VK_FORMAT_R64G64_SINT, + VK_FORMAT_R64G64_SFLOAT, + VK_FORMAT_R64G64B64_UINT, + VK_FORMAT_R64G64B64_SINT, + VK_FORMAT_R64G64B64_SFLOAT, + VK_FORMAT_R64G64B64A64_UINT, + VK_FORMAT_R64G64B64A64_SINT, + VK_FORMAT_R64G64B64A64_SFLOAT, + VK_FORMAT_B10G11R11_UFLOAT_PACK32, + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, + VK_FORMAT_D16_UNORM, + VK_FORMAT_X8_D24_UNORM_PACK32, + VK_FORMAT_D32_SFLOAT, + VK_FORMAT_S8_UINT, + VK_FORMAT_D16_UNORM_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_FORMAT_BC1_RGB_UNORM_BLOCK, + VK_FORMAT_BC1_RGB_SRGB_BLOCK, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK, + VK_FORMAT_BC1_RGBA_SRGB_BLOCK, + VK_FORMAT_BC2_UNORM_BLOCK, + VK_FORMAT_BC2_SRGB_BLOCK, + VK_FORMAT_BC3_UNORM_BLOCK, + VK_FORMAT_BC3_SRGB_BLOCK, + VK_FORMAT_BC4_UNORM_BLOCK, + VK_FORMAT_BC4_SNORM_BLOCK, + VK_FORMAT_BC5_UNORM_BLOCK, + VK_FORMAT_BC5_SNORM_BLOCK, + VK_FORMAT_BC6H_UFLOAT_BLOCK, + VK_FORMAT_BC6H_SFLOAT_BLOCK, + VK_FORMAT_BC7_UNORM_BLOCK, + VK_FORMAT_BC7_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + VK_FORMAT_EAC_R11_UNORM_BLOCK, + VK_FORMAT_EAC_R11_SNORM_BLOCK, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK, + VK_FORMAT_ASTC_4x4_SRGB_BLOCK, + VK_FORMAT_ASTC_5x4_UNORM_BLOCK, + VK_FORMAT_ASTC_5x4_SRGB_BLOCK, + VK_FORMAT_ASTC_5x5_UNORM_BLOCK, + VK_FORMAT_ASTC_5x5_SRGB_BLOCK, + VK_FORMAT_ASTC_6x5_UNORM_BLOCK, + VK_FORMAT_ASTC_6x5_SRGB_BLOCK, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK, + VK_FORMAT_ASTC_6x6_SRGB_BLOCK, + VK_FORMAT_ASTC_8x5_UNORM_BLOCK, + VK_FORMAT_ASTC_8x5_SRGB_BLOCK, + VK_FORMAT_ASTC_8x6_UNORM_BLOCK, + VK_FORMAT_ASTC_8x6_SRGB_BLOCK, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK, + VK_FORMAT_ASTC_8x8_SRGB_BLOCK, + VK_FORMAT_ASTC_10x5_UNORM_BLOCK, + VK_FORMAT_ASTC_10x5_SRGB_BLOCK, + VK_FORMAT_ASTC_10x6_UNORM_BLOCK, + VK_FORMAT_ASTC_10x6_SRGB_BLOCK, + VK_FORMAT_ASTC_10x8_UNORM_BLOCK, + VK_FORMAT_ASTC_10x8_SRGB_BLOCK, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK, + VK_FORMAT_ASTC_10x10_SRGB_BLOCK, + VK_FORMAT_ASTC_12x10_UNORM_BLOCK, + VK_FORMAT_ASTC_12x10_SRGB_BLOCK, + VK_FORMAT_ASTC_12x12_UNORM_BLOCK, + VK_FORMAT_ASTC_12x12_SRGB_BLOCK, + VK_FORMAT_G8B8G8R8_422_UNORM, + VK_FORMAT_B8G8R8G8_422_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, + VK_FORMAT_R10X6_UNORM_PACK16, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16, + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, + VK_FORMAT_R12X4_UNORM_PACK16, + VK_FORMAT_R12X4G12X4_UNORM_2PACK16, + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, + VK_FORMAT_G16B16G16R16_422_UNORM, + VK_FORMAT_B16G16R16G16_422_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, +}; + +// RDD::CompareOperator == VkCompareOp. +static_assert(ENUM_MEMBERS_EQUAL(RDD::COMPARE_OP_NEVER, VK_COMPARE_OP_NEVER)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::COMPARE_OP_LESS, VK_COMPARE_OP_LESS)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::COMPARE_OP_EQUAL, VK_COMPARE_OP_EQUAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::COMPARE_OP_LESS_OR_EQUAL, VK_COMPARE_OP_LESS_OR_EQUAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::COMPARE_OP_GREATER, VK_COMPARE_OP_GREATER)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::COMPARE_OP_NOT_EQUAL, VK_COMPARE_OP_NOT_EQUAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::COMPARE_OP_GREATER_OR_EQUAL, VK_COMPARE_OP_GREATER_OR_EQUAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::COMPARE_OP_ALWAYS, VK_COMPARE_OP_ALWAYS)); + +static_assert(ARRAYS_COMPATIBLE_FIELDWISE(Rect2i, VkRect2D)); + +/****************/ +/**** MEMORY ****/ +/****************/ + +static const uint32_t SMALL_ALLOCATION_MAX_SIZE = 4096; + +VmaPool RenderingDeviceDriverVulkan::_find_or_create_small_allocs_pool(uint32_t p_mem_type_index) { + if (small_allocs_pools.has(p_mem_type_index)) { + return small_allocs_pools[p_mem_type_index]; + } + + print_verbose("Creating VMA small objects pool for memory type index " + itos(p_mem_type_index)); + + VmaPoolCreateInfo pci = {}; + pci.memoryTypeIndex = p_mem_type_index; + pci.flags = 0; + pci.blockSize = 0; + pci.minBlockCount = 0; + pci.maxBlockCount = SIZE_MAX; + pci.priority = 0.5f; + pci.minAllocationAlignment = 0; + pci.pMemoryAllocateNext = nullptr; + VmaPool pool = VK_NULL_HANDLE; + VkResult res = vmaCreatePool(allocator, &pci, &pool); + small_allocs_pools[p_mem_type_index] = pool; // Don't try to create it again if failed the first time. + ERR_FAIL_COND_V_MSG(res, pool, "vmaCreatePool failed with error " + itos(res) + "."); + + return pool; +} + +/*****************/ +/**** BUFFERS ****/ +/*****************/ + +// RDD::BufferUsageBits == VkBufferUsageFlagBits. +static_assert(ENUM_MEMBERS_EQUAL(RDD::BUFFER_USAGE_TRANSFER_FROM_BIT, VK_BUFFER_USAGE_TRANSFER_SRC_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BUFFER_USAGE_TRANSFER_TO_BIT, VK_BUFFER_USAGE_TRANSFER_DST_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BUFFER_USAGE_TEXEL_BIT, VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BUFFER_USAGE_UNIFORM_BIT, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BUFFER_USAGE_STORAGE_BIT, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BUFFER_USAGE_INDEX_BIT, VK_BUFFER_USAGE_INDEX_BUFFER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BUFFER_USAGE_VERTEX_BIT, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BUFFER_USAGE_INDIRECT_BIT, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT)); + +RDD::BufferID RenderingDeviceDriverVulkan::buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type) { + VkBufferCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + create_info.size = p_size; + create_info.usage = p_usage; + create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VmaAllocationCreateInfo alloc_create_info = {}; + switch (p_allocation_type) { + case MEMORY_ALLOCATION_TYPE_CPU: { + bool is_src = p_usage.has_flag(BUFFER_USAGE_TRANSFER_FROM_BIT); + bool is_dst = p_usage.has_flag(BUFFER_USAGE_TRANSFER_TO_BIT); + if (is_src && !is_dst) { + // Looks like a staging buffer: CPU maps, writes sequentially, then GPU copies to VRAM. + alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + } + if (is_dst && !is_src) { + // Looks like a readback buffer: GPU copies from VRAM, then CPU maps and reads. + alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; + } + alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; + alloc_create_info.requiredFlags = (VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + } break; + case MEMORY_ALLOCATION_TYPE_GPU: { + alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + if (p_size <= SMALL_ALLOCATION_MAX_SIZE) { + uint32_t mem_type_index = 0; + vmaFindMemoryTypeIndexForBufferInfo(allocator, &create_info, &alloc_create_info, &mem_type_index); + alloc_create_info.pool = _find_or_create_small_allocs_pool(mem_type_index); + } + } break; + } + + VkBuffer vk_buffer = VK_NULL_HANDLE; + VmaAllocation allocation = nullptr; + VmaAllocationInfo alloc_info = {}; + VkResult err = vmaCreateBuffer(allocator, &create_info, &alloc_create_info, &vk_buffer, &allocation, &alloc_info); + ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't create buffer of size: " + itos(p_size) + ", error " + itos(err) + "."); + + // Bookkeep. + + BufferInfo *buf_info = VersatileResource::allocate<BufferInfo>(resources_allocator); + buf_info->vk_buffer = vk_buffer; + buf_info->allocation.handle = allocation; + buf_info->allocation.size = alloc_info.size; + buf_info->size = p_size; + + return BufferID(buf_info); +} + +bool RenderingDeviceDriverVulkan::buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) { + BufferInfo *buf_info = (BufferInfo *)p_buffer.id; + + DEV_ASSERT(!buf_info->vk_view); + + VkBufferViewCreateInfo view_create_info = {}; + view_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; + view_create_info.buffer = buf_info->vk_buffer; + view_create_info.format = RD_TO_VK_FORMAT[p_format]; + view_create_info.range = buf_info->allocation.size; + + VkResult res = vkCreateBufferView(vk_device, &view_create_info, nullptr, &buf_info->vk_view); + ERR_FAIL_COND_V_MSG(res, false, "Unable to create buffer view, error " + itos(res) + "."); + + return true; +} + +void RenderingDeviceDriverVulkan::buffer_free(BufferID p_buffer) { + BufferInfo *buf_info = (BufferInfo *)p_buffer.id; + if (buf_info->vk_view) { + vkDestroyBufferView(vk_device, buf_info->vk_view, nullptr); + } + vmaDestroyBuffer(allocator, buf_info->vk_buffer, buf_info->allocation.handle); + VersatileResource::free(resources_allocator, buf_info); +} + +uint64_t RenderingDeviceDriverVulkan::buffer_get_allocation_size(BufferID p_buffer) { + const BufferInfo *buf_info = (const BufferInfo *)p_buffer.id; + return buf_info->allocation.size; +} + +uint8_t *RenderingDeviceDriverVulkan::buffer_map(BufferID p_buffer) { + const BufferInfo *buf_info = (const BufferInfo *)p_buffer.id; + void *data_ptr = nullptr; + VkResult err = vmaMapMemory(allocator, buf_info->allocation.handle, &data_ptr); + ERR_FAIL_COND_V_MSG(err, nullptr, "vmaMapMemory failed with error " + itos(err) + "."); + return (uint8_t *)data_ptr; +} + +void RenderingDeviceDriverVulkan::buffer_unmap(BufferID p_buffer) { + const BufferInfo *buf_info = (const BufferInfo *)p_buffer.id; + vmaUnmapMemory(allocator, buf_info->allocation.handle); +} + +/*****************/ +/**** TEXTURE ****/ +/*****************/ + +static const VkImageType RD_TEX_TYPE_TO_VK_IMG_TYPE[RDD::TEXTURE_TYPE_MAX] = { + VK_IMAGE_TYPE_1D, + VK_IMAGE_TYPE_2D, + VK_IMAGE_TYPE_3D, + VK_IMAGE_TYPE_2D, + VK_IMAGE_TYPE_1D, + VK_IMAGE_TYPE_2D, + VK_IMAGE_TYPE_2D, +}; + +static const VkSampleCountFlagBits RD_TO_VK_SAMPLE_COUNT[RDD::TEXTURE_SAMPLES_MAX] = { + VK_SAMPLE_COUNT_1_BIT, + VK_SAMPLE_COUNT_2_BIT, + VK_SAMPLE_COUNT_4_BIT, + VK_SAMPLE_COUNT_8_BIT, + VK_SAMPLE_COUNT_16_BIT, + VK_SAMPLE_COUNT_32_BIT, + VK_SAMPLE_COUNT_64_BIT, +}; + +// RDD::TextureType == VkImageViewType. +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_TYPE_1D, VK_IMAGE_VIEW_TYPE_1D)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_TYPE_2D, VK_IMAGE_VIEW_TYPE_2D)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_TYPE_3D, VK_IMAGE_VIEW_TYPE_3D)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_TYPE_CUBE, VK_IMAGE_VIEW_TYPE_CUBE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_TYPE_1D_ARRAY, VK_IMAGE_VIEW_TYPE_1D_ARRAY)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_TYPE_2D_ARRAY, VK_IMAGE_VIEW_TYPE_2D_ARRAY)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_TYPE_CUBE_ARRAY, VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)); + +// RDD::TextureSwizzle == VkComponentSwizzle. +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_SWIZZLE_G, VK_COMPONENT_SWIZZLE_G)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_SWIZZLE_B, VK_COMPONENT_SWIZZLE_B)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_SWIZZLE_A, VK_COMPONENT_SWIZZLE_A)); + +// RDD::TextureLayout == VkImageLayout. +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_UNDEFINED)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_PREINITIALIZED)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_LAYOUT_VRS_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR)); + +// RDD::TextureAspectBits == VkImageAspectFlagBits. +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_ASPECT_COLOR_BIT, VK_IMAGE_ASPECT_COLOR_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_ASPECT_DEPTH_BIT, VK_IMAGE_ASPECT_DEPTH_BIT)); +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); + + if ((sample_count_flags & RD_TO_VK_SAMPLE_COUNT[p_requested_sample_count])) { + // The requested sample count is supported. + return RD_TO_VK_SAMPLE_COUNT[p_requested_sample_count]; + } else { + // Find the closest lower supported sample count. + VkSampleCountFlagBits sample_count = RD_TO_VK_SAMPLE_COUNT[p_requested_sample_count]; + while (sample_count > VK_SAMPLE_COUNT_1_BIT) { + if (sample_count_flags & sample_count) { + return sample_count; + } + sample_count = (VkSampleCountFlagBits)(sample_count >> 1); + } + } + return VK_SAMPLE_COUNT_1_BIT; +} + +RDD::TextureID RenderingDeviceDriverVulkan::texture_create(const TextureFormat &p_format, const TextureView &p_view) { + VkImageCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + + 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)) { + 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]]; + } + + VkImageFormatListCreateInfoKHR *format_list_create_info = ALLOCA_SINGLE(VkImageFormatListCreateInfoKHR); + *format_list_create_info = {}; + format_list_create_info->sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR; + format_list_create_info->viewFormatCount = p_format.shareable_formats.size(); + format_list_create_info->pViewFormats = vk_allowed_formats; + + create_info.pNext = format_list_create_info; + } + } + + if (p_format.texture_type == TEXTURE_TYPE_CUBE || p_format.texture_type == TEXTURE_TYPE_CUBE_ARRAY) { + create_info.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + } + /*if (p_format.texture_type == TEXTURE_TYPE_2D || p_format.texture_type == TEXTURE_TYPE_2D_ARRAY) { + create_info.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; + }*/ + + create_info.imageType = RD_TEX_TYPE_TO_VK_IMG_TYPE[p_format.texture_type]; + + create_info.format = RD_TO_VK_FORMAT[p_format.format]; + + create_info.extent.width = p_format.width; + create_info.extent.height = p_format.height; + create_info.extent.depth = p_format.depth; + + create_info.mipLevels = p_format.mipmaps; + create_info.arrayLayers = p_format.array_layers; + + create_info.samples = _ensure_supported_sample_count(p_format.samples); + create_info.tiling = (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; + + // Usage. + if ((p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT)) { + create_info.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; + } + if ((p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT)) { + create_info.usage |= VK_IMAGE_USAGE_STORAGE_BIT; + } + if ((p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + create_info.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + if ((p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + create_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + if ((p_format.usage_bits & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) { + create_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + } + if ((p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) { + create_info.usage |= VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR; + } + if ((p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT)) { + create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + if ((p_format.usage_bits & TEXTURE_USAGE_CAN_COPY_FROM_BIT)) { + create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + if ((p_format.usage_bits & TEXTURE_USAGE_CAN_COPY_TO_BIT)) { + create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + + create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + // Allocate memory. + + uint32_t width = 0, height = 0; + uint32_t image_size = get_image_format_required_size(p_format.format, p_format.width, p_format.height, p_format.depth, p_format.mipmaps, &width, &height); + + VmaAllocationCreateInfo alloc_create_info = {}; + alloc_create_info.flags = (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) ? VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT : 0; + alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + if (image_size <= SMALL_ALLOCATION_MAX_SIZE) { + uint32_t mem_type_index = 0; + vmaFindMemoryTypeIndexForImageInfo(allocator, &create_info, &alloc_create_info, &mem_type_index); + alloc_create_info.pool = _find_or_create_small_allocs_pool(mem_type_index); + } + + // Create. + + VkImage vk_image = VK_NULL_HANDLE; + VmaAllocation allocation = nullptr; + VmaAllocationInfo alloc_info = {}; + VkResult err = vmaCreateImage(allocator, &create_info, &alloc_create_info, &vk_image, &allocation, &alloc_info); + ERR_FAIL_COND_V_MSG(err, TextureID(), "vmaCreateImage failed with error " + itos(err) + "."); + + // Create view. + + VkImageViewCreateInfo image_view_create_info = {}; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.image = vk_image; + image_view_create_info.viewType = (VkImageViewType)p_format.texture_type; + image_view_create_info.format = RD_TO_VK_FORMAT[p_view.format]; + image_view_create_info.components.r = (VkComponentSwizzle)p_view.swizzle_r; + image_view_create_info.components.g = (VkComponentSwizzle)p_view.swizzle_g; + image_view_create_info.components.b = (VkComponentSwizzle)p_view.swizzle_b; + image_view_create_info.components.a = (VkComponentSwizzle)p_view.swizzle_a; + image_view_create_info.subresourceRange.levelCount = create_info.mipLevels; + image_view_create_info.subresourceRange.layerCount = create_info.arrayLayers; + if ((p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + } else { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + VkImageView vk_image_view = VK_NULL_HANDLE; + err = vkCreateImageView(vk_device, &image_view_create_info, nullptr, &vk_image_view); + if (err) { + vmaDestroyImage(allocator, vk_image, allocation); + ERR_FAIL_COND_V_MSG(err, TextureID(), "vkCreateImageView failed with error " + itos(err) + "."); + } + + // Bookkeep. + + TextureInfo *tex_info = VersatileResource::allocate<TextureInfo>(resources_allocator); + tex_info->vk_view = vk_image_view; + tex_info->rd_format = p_format.format; + tex_info->vk_create_info = create_info; + tex_info->vk_view_create_info = image_view_create_info; + tex_info->allocation.handle = allocation; + vmaGetAllocationInfo(allocator, tex_info->allocation.handle, &tex_info->allocation.info); + + return TextureID(tex_info); +} + +RDD::TextureID RenderingDeviceDriverVulkan::texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) { + VkImage vk_image = (VkImage)p_native_texture; + + // We only need to create a view into the already existing natively-provided texture. + + VkImageViewCreateInfo image_view_create_info = {}; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.image = vk_image; + image_view_create_info.viewType = (VkImageViewType)p_type; + image_view_create_info.format = RD_TO_VK_FORMAT[p_format]; + image_view_create_info.components.r = VK_COMPONENT_SWIZZLE_R; + image_view_create_info.components.g = VK_COMPONENT_SWIZZLE_G; + image_view_create_info.components.b = VK_COMPONENT_SWIZZLE_B; + image_view_create_info.components.a = VK_COMPONENT_SWIZZLE_A; + image_view_create_info.subresourceRange.levelCount = 1; + image_view_create_info.subresourceRange.layerCount = p_array_layers; + image_view_create_info.subresourceRange.aspectMask = p_depth_stencil ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; + + VkImageView vk_image_view = VK_NULL_HANDLE; + VkResult err = vkCreateImageView(vk_device, &image_view_create_info, nullptr, &vk_image_view); + if (err) { + ERR_FAIL_COND_V_MSG(err, TextureID(), "vkCreateImageView failed with error " + itos(err) + "."); + } + + // Bookkeep. + + TextureInfo *tex_info = VersatileResource::allocate<TextureInfo>(resources_allocator); + tex_info->vk_view = vk_image_view; + tex_info->rd_format = p_format; + tex_info->vk_view_create_info = image_view_create_info; + + return TextureID(tex_info); +} + +RDD::TextureID RenderingDeviceDriverVulkan::texture_create_shared(TextureID p_original_texture, const TextureView &p_view) { + const TextureInfo *owner_tex_info = (const TextureInfo *)p_original_texture.id; +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V(!owner_tex_info->allocation.handle, TextureID()); +#endif + + VkImageViewCreateInfo image_view_create_info = owner_tex_info->vk_view_create_info; + image_view_create_info.format = RD_TO_VK_FORMAT[p_view.format]; + image_view_create_info.components.r = (VkComponentSwizzle)p_view.swizzle_r; + image_view_create_info.components.g = (VkComponentSwizzle)p_view.swizzle_g; + 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)) { + // 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) { + VkImageViewUsageCreateInfo *usage_info = ALLOCA_SINGLE(VkImageViewUsageCreateInfo); + *usage_info = {}; + usage_info->sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO; + usage_info->usage = owner_tex_info->vk_create_info.usage; + + // 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); + 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; + } + if ((usage_info->usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) && !(supported_flags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { + usage_info->usage &= ~VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + } + + image_view_create_info.pNext = usage_info; + } + } + + VkImageView new_vk_image_view = VK_NULL_HANDLE; + VkResult err = vkCreateImageView(vk_device, &image_view_create_info, nullptr, &new_vk_image_view); + ERR_FAIL_COND_V_MSG(err, TextureID(), "vkCreateImageView failed with error " + itos(err) + "."); + + // Bookkeep. + + TextureInfo *tex_info = VersatileResource::allocate<TextureInfo>(resources_allocator); + *tex_info = *owner_tex_info; + tex_info->vk_view = new_vk_image_view; + tex_info->vk_view_create_info = image_view_create_info; + tex_info->allocation = {}; + + return TextureID(tex_info); +} + +RDD::TextureID RenderingDeviceDriverVulkan::texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) { + const TextureInfo *owner_tex_info = (const TextureInfo *)p_original_texture.id; +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V(!owner_tex_info->allocation.handle, TextureID()); +#endif + + VkImageViewCreateInfo image_view_create_info = owner_tex_info->vk_view_create_info; + switch (p_slice_type) { + case TEXTURE_SLICE_2D: { + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + } break; + case TEXTURE_SLICE_3D: { + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_3D; + } break; + case TEXTURE_SLICE_CUBEMAP: { + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + } break; + case TEXTURE_SLICE_2D_ARRAY: { + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + } break; + default: { + return TextureID(nullptr); + } + } + image_view_create_info.format = RD_TO_VK_FORMAT[p_view.format]; + image_view_create_info.components.r = (VkComponentSwizzle)p_view.swizzle_r; + image_view_create_info.components.g = (VkComponentSwizzle)p_view.swizzle_g; + image_view_create_info.components.b = (VkComponentSwizzle)p_view.swizzle_b; + image_view_create_info.components.a = (VkComponentSwizzle)p_view.swizzle_a; + image_view_create_info.subresourceRange.baseMipLevel = p_mipmap; + image_view_create_info.subresourceRange.levelCount = p_mipmaps; + image_view_create_info.subresourceRange.baseArrayLayer = p_layer; + image_view_create_info.subresourceRange.layerCount = p_layers; + + VkImageView new_vk_image_view = VK_NULL_HANDLE; + VkResult err = vkCreateImageView(vk_device, &image_view_create_info, nullptr, &new_vk_image_view); + ERR_FAIL_COND_V_MSG(err, TextureID(), "vkCreateImageView failed with error " + itos(err) + "."); + + // Bookkeep. + + TextureInfo *tex_info = VersatileResource::allocate<TextureInfo>(resources_allocator); + *tex_info = *owner_tex_info; + tex_info->vk_view = new_vk_image_view; + tex_info->vk_view_create_info = image_view_create_info; + tex_info->allocation = {}; + + return TextureID(tex_info); +} + +void RenderingDeviceDriverVulkan::texture_free(TextureID p_texture) { + TextureInfo *tex_info = (TextureInfo *)p_texture.id; + vkDestroyImageView(vk_device, tex_info->vk_view, nullptr); + if (tex_info->allocation.handle) { + vmaDestroyImage(allocator, tex_info->vk_view_create_info.image, tex_info->allocation.handle); + } + VersatileResource::free(resources_allocator, tex_info); +} + +uint64_t RenderingDeviceDriverVulkan::texture_get_allocation_size(TextureID p_texture) { + const TextureInfo *tex_info = (const TextureInfo *)p_texture.id; + return tex_info->allocation.info.size; +} + +void RenderingDeviceDriverVulkan::texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) { + const TextureInfo *tex_info = (const TextureInfo *)p_texture.id; + + *r_layout = {}; + + if (tex_info->vk_create_info.tiling == VK_IMAGE_TILING_LINEAR) { + VkImageSubresource vk_subres = {}; + vk_subres.aspectMask = (VkImageAspectFlags)(1 << p_subresource.aspect); + vk_subres.arrayLayer = p_subresource.layer; + vk_subres.mipLevel = p_subresource.mipmap; + + VkSubresourceLayout vk_layout = {}; + vkGetImageSubresourceLayout(vk_device, tex_info->vk_view_create_info.image, &vk_subres, &vk_layout); + + r_layout->offset = vk_layout.offset; + r_layout->size = vk_layout.size; + r_layout->row_pitch = vk_layout.rowPitch; + r_layout->depth_pitch = vk_layout.depthPitch; + r_layout->layer_pitch = vk_layout.arrayPitch; + } else { + // Tight. + uint32_t w = tex_info->vk_create_info.extent.width; + uint32_t h = tex_info->vk_create_info.extent.height; + uint32_t d = tex_info->vk_create_info.extent.depth; + if (p_subresource.mipmap > 0) { + r_layout->offset = get_image_format_required_size(tex_info->rd_format, w, h, d, p_subresource.mipmap); + } + for (uint32_t i = 0; i < p_subresource.mipmap; i++) { + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); + d = MAX(1u, d >> 1); + } + uint32_t bw = 0, bh = 0; + get_compressed_image_format_block_dimensions(tex_info->rd_format, bw, bh); + uint32_t sbw = 0, sbh = 0; + r_layout->size = get_image_format_required_size(tex_info->rd_format, w, h, d, 1, &sbw, &sbh); + r_layout->row_pitch = r_layout->size / ((sbh / bh) * d); + r_layout->depth_pitch = r_layout->size / d; + r_layout->layer_pitch = r_layout->size / tex_info->vk_create_info.arrayLayers; + } +} + +uint8_t *RenderingDeviceDriverVulkan::texture_map(TextureID p_texture, const TextureSubresource &p_subresource) { + const TextureInfo *tex_info = (const TextureInfo *)p_texture.id; + + VkImageSubresource vk_subres = {}; + vk_subres.aspectMask = (VkImageAspectFlags)(1 << p_subresource.aspect); + vk_subres.arrayLayer = p_subresource.layer; + vk_subres.mipLevel = p_subresource.mipmap; + + VkSubresourceLayout vk_layout = {}; + vkGetImageSubresourceLayout(vk_device, tex_info->vk_view_create_info.image, &vk_subres, &vk_layout); + + void *data_ptr = nullptr; + VkResult err = vkMapMemory( + vk_device, + tex_info->allocation.info.deviceMemory, + tex_info->allocation.info.offset + vk_layout.offset, + vk_layout.size, + 0, + &data_ptr); + + vmaMapMemory(allocator, tex_info->allocation.handle, &data_ptr); + ERR_FAIL_COND_V_MSG(err, nullptr, "vkMapMemory failed with error " + itos(err) + "."); + return (uint8_t *)data_ptr; +} + +void RenderingDeviceDriverVulkan::texture_unmap(TextureID p_texture) { + const TextureInfo *tex_info = (const TextureInfo *)p_texture.id; + vkUnmapMemory(vk_device, tex_info->allocation.info.deviceMemory); +} + +BitField<RDD::TextureUsageBits> 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); + + const VkFormatFeatureFlags &flags = p_cpu_readable ? properties.linearTilingFeatures : properties.optimalTilingFeatures; + + // Everything supported by default makes an all-or-nothing check easier for the caller. + BitField<RDD::TextureUsageBits> supported = INT64_MAX; + + if (!(flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { + supported.clear_flag(TEXTURE_USAGE_SAMPLING_BIT); + } + if (!(flags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { + supported.clear_flag(TEXTURE_USAGE_COLOR_ATTACHMENT_BIT); + } + if (!(flags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + supported.clear_flag(TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + } + if (!(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)) { + supported.clear_flag(TEXTURE_USAGE_STORAGE_BIT); + } + if (!(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT)) { + supported.clear_flag(TEXTURE_USAGE_STORAGE_ATOMIC_BIT); + } + // Validation via VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR fails if VRS attachment is not supported. + if (p_format != DATA_FORMAT_R8_UINT) { + supported.clear_flag(TEXTURE_USAGE_VRS_ATTACHMENT_BIT); + } + + return supported; +} + +/*****************/ +/**** SAMPLER ****/ +/*****************/ + +// RDD::SamplerRepeatMode == VkSamplerAddressMode. +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_REPEAT_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT, VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_REPEAT_MODE_MIRROR_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE)); + +// RDD::SamplerBorderColor == VkBorderColor. +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_BORDER_COLOR_INT_TRANSPARENT_BLACK, VK_BORDER_COLOR_INT_TRANSPARENT_BLACK)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_BLACK, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_BORDER_COLOR_INT_OPAQUE_BLACK, VK_BORDER_COLOR_INT_OPAQUE_BLACK)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_WHITE, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::SAMPLER_BORDER_COLOR_INT_OPAQUE_WHITE, VK_BORDER_COLOR_INT_OPAQUE_WHITE)); + +RDD::SamplerID RenderingDeviceDriverVulkan::sampler_create(const SamplerState &p_state) { + VkSamplerCreateInfo sampler_create_info = {}; + sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_create_info.pNext = nullptr; + sampler_create_info.flags = 0; + sampler_create_info.magFilter = p_state.mag_filter == SAMPLER_FILTER_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; + sampler_create_info.minFilter = p_state.min_filter == SAMPLER_FILTER_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; + sampler_create_info.mipmapMode = p_state.mip_filter == SAMPLER_FILTER_LINEAR ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST; + sampler_create_info.addressModeU = (VkSamplerAddressMode)p_state.repeat_u; + 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.maxAnisotropy = p_state.anisotropy_max; + sampler_create_info.compareEnable = p_state.enable_compare; + sampler_create_info.compareOp = (VkCompareOp)p_state.compare_op; + sampler_create_info.minLod = p_state.min_lod; + sampler_create_info.maxLod = p_state.max_lod; + sampler_create_info.borderColor = (VkBorderColor)p_state.border_color; + sampler_create_info.unnormalizedCoordinates = p_state.unnormalized_uvw; + + VkSampler vk_sampler = VK_NULL_HANDLE; + VkResult res = vkCreateSampler(vk_device, &sampler_create_info, nullptr, &vk_sampler); + ERR_FAIL_COND_V_MSG(res, SamplerID(), "vkCreateSampler failed with error " + itos(res) + "."); + + return SamplerID(vk_sampler); +} + +void RenderingDeviceDriverVulkan::sampler_free(SamplerID p_sampler) { + vkDestroySampler(vk_device, (VkSampler)p_sampler.id, nullptr); +} + +bool RenderingDeviceDriverVulkan::sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) { + switch (p_filter) { + case RD::SAMPLER_FILTER_NEAREST: { + return true; + } + case RD::SAMPLER_FILTER_LINEAR: { + VkFormatProperties properties = {}; + vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), RD_TO_VK_FORMAT[p_format], &properties); + return (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT); + } + } + return false; +} + +/**********************/ +/**** VERTEX ARRAY ****/ +/**********************/ + +RDD::VertexFormatID RenderingDeviceDriverVulkan::vertex_format_create(VectorView<VertexAttribute> p_vertex_attribs) { + // Pre-bookkeep. + VertexFormatInfo *vf_info = VersatileResource::allocate<VertexFormatInfo>(resources_allocator); + + vf_info->vk_bindings.resize(p_vertex_attribs.size()); + vf_info->vk_attributes.resize(p_vertex_attribs.size()); + for (uint32_t i = 0; i < p_vertex_attribs.size(); i++) { + vf_info->vk_bindings[i] = {}; + vf_info->vk_bindings[i].binding = i; + vf_info->vk_bindings[i].stride = p_vertex_attribs[i].stride; + vf_info->vk_bindings[i].inputRate = p_vertex_attribs[i].frequency == VERTEX_FREQUENCY_INSTANCE ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; + vf_info->vk_attributes[i] = {}; + vf_info->vk_attributes[i].binding = i; + vf_info->vk_attributes[i].location = p_vertex_attribs[i].location; + vf_info->vk_attributes[i].format = RD_TO_VK_FORMAT[p_vertex_attribs[i].format]; + vf_info->vk_attributes[i].offset = p_vertex_attribs[i].offset; + } + + vf_info->vk_create_info = {}; + vf_info->vk_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vf_info->vk_create_info.vertexBindingDescriptionCount = vf_info->vk_bindings.size(); + vf_info->vk_create_info.pVertexBindingDescriptions = vf_info->vk_bindings.ptr(); + vf_info->vk_create_info.vertexAttributeDescriptionCount = vf_info->vk_attributes.size(); + vf_info->vk_create_info.pVertexAttributeDescriptions = vf_info->vk_attributes.ptr(); + + return VertexFormatID(vf_info); +} + +void RenderingDeviceDriverVulkan::vertex_format_free(VertexFormatID p_vertex_format) { + VertexFormatInfo *vf_info = (VertexFormatInfo *)p_vertex_format.id; + VersatileResource::free(resources_allocator, vf_info); +} + +/******************/ +/**** BARRIERS ****/ +/******************/ + +// RDD::PipelineStageBits == VkPipelineStageFlagBits. +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT, VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT, VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_GEOMETRY_SHADER_BIT, VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT)); + +// RDD::BarrierAccessBits == VkAccessFlagBits. +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT, VK_ACCESS_INDIRECT_COMMAND_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_INDEX_READ_BIT, VK_ACCESS_INDEX_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_UNIFORM_READ_BIT, VK_ACCESS_UNIFORM_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT, VK_ACCESS_INPUT_ATTACHMENT_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_WRITE_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_HOST_READ_BIT, VK_ACCESS_HOST_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_HOST_WRITE_BIT, VK_ACCESS_HOST_WRITE_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_MEMORY_READ_BIT, VK_ACCESS_MEMORY_READ_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_MEMORY_WRITE_BIT, VK_ACCESS_MEMORY_WRITE_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BARRIER_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT, VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR)); + +void RenderingDeviceDriverVulkan::command_pipeline_barrier( + CommandBufferID p_cmd_buffer, + BitField<PipelineStageBits> p_src_stages, + BitField<PipelineStageBits> p_dst_stages, + VectorView<MemoryBarrier> p_memory_barriers, + VectorView<BufferBarrier> p_buffer_barriers, + VectorView<TextureBarrier> p_texture_barriers) { + VkMemoryBarrier *vk_memory_barriers = ALLOCA_ARRAY(VkMemoryBarrier, p_memory_barriers.size()); + for (uint32_t i = 0; i < p_memory_barriers.size(); i++) { + vk_memory_barriers[i] = {}; + vk_memory_barriers[i].sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + vk_memory_barriers[i].srcAccessMask = (VkPipelineStageFlags)p_memory_barriers[i].src_access; + vk_memory_barriers[i].dstAccessMask = (VkAccessFlags)p_memory_barriers[i].dst_access; + } + + VkBufferMemoryBarrier *vk_buffer_barriers = ALLOCA_ARRAY(VkBufferMemoryBarrier, p_buffer_barriers.size()); + for (uint32_t i = 0; i < p_buffer_barriers.size(); i++) { + vk_buffer_barriers[i] = {}; + vk_buffer_barriers[i].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + vk_buffer_barriers[i].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_buffer_barriers[i].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_buffer_barriers[i].srcAccessMask = (VkAccessFlags)p_buffer_barriers[i].src_access; + vk_buffer_barriers[i].dstAccessMask = (VkAccessFlags)p_buffer_barriers[i].dst_access; + vk_buffer_barriers[i].buffer = ((const BufferInfo *)p_buffer_barriers[i].buffer.id)->vk_buffer; + vk_buffer_barriers[i].offset = p_buffer_barriers[i].offset; + vk_buffer_barriers[i].size = p_buffer_barriers[i].size; + } + + VkImageMemoryBarrier *vk_image_barriers = ALLOCA_ARRAY(VkImageMemoryBarrier, p_texture_barriers.size()); + for (uint32_t i = 0; i < p_texture_barriers.size(); i++) { + const TextureInfo *tex_info = (const TextureInfo *)p_texture_barriers[i].texture.id; + vk_image_barriers[i] = {}; + vk_image_barriers[i].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + vk_image_barriers[i].srcAccessMask = (VkAccessFlags)p_texture_barriers[i].src_access; + vk_image_barriers[i].dstAccessMask = (VkAccessFlags)p_texture_barriers[i].dst_access; + vk_image_barriers[i].oldLayout = (VkImageLayout)p_texture_barriers[i].prev_layout; + vk_image_barriers[i].newLayout = (VkImageLayout)p_texture_barriers[i].next_layout; + vk_image_barriers[i].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_image_barriers[i].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_image_barriers[i].image = tex_info->vk_view_create_info.image; + vk_image_barriers[i].subresourceRange.aspectMask = (VkImageAspectFlags)p_texture_barriers[i].subresources.aspect; + vk_image_barriers[i].subresourceRange.baseMipLevel = p_texture_barriers[i].subresources.base_mipmap; + vk_image_barriers[i].subresourceRange.levelCount = p_texture_barriers[i].subresources.mipmap_count; + vk_image_barriers[i].subresourceRange.baseArrayLayer = p_texture_barriers[i].subresources.base_layer; + vk_image_barriers[i].subresourceRange.layerCount = p_texture_barriers[i].subresources.layer_count; + } + + vkCmdPipelineBarrier( + (VkCommandBuffer)p_cmd_buffer.id, + (VkPipelineStageFlags)p_src_stages, + (VkPipelineStageFlags)p_dst_stages, + 0, + p_memory_barriers.size(), vk_memory_barriers, + p_buffer_barriers.size(), vk_buffer_barriers, + p_texture_barriers.size(), vk_image_barriers); +} + +/*************************/ +/**** COMMAND BUFFERS ****/ +/*************************/ + +// ----- POOL ----- + +RDD::CommandPoolID RenderingDeviceDriverVulkan::command_pool_create(CommandBufferType p_cmd_buffer_type) { + 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.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); + 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); +} + +void RenderingDeviceDriverVulkan::command_pool_free(CommandPoolID p_cmd_pool) { + vkDestroyCommandPool(vk_device, (VkCommandPool)p_cmd_pool.id, nullptr); + +#ifdef DEBUG_ENABLED + secondary_cmd_pools.erase(p_cmd_pool); +#endif +} + +// ----- 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 + + 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.commandBufferCount = 1; + + 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; +} + +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 = {}; + cmd_buf_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmd_buf_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + VkResult err = vkBeginCommandBuffer((VkCommandBuffer)p_cmd_buffer.id, &cmd_buf_begin_info); + ERR_FAIL_COND_V_MSG(err, false, "vkBeginCommandBuffer failed with error " + itos(err) + "."); + + return true; +} + +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 = {}; + inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; + inheritance_info.renderPass = (VkRenderPass)p_render_pass.id; + inheritance_info.subpass = p_subpass; + inheritance_info.framebuffer = (VkFramebuffer)p_framebuffer.id; + + VkCommandBufferBeginInfo cmd_buf_begin_info = {}; + cmd_buf_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmd_buf_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + cmd_buf_begin_info.pInheritanceInfo = &inheritance_info; + + VkResult err = vkBeginCommandBuffer((VkCommandBuffer)p_cmd_buffer.id, &cmd_buf_begin_info); + ERR_FAIL_COND_V_MSG(err, false, "vkBeginCommandBuffer failed with error " + itos(err) + "."); + + return true; +} + +void RenderingDeviceDriverVulkan::command_buffer_end(CommandBufferID p_cmd_buffer) { + vkEndCommandBuffer((VkCommandBuffer)p_cmd_buffer.id); +} + +void RenderingDeviceDriverVulkan::command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> 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()); +} + +/*********************/ +/**** FRAMEBUFFER ****/ +/*********************/ + +RDD::FramebufferID RenderingDeviceDriverVulkan::framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) { + VkImageView *vk_img_views = ALLOCA_ARRAY(VkImageView, p_attachments.size()); + for (uint32_t i = 0; i < p_attachments.size(); i++) { + vk_img_views[i] = ((const TextureInfo *)p_attachments[i].id)->vk_view; + } + + VkFramebufferCreateInfo framebuffer_create_info = {}; + framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_create_info.renderPass = (VkRenderPass)p_render_pass.id; + framebuffer_create_info.attachmentCount = p_attachments.size(); + framebuffer_create_info.pAttachments = vk_img_views; + framebuffer_create_info.width = p_width; + framebuffer_create_info.height = p_height; + framebuffer_create_info.layers = 1; + + VkFramebuffer vk_framebuffer = VK_NULL_HANDLE; + VkResult err = vkCreateFramebuffer(vk_device, &framebuffer_create_info, nullptr, &vk_framebuffer); + ERR_FAIL_COND_V_MSG(err, FramebufferID(), "vkCreateFramebuffer failed with error " + itos(err) + "."); + + return FramebufferID(vk_framebuffer); +} + +void RenderingDeviceDriverVulkan::framebuffer_free(FramebufferID p_framebuffer) { + vkDestroyFramebuffer(vk_device, (VkFramebuffer)p_framebuffer.id, nullptr); +} + +/****************/ +/**** SHADER ****/ +/****************/ + +static VkShaderStageFlagBits RD_STAGE_TO_VK_SHADER_STAGE_BITS[RDD::SHADER_STAGE_MAX] = { + VK_SHADER_STAGE_VERTEX_BIT, + VK_SHADER_STAGE_FRAGMENT_BIT, + VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, + VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, + VK_SHADER_STAGE_COMPUTE_BIT, +}; + +String RenderingDeviceDriverVulkan::shader_get_binary_cache_key() { + return "Vulkan-SV" + uitos(ShaderBinary::VERSION); +} + +Vector<uint8_t> RenderingDeviceDriverVulkan::shader_compile_binary_from_spirv(VectorView<ShaderStageSPIRVData> p_spirv, const String &p_shader_name) { + ShaderReflection shader_refl; + if (_reflect_spirv(p_spirv, shader_refl) != OK) { + return Vector<uint8_t>(); + } + + ERR_FAIL_COND_V_MSG((uint32_t)shader_refl.uniform_sets.size() > limits.maxBoundDescriptorSets, Vector<uint8_t>(), + "Number of uniform sets is larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."); + + // Collect reflection data into binary data. + ShaderBinary::Data binary_data; + Vector<Vector<ShaderBinary::DataBinding>> uniforms; // Set bindings. + Vector<ShaderBinary::SpecializationConstant> specialization_constants; + { + binary_data.vertex_input_mask = shader_refl.vertex_input_mask; + binary_data.fragment_output_mask = shader_refl.fragment_output_mask; + binary_data.specialization_constants_count = shader_refl.specialization_constants.size(); + binary_data.is_compute = shader_refl.is_compute; + binary_data.compute_local_size[0] = shader_refl.compute_local_size[0]; + binary_data.compute_local_size[1] = shader_refl.compute_local_size[1]; + binary_data.compute_local_size[2] = shader_refl.compute_local_size[2]; + binary_data.set_count = shader_refl.uniform_sets.size(); + binary_data.push_constant_size = shader_refl.push_constant_size; + for (uint32_t i = 0; i < SHADER_STAGE_MAX; i++) { + if (shader_refl.push_constant_stages.has_flag((ShaderStage)(1 << i))) { + binary_data.vk_push_constant_stages_mask |= RD_STAGE_TO_VK_SHADER_STAGE_BITS[i]; + } + } + + for (const Vector<ShaderUniform> &set_refl : shader_refl.uniform_sets) { + Vector<ShaderBinary::DataBinding> set_bindings; + for (const ShaderUniform &uniform_refl : set_refl) { + ShaderBinary::DataBinding binding; + binding.type = (uint32_t)uniform_refl.type; + binding.binding = uniform_refl.binding; + binding.stages = (uint32_t)uniform_refl.stages; + binding.length = uniform_refl.length; + binding.writable = (uint32_t)uniform_refl.writable; + set_bindings.push_back(binding); + } + uniforms.push_back(set_bindings); + } + + for (const ShaderSpecializationConstant &refl_sc : shader_refl.specialization_constants) { + ShaderBinary::SpecializationConstant spec_constant; + spec_constant.type = (uint32_t)refl_sc.type; + spec_constant.constant_id = refl_sc.constant_id; + spec_constant.int_value = refl_sc.int_value; + spec_constant.stage_flags = (uint32_t)refl_sc.stages; + specialization_constants.push_back(spec_constant); + } + } + + Vector<Vector<uint8_t>> compressed_stages; + Vector<uint32_t> smolv_size; + Vector<uint32_t> zstd_size; // If 0, zstd not used. + + uint32_t stages_binary_size = 0; + + bool strip_debug = false; + + for (uint32_t i = 0; i < p_spirv.size(); i++) { + smolv::ByteArray smolv; + if (!smolv::Encode(p_spirv[i].spirv.ptr(), p_spirv[i].spirv.size(), smolv, strip_debug ? smolv::kEncodeFlagStripDebugInfo : 0)) { + ERR_FAIL_V_MSG(Vector<uint8_t>(), "Error compressing shader stage :" + String(SHADER_STAGE_NAMES[p_spirv[i].shader_stage])); + } else { + smolv_size.push_back(smolv.size()); + { // zstd. + Vector<uint8_t> zstd; + zstd.resize(Compression::get_max_compressed_buffer_size(smolv.size(), Compression::MODE_ZSTD)); + int dst_size = Compression::compress(zstd.ptrw(), &smolv[0], smolv.size(), Compression::MODE_ZSTD); + + if (dst_size > 0 && (uint32_t)dst_size < smolv.size()) { + zstd_size.push_back(dst_size); + zstd.resize(dst_size); + compressed_stages.push_back(zstd); + } else { + Vector<uint8_t> smv; + smv.resize(smolv.size()); + memcpy(smv.ptrw(), &smolv[0], smolv.size()); + zstd_size.push_back(0); // Not using zstd. + compressed_stages.push_back(smv); + } + } + } + uint32_t s = compressed_stages[i].size(); + if (s % 4 != 0) { + s += 4 - (s % 4); + } + stages_binary_size += s; + } + + binary_data.specialization_constants_count = specialization_constants.size(); + binary_data.set_count = uniforms.size(); + binary_data.stage_count = p_spirv.size(); + + CharString shader_name_utf = p_shader_name.utf8(); + + binary_data.shader_name_len = shader_name_utf.length(); + + uint32_t total_size = sizeof(uint32_t) * 3; // Header + version + main datasize;. + total_size += sizeof(ShaderBinary::Data); + + total_size += binary_data.shader_name_len; + + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. + total_size += 4 - (binary_data.shader_name_len % 4); + } + + for (int i = 0; i < uniforms.size(); i++) { + total_size += sizeof(uint32_t); + total_size += uniforms[i].size() * sizeof(ShaderBinary::DataBinding); + } + + total_size += sizeof(ShaderBinary::SpecializationConstant) * specialization_constants.size(); + + total_size += compressed_stages.size() * sizeof(uint32_t) * 3; // Sizes. + total_size += stages_binary_size; + + Vector<uint8_t> ret; + ret.resize(total_size); + { + uint32_t offset = 0; + uint8_t *binptr = ret.ptrw(); + binptr[0] = 'G'; + binptr[1] = 'S'; + binptr[2] = 'B'; + binptr[3] = 'D'; // Godot Shader Binary Data. + offset += 4; + encode_uint32(ShaderBinary::VERSION, binptr + offset); + offset += sizeof(uint32_t); + encode_uint32(sizeof(ShaderBinary::Data), binptr + offset); + offset += sizeof(uint32_t); + memcpy(binptr + offset, &binary_data, sizeof(ShaderBinary::Data)); + offset += sizeof(ShaderBinary::Data); + + if (binary_data.shader_name_len > 0) { + memcpy(binptr + offset, shader_name_utf.ptr(), binary_data.shader_name_len); + offset += binary_data.shader_name_len; + + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. + offset += 4 - (binary_data.shader_name_len % 4); + } + } + + for (int i = 0; i < uniforms.size(); i++) { + int count = uniforms[i].size(); + encode_uint32(count, binptr + offset); + offset += sizeof(uint32_t); + if (count > 0) { + memcpy(binptr + offset, uniforms[i].ptr(), sizeof(ShaderBinary::DataBinding) * count); + offset += sizeof(ShaderBinary::DataBinding) * count; + } + } + + if (specialization_constants.size()) { + memcpy(binptr + offset, specialization_constants.ptr(), sizeof(ShaderBinary::SpecializationConstant) * specialization_constants.size()); + offset += sizeof(ShaderBinary::SpecializationConstant) * specialization_constants.size(); + } + + for (int i = 0; i < compressed_stages.size(); i++) { + encode_uint32(p_spirv[i].shader_stage, binptr + offset); + offset += sizeof(uint32_t); + encode_uint32(smolv_size[i], binptr + offset); + offset += sizeof(uint32_t); + encode_uint32(zstd_size[i], binptr + offset); + offset += sizeof(uint32_t); + memcpy(binptr + offset, compressed_stages[i].ptr(), compressed_stages[i].size()); + + uint32_t s = compressed_stages[i].size(); + + if (s % 4 != 0) { + s += 4 - (s % 4); + } + + offset += s; + } + + DEV_ASSERT(offset == (uint32_t)ret.size()); + } + + return ret; +} + +RDD::ShaderID RenderingDeviceDriverVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name) { + r_shader_desc = {}; // Driver-agnostic. + ShaderInfo shader_info; // Driver-specific. + + const uint8_t *binptr = p_shader_binary.ptr(); + uint32_t binsize = p_shader_binary.size(); + + uint32_t read_offset = 0; + + // Consistency check. + ERR_FAIL_COND_V(binsize < sizeof(uint32_t) * 3 + sizeof(ShaderBinary::Data), ShaderID()); + ERR_FAIL_COND_V(binptr[0] != 'G' || binptr[1] != 'S' || binptr[2] != 'B' || binptr[3] != 'D', ShaderID()); + + uint32_t bin_version = decode_uint32(binptr + 4); + ERR_FAIL_COND_V(bin_version != ShaderBinary::VERSION, ShaderID()); + + uint32_t bin_data_size = decode_uint32(binptr + 8); + + const ShaderBinary::Data &binary_data = *(reinterpret_cast<const ShaderBinary::Data *>(binptr + 12)); + + r_shader_desc.push_constant_size = binary_data.push_constant_size; + shader_info.vk_push_constant_stages = binary_data.vk_push_constant_stages_mask; + + r_shader_desc.vertex_input_mask = binary_data.vertex_input_mask; + r_shader_desc.fragment_output_mask = binary_data.fragment_output_mask; + + r_shader_desc.is_compute = binary_data.is_compute; + r_shader_desc.compute_local_size[0] = binary_data.compute_local_size[0]; + r_shader_desc.compute_local_size[1] = binary_data.compute_local_size[1]; + r_shader_desc.compute_local_size[2] = binary_data.compute_local_size[2]; + + read_offset += sizeof(uint32_t) * 3 + bin_data_size; + + if (binary_data.shader_name_len) { + r_name.parse_utf8((const char *)(binptr + read_offset), binary_data.shader_name_len); + read_offset += binary_data.shader_name_len; + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. + read_offset += 4 - (binary_data.shader_name_len % 4); + } + } + + Vector<Vector<VkDescriptorSetLayoutBinding>> vk_set_bindings; + + r_shader_desc.uniform_sets.resize(binary_data.set_count); + vk_set_bindings.resize(binary_data.set_count); + + for (uint32_t i = 0; i < binary_data.set_count; i++) { + ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) >= binsize, ShaderID()); + uint32_t set_count = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + const ShaderBinary::DataBinding *set_ptr = reinterpret_cast<const ShaderBinary::DataBinding *>(binptr + read_offset); + uint32_t set_size = set_count * sizeof(ShaderBinary::DataBinding); + ERR_FAIL_COND_V(read_offset + set_size >= binsize, ShaderID()); + + for (uint32_t j = 0; j < set_count; j++) { + ShaderUniform info; + info.type = UniformType(set_ptr[j].type); + info.writable = set_ptr[j].writable; + info.length = set_ptr[j].length; + info.binding = set_ptr[j].binding; + info.stages = set_ptr[j].stages; + + VkDescriptorSetLayoutBinding layout_binding = {}; + layout_binding.binding = set_ptr[j].binding; + layout_binding.descriptorCount = 1; + for (uint32_t k = 0; k < SHADER_STAGE_MAX; k++) { + if ((set_ptr[j].stages & (1 << k))) { + layout_binding.stageFlags |= RD_STAGE_TO_VK_SHADER_STAGE_BITS[k]; + } + } + + switch (info.type) { + case UNIFORM_TYPE_SAMPLER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + layout_binding.descriptorCount = set_ptr[j].length; + } break; + case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + layout_binding.descriptorCount = set_ptr[j].length; + } break; + case UNIFORM_TYPE_TEXTURE: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + layout_binding.descriptorCount = set_ptr[j].length; + } break; + case UNIFORM_TYPE_IMAGE: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + layout_binding.descriptorCount = set_ptr[j].length; + } break; + case UNIFORM_TYPE_TEXTURE_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + layout_binding.descriptorCount = set_ptr[j].length; + } break; + case UNIFORM_TYPE_IMAGE_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + } break; + case UNIFORM_TYPE_UNIFORM_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + } break; + case UNIFORM_TYPE_STORAGE_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + } break; + case UNIFORM_TYPE_INPUT_ATTACHMENT: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + } break; + default: { + DEV_ASSERT(false); + } + } + + r_shader_desc.uniform_sets.write[i].push_back(info); + vk_set_bindings.write[i].push_back(layout_binding); + } + + read_offset += set_size; + } + + ERR_FAIL_COND_V(read_offset + binary_data.specialization_constants_count * sizeof(ShaderBinary::SpecializationConstant) >= binsize, ShaderID()); + + r_shader_desc.specialization_constants.resize(binary_data.specialization_constants_count); + for (uint32_t i = 0; i < binary_data.specialization_constants_count; i++) { + const ShaderBinary::SpecializationConstant &src_sc = *(reinterpret_cast<const ShaderBinary::SpecializationConstant *>(binptr + read_offset)); + ShaderSpecializationConstant sc; + sc.type = PipelineSpecializationConstantType(src_sc.type); + sc.constant_id = src_sc.constant_id; + sc.int_value = src_sc.int_value; + sc.stages = src_sc.stage_flags; + r_shader_desc.specialization_constants.write[i] = sc; + + read_offset += sizeof(ShaderBinary::SpecializationConstant); + } + + Vector<Vector<uint8_t>> stages_spirv; + stages_spirv.resize(binary_data.stage_count); + r_shader_desc.stages.resize(binary_data.stage_count); + + for (uint32_t i = 0; i < binary_data.stage_count; i++) { + ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) * 3 >= binsize, ShaderID()); + uint32_t stage = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + uint32_t smolv_size = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + uint32_t zstd_size = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + + uint32_t buf_size = (zstd_size > 0) ? zstd_size : smolv_size; + + Vector<uint8_t> smolv; + const uint8_t *src_smolv = nullptr; + + if (zstd_size > 0) { + // Decompress to smolv. + smolv.resize(smolv_size); + int dec_smolv_size = Compression::decompress(smolv.ptrw(), smolv.size(), binptr + read_offset, zstd_size, Compression::MODE_ZSTD); + ERR_FAIL_COND_V(dec_smolv_size != (int32_t)smolv_size, ShaderID()); + src_smolv = smolv.ptr(); + } else { + src_smolv = binptr + read_offset; + } + + Vector<uint8_t> &spirv = stages_spirv.ptrw()[i]; + uint32_t spirv_size = smolv::GetDecodedBufferSize(src_smolv, smolv_size); + spirv.resize(spirv_size); + if (!smolv::Decode(src_smolv, smolv_size, spirv.ptrw(), spirv_size)) { + ERR_FAIL_V_MSG(ShaderID(), "Malformed smolv input uncompressing shader stage:" + String(SHADER_STAGE_NAMES[stage])); + } + + r_shader_desc.stages.set(i, ShaderStage(stage)); + + if (buf_size % 4 != 0) { + buf_size += 4 - (buf_size % 4); + } + + DEV_ASSERT(read_offset + buf_size <= binsize); + + read_offset += buf_size; + } + + DEV_ASSERT(read_offset == binsize); + + // Modules. + + String error_text; + + for (int i = 0; i < r_shader_desc.stages.size(); i++) { + VkShaderModuleCreateInfo shader_module_create_info = {}; + shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shader_module_create_info.codeSize = stages_spirv[i].size(); + shader_module_create_info.pCode = (const uint32_t *)stages_spirv[i].ptr(); + + VkShaderModule vk_module = VK_NULL_HANDLE; + VkResult res = vkCreateShaderModule(vk_device, &shader_module_create_info, nullptr, &vk_module); + if (res) { + error_text = "Error (" + itos(res) + ") creating shader module for stage: " + String(SHADER_STAGE_NAMES[r_shader_desc.stages[i]]); + break; + } + + VkPipelineShaderStageCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + create_info.stage = RD_STAGE_TO_VK_SHADER_STAGE_BITS[r_shader_desc.stages[i]]; + create_info.module = vk_module; + create_info.pName = "main"; + + shader_info.vk_stages_create_info.push_back(create_info); + } + + // Descriptor sets. + + if (error_text.is_empty()) { + DEV_ASSERT((uint32_t)vk_set_bindings.size() == binary_data.set_count); + for (uint32_t i = 0; i < binary_data.set_count; i++) { + // Empty ones are fine if they were not used according to spec (binding count will be 0). + VkDescriptorSetLayoutCreateInfo layout_create_info = {}; + layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layout_create_info.bindingCount = vk_set_bindings[i].size(); + layout_create_info.pBindings = vk_set_bindings[i].ptr(); + + VkDescriptorSetLayout layout = VK_NULL_HANDLE; + VkResult res = vkCreateDescriptorSetLayout(vk_device, &layout_create_info, nullptr, &layout); + if (res) { + error_text = "Error (" + itos(res) + ") creating descriptor set layout for set " + itos(i); + break; + } + + shader_info.vk_descriptor_set_layouts.push_back(layout); + } + } + + if (error_text.is_empty()) { + // Pipeline layout. + + VkPipelineLayoutCreateInfo pipeline_layout_create_info = {}; + pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_create_info.setLayoutCount = binary_data.set_count; + pipeline_layout_create_info.pSetLayouts = shader_info.vk_descriptor_set_layouts.ptr(); + + if (binary_data.push_constant_size) { + VkPushConstantRange *push_constant_range = ALLOCA_SINGLE(VkPushConstantRange); + *push_constant_range = {}; + push_constant_range->stageFlags = binary_data.vk_push_constant_stages_mask; + push_constant_range->size = binary_data.push_constant_size; + pipeline_layout_create_info.pushConstantRangeCount = 1; + pipeline_layout_create_info.pPushConstantRanges = push_constant_range; + } + + VkResult err = vkCreatePipelineLayout(vk_device, &pipeline_layout_create_info, nullptr, &shader_info.vk_pipeline_layout); + if (err) { + error_text = "Error (" + itos(err) + ") creating pipeline layout."; + } + } + + if (!error_text.is_empty()) { + // Clean up if failed. + for (uint32_t i = 0; i < shader_info.vk_stages_create_info.size(); i++) { + vkDestroyShaderModule(vk_device, shader_info.vk_stages_create_info[i].module, nullptr); + } + for (uint32_t i = 0; i < binary_data.set_count; i++) { + vkDestroyDescriptorSetLayout(vk_device, shader_info.vk_descriptor_set_layouts[i], nullptr); + } + + ERR_FAIL_V_MSG(ShaderID(), error_text); + } + + // Bookkeep. + + ShaderInfo *shader_info_ptr = VersatileResource::allocate<ShaderInfo>(resources_allocator); + *shader_info_ptr = shader_info; + return ShaderID(shader_info_ptr); +} + +void RenderingDeviceDriverVulkan::shader_free(ShaderID p_shader) { + ShaderInfo *shader_info = (ShaderInfo *)p_shader.id; + + for (uint32_t i = 0; i < shader_info->vk_descriptor_set_layouts.size(); i++) { + vkDestroyDescriptorSetLayout(vk_device, shader_info->vk_descriptor_set_layouts[i], nullptr); + } + + vkDestroyPipelineLayout(vk_device, shader_info->vk_pipeline_layout, nullptr); + + for (uint32_t i = 0; i < shader_info->vk_stages_create_info.size(); i++) { + vkDestroyShaderModule(vk_device, shader_info->vk_stages_create_info[i].module, nullptr); + } + + VersatileResource::free(resources_allocator, shader_info); +} + +/*********************/ +/**** UNIFORM SET ****/ +/*********************/ + +VkDescriptorPool RenderingDeviceDriverVulkan::_descriptor_set_pool_find_or_create(const DescriptorSetPoolKey &p_key, DescriptorSetPools::Iterator *r_pool_sets_it) { + DescriptorSetPools::Iterator pool_sets_it = descriptor_set_pools.find(p_key); + + if (pool_sets_it) { + for (KeyValue<VkDescriptorPool, uint32_t> &E : pool_sets_it->value) { + if (E.value < max_descriptor_sets_per_pool) { + *r_pool_sets_it = pool_sets_it; + return E.key; + } + } + } + + // Create a new one. + + // Here comes more vulkan API strangeness. + VkDescriptorPoolSize *vk_sizes = ALLOCA_ARRAY(VkDescriptorPoolSize, UNIFORM_TYPE_MAX); + uint32_t vk_sizes_count = 0; + { + VkDescriptorPoolSize *curr_vk_size = vk_sizes; + if (p_key.uniform_type[UNIFORM_TYPE_SAMPLER]) { + *curr_vk_size = {}; + curr_vk_size->type = VK_DESCRIPTOR_TYPE_SAMPLER; + curr_vk_size->descriptorCount = p_key.uniform_type[UNIFORM_TYPE_SAMPLER] * max_descriptor_sets_per_pool; + curr_vk_size++; + vk_sizes_count++; + } + if (p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE]) { + *curr_vk_size = {}; + curr_vk_size->type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + curr_vk_size->descriptorCount = p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE] * max_descriptor_sets_per_pool; + curr_vk_size++; + vk_sizes_count++; + } + if (p_key.uniform_type[UNIFORM_TYPE_TEXTURE]) { + *curr_vk_size = {}; + curr_vk_size->type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + curr_vk_size->descriptorCount = p_key.uniform_type[UNIFORM_TYPE_TEXTURE] * max_descriptor_sets_per_pool; + curr_vk_size++; + vk_sizes_count++; + } + if (p_key.uniform_type[UNIFORM_TYPE_IMAGE]) { + *curr_vk_size = {}; + curr_vk_size->type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + curr_vk_size->descriptorCount = p_key.uniform_type[UNIFORM_TYPE_IMAGE] * max_descriptor_sets_per_pool; + curr_vk_size++; + vk_sizes_count++; + } + if (p_key.uniform_type[UNIFORM_TYPE_TEXTURE_BUFFER] || p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER]) { + *curr_vk_size = {}; + curr_vk_size->type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + curr_vk_size->descriptorCount = (p_key.uniform_type[UNIFORM_TYPE_TEXTURE_BUFFER] + p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER]) * max_descriptor_sets_per_pool; + curr_vk_size++; + vk_sizes_count++; + } + if (p_key.uniform_type[UNIFORM_TYPE_IMAGE_BUFFER]) { + *curr_vk_size = {}; + curr_vk_size->type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + curr_vk_size->descriptorCount = p_key.uniform_type[UNIFORM_TYPE_IMAGE_BUFFER] * max_descriptor_sets_per_pool; + curr_vk_size++; + vk_sizes_count++; + } + if (p_key.uniform_type[UNIFORM_TYPE_UNIFORM_BUFFER]) { + *curr_vk_size = {}; + curr_vk_size->type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + curr_vk_size->descriptorCount = p_key.uniform_type[UNIFORM_TYPE_UNIFORM_BUFFER] * max_descriptor_sets_per_pool; + curr_vk_size++; + vk_sizes_count++; + } + if (p_key.uniform_type[UNIFORM_TYPE_STORAGE_BUFFER]) { + *curr_vk_size = {}; + curr_vk_size->type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + curr_vk_size->descriptorCount = p_key.uniform_type[UNIFORM_TYPE_STORAGE_BUFFER] * max_descriptor_sets_per_pool; + curr_vk_size++; + vk_sizes_count++; + } + if (p_key.uniform_type[UNIFORM_TYPE_INPUT_ATTACHMENT]) { + *curr_vk_size = {}; + curr_vk_size->type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + curr_vk_size->descriptorCount = p_key.uniform_type[UNIFORM_TYPE_INPUT_ATTACHMENT] * max_descriptor_sets_per_pool; + curr_vk_size++; + vk_sizes_count++; + } + DEV_ASSERT(vk_sizes_count <= UNIFORM_TYPE_MAX); + } + + VkDescriptorPoolCreateInfo descriptor_set_pool_create_info = {}; + descriptor_set_pool_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptor_set_pool_create_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; // Can't think how somebody may NOT need this flag. + descriptor_set_pool_create_info.maxSets = max_descriptor_sets_per_pool; + descriptor_set_pool_create_info.poolSizeCount = vk_sizes_count; + descriptor_set_pool_create_info.pPoolSizes = vk_sizes; + + VkDescriptorPool vk_pool = VK_NULL_HANDLE; + VkResult res = vkCreateDescriptorPool(vk_device, &descriptor_set_pool_create_info, nullptr, &vk_pool); + if (res) { + ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateDescriptorPool failed with error " + itos(res) + "."); + } + + // Bookkeep. + + if (!pool_sets_it) { + pool_sets_it = descriptor_set_pools.insert(p_key, HashMap<VkDescriptorPool, uint32_t>()); + } + HashMap<VkDescriptorPool, uint32_t> &pool_rcs = pool_sets_it->value; + pool_rcs.insert(vk_pool, 0); + *r_pool_sets_it = pool_sets_it; + return vk_pool; +} + +void RenderingDeviceDriverVulkan::_descriptor_set_pool_unreference(DescriptorSetPools::Iterator p_pool_sets_it, VkDescriptorPool p_vk_descriptor_pool) { + HashMap<VkDescriptorPool, uint32_t>::Iterator pool_rcs_it = p_pool_sets_it->value.find(p_vk_descriptor_pool); + pool_rcs_it->value--; + if (pool_rcs_it->value == 0) { + vkDestroyDescriptorPool(vk_device, p_vk_descriptor_pool, nullptr); + p_pool_sets_it->value.erase(p_vk_descriptor_pool); + if (p_pool_sets_it->value.is_empty()) { + descriptor_set_pools.remove(p_pool_sets_it); + } + } +} + +RDD::UniformSetID RenderingDeviceDriverVulkan::uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index) { + DescriptorSetPoolKey pool_key; + + VkWriteDescriptorSet *vk_writes = ALLOCA_ARRAY(VkWriteDescriptorSet, p_uniforms.size()); + for (uint32_t i = 0; i < p_uniforms.size(); i++) { + const BoundUniform &uniform = p_uniforms[i]; + + vk_writes[i] = {}; + vk_writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + vk_writes[i].dstBinding = uniform.binding; + vk_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM; // Invalid value. + + uint32_t num_descriptors = 1; + + switch (uniform.type) { + case UNIFORM_TYPE_SAMPLER: { + num_descriptors = uniform.ids.size(); + VkDescriptorImageInfo *vk_img_infos = ALLOCA_ARRAY(VkDescriptorImageInfo, num_descriptors); + + for (uint32_t j = 0; j < num_descriptors; j++) { + vk_img_infos[j] = {}; + vk_img_infos[j].sampler = (VkSampler)uniform.ids[j].id; + vk_img_infos[j].imageView = VK_NULL_HANDLE; + vk_img_infos[j].imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + vk_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + vk_writes[i].pImageInfo = vk_img_infos; + } break; + case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { + num_descriptors = uniform.ids.size() / 2; + VkDescriptorImageInfo *vk_img_infos = ALLOCA_ARRAY(VkDescriptorImageInfo, num_descriptors); + + for (uint32_t j = 0; j < num_descriptors; j++) { + vk_img_infos[j] = {}; + vk_img_infos[j].sampler = (VkSampler)uniform.ids[j * 2 + 0].id; + vk_img_infos[j].imageView = ((const TextureInfo *)uniform.ids[j * 2 + 1].id)->vk_view; + vk_img_infos[j].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + vk_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + vk_writes[i].pImageInfo = vk_img_infos; + } break; + case UNIFORM_TYPE_TEXTURE: { + num_descriptors = uniform.ids.size(); + VkDescriptorImageInfo *vk_img_infos = ALLOCA_ARRAY(VkDescriptorImageInfo, num_descriptors); + + for (uint32_t j = 0; j < num_descriptors; j++) { + vk_img_infos[j] = {}; + vk_img_infos[j].imageView = ((const TextureInfo *)uniform.ids[j].id)->vk_view; + vk_img_infos[j].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + vk_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + vk_writes[i].pImageInfo = vk_img_infos; + } break; + case UNIFORM_TYPE_IMAGE: { + num_descriptors = uniform.ids.size(); + VkDescriptorImageInfo *vk_img_infos = ALLOCA_ARRAY(VkDescriptorImageInfo, num_descriptors); + + for (uint32_t j = 0; j < num_descriptors; j++) { + vk_img_infos[j] = {}; + vk_img_infos[j].imageView = ((const TextureInfo *)uniform.ids[j].id)->vk_view; + vk_img_infos[j].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + vk_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + vk_writes[i].pImageInfo = vk_img_infos; + } break; + case UNIFORM_TYPE_TEXTURE_BUFFER: { + num_descriptors = uniform.ids.size(); + VkDescriptorBufferInfo *vk_buf_infos = ALLOCA_ARRAY(VkDescriptorBufferInfo, num_descriptors); + VkBufferView *vk_buf_views = ALLOCA_ARRAY(VkBufferView, num_descriptors); + + for (uint32_t j = 0; j < num_descriptors; j++) { + const BufferInfo *buf_info = (const BufferInfo *)uniform.ids[j].id; + vk_buf_infos[j] = {}; + vk_buf_infos[j].buffer = buf_info->vk_buffer; + vk_buf_infos[j].range = buf_info->size; + + vk_buf_views[j] = buf_info->vk_view; + } + + vk_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + vk_writes[i].pBufferInfo = vk_buf_infos; + vk_writes[i].pTexelBufferView = vk_buf_views; + } break; + case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: { + num_descriptors = uniform.ids.size() / 2; + VkDescriptorImageInfo *vk_img_infos = ALLOCA_ARRAY(VkDescriptorImageInfo, num_descriptors); + VkDescriptorBufferInfo *vk_buf_infos = ALLOCA_ARRAY(VkDescriptorBufferInfo, num_descriptors); + VkBufferView *vk_buf_views = ALLOCA_ARRAY(VkBufferView, num_descriptors); + + for (uint32_t j = 0; j < num_descriptors; j++) { + vk_img_infos[j] = {}; + vk_img_infos[j].sampler = (VkSampler)uniform.ids[j * 2 + 0].id; + + const BufferInfo *buf_info = (const BufferInfo *)uniform.ids[j * 2 + 1].id; + vk_buf_infos[j] = {}; + vk_buf_infos[j].buffer = buf_info->vk_buffer; + vk_buf_infos[j].range = buf_info->size; + + vk_buf_views[j] = buf_info->vk_view; + } + + vk_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + vk_writes[i].pImageInfo = vk_img_infos; + vk_writes[i].pBufferInfo = vk_buf_infos; + vk_writes[i].pTexelBufferView = vk_buf_views; + } break; + case UNIFORM_TYPE_IMAGE_BUFFER: { + CRASH_NOW_MSG("Unimplemented!"); // TODO. + } break; + case UNIFORM_TYPE_UNIFORM_BUFFER: { + const BufferInfo *buf_info = (const BufferInfo *)uniform.ids[0].id; + VkDescriptorBufferInfo *vk_buf_info = ALLOCA_SINGLE(VkDescriptorBufferInfo); + *vk_buf_info = {}; + vk_buf_info->buffer = buf_info->vk_buffer; + vk_buf_info->range = buf_info->size; + + vk_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + vk_writes[i].pBufferInfo = vk_buf_info; + } break; + case UNIFORM_TYPE_STORAGE_BUFFER: { + const BufferInfo *buf_info = (const BufferInfo *)uniform.ids[0].id; + VkDescriptorBufferInfo *vk_buf_info = ALLOCA_SINGLE(VkDescriptorBufferInfo); + *vk_buf_info = {}; + vk_buf_info->buffer = buf_info->vk_buffer; + vk_buf_info->range = buf_info->size; + + vk_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + vk_writes[i].pBufferInfo = vk_buf_info; + } break; + case UNIFORM_TYPE_INPUT_ATTACHMENT: { + num_descriptors = uniform.ids.size(); + VkDescriptorImageInfo *vk_img_infos = ALLOCA_ARRAY(VkDescriptorImageInfo, num_descriptors); + + for (uint32_t j = 0; j < uniform.ids.size(); j++) { + vk_img_infos[j] = {}; + vk_img_infos[j].imageView = ((const TextureInfo *)uniform.ids[j].id)->vk_view; + vk_img_infos[j].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + vk_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + vk_writes[i].pImageInfo = vk_img_infos; + } break; + default: { + DEV_ASSERT(false); + } + } + + vk_writes[i].descriptorCount = num_descriptors; + + ERR_FAIL_COND_V_MSG(pool_key.uniform_type[uniform.type] == MAX_UNIFORM_POOL_ELEMENT, UniformSetID(), + "Uniform set reached the limit of bindings for the same type (" + itos(MAX_UNIFORM_POOL_ELEMENT) + ")."); + pool_key.uniform_type[uniform.type] += num_descriptors; + } + + // Need a descriptor pool. + DescriptorSetPools::Iterator pool_sets_it = {}; + VkDescriptorPool vk_pool = _descriptor_set_pool_find_or_create(pool_key, &pool_sets_it); + DEV_ASSERT(vk_pool); + pool_sets_it->value[vk_pool]++; + + VkDescriptorSetAllocateInfo descriptor_set_allocate_info = {}; + descriptor_set_allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + descriptor_set_allocate_info.descriptorPool = vk_pool; + descriptor_set_allocate_info.descriptorSetCount = 1; + const ShaderInfo *shader_info = (const ShaderInfo *)p_shader.id; + descriptor_set_allocate_info.pSetLayouts = &shader_info->vk_descriptor_set_layouts[p_set_index]; + + VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE; + VkResult res = vkAllocateDescriptorSets(vk_device, &descriptor_set_allocate_info, &vk_descriptor_set); + if (res) { + _descriptor_set_pool_unreference(pool_sets_it, vk_pool); + ERR_FAIL_V_MSG(UniformSetID(), "Cannot allocate descriptor sets, error " + itos(res) + "."); + } + + for (uint32_t i = 0; i < p_uniforms.size(); i++) { + vk_writes[i].dstSet = vk_descriptor_set; + } + vkUpdateDescriptorSets(vk_device, p_uniforms.size(), vk_writes, 0, nullptr); + + // Bookkeep. + + UniformSetInfo *usi = VersatileResource::allocate<UniformSetInfo>(resources_allocator); + usi->vk_descriptor_set = vk_descriptor_set; + usi->vk_descriptor_pool = vk_pool; + usi->pool_sets_it = pool_sets_it; + + return UniformSetID(usi); +} + +void RenderingDeviceDriverVulkan::uniform_set_free(UniformSetID p_uniform_set) { + UniformSetInfo *usi = (UniformSetInfo *)p_uniform_set.id; + vkFreeDescriptorSets(vk_device, usi->vk_descriptor_pool, 1, &usi->vk_descriptor_set); + + _descriptor_set_pool_unreference(usi->pool_sets_it, usi->vk_descriptor_pool); + + VersatileResource::free(resources_allocator, usi); +} + +// ----- COMMANDS ----- + +void RenderingDeviceDriverVulkan::command_uniform_set_prepare_for_use(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) { +} + +/******************/ +/**** TRANSFER ****/ +/******************/ + +static_assert(ARRAYS_COMPATIBLE_FIELDWISE(RDD::BufferCopyRegion, VkBufferCopy)); + +static void _texture_subresource_range_to_vk(const RDD::TextureSubresourceRange &p_subresources, VkImageSubresourceRange *r_vk_subreources) { + *r_vk_subreources = {}; + r_vk_subreources->aspectMask = (VkImageAspectFlags)p_subresources.aspect; + r_vk_subreources->baseMipLevel = p_subresources.base_mipmap; + r_vk_subreources->levelCount = p_subresources.mipmap_count; + r_vk_subreources->baseArrayLayer = p_subresources.base_layer; + r_vk_subreources->layerCount = p_subresources.layer_count; +} + +static void _texture_subresource_layers_to_vk(const RDD::TextureSubresourceLayers &p_subresources, VkImageSubresourceLayers *r_vk_subreources) { + *r_vk_subreources = {}; + r_vk_subreources->aspectMask = (VkImageAspectFlags)p_subresources.aspect; + r_vk_subreources->mipLevel = p_subresources.mipmap; + r_vk_subreources->baseArrayLayer = p_subresources.base_layer; + r_vk_subreources->layerCount = p_subresources.layer_count; +} + +static void _buffer_texture_copy_region_to_vk(const RDD::BufferTextureCopyRegion &p_copy_region, VkBufferImageCopy *r_vk_copy_region) { + *r_vk_copy_region = {}; + r_vk_copy_region->bufferOffset = p_copy_region.buffer_offset; + _texture_subresource_layers_to_vk(p_copy_region.texture_subresources, &r_vk_copy_region->imageSubresource); + r_vk_copy_region->imageOffset.x = p_copy_region.texture_offset.x; + r_vk_copy_region->imageOffset.y = p_copy_region.texture_offset.y; + r_vk_copy_region->imageOffset.z = p_copy_region.texture_offset.z; + r_vk_copy_region->imageExtent.width = p_copy_region.texture_region_size.x; + r_vk_copy_region->imageExtent.height = p_copy_region.texture_region_size.y; + r_vk_copy_region->imageExtent.depth = p_copy_region.texture_region_size.z; +} + +static void _texture_copy_region_to_vk(const RDD::TextureCopyRegion &p_copy_region, VkImageCopy *r_vk_copy_region) { + *r_vk_copy_region = {}; + _texture_subresource_layers_to_vk(p_copy_region.src_subresources, &r_vk_copy_region->srcSubresource); + r_vk_copy_region->srcOffset.x = p_copy_region.src_offset.x; + r_vk_copy_region->srcOffset.y = p_copy_region.src_offset.y; + r_vk_copy_region->srcOffset.z = p_copy_region.src_offset.z; + _texture_subresource_layers_to_vk(p_copy_region.dst_subresources, &r_vk_copy_region->dstSubresource); + r_vk_copy_region->dstOffset.x = p_copy_region.dst_offset.x; + r_vk_copy_region->dstOffset.y = p_copy_region.dst_offset.y; + r_vk_copy_region->dstOffset.z = p_copy_region.dst_offset.z; + r_vk_copy_region->extent.width = p_copy_region.size.x; + r_vk_copy_region->extent.height = p_copy_region.size.y; + r_vk_copy_region->extent.depth = p_copy_region.size.z; +} + +void RenderingDeviceDriverVulkan::command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) { + const BufferInfo *buf_info = (const BufferInfo *)p_buffer.id; + vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, buf_info->vk_buffer, p_offset, p_size, 0); +} + +void RenderingDeviceDriverVulkan::command_copy_buffer(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, BufferID p_dst_buffer, VectorView<BufferCopyRegion> p_regions) { + const BufferInfo *src_buf_info = (const BufferInfo *)p_src_buffer.id; + const BufferInfo *dst_buf_info = (const BufferInfo *)p_dst_buffer.id; + vkCmdCopyBuffer((VkCommandBuffer)p_cmd_buffer.id, src_buf_info->vk_buffer, dst_buf_info->vk_buffer, p_regions.size(), (const VkBufferCopy *)p_regions.ptr()); +} + +void RenderingDeviceDriverVulkan::command_copy_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<TextureCopyRegion> p_regions) { + VkImageCopy *vk_copy_regions = ALLOCA_ARRAY(VkImageCopy, p_regions.size()); + for (uint32_t i = 0; i < p_regions.size(); i++) { + _texture_copy_region_to_vk(p_regions[i], &vk_copy_regions[i]); + } + + const TextureInfo *src_tex_info = (const TextureInfo *)p_src_texture.id; + const TextureInfo *dst_tex_info = (const TextureInfo *)p_dst_texture.id; + vkCmdCopyImage((VkCommandBuffer)p_cmd_buffer.id, src_tex_info->vk_view_create_info.image, (VkImageLayout)p_src_texture_layout, dst_tex_info->vk_view_create_info.image, (VkImageLayout)p_dst_texture_layout, p_regions.size(), vk_copy_regions); +} + +void RenderingDeviceDriverVulkan::command_resolve_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap) { + const TextureInfo *src_tex_info = (const TextureInfo *)p_src_texture.id; + const TextureInfo *dst_tex_info = (const TextureInfo *)p_dst_texture.id; + + VkImageResolve vk_resolve = {}; + vk_resolve.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vk_resolve.srcSubresource.mipLevel = p_src_mipmap; + vk_resolve.srcSubresource.baseArrayLayer = p_src_layer; + vk_resolve.srcSubresource.layerCount = 1; + vk_resolve.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vk_resolve.dstSubresource.mipLevel = p_dst_mipmap; + vk_resolve.dstSubresource.baseArrayLayer = p_dst_layer; + vk_resolve.dstSubresource.layerCount = 1; + vk_resolve.extent.width = MAX(1u, src_tex_info->vk_create_info.extent.width >> p_src_mipmap); + vk_resolve.extent.height = MAX(1u, src_tex_info->vk_create_info.extent.height >> p_src_mipmap); + vk_resolve.extent.depth = MAX(1u, src_tex_info->vk_create_info.extent.depth >> p_src_mipmap); + + vkCmdResolveImage((VkCommandBuffer)p_cmd_buffer.id, src_tex_info->vk_view_create_info.image, (VkImageLayout)p_src_texture_layout, dst_tex_info->vk_view_create_info.image, (VkImageLayout)p_dst_texture_layout, 1, &vk_resolve); +} + +void RenderingDeviceDriverVulkan::command_clear_color_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, const Color &p_color, const TextureSubresourceRange &p_subresources) { + VkClearColorValue vk_color = {}; + memcpy(&vk_color.float32, p_color.components, sizeof(VkClearColorValue::float32)); + + VkImageSubresourceRange vk_subresources = {}; + _texture_subresource_range_to_vk(p_subresources, &vk_subresources); + + const TextureInfo *tex_info = (const TextureInfo *)p_texture.id; + vkCmdClearColorImage((VkCommandBuffer)p_cmd_buffer.id, tex_info->vk_view_create_info.image, (VkImageLayout)p_texture_layout, &vk_color, 1, &vk_subresources); +} + +void RenderingDeviceDriverVulkan::command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<BufferTextureCopyRegion> p_regions) { + VkBufferImageCopy *vk_copy_regions = ALLOCA_ARRAY(VkBufferImageCopy, p_regions.size()); + for (uint32_t i = 0; i < p_regions.size(); i++) { + _buffer_texture_copy_region_to_vk(p_regions[i], &vk_copy_regions[i]); + } + + const BufferInfo *buf_info = (const BufferInfo *)p_src_buffer.id; + const TextureInfo *tex_info = (const TextureInfo *)p_dst_texture.id; + vkCmdCopyBufferToImage((VkCommandBuffer)p_cmd_buffer.id, buf_info->vk_buffer, tex_info->vk_view_create_info.image, (VkImageLayout)p_dst_texture_layout, p_regions.size(), vk_copy_regions); +} + +void RenderingDeviceDriverVulkan::command_copy_texture_to_buffer(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, BufferID p_dst_buffer, VectorView<BufferTextureCopyRegion> p_regions) { + VkBufferImageCopy *vk_copy_regions = ALLOCA_ARRAY(VkBufferImageCopy, p_regions.size()); + for (uint32_t i = 0; i < p_regions.size(); i++) { + _buffer_texture_copy_region_to_vk(p_regions[i], &vk_copy_regions[i]); + } + + const TextureInfo *tex_info = (const TextureInfo *)p_src_texture.id; + const BufferInfo *buf_info = (const BufferInfo *)p_dst_buffer.id; + vkCmdCopyImageToBuffer((VkCommandBuffer)p_cmd_buffer.id, tex_info->vk_view_create_info.image, (VkImageLayout)p_src_texture_layout, buf_info->vk_buffer, p_regions.size(), vk_copy_regions); +} + +/******************/ +/**** PIPELINE ****/ +/******************/ + +void RenderingDeviceDriverVulkan::pipeline_free(PipelineID p_pipeline) { + vkDestroyPipeline(vk_device, (VkPipeline)p_pipeline.id, nullptr); +} + +// ----- BINDING ----- + +void RenderingDeviceDriverVulkan::command_bind_push_constants(CommandBufferID p_cmd_buffer, ShaderID p_shader, uint32_t p_dst_first_index, VectorView<uint32_t> p_data) { + const ShaderInfo *shader_info = (const ShaderInfo *)p_shader.id; + vkCmdPushConstants((VkCommandBuffer)p_cmd_buffer.id, shader_info->vk_pipeline_layout, shader_info->vk_push_constant_stages, p_dst_first_index * sizeof(uint32_t), p_data.size() * sizeof(uint32_t), p_data.ptr()); +} + +// ----- CACHE ----- + +int RenderingDeviceDriverVulkan::caching_instance_count = 0; + +bool RenderingDeviceDriverVulkan::pipeline_cache_create(const Vector<uint8_t> &p_data) { + if (caching_instance_count) { + WARN_PRINT("There's already a RenderingDeviceDriverVulkan instance doing PSO caching. Only one can at the same time. This one won't."); + return false; + } + caching_instance_count++; + + pipelines_cache.current_size = 0; + pipelines_cache.buffer.resize(sizeof(PipelineCacheHeader)); + + // Parse. + { + if (p_data.size() <= (int)sizeof(PipelineCacheHeader)) { + WARN_PRINT("Invalid/corrupt pipelines cache."); + } else { + const PipelineCacheHeader *loaded_header = reinterpret_cast<const PipelineCacheHeader *>(p_data.ptr()); + if (loaded_header->magic != 868 + VK_PIPELINE_CACHE_HEADER_VERSION_ONE) { + WARN_PRINT("Invalid pipelines cache magic number."); + } else { + const uint8_t *loaded_buffer_start = p_data.ptr() + sizeof(PipelineCacheHeader); + uint32_t loaded_buffer_size = p_data.size() - sizeof(PipelineCacheHeader); + const PipelineCacheHeader *current_header = (PipelineCacheHeader *)pipelines_cache.buffer.ptr(); + if (loaded_header->data_hash != hash_murmur3_buffer(loaded_buffer_start, loaded_buffer_size) || + loaded_header->data_size != loaded_buffer_size || + loaded_header->vendor_id != current_header->vendor_id || + loaded_header->device_id != current_header->device_id || + loaded_header->driver_version != current_header->driver_version || + memcmp(loaded_header->uuid, current_header->uuid, VK_UUID_SIZE) != 0 || + loaded_header->driver_abi != current_header->driver_abi) { + WARN_PRINT("Invalid pipelines cache header."); + } else { + pipelines_cache.current_size = loaded_buffer_size; + pipelines_cache.buffer = p_data; + } + } + } + } + + // Create. + { + 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); + + VkResult err = vkCreatePipelineCache(vk_device, &cache_info, nullptr, &pipelines_cache.vk_cache); + if (err != VK_SUCCESS) { + WARN_PRINT("vkCreatePipelinecache failed with error " + itos(err) + "."); + return false; + } + } + + return true; +} + +void RenderingDeviceDriverVulkan::pipeline_cache_free() { + DEV_ASSERT(pipelines_cache.vk_cache); + + vkDestroyPipelineCache(vk_device, pipelines_cache.vk_cache, nullptr); + + DEV_ASSERT(caching_instance_count > 0); + caching_instance_count--; +} + +size_t RenderingDeviceDriverVulkan::pipeline_cache_query_size() { + DEV_ASSERT(pipelines_cache.vk_cache); + + // FIXME: + // We're letting the cache grow unboundedly. We may want to set at limit and see if implementations use LRU or the like. + // If we do, we won't be able to assume any longer that the cache is dirty if, and only if, it has grown. + VkResult err = vkGetPipelineCacheData(vk_device, pipelines_cache.vk_cache, &pipelines_cache.current_size, nullptr); + ERR_FAIL_COND_V_MSG(err, 0, "vkGetPipelineCacheData failed with error " + itos(err) + "."); + + return pipelines_cache.current_size; +} + +Vector<uint8_t> RenderingDeviceDriverVulkan::pipeline_cache_serialize() { + DEV_ASSERT(pipelines_cache.vk_cache); + + pipelines_cache.buffer.resize(pipelines_cache.current_size + sizeof(PipelineCacheHeader)); + + VkResult err = vkGetPipelineCacheData(vk_device, pipelines_cache.vk_cache, &pipelines_cache.current_size, pipelines_cache.buffer.ptrw() + sizeof(PipelineCacheHeader)); + ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_INCOMPLETE, Vector<uint8_t>()); // Incomplete is OK because the cache may have grown since the size was queried (unless when exiting). + + // The real buffer size may now be bigger than the updated current_size. + // We take into account the new size but keep the buffer resized in a worst-case fashion. + + PipelineCacheHeader *header = (PipelineCacheHeader *)pipelines_cache.buffer.ptrw(); + header->data_size = pipelines_cache.current_size; + header->data_hash = hash_murmur3_buffer(pipelines_cache.buffer.ptr() + sizeof(PipelineCacheHeader), pipelines_cache.current_size); + + return pipelines_cache.buffer; +} + +/*******************/ +/**** RENDERING ****/ +/*******************/ + +// ----- SUBPASS ----- + +// RDD::AttachmentLoadOp == VkAttachmentLoadOp. +static_assert(ENUM_MEMBERS_EQUAL(RDD::ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_LOAD_OP_LOAD)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_LOAD_OP_CLEAR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE)); + +// RDD::AttachmentStoreOp == VkAttachmentStoreOp. +static_assert(ENUM_MEMBERS_EQUAL(RDD::ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_STORE_OP_STORE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE)); + +// Assuming Vulkan and RDD's are backed by uint32_t in: +// - VkSubpassDescription2::pPreserveAttachments and RDD::Subpass::preserve_attachments. +// - VkRenderPassCreateInfo2KHR::pCorrelatedViewMasks and p_view_correlation_mask. + +static void _attachment_reference_to_vk(const RDD::AttachmentReference &p_attachment_reference, VkAttachmentReference2KHR *r_vk_attachment_reference) { + *r_vk_attachment_reference = {}; + r_vk_attachment_reference->sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + r_vk_attachment_reference->attachment = p_attachment_reference.attachment; + r_vk_attachment_reference->layout = (VkImageLayout)p_attachment_reference.layout; + r_vk_attachment_reference->aspectMask = (VkImageAspectFlags)p_attachment_reference.aspect; +} + +RDD::RenderPassID RenderingDeviceDriverVulkan::render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) { + // These are only used if we use multiview but we need to define them in scope. + const uint32_t view_mask = (1 << p_view_count) - 1; + const uint32_t correlation_mask = (1 << p_view_count) - 1; + + VkAttachmentDescription2KHR *vk_attachments = ALLOCA_ARRAY(VkAttachmentDescription2KHR, p_attachments.size()); + for (uint32_t i = 0; i < p_attachments.size(); i++) { + vk_attachments[i] = {}; + vk_attachments[i].sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR; + vk_attachments[i].format = RD_TO_VK_FORMAT[p_attachments[i].format]; + vk_attachments[i].samples = _ensure_supported_sample_count(p_attachments[i].samples); + vk_attachments[i].loadOp = (VkAttachmentLoadOp)p_attachments[i].load_op; + vk_attachments[i].storeOp = (VkAttachmentStoreOp)p_attachments[i].store_op; + vk_attachments[i].stencilLoadOp = (VkAttachmentLoadOp)p_attachments[i].stencil_load_op; + vk_attachments[i].stencilStoreOp = (VkAttachmentStoreOp)p_attachments[i].stencil_store_op; + vk_attachments[i].initialLayout = (VkImageLayout)p_attachments[i].initial_layout; + vk_attachments[i].finalLayout = (VkImageLayout)p_attachments[i].final_layout; + } + + VkSubpassDescription2KHR *vk_subpasses = ALLOCA_ARRAY(VkSubpassDescription2KHR, p_subpasses.size()); + for (uint32_t i = 0; i < p_subpasses.size(); i++) { + VkAttachmentReference2KHR *vk_subpass_input_attachments = ALLOCA_ARRAY(VkAttachmentReference2KHR, p_subpasses[i].input_references.size()); + for (uint32_t j = 0; j < p_subpasses[i].input_references.size(); j++) { + _attachment_reference_to_vk(p_subpasses[i].input_references[j], &vk_subpass_input_attachments[j]); + } + + VkAttachmentReference2KHR *vk_subpass_color_attachments = ALLOCA_ARRAY(VkAttachmentReference2KHR, p_subpasses[i].color_references.size()); + for (uint32_t j = 0; j < p_subpasses[i].color_references.size(); j++) { + _attachment_reference_to_vk(p_subpasses[i].color_references[j], &vk_subpass_color_attachments[j]); + } + + VkAttachmentReference2KHR *vk_subpass_resolve_attachments = ALLOCA_ARRAY(VkAttachmentReference2KHR, p_subpasses[i].resolve_references.size()); + for (uint32_t j = 0; j < p_subpasses[i].resolve_references.size(); j++) { + _attachment_reference_to_vk(p_subpasses[i].resolve_references[j], &vk_subpass_resolve_attachments[j]); + } + + VkAttachmentReference2KHR *vk_subpass_depth_stencil_attachment = nullptr; + if (p_subpasses[i].depth_stencil_reference.attachment != AttachmentReference::UNUSED) { + vk_subpass_depth_stencil_attachment = ALLOCA_SINGLE(VkAttachmentReference2KHR); + _attachment_reference_to_vk(p_subpasses[i].depth_stencil_reference, vk_subpass_depth_stencil_attachment); + } + + vk_subpasses[i] = {}; + vk_subpasses[i].sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; + vk_subpasses[i].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + vk_subpasses[i].viewMask = p_view_count == 1 ? 0 : view_mask; + vk_subpasses[i].inputAttachmentCount = p_subpasses[i].input_references.size(); + vk_subpasses[i].pInputAttachments = vk_subpass_input_attachments; + vk_subpasses[i].colorAttachmentCount = p_subpasses[i].color_references.size(); + vk_subpasses[i].pColorAttachments = vk_subpass_color_attachments; + vk_subpasses[i].pResolveAttachments = vk_subpass_resolve_attachments; + vk_subpasses[i].pDepthStencilAttachment = vk_subpass_depth_stencil_attachment; + vk_subpasses[i].preserveAttachmentCount = p_subpasses[i].preserve_attachments.size(); + vk_subpasses[i].pPreserveAttachments = p_subpasses[i].preserve_attachments.ptr(); + + // VRS. + if (context->get_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; + vk_subpass_vrs_attachment->attachment = p_subpasses[i].vrs_reference.attachment; + vk_subpass_vrs_attachment->layout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR; + + VkFragmentShadingRateAttachmentInfoKHR *vk_vrs_info = ALLOCA_SINGLE(VkFragmentShadingRateAttachmentInfoKHR); + *vk_vrs_info = {}; + vk_vrs_info->sType = 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_subpasses[i].pNext = vk_vrs_info; + } + } + + VkSubpassDependency2KHR *vk_subpass_dependencies = ALLOCA_ARRAY(VkSubpassDependency2KHR, p_subpass_dependencies.size()); + for (uint32_t i = 0; i < p_subpass_dependencies.size(); i++) { + vk_subpass_dependencies[i] = {}; + vk_subpass_dependencies[i].sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2; + vk_subpass_dependencies[i].srcSubpass = p_subpass_dependencies[i].src_subpass; + vk_subpass_dependencies[i].dstSubpass = p_subpass_dependencies[i].dst_subpass; + vk_subpass_dependencies[i].srcStageMask = (VkPipelineStageFlags)p_subpass_dependencies[i].src_stages; + vk_subpass_dependencies[i].dstStageMask = (VkPipelineStageFlags)p_subpass_dependencies[i].dst_stages; + vk_subpass_dependencies[i].srcAccessMask = (VkAccessFlags)p_subpass_dependencies[i].src_access; + vk_subpass_dependencies[i].dstAccessMask = (VkAccessFlags)p_subpass_dependencies[i].dst_access; + } + + VkRenderPassCreateInfo2KHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; + create_info.attachmentCount = p_attachments.size(); + create_info.pAttachments = vk_attachments; + create_info.subpassCount = p_subpasses.size(); + create_info.pSubpasses = vk_subpasses; + create_info.dependencyCount = p_subpass_dependencies.size(); + create_info.pDependencies = vk_subpass_dependencies; + create_info.correlatedViewMaskCount = p_view_count == 1 ? 0 : 1; + create_info.pCorrelatedViewMasks = p_view_count == 1 ? nullptr : &correlation_mask; + + // Multiview. + if (p_view_count > 1 && !context->supports_renderpass2()) { + // This is only required when using vkCreateRenderPass. + // We add it if vkCreateRenderPass2KHR is not supported, + // resulting this in being passed to our vkCreateRenderPass fallback. + + uint32_t *vk_view_masks = ALLOCA_ARRAY(uint32_t, p_subpasses.size()); + for (uint32_t i = 0; i < p_subpasses.size(); i++) { + vk_view_masks[i] = view_mask; + } + + VkRenderPassMultiviewCreateInfo *multiview_create_info = ALLOCA_SINGLE(VkRenderPassMultiviewCreateInfo); + *multiview_create_info = {}; + multiview_create_info->sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO; + multiview_create_info->subpassCount = p_subpasses.size(); + multiview_create_info->pViewMasks = vk_view_masks; + multiview_create_info->correlationMaskCount = 1; + multiview_create_info->pCorrelationMasks = &correlation_mask; + + create_info.pNext = multiview_create_info; + } + + VkRenderPass vk_render_pass = VK_NULL_HANDLE; + VkResult res = context->vkCreateRenderPass2KHR(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); +} + +void RenderingDeviceDriverVulkan::render_pass_free(RenderPassID p_render_pass) { + vkDestroyRenderPass(vk_device, (VkRenderPass)p_render_pass.id, nullptr); +} + +// ----- COMMANDS ----- + +static_assert(ARRAYS_COMPATIBLE_FIELDWISE(RDD::RenderPassClearValue, VkClearValue)); + +void RenderingDeviceDriverVulkan::command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_clear_values) { + VkRenderPassBeginInfo render_pass_begin = {}; + render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_begin.renderPass = (VkRenderPass)p_render_pass.id; + render_pass_begin.framebuffer = (VkFramebuffer)p_framebuffer.id; + + render_pass_begin.renderArea.offset.x = p_rect.position.x; + render_pass_begin.renderArea.offset.y = p_rect.position.y; + render_pass_begin.renderArea.extent.width = p_rect.size.x; + render_pass_begin.renderArea.extent.height = p_rect.size.y; + + render_pass_begin.clearValueCount = p_clear_values.size(); + render_pass_begin.pClearValues = (const VkClearValue *)p_clear_values.ptr(); + + VkSubpassContents vk_subpass_contents = p_cmd_buffer_type == COMMAND_BUFFER_TYPE_PRIMARY ? VK_SUBPASS_CONTENTS_INLINE : VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS; + vkCmdBeginRenderPass((VkCommandBuffer)p_cmd_buffer.id, &render_pass_begin, vk_subpass_contents); +} + +void RenderingDeviceDriverVulkan::command_end_render_pass(CommandBufferID p_cmd_buffer) { + vkCmdEndRenderPass((VkCommandBuffer)p_cmd_buffer.id); +} + +void RenderingDeviceDriverVulkan::command_next_render_subpass(CommandBufferID p_cmd_buffer, CommandBufferType p_cmd_buffer_type) { + VkSubpassContents vk_subpass_contents = p_cmd_buffer_type == COMMAND_BUFFER_TYPE_PRIMARY ? VK_SUBPASS_CONTENTS_INLINE : VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS; + vkCmdNextSubpass((VkCommandBuffer)p_cmd_buffer.id, vk_subpass_contents); +} + +void RenderingDeviceDriverVulkan::command_render_set_viewport(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_viewports) { + VkViewport *vk_viewports = ALLOCA_ARRAY(VkViewport, p_viewports.size()); + for (uint32_t i = 0; i < p_viewports.size(); i++) { + vk_viewports[i] = {}; + vk_viewports[i].x = p_viewports[i].position.x; + vk_viewports[i].y = p_viewports[i].position.y; + vk_viewports[i].width = p_viewports[i].size.x; + vk_viewports[i].height = p_viewports[i].size.y; + vk_viewports[i].minDepth = 0.0f; + vk_viewports[i].maxDepth = 1.0f; + } + vkCmdSetViewport((VkCommandBuffer)p_cmd_buffer.id, 0, p_viewports.size(), vk_viewports); +} + +void RenderingDeviceDriverVulkan::command_render_set_scissor(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_scissors) { + vkCmdSetScissor((VkCommandBuffer)p_cmd_buffer.id, 0, p_scissors.size(), (VkRect2D *)p_scissors.ptr()); +} + +void RenderingDeviceDriverVulkan::command_render_clear_attachments(CommandBufferID p_cmd_buffer, VectorView<AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) { + VkClearAttachment *vk_clears = ALLOCA_ARRAY(VkClearAttachment, p_attachment_clears.size()); + for (uint32_t i = 0; i < p_attachment_clears.size(); i++) { + vk_clears[i] = {}; + memcpy(&vk_clears[i].clearValue, &p_attachment_clears[i].value, sizeof(VkClearValue)); + vk_clears[i].colorAttachment = p_attachment_clears[i].color_attachment; + vk_clears[i].aspectMask = p_attachment_clears[i].aspect; + } + + VkClearRect *vk_rects = ALLOCA_ARRAY(VkClearRect, p_rects.size()); + for (uint32_t i = 0; i < p_rects.size(); i++) { + vk_rects[i] = {}; + vk_rects[i].rect.offset.x = p_rects[i].position.x; + vk_rects[i].rect.offset.y = p_rects[i].position.y; + vk_rects[i].rect.extent.width = p_rects[i].size.x; + vk_rects[i].rect.extent.height = p_rects[i].size.y; + vk_rects[i].baseArrayLayer = 0; + vk_rects[i].layerCount = 1; + } + + vkCmdClearAttachments((VkCommandBuffer)p_cmd_buffer.id, p_attachment_clears.size(), vk_clears, p_rects.size(), vk_rects); +} + +void RenderingDeviceDriverVulkan::command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) { + vkCmdBindPipeline((VkCommandBuffer)p_cmd_buffer.id, VK_PIPELINE_BIND_POINT_GRAPHICS, (VkPipeline)p_pipeline.id); +} + +void RenderingDeviceDriverVulkan::command_bind_render_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) { + const ShaderInfo *shader_info = (const ShaderInfo *)p_shader.id; + const UniformSetInfo *usi = (const UniformSetInfo *)p_uniform_set.id; + vkCmdBindDescriptorSets((VkCommandBuffer)p_cmd_buffer.id, VK_PIPELINE_BIND_POINT_GRAPHICS, shader_info->vk_pipeline_layout, p_set_index, 1, &usi->vk_descriptor_set, 0, nullptr); +} + +void RenderingDeviceDriverVulkan::command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) { + vkCmdDraw((VkCommandBuffer)p_cmd_buffer.id, p_vertex_count, p_instance_count, p_base_vertex, p_first_instance); +} + +void RenderingDeviceDriverVulkan::command_render_draw_indexed(CommandBufferID p_cmd_buffer, uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index, int32_t p_vertex_offset, uint32_t p_first_instance) { + vkCmdDrawIndexed((VkCommandBuffer)p_cmd_buffer.id, p_index_count, p_instance_count, p_first_index, p_vertex_offset, p_first_instance); +} + +void RenderingDeviceDriverVulkan::command_render_draw_indexed_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) { + const BufferInfo *buf_info = (const BufferInfo *)p_indirect_buffer.id; + vkCmdDrawIndexedIndirect((VkCommandBuffer)p_cmd_buffer.id, buf_info->vk_buffer, p_offset, p_draw_count, p_stride); +} + +void RenderingDeviceDriverVulkan::command_render_draw_indexed_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) { + const BufferInfo *indirect_buf_info = (const BufferInfo *)p_indirect_buffer.id; + const BufferInfo *count_buf_info = (const BufferInfo *)p_count_buffer.id; + vkCmdDrawIndexedIndirectCount((VkCommandBuffer)p_cmd_buffer.id, indirect_buf_info->vk_buffer, p_offset, count_buf_info->vk_buffer, p_count_buffer_offset, p_max_draw_count, p_stride); +} + +void RenderingDeviceDriverVulkan::command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) { + const BufferInfo *buf_info = (const BufferInfo *)p_indirect_buffer.id; + vkCmdDrawIndirect((VkCommandBuffer)p_cmd_buffer.id, buf_info->vk_buffer, p_offset, p_draw_count, p_stride); +} + +void RenderingDeviceDriverVulkan::command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) { + const BufferInfo *indirect_buf_info = (const BufferInfo *)p_indirect_buffer.id; + const BufferInfo *count_buf_info = (const BufferInfo *)p_count_buffer.id; + vkCmdDrawIndirectCount((VkCommandBuffer)p_cmd_buffer.id, indirect_buf_info->vk_buffer, p_offset, count_buf_info->vk_buffer, p_count_buffer_offset, p_max_draw_count, p_stride); +} + +void RenderingDeviceDriverVulkan::command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) { + VkBuffer *vk_buffers = ALLOCA_ARRAY(VkBuffer, p_binding_count); + for (uint32_t i = 0; i < p_binding_count; i++) { + vk_buffers[i] = ((const BufferInfo *)p_buffers[i].id)->vk_buffer; + } + vkCmdBindVertexBuffers((VkCommandBuffer)p_cmd_buffer.id, 0, p_binding_count, vk_buffers, p_offsets); +} + +void RenderingDeviceDriverVulkan::command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) { + const BufferInfo *buf_info = (const BufferInfo *)p_buffer.id; + vkCmdBindIndexBuffer((VkCommandBuffer)p_cmd_buffer.id, buf_info->vk_buffer, p_offset, p_format == INDEX_BUFFER_FORMAT_UINT16 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32); +} + +void RenderingDeviceDriverVulkan::command_render_set_blend_constants(CommandBufferID p_cmd_buffer, const Color &p_constants) { + vkCmdSetBlendConstants((VkCommandBuffer)p_cmd_buffer.id, p_constants.components); +} + +void RenderingDeviceDriverVulkan::command_render_set_line_width(CommandBufferID p_cmd_buffer, float p_width) { + vkCmdSetLineWidth((VkCommandBuffer)p_cmd_buffer.id, p_width); +} + +// ----- PIPELINE ----- + +static const VkPrimitiveTopology RD_TO_VK_PRIMITIVE[RDD::RENDER_PRIMITIVE_MAX] = { + VK_PRIMITIVE_TOPOLOGY_POINT_LIST, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, +}; + +// RDD::PolygonCullMode == VkCullModeFlagBits. +static_assert(ENUM_MEMBERS_EQUAL(RDD::POLYGON_CULL_DISABLED, VK_CULL_MODE_NONE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::POLYGON_CULL_FRONT, VK_CULL_MODE_FRONT_BIT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::POLYGON_CULL_BACK, VK_CULL_MODE_BACK_BIT)); + +// RDD::StencilOperation == VkStencilOp. +static_assert(ENUM_MEMBERS_EQUAL(RDD::STENCIL_OP_KEEP, VK_STENCIL_OP_KEEP)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::STENCIL_OP_ZERO, VK_STENCIL_OP_ZERO)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::STENCIL_OP_INCREMENT_AND_CLAMP, VK_STENCIL_OP_INCREMENT_AND_CLAMP)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::STENCIL_OP_DECREMENT_AND_CLAMP, VK_STENCIL_OP_DECREMENT_AND_CLAMP)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::STENCIL_OP_INVERT, VK_STENCIL_OP_INVERT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::STENCIL_OP_INCREMENT_AND_WRAP, VK_STENCIL_OP_INCREMENT_AND_WRAP)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::STENCIL_OP_DECREMENT_AND_WRAP, VK_STENCIL_OP_DECREMENT_AND_WRAP)); + +// RDD::LogicOperation == VkLogicOp. +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_CLEAR, VK_LOGIC_OP_CLEAR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_AND, VK_LOGIC_OP_AND)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_AND_REVERSE, VK_LOGIC_OP_AND_REVERSE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_COPY, VK_LOGIC_OP_COPY)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_AND_INVERTED, VK_LOGIC_OP_AND_INVERTED)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_NO_OP, VK_LOGIC_OP_NO_OP)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_XOR, VK_LOGIC_OP_XOR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_OR, VK_LOGIC_OP_OR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_NOR, VK_LOGIC_OP_NOR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_EQUIVALENT, VK_LOGIC_OP_EQUIVALENT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_INVERT, VK_LOGIC_OP_INVERT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_OR_REVERSE, VK_LOGIC_OP_OR_REVERSE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_COPY_INVERTED, VK_LOGIC_OP_COPY_INVERTED)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_OR_INVERTED, VK_LOGIC_OP_OR_INVERTED)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_NAND, VK_LOGIC_OP_NAND)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::LOGIC_OP_SET, VK_LOGIC_OP_SET)); + +// RDD::BlendFactor == VkBlendFactor. +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ONE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_SRC_COLOR, VK_BLEND_FACTOR_SRC_COLOR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_ONE_MINUS_SRC_COLOR, VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_DST_COLOR, VK_BLEND_FACTOR_DST_COLOR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_ONE_MINUS_DST_COLOR, VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_SRC_ALPHA)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_DST_ALPHA, VK_BLEND_FACTOR_DST_ALPHA)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_ONE_MINUS_DST_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_CONSTANT_COLOR, VK_BLEND_FACTOR_CONSTANT_COLOR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_CONSTANT_ALPHA, VK_BLEND_FACTOR_CONSTANT_ALPHA)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_SRC_ALPHA_SATURATE, VK_BLEND_FACTOR_SRC_ALPHA_SATURATE)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_SRC1_COLOR, VK_BLEND_FACTOR_SRC1_COLOR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_SRC1_ALPHA, VK_BLEND_FACTOR_SRC1_ALPHA)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA)); + +// RDD::BlendOperation == VkBlendOp. +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_OP_ADD, VK_BLEND_OP_ADD)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_OP_SUBTRACT, VK_BLEND_OP_SUBTRACT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_OP_REVERSE_SUBTRACT, VK_BLEND_OP_REVERSE_SUBTRACT)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_OP_MINIMUM, VK_BLEND_OP_MIN)); +static_assert(ENUM_MEMBERS_EQUAL(RDD::BLEND_OP_MAXIMUM, VK_BLEND_OP_MAX)); + +RDD::PipelineID RenderingDeviceDriverVulkan::render_pipeline_create( + ShaderID p_shader, + VertexFormatID p_vertex_format, + RenderPrimitive p_render_primitive, + PipelineRasterizationState p_rasterization_state, + PipelineMultisampleState p_multisample_state, + PipelineDepthStencilState p_depth_stencil_state, + PipelineColorBlendState p_blend_state, + VectorView<int32_t> p_color_attachments, + BitField<PipelineDynamicStateFlags> p_dynamic_state, + RenderPassID p_render_pass, + uint32_t p_render_subpass, + VectorView<PipelineSpecializationConstant> p_specialization_constants) { + // Vertex. + const VkPipelineVertexInputStateCreateInfo *vertex_input_state_create_info = nullptr; + if (p_vertex_format.id) { + const VertexFormatInfo *vf_info = (const VertexFormatInfo *)p_vertex_format.id; + vertex_input_state_create_info = &vf_info->vk_create_info; + } else { + VkPipelineVertexInputStateCreateInfo *null_vertex_input_state = ALLOCA_SINGLE(VkPipelineVertexInputStateCreateInfo); + *null_vertex_input_state = {}; + null_vertex_input_state->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input_state_create_info = null_vertex_input_state; + } + + // Input assembly. + VkPipelineInputAssemblyStateCreateInfo input_assembly_create_info = {}; + input_assembly_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly_create_info.topology = RD_TO_VK_PRIMITIVE[p_render_primitive]; + input_assembly_create_info.primitiveRestartEnable = (p_render_primitive == RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_RESTART_INDEX); + + // 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()); + tessellation_create_info.patchControlPoints = p_rasterization_state.patch_control_points; + + // Viewport. + VkPipelineViewportStateCreateInfo viewport_state_create_info = {}; + viewport_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state_create_info.viewportCount = 1; // If VR extensions are supported at some point, this will have to be customizable in the framebuffer format. + viewport_state_create_info.scissorCount = 1; + + // Rasterization. + VkPipelineRasterizationStateCreateInfo rasterization_state_create_info = {}; + rasterization_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterization_state_create_info.depthClampEnable = p_rasterization_state.enable_depth_clamp; + rasterization_state_create_info.rasterizerDiscardEnable = p_rasterization_state.discard_primitives; + rasterization_state_create_info.polygonMode = p_rasterization_state.wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL; + rasterization_state_create_info.cullMode = (PolygonCullMode)p_rasterization_state.cull_mode; + rasterization_state_create_info.frontFace = (p_rasterization_state.front_face == POLYGON_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE); + rasterization_state_create_info.depthBiasEnable = p_rasterization_state.depth_bias_enabled; + rasterization_state_create_info.depthBiasConstantFactor = p_rasterization_state.depth_bias_constant_factor; + rasterization_state_create_info.depthBiasClamp = p_rasterization_state.depth_bias_clamp; + rasterization_state_create_info.depthBiasSlopeFactor = p_rasterization_state.depth_bias_slope_factor; + rasterization_state_create_info.lineWidth = p_rasterization_state.line_width; + + // Multisample. + VkPipelineMultisampleStateCreateInfo multisample_state_create_info = {}; + multisample_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample_state_create_info.rasterizationSamples = _ensure_supported_sample_count(p_multisample_state.sample_count); + multisample_state_create_info.sampleShadingEnable = p_multisample_state.enable_sample_shading; + multisample_state_create_info.minSampleShading = p_multisample_state.min_sample_shading; + if (p_multisample_state.sample_mask.size()) { + static_assert(ARRAYS_COMPATIBLE(uint32_t, VkSampleMask)); + multisample_state_create_info.pSampleMask = p_multisample_state.sample_mask.ptr(); + } else { + multisample_state_create_info.pSampleMask = nullptr; + } + multisample_state_create_info.alphaToCoverageEnable = p_multisample_state.enable_alpha_to_coverage; + multisample_state_create_info.alphaToOneEnable = p_multisample_state.enable_alpha_to_one; + + // Depth stencil. + + VkPipelineDepthStencilStateCreateInfo depth_stencil_state_create_info = {}; + depth_stencil_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depth_stencil_state_create_info.depthTestEnable = p_depth_stencil_state.enable_depth_test; + depth_stencil_state_create_info.depthWriteEnable = p_depth_stencil_state.enable_depth_write; + depth_stencil_state_create_info.depthCompareOp = (VkCompareOp)p_depth_stencil_state.depth_compare_operator; + depth_stencil_state_create_info.depthBoundsTestEnable = p_depth_stencil_state.enable_depth_range; + depth_stencil_state_create_info.stencilTestEnable = p_depth_stencil_state.enable_stencil; + + depth_stencil_state_create_info.front.failOp = (VkStencilOp)p_depth_stencil_state.front_op.fail; + depth_stencil_state_create_info.front.passOp = (VkStencilOp)p_depth_stencil_state.front_op.pass; + depth_stencil_state_create_info.front.depthFailOp = (VkStencilOp)p_depth_stencil_state.front_op.depth_fail; + depth_stencil_state_create_info.front.compareOp = (VkCompareOp)p_depth_stencil_state.front_op.compare; + depth_stencil_state_create_info.front.compareMask = p_depth_stencil_state.front_op.compare_mask; + depth_stencil_state_create_info.front.writeMask = p_depth_stencil_state.front_op.write_mask; + depth_stencil_state_create_info.front.reference = p_depth_stencil_state.front_op.reference; + + depth_stencil_state_create_info.back.failOp = (VkStencilOp)p_depth_stencil_state.back_op.fail; + depth_stencil_state_create_info.back.passOp = (VkStencilOp)p_depth_stencil_state.back_op.pass; + depth_stencil_state_create_info.back.depthFailOp = (VkStencilOp)p_depth_stencil_state.back_op.depth_fail; + depth_stencil_state_create_info.back.compareOp = (VkCompareOp)p_depth_stencil_state.back_op.compare; + depth_stencil_state_create_info.back.compareMask = p_depth_stencil_state.back_op.compare_mask; + depth_stencil_state_create_info.back.writeMask = p_depth_stencil_state.back_op.write_mask; + depth_stencil_state_create_info.back.reference = p_depth_stencil_state.back_op.reference; + + depth_stencil_state_create_info.minDepthBounds = p_depth_stencil_state.depth_range_min; + depth_stencil_state_create_info.maxDepthBounds = p_depth_stencil_state.depth_range_max; + + // Blend state. + + VkPipelineColorBlendStateCreateInfo color_blend_state_create_info = {}; + color_blend_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blend_state_create_info.logicOpEnable = p_blend_state.enable_logic_op; + color_blend_state_create_info.logicOp = (VkLogicOp)p_blend_state.logic_op; + + VkPipelineColorBlendAttachmentState *vk_attachment_states = ALLOCA_ARRAY(VkPipelineColorBlendAttachmentState, p_color_attachments.size()); + { + for (uint32_t i = 0; i < p_color_attachments.size(); i++) { + vk_attachment_states[i] = {}; + if (p_color_attachments[i] != ATTACHMENT_UNUSED) { + vk_attachment_states[i].blendEnable = p_blend_state.attachments[i].enable_blend; + + vk_attachment_states[i].srcColorBlendFactor = (VkBlendFactor)p_blend_state.attachments[i].src_color_blend_factor; + vk_attachment_states[i].dstColorBlendFactor = (VkBlendFactor)p_blend_state.attachments[i].dst_color_blend_factor; + vk_attachment_states[i].colorBlendOp = (VkBlendOp)p_blend_state.attachments[i].color_blend_op; + + vk_attachment_states[i].srcAlphaBlendFactor = (VkBlendFactor)p_blend_state.attachments[i].src_alpha_blend_factor; + vk_attachment_states[i].dstAlphaBlendFactor = (VkBlendFactor)p_blend_state.attachments[i].dst_alpha_blend_factor; + vk_attachment_states[i].alphaBlendOp = (VkBlendOp)p_blend_state.attachments[i].alpha_blend_op; + + if (p_blend_state.attachments[i].write_r) { + vk_attachment_states[i].colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; + } + if (p_blend_state.attachments[i].write_g) { + vk_attachment_states[i].colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; + } + if (p_blend_state.attachments[i].write_b) { + vk_attachment_states[i].colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; + } + if (p_blend_state.attachments[i].write_a) { + vk_attachment_states[i].colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; + } + } + } + } + color_blend_state_create_info.attachmentCount = p_color_attachments.size(); + color_blend_state_create_info.pAttachments = vk_attachment_states; + + color_blend_state_create_info.blendConstants[0] = p_blend_state.blend_constant.r; + color_blend_state_create_info.blendConstants[1] = p_blend_state.blend_constant.g; + color_blend_state_create_info.blendConstants[2] = p_blend_state.blend_constant.b; + color_blend_state_create_info.blendConstants[3] = p_blend_state.blend_constant.a; + + // Dynamic state. + + VkPipelineDynamicStateCreateInfo dynamic_state_create_info = {}; + dynamic_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + + static const uint32_t MAX_DYN_STATE_COUNT = 9; + VkDynamicState *vk_dynamic_states = ALLOCA_ARRAY(VkDynamicState, MAX_DYN_STATE_COUNT); + uint32_t vk_dynamic_states_count = 0; + + vk_dynamic_states[vk_dynamic_states_count] = VK_DYNAMIC_STATE_VIEWPORT; // Viewport and scissor are always dynamic. + vk_dynamic_states_count++; + vk_dynamic_states[vk_dynamic_states_count] = VK_DYNAMIC_STATE_SCISSOR; + vk_dynamic_states_count++; + if (p_dynamic_state.has_flag(DYNAMIC_STATE_LINE_WIDTH)) { + vk_dynamic_states[vk_dynamic_states_count] = VK_DYNAMIC_STATE_LINE_WIDTH; + vk_dynamic_states_count++; + } + if (p_dynamic_state.has_flag(DYNAMIC_STATE_DEPTH_BIAS)) { + vk_dynamic_states[vk_dynamic_states_count] = VK_DYNAMIC_STATE_DEPTH_BIAS; + vk_dynamic_states_count++; + } + if (p_dynamic_state.has_flag(DYNAMIC_STATE_BLEND_CONSTANTS)) { + vk_dynamic_states[vk_dynamic_states_count] = VK_DYNAMIC_STATE_BLEND_CONSTANTS; + vk_dynamic_states_count++; + } + if (p_dynamic_state.has_flag(DYNAMIC_STATE_DEPTH_BOUNDS)) { + vk_dynamic_states[vk_dynamic_states_count] = VK_DYNAMIC_STATE_DEPTH_BOUNDS; + vk_dynamic_states_count++; + } + if (p_dynamic_state.has_flag(DYNAMIC_STATE_STENCIL_COMPARE_MASK)) { + vk_dynamic_states[vk_dynamic_states_count] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK; + vk_dynamic_states_count++; + } + if (p_dynamic_state.has_flag(DYNAMIC_STATE_STENCIL_WRITE_MASK)) { + vk_dynamic_states[vk_dynamic_states_count] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK; + vk_dynamic_states_count++; + } + if (p_dynamic_state.has_flag(DYNAMIC_STATE_STENCIL_REFERENCE)) { + vk_dynamic_states[vk_dynamic_states_count] = VK_DYNAMIC_STATE_STENCIL_REFERENCE; + vk_dynamic_states_count++; + } + DEV_ASSERT(vk_dynamic_states_count <= MAX_DYN_STATE_COUNT); + + dynamic_state_create_info.dynamicStateCount = vk_dynamic_states_count; + dynamic_state_create_info.pDynamicStates = vk_dynamic_states; + + // VRS. + + void *graphics_pipeline_nextptr = nullptr; + + if (context->get_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. + + VkPipelineFragmentShadingRateStateCreateInfoKHR *vrs_create_info = ALLOCA_SINGLE(VkPipelineFragmentShadingRateStateCreateInfoKHR); + *vrs_create_info = {}; + vrs_create_info->sType = VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR; + vrs_create_info->fragmentSize = { 4, 4 }; + vrs_create_info->combinerOps[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR; // We don't use pipeline/primitive VRS so this really doesn't matter. + vrs_create_info->combinerOps[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR; // Always use the outcome of attachment VRS if enabled. + + graphics_pipeline_nextptr = vrs_create_info; + } + + // Finally, pipeline create info. + + const ShaderInfo *shader_info = (const ShaderInfo *)p_shader.id; + + VkGraphicsPipelineCreateInfo pipeline_create_info = {}; + + pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_create_info.pNext = graphics_pipeline_nextptr; + pipeline_create_info.stageCount = shader_info->vk_stages_create_info.size(); + + VkPipelineShaderStageCreateInfo *vk_pipeline_stages = ALLOCA_ARRAY(VkPipelineShaderStageCreateInfo, shader_info->vk_stages_create_info.size()); + + for (uint32_t i = 0; i < shader_info->vk_stages_create_info.size(); i++) { + vk_pipeline_stages[i] = shader_info->vk_stages_create_info[i]; + + if (p_specialization_constants.size()) { + VkSpecializationMapEntry *specialization_map_entries = ALLOCA_ARRAY(VkSpecializationMapEntry, p_specialization_constants.size()); + for (uint32_t j = 0; j < p_specialization_constants.size(); j++) { + specialization_map_entries[j] = {}; + specialization_map_entries[j].constantID = p_specialization_constants[j].constant_id; + specialization_map_entries[j].offset = (const char *)&p_specialization_constants[j].int_value - (const char *)p_specialization_constants.ptr(); + specialization_map_entries[j].size = sizeof(uint32_t); + } + + VkSpecializationInfo *specialization_info = ALLOCA_SINGLE(VkSpecializationInfo); + *specialization_info = {}; + specialization_info->dataSize = p_specialization_constants.size() * sizeof(PipelineSpecializationConstant); + specialization_info->pData = p_specialization_constants.ptr(); + specialization_info->mapEntryCount = p_specialization_constants.size(); + specialization_info->pMapEntries = specialization_map_entries; + + vk_pipeline_stages[i].pSpecializationInfo = specialization_info; + } + } + + pipeline_create_info.pStages = vk_pipeline_stages; + pipeline_create_info.pVertexInputState = vertex_input_state_create_info; + pipeline_create_info.pInputAssemblyState = &input_assembly_create_info; + pipeline_create_info.pTessellationState = &tessellation_create_info; + pipeline_create_info.pViewportState = &viewport_state_create_info; + pipeline_create_info.pRasterizationState = &rasterization_state_create_info; + pipeline_create_info.pMultisampleState = &multisample_state_create_info; + pipeline_create_info.pDepthStencilState = &depth_stencil_state_create_info; + pipeline_create_info.pColorBlendState = &color_blend_state_create_info; + pipeline_create_info.pDynamicState = &dynamic_state_create_info; + pipeline_create_info.layout = shader_info->vk_pipeline_layout; + pipeline_create_info.renderPass = (VkRenderPass)p_render_pass.id; + pipeline_create_info.subpass = p_render_subpass; + + // --- + + VkPipeline vk_pipeline = VK_NULL_HANDLE; + VkResult err = vkCreateGraphicsPipelines(vk_device, pipelines_cache.vk_cache, 1, &pipeline_create_info, nullptr, &vk_pipeline); + ERR_FAIL_COND_V_MSG(err, PipelineID(), "vkCreateGraphicsPipelines failed with error " + itos(err) + "."); + + return PipelineID(vk_pipeline); +} + +/*****************/ +/**** COMPUTE ****/ +/*****************/ + +// ----- COMMANDS ----- + +void RenderingDeviceDriverVulkan::command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) { + vkCmdBindPipeline((VkCommandBuffer)p_cmd_buffer.id, VK_PIPELINE_BIND_POINT_COMPUTE, (VkPipeline)p_pipeline.id); +} + +void RenderingDeviceDriverVulkan::command_bind_compute_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) { + const ShaderInfo *shader_info = (const ShaderInfo *)p_shader.id; + const UniformSetInfo *usi = (const UniformSetInfo *)p_uniform_set.id; + vkCmdBindDescriptorSets((VkCommandBuffer)p_cmd_buffer.id, VK_PIPELINE_BIND_POINT_COMPUTE, shader_info->vk_pipeline_layout, p_set_index, 1, &usi->vk_descriptor_set, 0, nullptr); +} + +void RenderingDeviceDriverVulkan::command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) { + vkCmdDispatch((VkCommandBuffer)p_cmd_buffer.id, p_x_groups, p_y_groups, p_z_groups); +} + +void RenderingDeviceDriverVulkan::command_compute_dispatch_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset) { + const BufferInfo *buf_info = (const BufferInfo *)p_indirect_buffer.id; + vkCmdDispatchIndirect((VkCommandBuffer)p_cmd_buffer.id, buf_info->vk_buffer, p_offset); +} + +// ----- PIPELINE ----- + +RDD::PipelineID RenderingDeviceDriverVulkan::compute_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) { + const ShaderInfo *shader_info = (const ShaderInfo *)p_shader.id; + + VkComputePipelineCreateInfo pipeline_create_info = {}; + pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pipeline_create_info.stage = shader_info->vk_stages_create_info[0]; + pipeline_create_info.layout = shader_info->vk_pipeline_layout; + + if (p_specialization_constants.size()) { + VkSpecializationMapEntry *specialization_map_entries = ALLOCA_ARRAY(VkSpecializationMapEntry, p_specialization_constants.size()); + for (uint32_t i = 0; i < p_specialization_constants.size(); i++) { + specialization_map_entries[i] = {}; + specialization_map_entries[i].constantID = p_specialization_constants[i].constant_id; + specialization_map_entries[i].offset = (const char *)&p_specialization_constants[i].int_value - (const char *)p_specialization_constants.ptr(); + specialization_map_entries[i].size = sizeof(uint32_t); + } + + VkSpecializationInfo *specialization_info = ALLOCA_SINGLE(VkSpecializationInfo); + *specialization_info = {}; + specialization_info->dataSize = p_specialization_constants.size() * sizeof(PipelineSpecializationConstant); + specialization_info->pData = p_specialization_constants.ptr(); + specialization_info->mapEntryCount = p_specialization_constants.size(); + specialization_info->pMapEntries = specialization_map_entries; + + pipeline_create_info.stage.pSpecializationInfo = specialization_info; + } + + VkPipeline vk_pipeline = VK_NULL_HANDLE; + VkResult err = vkCreateComputePipelines(vk_device, pipelines_cache.vk_cache, 1, &pipeline_create_info, nullptr, &vk_pipeline); + ERR_FAIL_COND_V_MSG(err, PipelineID(), "vkCreateComputePipelines failed with error " + itos(err) + "."); + + return PipelineID(vk_pipeline); +} + +/*****************/ +/**** QUERIES ****/ +/*****************/ + +// ----- TIMESTAMP ----- + +RDD::QueryPoolID RenderingDeviceDriverVulkan::timestamp_query_pool_create(uint32_t p_query_count) { + VkQueryPoolCreateInfo query_pool_create_info = {}; + query_pool_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; + query_pool_create_info.queryType = VK_QUERY_TYPE_TIMESTAMP; + query_pool_create_info.queryCount = p_query_count; + + VkQueryPool vk_query_pool = VK_NULL_HANDLE; + vkCreateQueryPool(vk_device, &query_pool_create_info, nullptr, &vk_query_pool); + return RDD::QueryPoolID(vk_query_pool); +} + +void RenderingDeviceDriverVulkan::timestamp_query_pool_free(QueryPoolID p_pool_id) { + vkDestroyQueryPool(vk_device, (VkQueryPool)p_pool_id.id, nullptr); +} + +void RenderingDeviceDriverVulkan::timestamp_query_pool_get_results(QueryPoolID p_pool_id, uint32_t p_query_count, uint64_t *r_results) { + vkGetQueryPoolResults(vk_device, (VkQueryPool)p_pool_id.id, 0, p_query_count, sizeof(uint64_t) * p_query_count, r_results, sizeof(uint64_t), VK_QUERY_RESULT_64_BIT); +} + +uint64_t RenderingDeviceDriverVulkan::timestamp_query_result_to_time(uint64_t p_result) { + // This sucks because timestampPeriod multiplier is a float, while the timestamp is 64 bits nanosecs. + // So, in cases like nvidia which give you enormous numbers and 1 as multiplier, multiplying is next to impossible. + // Need to do 128 bits fixed point multiplication to get the right value. + + auto mult64to128 = [](uint64_t u, uint64_t v, uint64_t &h, uint64_t &l) { + uint64_t u1 = (u & 0xffffffff); + uint64_t v1 = (v & 0xffffffff); + uint64_t t = (u1 * v1); + uint64_t w3 = (t & 0xffffffff); + uint64_t k = (t >> 32); + + u >>= 32; + t = (u * v1) + k; + k = (t & 0xffffffff); + uint64_t w1 = (t >> 32); + + v >>= 32; + t = (u1 * v) + k; + k = (t >> 32); + + h = (u * v) + w1 + k; + l = (t << 32) + w3; + }; + + 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); + l >>= shift_bits; + l |= h << (64 - shift_bits); + + return l; +} + +void RenderingDeviceDriverVulkan::command_timestamp_query_pool_reset(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_query_count) { + vkCmdResetQueryPool((VkCommandBuffer)p_cmd_buffer.id, (VkQueryPool)p_pool_id.id, 0, p_query_count); +} + +void RenderingDeviceDriverVulkan::command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) { + vkCmdWriteTimestamp((VkCommandBuffer)p_cmd_buffer.id, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, (VkQueryPool)p_pool_id.id, p_index); +} + +/****************/ +/**** LABELS ****/ +/****************/ + +void RenderingDeviceDriverVulkan::command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) { + VkDebugUtilsLabelEXT label; + label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; + label.pNext = nullptr; + label.pLabelName = p_label_name; + label.color[0] = p_color[0]; + 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); +} + +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; +} + +/********************/ +/**** SUBMISSION ****/ +/********************/ + +void RenderingDeviceDriverVulkan::begin_segment(CommandBufferID p_cmd_buffer, uint32_t p_frame_index, uint32_t p_frames_drawn) { +} + +void RenderingDeviceDriverVulkan::end_segment() { +} + +/**************/ +/**** MISC ****/ +/**************/ + +void RenderingDeviceDriverVulkan::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->allocation.handle) { + context->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"); + } break; + case OBJECT_TYPE_SAMPLER: { + context->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); + if (buf_info->vk_view) { + context->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); + } + context->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); + } break; + case OBJECT_TYPE_PIPELINE: { + context->set_object_name(VK_OBJECT_TYPE_PIPELINE, (uint64_t)p_driver_id.id, p_name); + } break; + default: { + DEV_ASSERT(false); + } + } +} + +uint64_t RenderingDeviceDriverVulkan::get_resource_native_handle(DriverResource p_type, ID p_driver_id) { + switch (p_type) { + case DRIVER_RESOURCE_LOGICAL_DEVICE: { + return (uint64_t)vk_device; + } + case DRIVER_RESOURCE_PHYSICAL_DEVICE: { + return (uint64_t)context->get_physical_device(); + } + case DRIVER_RESOURCE_TOPMOST_OBJECT: { + return (uint64_t)context->get_instance(); + } + case DRIVER_RESOURCE_COMMAND_QUEUE: { + return (uint64_t)context->get_graphics_queue(); + } + case DRIVER_RESOURCE_QUEUE_FAMILY: { + return context->get_graphics_queue_family_index(); + } + case DRIVER_RESOURCE_TEXTURE: { + const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id; + return (uint64_t)tex_info->vk_view_create_info.image; + } + case DRIVER_RESOURCE_TEXTURE_VIEW: { + const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id; + return (uint64_t)tex_info->vk_view; + } + case DRIVER_RESOURCE_TEXTURE_DATA_FORMAT: { + const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id; + return (uint64_t)tex_info->vk_view_create_info.format; + } + case DRIVER_RESOURCE_SAMPLER: + case DRIVER_RESOURCE_UNIFORM_SET: + case DRIVER_RESOURCE_BUFFER: + case DRIVER_RESOURCE_COMPUTE_PIPELINE: + case DRIVER_RESOURCE_RENDER_PIPELINE: { + return p_driver_id.id; + } + default: { + return 0; + } + } +} + +uint64_t RenderingDeviceDriverVulkan::get_total_memory_used() { + VmaTotalStatistics stats = {}; + vmaCalculateStatistics(allocator, &stats); + return stats.total.statistics.allocationBytes; +} + +uint64_t RenderingDeviceDriverVulkan::limit_get(Limit p_limit) { + switch (p_limit) { + case LIMIT_MAX_BOUND_UNIFORM_SETS: + return limits.maxBoundDescriptorSets; + case LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS: + return limits.maxColorAttachments; + case LIMIT_MAX_TEXTURES_PER_UNIFORM_SET: + return limits.maxDescriptorSetSampledImages; + case LIMIT_MAX_SAMPLERS_PER_UNIFORM_SET: + return limits.maxDescriptorSetSamplers; + case LIMIT_MAX_STORAGE_BUFFERS_PER_UNIFORM_SET: + return limits.maxDescriptorSetStorageBuffers; + case LIMIT_MAX_STORAGE_IMAGES_PER_UNIFORM_SET: + return limits.maxDescriptorSetStorageImages; + case LIMIT_MAX_UNIFORM_BUFFERS_PER_UNIFORM_SET: + return limits.maxDescriptorSetUniformBuffers; + case LIMIT_MAX_DRAW_INDEXED_INDEX: + return limits.maxDrawIndexedIndexValue; + case LIMIT_MAX_FRAMEBUFFER_HEIGHT: + return limits.maxFramebufferHeight; + case LIMIT_MAX_FRAMEBUFFER_WIDTH: + return limits.maxFramebufferWidth; + case LIMIT_MAX_TEXTURE_ARRAY_LAYERS: + return limits.maxImageArrayLayers; + case LIMIT_MAX_TEXTURE_SIZE_1D: + return limits.maxImageDimension1D; + case LIMIT_MAX_TEXTURE_SIZE_2D: + return limits.maxImageDimension2D; + case LIMIT_MAX_TEXTURE_SIZE_3D: + return limits.maxImageDimension3D; + case LIMIT_MAX_TEXTURE_SIZE_CUBE: + return limits.maxImageDimensionCube; + case LIMIT_MAX_TEXTURES_PER_SHADER_STAGE: + return limits.maxPerStageDescriptorSampledImages; + case LIMIT_MAX_SAMPLERS_PER_SHADER_STAGE: + return limits.maxPerStageDescriptorSamplers; + case LIMIT_MAX_STORAGE_BUFFERS_PER_SHADER_STAGE: + return limits.maxPerStageDescriptorStorageBuffers; + case LIMIT_MAX_STORAGE_IMAGES_PER_SHADER_STAGE: + return limits.maxPerStageDescriptorStorageImages; + case LIMIT_MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE: + return limits.maxPerStageDescriptorUniformBuffers; + case LIMIT_MAX_PUSH_CONSTANT_SIZE: + return limits.maxPushConstantsSize; + case LIMIT_MAX_UNIFORM_BUFFER_SIZE: + return limits.maxUniformBufferRange; + case LIMIT_MAX_VERTEX_INPUT_ATTRIBUTE_OFFSET: + return limits.maxVertexInputAttributeOffset; + case LIMIT_MAX_VERTEX_INPUT_ATTRIBUTES: + return limits.maxVertexInputAttributes; + case LIMIT_MAX_VERTEX_INPUT_BINDINGS: + return limits.maxVertexInputBindings; + case LIMIT_MAX_VERTEX_INPUT_BINDING_STRIDE: + return limits.maxVertexInputBindingStride; + case LIMIT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT: + return limits.minUniformBufferOffsetAlignment; + case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X: + return limits.maxComputeWorkGroupCount[0]; + case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y: + return limits.maxComputeWorkGroupCount[1]; + case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z: + return limits.maxComputeWorkGroupCount[2]; + case LIMIT_MAX_COMPUTE_WORKGROUP_INVOCATIONS: + return limits.maxComputeWorkGroupInvocations; + case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X: + return limits.maxComputeWorkGroupSize[0]; + case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y: + return limits.maxComputeWorkGroupSize[1]; + case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z: + return limits.maxComputeWorkGroupSize[2]; + case LIMIT_MAX_VIEWPORT_DIMENSIONS_X: + 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(); + return subgroup_capabilities.size; + } + case LIMIT_SUBGROUP_MIN_SIZE: { + VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.min_size; + } + case LIMIT_SUBGROUP_MAX_SIZE: { + VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.max_size; + } + case LIMIT_SUBGROUP_IN_SHADERS: { + VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.supported_stages_flags_rd(); + } + case LIMIT_SUBGROUP_OPERATIONS: { + VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.supported_operations_flags_rd(); + } + case LIMIT_VRS_TEXEL_WIDTH: + return context->get_vrs_capabilities().texel_size.x; + case LIMIT_VRS_TEXEL_HEIGHT: + return context->get_vrs_capabilities().texel_size.y; + default: + ERR_FAIL_V(0); + } +} + +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); + case API_TRAIT_SHADER_CHANGE_INVALIDATION: + return (uint64_t)SHADER_CHANGE_INVALIDATION_INCOMPATIBLE_SETS_PLUS_CASCADE; + default: + return RenderingDeviceDriver::api_trait_get(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: { + return true; + } break; + default: { + return false; + } + } +} + +const RDD::MultiviewCapabilities &RenderingDeviceDriverVulkan::get_multiview_capabilities() { + return context->get_multiview_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) + "."); + + 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(); +} + +RenderingDeviceDriverVulkan::~RenderingDeviceDriverVulkan() { + while (small_allocs_pools.size()) { + HashMap<uint32_t, VmaPool>::Iterator E = small_allocs_pools.begin(); + vmaDestroyPool(allocator, E->value); + small_allocs_pools.remove(E); + } + vmaDestroyAllocator(allocator); +} diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h new file mode 100644 index 0000000000..1edee6b76e --- /dev/null +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -0,0 +1,489 @@ +/**************************************************************************/ +/* rendering_device_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_DEVICE_DRIVER_VULKAN_H +#define RENDERING_DEVICE_DRIVER_VULKAN_H + +#include "core/templates/hash_map.h" +#include "core/templates/paged_allocator.h" +#include "servers/rendering/rendering_device_driver.h" + +#ifdef DEBUG_ENABLED +#ifndef _MSC_VER +#define _DEBUG +#endif +#endif +#include "thirdparty/vulkan/vk_mem_alloc.h" + +#ifdef USE_VOLK +#include <volk.h> +#else +#include <vulkan/vulkan.h> +#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 { + /*****************/ + /**** GENERIC ****/ + /*****************/ + + VulkanContext *context = nullptr; + VkDevice vk_device = VK_NULL_HANDLE; // Owned by the context. + + /****************/ + /**** MEMORY ****/ + /****************/ + + VmaAllocator allocator = nullptr; + HashMap<uint32_t, VmaPool> small_allocs_pools; + + VmaPool _find_or_create_small_allocs_pool(uint32_t p_mem_type_index); + + /*****************/ + /**** BUFFERS ****/ + /*****************/ +private: + struct BufferInfo { + VkBuffer vk_buffer = VK_NULL_HANDLE; + struct { + VmaAllocation handle = nullptr; + uint64_t size = UINT64_MAX; + } allocation; + uint64_t size = 0; + VkBufferView vk_view = VK_NULL_HANDLE; // For texel buffers. + }; + +public: + virtual BufferID buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type) override final; + virtual bool buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) override final; + virtual void buffer_free(BufferID p_buffer) override final; + virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final; + virtual uint8_t *buffer_map(BufferID p_buffer) override final; + virtual void buffer_unmap(BufferID p_buffer) override final; + + /*****************/ + /**** TEXTURE ****/ + /*****************/ + + struct TextureInfo { + VkImageView vk_view = VK_NULL_HANDLE; + DataFormat rd_format = DATA_FORMAT_MAX; + VkImageCreateInfo vk_create_info = {}; + VkImageViewCreateInfo vk_view_create_info = {}; + struct { + VmaAllocation handle = nullptr; + VmaAllocationInfo info = {}; + } allocation; // All 0/null if just a view. + }; + + VkSampleCountFlagBits _ensure_supported_sample_count(TextureSamples p_requested_sample_count); + +public: + virtual TextureID texture_create(const TextureFormat &p_format, const TextureView &p_view) override final; + virtual TextureID texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) override final; + virtual TextureID texture_create_shared(TextureID p_original_texture, const TextureView &p_view) override final; + virtual TextureID texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) override final; + virtual void texture_free(TextureID p_texture) override final; + virtual uint64_t texture_get_allocation_size(TextureID p_texture) override final; + virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) override final; + virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) override final; + virtual void texture_unmap(TextureID p_texture) override final; + virtual BitField<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final; + + /*****************/ + /**** SAMPLER ****/ + /*****************/ +public: + virtual SamplerID sampler_create(const SamplerState &p_state) final override; + virtual void sampler_free(SamplerID p_sampler) final override; + virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) override final; + + /**********************/ + /**** VERTEX ARRAY ****/ + /**********************/ +private: + struct VertexFormatInfo { + TightLocalVector<VkVertexInputBindingDescription> vk_bindings; + TightLocalVector<VkVertexInputAttributeDescription> vk_attributes; + VkPipelineVertexInputStateCreateInfo vk_create_info = {}; + }; + +public: + virtual VertexFormatID vertex_format_create(VectorView<VertexAttribute> p_vertex_attribs) override final; + virtual void vertex_format_free(VertexFormatID p_vertex_format) override final; + + /******************/ + /**** BARRIERS ****/ + /******************/ + + virtual void command_pipeline_barrier( + CommandBufferID p_cmd_buffer, + BitField<PipelineStageBits> p_src_stages, + BitField<PipelineStageBits> p_dst_stages, + VectorView<MemoryBarrier> p_memory_barriers, + VectorView<BufferBarrier> p_buffer_barriers, + VectorView<TextureBarrier> 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. + + HashSet<CommandPoolID> secondary_cmd_pools; + HashSet<CommandBufferID> secondary_cmd_buffers; +#endif + +public: + // ----- POOL ----- + + virtual CommandPoolID command_pool_create(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 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<CommandBufferID> p_secondary_cmd_buffers) override final; + + /*********************/ + /**** FRAMEBUFFER ****/ + /*********************/ + + virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) override final; + virtual void framebuffer_free(FramebufferID p_framebuffer) override final; + + /****************/ + /**** SHADER ****/ + /****************/ +private: + struct ShaderBinary { + // Version 1: initial. + // Version 2: Added shader name. + // Version 3: Added writable. + // Version 4: 64-bit vertex input mask. + static const uint32_t VERSION = 4; + + struct DataBinding { + uint32_t type = 0; + uint32_t binding = 0; + uint32_t stages = 0; + uint32_t length = 0; // Size of arrays (in total elements), or UBOs (in bytes * total elements). + uint32_t writable = 0; + }; + + struct SpecializationConstant { + uint32_t type = 0; + uint32_t constant_id = 0; + uint32_t int_value = 0; + uint32_t stage_flags = 0; + }; + + struct Data { + uint64_t vertex_input_mask = 0; + uint32_t fragment_output_mask = 0; + uint32_t specialization_constants_count = 0; + uint32_t is_compute = 0; + uint32_t compute_local_size[3] = {}; + uint32_t set_count = 0; + uint32_t push_constant_size = 0; + uint32_t vk_push_constant_stages_mask = 0; + uint32_t stage_count = 0; + uint32_t shader_name_len = 0; + }; + }; + + struct ShaderInfo { + VkShaderStageFlags vk_push_constant_stages = 0; + TightLocalVector<VkPipelineShaderStageCreateInfo> vk_stages_create_info; + TightLocalVector<VkDescriptorSetLayout> vk_descriptor_set_layouts; + VkPipelineLayout vk_pipeline_layout = VK_NULL_HANDLE; + }; + +public: + virtual String shader_get_binary_cache_key() override final; + virtual Vector<uint8_t> shader_compile_binary_from_spirv(VectorView<ShaderStageSPIRVData> p_spirv, const String &p_shader_name) override final; + virtual ShaderID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name) override final; + virtual void shader_free(ShaderID p_shader) override final; + + /*********************/ + /**** UNIFORM SET ****/ + /*********************/ + + // Descriptor sets require allocation from a pool. + // The documentation on how to use pools properly + // is scarce, and the documentation is strange. + // + // Basically, you can mix and match pools as you + // like, but you'll run into fragmentation issues. + // Because of this, the recommended approach is to + // create a pool for every descriptor set type, as + // this prevents fragmentation. + // + // This is implemented here as a having a list of + // pools (each can contain up to 64 sets) for each + // set layout. The amount of sets for each type + // is used as the key. + +private: + static const uint32_t MAX_UNIFORM_POOL_ELEMENT = 65535; + + struct DescriptorSetPoolKey { + uint16_t uniform_type[UNIFORM_TYPE_MAX] = {}; + + bool operator<(const DescriptorSetPoolKey &p_other) const { + return memcmp(uniform_type, p_other.uniform_type, sizeof(uniform_type)) < 0; + } + }; + + using DescriptorSetPools = RBMap<DescriptorSetPoolKey, HashMap<VkDescriptorPool, uint32_t>>; + DescriptorSetPools descriptor_set_pools; + uint32_t max_descriptor_sets_per_pool = 0; + + VkDescriptorPool _descriptor_set_pool_find_or_create(const DescriptorSetPoolKey &p_key, DescriptorSetPools::Iterator *r_pool_sets_it); + void _descriptor_set_pool_unreference(DescriptorSetPools::Iterator p_pool_sets_it, VkDescriptorPool p_vk_descriptor_pool); + + struct UniformSetInfo { + VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE; + VkDescriptorPool vk_descriptor_pool = VK_NULL_HANDLE; + DescriptorSetPools::Iterator pool_sets_it = {}; + }; + +public: + virtual UniformSetID uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index) override final; + virtual void uniform_set_free(UniformSetID p_uniform_set) override final; + + // ----- COMMANDS ----- + + 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; + + /******************/ + /**** TRANSFER ****/ + /******************/ + + virtual void command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) override final; + virtual void command_copy_buffer(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, BufferID p_dst_buffer, VectorView<BufferCopyRegion> p_regions) override final; + + virtual void command_copy_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<TextureCopyRegion> p_regions) override final; + virtual void command_resolve_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap) override final; + virtual void command_clear_color_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, const Color &p_color, const TextureSubresourceRange &p_subresources) override final; + + virtual void command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<BufferTextureCopyRegion> p_regions) override final; + virtual void command_copy_texture_to_buffer(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, BufferID p_dst_buffer, VectorView<BufferTextureCopyRegion> p_regions) override final; + + /******************/ + /**** PIPELINE ****/ + /******************/ +private: + struct PipelineCacheHeader { + uint32_t magic = 0; + uint32_t data_size = 0; + uint64_t data_hash = 0; + uint32_t vendor_id = 0; + uint32_t device_id = 0; + uint32_t driver_version = 0; + uint8_t uuid[VK_UUID_SIZE] = {}; + uint8_t driver_abi = 0; + }; + + struct PipelineCache { + String file_path; + size_t current_size = 0; + Vector<uint8_t> buffer; // Header then data. + VkPipelineCache vk_cache = VK_NULL_HANDLE; + }; + + static int caching_instance_count; + PipelineCache pipelines_cache; + +public: + virtual void pipeline_free(PipelineID p_pipeline) override final; + + // ----- BINDING ----- + + virtual void command_bind_push_constants(CommandBufferID p_cmd_buffer, ShaderID p_shader, uint32_t p_first_index, VectorView<uint32_t> p_data) override final; + + // ----- CACHE ----- + + virtual bool pipeline_cache_create(const Vector<uint8_t> &p_data) override final; + virtual void pipeline_cache_free() override final; + virtual size_t pipeline_cache_query_size() override final; + virtual Vector<uint8_t> pipeline_cache_serialize() override final; + + /*******************/ + /**** RENDERING ****/ + /*******************/ + + // ----- SUBPASS ----- + + virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) override final; + virtual void render_pass_free(RenderPassID p_render_pass) override final; + + // ----- COMMANDS ----- + + virtual void command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_clear_values) override final; + virtual void command_end_render_pass(CommandBufferID p_cmd_buffer) override final; + virtual void command_next_render_subpass(CommandBufferID p_cmd_buffer, CommandBufferType p_cmd_buffer_type) override final; + virtual void command_render_set_viewport(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_viewports) override final; + virtual void command_render_set_scissor(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_scissors) override final; + virtual void command_render_clear_attachments(CommandBufferID p_cmd_buffer, VectorView<AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) override final; + + // Binding. + virtual void command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final; + virtual void command_bind_render_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final; + + // Drawing. + virtual void command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) override final; + virtual void command_render_draw_indexed(CommandBufferID p_cmd_buffer, uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index, int32_t p_vertex_offset, uint32_t p_first_instance) override final; + virtual void command_render_draw_indexed_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final; + virtual void command_render_draw_indexed_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final; + virtual void command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final; + virtual void command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final; + + // Buffer binding. + virtual void command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) override final; + virtual void command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) override final; + + // Dynamic state. + virtual void command_render_set_blend_constants(CommandBufferID p_cmd_buffer, const Color &p_constants) override final; + virtual void command_render_set_line_width(CommandBufferID p_cmd_buffer, float p_width) override final; + + // ----- PIPELINE ----- + + virtual PipelineID render_pipeline_create( + ShaderID p_shader, + VertexFormatID p_vertex_format, + RenderPrimitive p_render_primitive, + PipelineRasterizationState p_rasterization_state, + PipelineMultisampleState p_multisample_state, + PipelineDepthStencilState p_depth_stencil_state, + PipelineColorBlendState p_blend_state, + VectorView<int32_t> p_color_attachments, + BitField<PipelineDynamicStateFlags> p_dynamic_state, + RenderPassID p_render_pass, + uint32_t p_render_subpass, + VectorView<PipelineSpecializationConstant> p_specialization_constants) override final; + + /*****************/ + /**** COMPUTE ****/ + /*****************/ + + // ----- COMMANDS ----- + + // Binding. + virtual void command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final; + virtual void command_bind_compute_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final; + + // Dispatching. + virtual void command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override final; + virtual void command_compute_dispatch_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset) override final; + + // ----- PIPELINE ----- + + virtual PipelineID compute_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) override final; + + /*****************/ + /**** QUERIES ****/ + /*****************/ + + // ----- TIMESTAMP ----- + + // Basic. + virtual QueryPoolID timestamp_query_pool_create(uint32_t p_query_count) override final; + virtual void timestamp_query_pool_free(QueryPoolID p_pool_id) override final; + virtual void timestamp_query_pool_get_results(QueryPoolID p_pool_id, uint32_t p_query_count, uint64_t *r_results) override final; + virtual uint64_t timestamp_query_result_to_time(uint64_t p_result) override final; + + // Commands. + virtual void command_timestamp_query_pool_reset(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_query_count) override final; + virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) override final; + + /****************/ + /**** LABELS ****/ + /****************/ + + 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 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; + virtual uint64_t limit_get(Limit p_limit) override final; + 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; + +private: + /*********************/ + /**** BOOKKEEPING ****/ + /*********************/ + + using VersatileResource = VersatileResourceTemplate< + BufferInfo, + TextureInfo, + VertexFormatInfo, + ShaderInfo, + UniformSetInfo>; + PagedAllocator<VersatileResource> resources_allocator; + + /******************/ + +public: + RenderingDeviceDriverVulkan(VulkanContext *p_context, VkDevice p_vk_device); + virtual ~RenderingDeviceDriverVulkan(); +}; + +#endif // RENDERING_DEVICE_DRIVER_VULKAN_H diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp deleted file mode 100644 index c3a365a3b8..0000000000 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ /dev/null @@ -1,9733 +0,0 @@ -/**************************************************************************/ -/* rendering_device_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. */ -/**************************************************************************/ - -#include "rendering_device_vulkan.h" - -#include "core/config/project_settings.h" -#include "core/io/compression.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/io/marshalls.h" -#include "core/os/os.h" -#include "core/templates/hashfuncs.h" -#include "drivers/vulkan/vulkan_context.h" - -#include "thirdparty/misc/smolv.h" - -//#define FORCE_FULL_BARRIER - -static const uint32_t SMALL_ALLOCATION_MAX_SIZE = 4096; - -// Get the Vulkan object information and possible stage access types (bitwise OR'd with incoming values). -RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &r_stage_mask, VkAccessFlags &r_access_mask, BitField<BarrierMask> p_post_barrier) { - Buffer *buffer = nullptr; - if (vertex_buffer_owner.owns(p_buffer)) { - buffer = vertex_buffer_owner.get_or_null(p_buffer); - - r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; - r_access_mask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; - if (buffer->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) { - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - r_stage_mask |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - } - } - } else if (index_buffer_owner.owns(p_buffer)) { - r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; - r_access_mask |= VK_ACCESS_INDEX_READ_BIT; - buffer = index_buffer_owner.get_or_null(p_buffer); - } else if (uniform_buffer_owner.owns(p_buffer)) { - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - r_stage_mask |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - } - r_access_mask |= VK_ACCESS_UNIFORM_READ_BIT; - buffer = uniform_buffer_owner.get_or_null(p_buffer); - } else if (texture_buffer_owner.owns(p_buffer)) { - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - r_access_mask |= VK_ACCESS_SHADER_READ_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - r_access_mask |= VK_ACCESS_SHADER_READ_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - r_stage_mask |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - r_access_mask |= VK_ACCESS_SHADER_READ_BIT; - } - - buffer = &texture_buffer_owner.get_or_null(p_buffer)->buffer; - } else if (storage_buffer_owner.owns(p_buffer)) { - buffer = storage_buffer_owner.get_or_null(p_buffer); - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - r_stage_mask |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - - if (buffer->usage & VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT) { - r_stage_mask |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; - r_access_mask |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - } - } - return buffer; -} - -static void update_external_dependency_for_store(VkSubpassDependency2KHR &dependency, bool is_sampled, bool is_storage, bool is_depth) { - // Transitioning from write to read, protect the shaders that may use this next. - // Allow for copies/image layout transitions. - dependency.dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT; - dependency.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT; - - if (is_sampled) { - dependency.dstStageMask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - dependency.dstAccessMask |= VK_ACCESS_SHADER_READ_BIT; - } else if (is_storage) { - dependency.dstStageMask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - dependency.dstAccessMask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } else { - dependency.dstStageMask |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - } - - if (is_depth) { - // Depth resources have additional stages that may be interested in them. - dependency.dstStageMask |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dependency.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - } -} - -void RenderingDeviceVulkan::_add_dependency(RID p_id, RID p_depends_on) { - if (!dependency_map.has(p_depends_on)) { - dependency_map[p_depends_on] = HashSet<RID>(); - } - - dependency_map[p_depends_on].insert(p_id); - - if (!reverse_dependency_map.has(p_id)) { - reverse_dependency_map[p_id] = HashSet<RID>(); - } - - reverse_dependency_map[p_id].insert(p_depends_on); -} - -void RenderingDeviceVulkan::_free_dependencies(RID p_id) { - // Direct dependencies must be freed. - - HashMap<RID, HashSet<RID>>::Iterator E = dependency_map.find(p_id); - if (E) { - while (E->value.size()) { - free(*E->value.begin()); - } - dependency_map.remove(E); - } - - // Reverse dependencies must be unreferenced. - E = reverse_dependency_map.find(p_id); - - if (E) { - for (const RID &F : E->value) { - HashMap<RID, HashSet<RID>>::Iterator G = dependency_map.find(F); - ERR_CONTINUE(!G); - ERR_CONTINUE(!G->value.has(p_id)); - G->value.erase(p_id); - } - - reverse_dependency_map.remove(E); - } -} - -const VkFormat RenderingDeviceVulkan::vulkan_formats[RenderingDevice::DATA_FORMAT_MAX] = { - VK_FORMAT_R4G4_UNORM_PACK8, - VK_FORMAT_R4G4B4A4_UNORM_PACK16, - VK_FORMAT_B4G4R4A4_UNORM_PACK16, - VK_FORMAT_R5G6B5_UNORM_PACK16, - VK_FORMAT_B5G6R5_UNORM_PACK16, - VK_FORMAT_R5G5B5A1_UNORM_PACK16, - VK_FORMAT_B5G5R5A1_UNORM_PACK16, - VK_FORMAT_A1R5G5B5_UNORM_PACK16, - VK_FORMAT_R8_UNORM, - VK_FORMAT_R8_SNORM, - VK_FORMAT_R8_USCALED, - VK_FORMAT_R8_SSCALED, - VK_FORMAT_R8_UINT, - VK_FORMAT_R8_SINT, - VK_FORMAT_R8_SRGB, - VK_FORMAT_R8G8_UNORM, - VK_FORMAT_R8G8_SNORM, - VK_FORMAT_R8G8_USCALED, - VK_FORMAT_R8G8_SSCALED, - VK_FORMAT_R8G8_UINT, - VK_FORMAT_R8G8_SINT, - VK_FORMAT_R8G8_SRGB, - VK_FORMAT_R8G8B8_UNORM, - VK_FORMAT_R8G8B8_SNORM, - VK_FORMAT_R8G8B8_USCALED, - VK_FORMAT_R8G8B8_SSCALED, - VK_FORMAT_R8G8B8_UINT, - VK_FORMAT_R8G8B8_SINT, - VK_FORMAT_R8G8B8_SRGB, - VK_FORMAT_B8G8R8_UNORM, - VK_FORMAT_B8G8R8_SNORM, - VK_FORMAT_B8G8R8_USCALED, - VK_FORMAT_B8G8R8_SSCALED, - VK_FORMAT_B8G8R8_UINT, - VK_FORMAT_B8G8R8_SINT, - VK_FORMAT_B8G8R8_SRGB, - VK_FORMAT_R8G8B8A8_UNORM, - VK_FORMAT_R8G8B8A8_SNORM, - VK_FORMAT_R8G8B8A8_USCALED, - VK_FORMAT_R8G8B8A8_SSCALED, - VK_FORMAT_R8G8B8A8_UINT, - VK_FORMAT_R8G8B8A8_SINT, - VK_FORMAT_R8G8B8A8_SRGB, - VK_FORMAT_B8G8R8A8_UNORM, - VK_FORMAT_B8G8R8A8_SNORM, - VK_FORMAT_B8G8R8A8_USCALED, - VK_FORMAT_B8G8R8A8_SSCALED, - VK_FORMAT_B8G8R8A8_UINT, - VK_FORMAT_B8G8R8A8_SINT, - VK_FORMAT_B8G8R8A8_SRGB, - VK_FORMAT_A8B8G8R8_UNORM_PACK32, - VK_FORMAT_A8B8G8R8_SNORM_PACK32, - VK_FORMAT_A8B8G8R8_USCALED_PACK32, - VK_FORMAT_A8B8G8R8_SSCALED_PACK32, - VK_FORMAT_A8B8G8R8_UINT_PACK32, - VK_FORMAT_A8B8G8R8_SINT_PACK32, - VK_FORMAT_A8B8G8R8_SRGB_PACK32, - VK_FORMAT_A2R10G10B10_UNORM_PACK32, - VK_FORMAT_A2R10G10B10_SNORM_PACK32, - VK_FORMAT_A2R10G10B10_USCALED_PACK32, - VK_FORMAT_A2R10G10B10_SSCALED_PACK32, - VK_FORMAT_A2R10G10B10_UINT_PACK32, - VK_FORMAT_A2R10G10B10_SINT_PACK32, - VK_FORMAT_A2B10G10R10_UNORM_PACK32, - VK_FORMAT_A2B10G10R10_SNORM_PACK32, - VK_FORMAT_A2B10G10R10_USCALED_PACK32, - VK_FORMAT_A2B10G10R10_SSCALED_PACK32, - VK_FORMAT_A2B10G10R10_UINT_PACK32, - VK_FORMAT_A2B10G10R10_SINT_PACK32, - VK_FORMAT_R16_UNORM, - VK_FORMAT_R16_SNORM, - VK_FORMAT_R16_USCALED, - VK_FORMAT_R16_SSCALED, - VK_FORMAT_R16_UINT, - VK_FORMAT_R16_SINT, - VK_FORMAT_R16_SFLOAT, - VK_FORMAT_R16G16_UNORM, - VK_FORMAT_R16G16_SNORM, - VK_FORMAT_R16G16_USCALED, - VK_FORMAT_R16G16_SSCALED, - VK_FORMAT_R16G16_UINT, - VK_FORMAT_R16G16_SINT, - VK_FORMAT_R16G16_SFLOAT, - VK_FORMAT_R16G16B16_UNORM, - VK_FORMAT_R16G16B16_SNORM, - VK_FORMAT_R16G16B16_USCALED, - VK_FORMAT_R16G16B16_SSCALED, - VK_FORMAT_R16G16B16_UINT, - VK_FORMAT_R16G16B16_SINT, - VK_FORMAT_R16G16B16_SFLOAT, - VK_FORMAT_R16G16B16A16_UNORM, - VK_FORMAT_R16G16B16A16_SNORM, - VK_FORMAT_R16G16B16A16_USCALED, - VK_FORMAT_R16G16B16A16_SSCALED, - VK_FORMAT_R16G16B16A16_UINT, - VK_FORMAT_R16G16B16A16_SINT, - VK_FORMAT_R16G16B16A16_SFLOAT, - VK_FORMAT_R32_UINT, - VK_FORMAT_R32_SINT, - VK_FORMAT_R32_SFLOAT, - VK_FORMAT_R32G32_UINT, - VK_FORMAT_R32G32_SINT, - VK_FORMAT_R32G32_SFLOAT, - VK_FORMAT_R32G32B32_UINT, - VK_FORMAT_R32G32B32_SINT, - VK_FORMAT_R32G32B32_SFLOAT, - VK_FORMAT_R32G32B32A32_UINT, - VK_FORMAT_R32G32B32A32_SINT, - VK_FORMAT_R32G32B32A32_SFLOAT, - VK_FORMAT_R64_UINT, - VK_FORMAT_R64_SINT, - VK_FORMAT_R64_SFLOAT, - VK_FORMAT_R64G64_UINT, - VK_FORMAT_R64G64_SINT, - VK_FORMAT_R64G64_SFLOAT, - VK_FORMAT_R64G64B64_UINT, - VK_FORMAT_R64G64B64_SINT, - VK_FORMAT_R64G64B64_SFLOAT, - VK_FORMAT_R64G64B64A64_UINT, - VK_FORMAT_R64G64B64A64_SINT, - VK_FORMAT_R64G64B64A64_SFLOAT, - VK_FORMAT_B10G11R11_UFLOAT_PACK32, - VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, - VK_FORMAT_D16_UNORM, - VK_FORMAT_X8_D24_UNORM_PACK32, - VK_FORMAT_D32_SFLOAT, - VK_FORMAT_S8_UINT, - VK_FORMAT_D16_UNORM_S8_UINT, - VK_FORMAT_D24_UNORM_S8_UINT, - VK_FORMAT_D32_SFLOAT_S8_UINT, - VK_FORMAT_BC1_RGB_UNORM_BLOCK, - VK_FORMAT_BC1_RGB_SRGB_BLOCK, - VK_FORMAT_BC1_RGBA_UNORM_BLOCK, - VK_FORMAT_BC1_RGBA_SRGB_BLOCK, - VK_FORMAT_BC2_UNORM_BLOCK, - VK_FORMAT_BC2_SRGB_BLOCK, - VK_FORMAT_BC3_UNORM_BLOCK, - VK_FORMAT_BC3_SRGB_BLOCK, - VK_FORMAT_BC4_UNORM_BLOCK, - VK_FORMAT_BC4_SNORM_BLOCK, - VK_FORMAT_BC5_UNORM_BLOCK, - VK_FORMAT_BC5_SNORM_BLOCK, - VK_FORMAT_BC6H_UFLOAT_BLOCK, - VK_FORMAT_BC6H_SFLOAT_BLOCK, - VK_FORMAT_BC7_UNORM_BLOCK, - VK_FORMAT_BC7_SRGB_BLOCK, - VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, - VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, - VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, - VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, - VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, - VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, - VK_FORMAT_EAC_R11_UNORM_BLOCK, - VK_FORMAT_EAC_R11_SNORM_BLOCK, - VK_FORMAT_EAC_R11G11_UNORM_BLOCK, - VK_FORMAT_EAC_R11G11_SNORM_BLOCK, - VK_FORMAT_ASTC_4x4_UNORM_BLOCK, - VK_FORMAT_ASTC_4x4_SRGB_BLOCK, - VK_FORMAT_ASTC_5x4_UNORM_BLOCK, - VK_FORMAT_ASTC_5x4_SRGB_BLOCK, - VK_FORMAT_ASTC_5x5_UNORM_BLOCK, - VK_FORMAT_ASTC_5x5_SRGB_BLOCK, - VK_FORMAT_ASTC_6x5_UNORM_BLOCK, - VK_FORMAT_ASTC_6x5_SRGB_BLOCK, - VK_FORMAT_ASTC_6x6_UNORM_BLOCK, - VK_FORMAT_ASTC_6x6_SRGB_BLOCK, - VK_FORMAT_ASTC_8x5_UNORM_BLOCK, - VK_FORMAT_ASTC_8x5_SRGB_BLOCK, - VK_FORMAT_ASTC_8x6_UNORM_BLOCK, - VK_FORMAT_ASTC_8x6_SRGB_BLOCK, - VK_FORMAT_ASTC_8x8_UNORM_BLOCK, - VK_FORMAT_ASTC_8x8_SRGB_BLOCK, - VK_FORMAT_ASTC_10x5_UNORM_BLOCK, - VK_FORMAT_ASTC_10x5_SRGB_BLOCK, - VK_FORMAT_ASTC_10x6_UNORM_BLOCK, - VK_FORMAT_ASTC_10x6_SRGB_BLOCK, - VK_FORMAT_ASTC_10x8_UNORM_BLOCK, - VK_FORMAT_ASTC_10x8_SRGB_BLOCK, - VK_FORMAT_ASTC_10x10_UNORM_BLOCK, - VK_FORMAT_ASTC_10x10_SRGB_BLOCK, - VK_FORMAT_ASTC_12x10_UNORM_BLOCK, - VK_FORMAT_ASTC_12x10_SRGB_BLOCK, - VK_FORMAT_ASTC_12x12_UNORM_BLOCK, - VK_FORMAT_ASTC_12x12_SRGB_BLOCK, - VK_FORMAT_G8B8G8R8_422_UNORM, - VK_FORMAT_B8G8R8G8_422_UNORM, - VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, - VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, - VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, - VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, - VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, - VK_FORMAT_R10X6_UNORM_PACK16, - VK_FORMAT_R10X6G10X6_UNORM_2PACK16, - VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, - VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, - VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, - VK_FORMAT_R12X4_UNORM_PACK16, - VK_FORMAT_R12X4G12X4_UNORM_2PACK16, - VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, - VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, - VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, - VK_FORMAT_G16B16G16R16_422_UNORM, - VK_FORMAT_B16G16R16G16_422_UNORM, - VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, - VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, - VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, - VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, - VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, -}; - -const char *RenderingDeviceVulkan::named_formats[RenderingDevice::DATA_FORMAT_MAX] = { - "R4G4_Unorm_Pack8", - "R4G4B4A4_Unorm_Pack16", - "B4G4R4A4_Unorm_Pack16", - "R5G6B5_Unorm_Pack16", - "B5G6R5_Unorm_Pack16", - "R5G5B5A1_Unorm_Pack16", - "B5G5R5A1_Unorm_Pack16", - "A1R5G5B5_Unorm_Pack16", - "R8_Unorm", - "R8_Snorm", - "R8_Uscaled", - "R8_Sscaled", - "R8_Uint", - "R8_Sint", - "R8_Srgb", - "R8G8_Unorm", - "R8G8_Snorm", - "R8G8_Uscaled", - "R8G8_Sscaled", - "R8G8_Uint", - "R8G8_Sint", - "R8G8_Srgb", - "R8G8B8_Unorm", - "R8G8B8_Snorm", - "R8G8B8_Uscaled", - "R8G8B8_Sscaled", - "R8G8B8_Uint", - "R8G8B8_Sint", - "R8G8B8_Srgb", - "B8G8R8_Unorm", - "B8G8R8_Snorm", - "B8G8R8_Uscaled", - "B8G8R8_Sscaled", - "B8G8R8_Uint", - "B8G8R8_Sint", - "B8G8R8_Srgb", - "R8G8B8A8_Unorm", - "R8G8B8A8_Snorm", - "R8G8B8A8_Uscaled", - "R8G8B8A8_Sscaled", - "R8G8B8A8_Uint", - "R8G8B8A8_Sint", - "R8G8B8A8_Srgb", - "B8G8R8A8_Unorm", - "B8G8R8A8_Snorm", - "B8G8R8A8_Uscaled", - "B8G8R8A8_Sscaled", - "B8G8R8A8_Uint", - "B8G8R8A8_Sint", - "B8G8R8A8_Srgb", - "A8B8G8R8_Unorm_Pack32", - "A8B8G8R8_Snorm_Pack32", - "A8B8G8R8_Uscaled_Pack32", - "A8B8G8R8_Sscaled_Pack32", - "A8B8G8R8_Uint_Pack32", - "A8B8G8R8_Sint_Pack32", - "A8B8G8R8_Srgb_Pack32", - "A2R10G10B10_Unorm_Pack32", - "A2R10G10B10_Snorm_Pack32", - "A2R10G10B10_Uscaled_Pack32", - "A2R10G10B10_Sscaled_Pack32", - "A2R10G10B10_Uint_Pack32", - "A2R10G10B10_Sint_Pack32", - "A2B10G10R10_Unorm_Pack32", - "A2B10G10R10_Snorm_Pack32", - "A2B10G10R10_Uscaled_Pack32", - "A2B10G10R10_Sscaled_Pack32", - "A2B10G10R10_Uint_Pack32", - "A2B10G10R10_Sint_Pack32", - "R16_Unorm", - "R16_Snorm", - "R16_Uscaled", - "R16_Sscaled", - "R16_Uint", - "R16_Sint", - "R16_Sfloat", - "R16G16_Unorm", - "R16G16_Snorm", - "R16G16_Uscaled", - "R16G16_Sscaled", - "R16G16_Uint", - "R16G16_Sint", - "R16G16_Sfloat", - "R16G16B16_Unorm", - "R16G16B16_Snorm", - "R16G16B16_Uscaled", - "R16G16B16_Sscaled", - "R16G16B16_Uint", - "R16G16B16_Sint", - "R16G16B16_Sfloat", - "R16G16B16A16_Unorm", - "R16G16B16A16_Snorm", - "R16G16B16A16_Uscaled", - "R16G16B16A16_Sscaled", - "R16G16B16A16_Uint", - "R16G16B16A16_Sint", - "R16G16B16A16_Sfloat", - "R32_Uint", - "R32_Sint", - "R32_Sfloat", - "R32G32_Uint", - "R32G32_Sint", - "R32G32_Sfloat", - "R32G32B32_Uint", - "R32G32B32_Sint", - "R32G32B32_Sfloat", - "R32G32B32A32_Uint", - "R32G32B32A32_Sint", - "R32G32B32A32_Sfloat", - "R64_Uint", - "R64_Sint", - "R64_Sfloat", - "R64G64_Uint", - "R64G64_Sint", - "R64G64_Sfloat", - "R64G64B64_Uint", - "R64G64B64_Sint", - "R64G64B64_Sfloat", - "R64G64B64A64_Uint", - "R64G64B64A64_Sint", - "R64G64B64A64_Sfloat", - "B10G11R11_Ufloat_Pack32", - "E5B9G9R9_Ufloat_Pack32", - "D16_Unorm", - "X8_D24_Unorm_Pack32", - "D32_Sfloat", - "S8_Uint", - "D16_Unorm_S8_Uint", - "D24_Unorm_S8_Uint", - "D32_Sfloat_S8_Uint", - "Bc1_Rgb_Unorm_Block", - "Bc1_Rgb_Srgb_Block", - "Bc1_Rgba_Unorm_Block", - "Bc1_Rgba_Srgb_Block", - "Bc2_Unorm_Block", - "Bc2_Srgb_Block", - "Bc3_Unorm_Block", - "Bc3_Srgb_Block", - "Bc4_Unorm_Block", - "Bc4_Snorm_Block", - "Bc5_Unorm_Block", - "Bc5_Snorm_Block", - "Bc6H_Ufloat_Block", - "Bc6H_Sfloat_Block", - "Bc7_Unorm_Block", - "Bc7_Srgb_Block", - "Etc2_R8G8B8_Unorm_Block", - "Etc2_R8G8B8_Srgb_Block", - "Etc2_R8G8B8A1_Unorm_Block", - "Etc2_R8G8B8A1_Srgb_Block", - "Etc2_R8G8B8A8_Unorm_Block", - "Etc2_R8G8B8A8_Srgb_Block", - "Eac_R11_Unorm_Block", - "Eac_R11_Snorm_Block", - "Eac_R11G11_Unorm_Block", - "Eac_R11G11_Snorm_Block", - "Astc_4X4_Unorm_Block", - "Astc_4X4_Srgb_Block", - "Astc_5X4_Unorm_Block", - "Astc_5X4_Srgb_Block", - "Astc_5X5_Unorm_Block", - "Astc_5X5_Srgb_Block", - "Astc_6X5_Unorm_Block", - "Astc_6X5_Srgb_Block", - "Astc_6X6_Unorm_Block", - "Astc_6X6_Srgb_Block", - "Astc_8X5_Unorm_Block", - "Astc_8X5_Srgb_Block", - "Astc_8X6_Unorm_Block", - "Astc_8X6_Srgb_Block", - "Astc_8X8_Unorm_Block", - "Astc_8X8_Srgb_Block", - "Astc_10X5_Unorm_Block", - "Astc_10X5_Srgb_Block", - "Astc_10X6_Unorm_Block", - "Astc_10X6_Srgb_Block", - "Astc_10X8_Unorm_Block", - "Astc_10X8_Srgb_Block", - "Astc_10X10_Unorm_Block", - "Astc_10X10_Srgb_Block", - "Astc_12X10_Unorm_Block", - "Astc_12X10_Srgb_Block", - "Astc_12X12_Unorm_Block", - "Astc_12X12_Srgb_Block", - "G8B8G8R8_422_Unorm", - "B8G8R8G8_422_Unorm", - "G8_B8_R8_3Plane_420_Unorm", - "G8_B8R8_2Plane_420_Unorm", - "G8_B8_R8_3Plane_422_Unorm", - "G8_B8R8_2Plane_422_Unorm", - "G8_B8_R8_3Plane_444_Unorm", - "R10X6_Unorm_Pack16", - "R10X6G10X6_Unorm_2Pack16", - "R10X6G10X6B10X6A10X6_Unorm_4Pack16", - "G10X6B10X6G10X6R10X6_422_Unorm_4Pack16", - "B10X6G10X6R10X6G10X6_422_Unorm_4Pack16", - "G10X6_B10X6_R10X6_3Plane_420_Unorm_3Pack16", - "G10X6_B10X6R10X6_2Plane_420_Unorm_3Pack16", - "G10X6_B10X6_R10X6_3Plane_422_Unorm_3Pack16", - "G10X6_B10X6R10X6_2Plane_422_Unorm_3Pack16", - "G10X6_B10X6_R10X6_3Plane_444_Unorm_3Pack16", - "R12X4_Unorm_Pack16", - "R12X4G12X4_Unorm_2Pack16", - "R12X4G12X4B12X4A12X4_Unorm_4Pack16", - "G12X4B12X4G12X4R12X4_422_Unorm_4Pack16", - "B12X4G12X4R12X4G12X4_422_Unorm_4Pack16", - "G12X4_B12X4_R12X4_3Plane_420_Unorm_3Pack16", - "G12X4_B12X4R12X4_2Plane_420_Unorm_3Pack16", - "G12X4_B12X4_R12X4_3Plane_422_Unorm_3Pack16", - "G12X4_B12X4R12X4_2Plane_422_Unorm_3Pack16", - "G12X4_B12X4_R12X4_3Plane_444_Unorm_3Pack16", - "G16B16G16R16_422_Unorm", - "B16G16R16G16_422_Unorm", - "G16_B16_R16_3Plane_420_Unorm", - "G16_B16R16_2Plane_420_Unorm", - "G16_B16_R16_3Plane_422_Unorm", - "G16_B16R16_2Plane_422_Unorm", - "G16_B16_R16_3Plane_444_Unorm", -}; - -int RenderingDeviceVulkan::get_format_vertex_size(DataFormat p_format) { - switch (p_format) { - case DATA_FORMAT_R8_UNORM: - case DATA_FORMAT_R8_SNORM: - case DATA_FORMAT_R8_UINT: - case DATA_FORMAT_R8_SINT: - case DATA_FORMAT_R8G8_UNORM: - case DATA_FORMAT_R8G8_SNORM: - case DATA_FORMAT_R8G8_UINT: - case DATA_FORMAT_R8G8_SINT: - case DATA_FORMAT_R8G8B8_UNORM: - case DATA_FORMAT_R8G8B8_SNORM: - case DATA_FORMAT_R8G8B8_UINT: - case DATA_FORMAT_R8G8B8_SINT: - case DATA_FORMAT_B8G8R8_UNORM: - case DATA_FORMAT_B8G8R8_SNORM: - case DATA_FORMAT_B8G8R8_UINT: - case DATA_FORMAT_B8G8R8_SINT: - case DATA_FORMAT_R8G8B8A8_UNORM: - case DATA_FORMAT_R8G8B8A8_SNORM: - case DATA_FORMAT_R8G8B8A8_UINT: - case DATA_FORMAT_R8G8B8A8_SINT: - case DATA_FORMAT_B8G8R8A8_UNORM: - case DATA_FORMAT_B8G8R8A8_SNORM: - case DATA_FORMAT_B8G8R8A8_UINT: - case DATA_FORMAT_B8G8R8A8_SINT: - case DATA_FORMAT_A2B10G10R10_UNORM_PACK32: - return 4; - case DATA_FORMAT_R16_UNORM: - case DATA_FORMAT_R16_SNORM: - case DATA_FORMAT_R16_UINT: - case DATA_FORMAT_R16_SINT: - case DATA_FORMAT_R16_SFLOAT: - return 4; - case DATA_FORMAT_R16G16_UNORM: - case DATA_FORMAT_R16G16_SNORM: - case DATA_FORMAT_R16G16_UINT: - case DATA_FORMAT_R16G16_SINT: - case DATA_FORMAT_R16G16_SFLOAT: - return 4; - case DATA_FORMAT_R16G16B16_UNORM: - case DATA_FORMAT_R16G16B16_SNORM: - case DATA_FORMAT_R16G16B16_UINT: - case DATA_FORMAT_R16G16B16_SINT: - case DATA_FORMAT_R16G16B16_SFLOAT: - return 8; - case DATA_FORMAT_R16G16B16A16_UNORM: - case DATA_FORMAT_R16G16B16A16_SNORM: - case DATA_FORMAT_R16G16B16A16_UINT: - case DATA_FORMAT_R16G16B16A16_SINT: - case DATA_FORMAT_R16G16B16A16_SFLOAT: - return 8; - case DATA_FORMAT_R32_UINT: - case DATA_FORMAT_R32_SINT: - case DATA_FORMAT_R32_SFLOAT: - return 4; - case DATA_FORMAT_R32G32_UINT: - case DATA_FORMAT_R32G32_SINT: - case DATA_FORMAT_R32G32_SFLOAT: - return 8; - case DATA_FORMAT_R32G32B32_UINT: - case DATA_FORMAT_R32G32B32_SINT: - case DATA_FORMAT_R32G32B32_SFLOAT: - return 12; - case DATA_FORMAT_R32G32B32A32_UINT: - case DATA_FORMAT_R32G32B32A32_SINT: - case DATA_FORMAT_R32G32B32A32_SFLOAT: - return 16; - case DATA_FORMAT_R64_UINT: - case DATA_FORMAT_R64_SINT: - case DATA_FORMAT_R64_SFLOAT: - return 8; - case DATA_FORMAT_R64G64_UINT: - case DATA_FORMAT_R64G64_SINT: - case DATA_FORMAT_R64G64_SFLOAT: - return 16; - case DATA_FORMAT_R64G64B64_UINT: - case DATA_FORMAT_R64G64B64_SINT: - case DATA_FORMAT_R64G64B64_SFLOAT: - return 24; - case DATA_FORMAT_R64G64B64A64_UINT: - case DATA_FORMAT_R64G64B64A64_SINT: - case DATA_FORMAT_R64G64B64A64_SFLOAT: - return 32; - default: - return 0; - } -} - -uint32_t RenderingDeviceVulkan::get_image_format_pixel_size(DataFormat p_format) { - switch (p_format) { - case DATA_FORMAT_R4G4_UNORM_PACK8: - return 1; - case DATA_FORMAT_R4G4B4A4_UNORM_PACK16: - case DATA_FORMAT_B4G4R4A4_UNORM_PACK16: - case DATA_FORMAT_R5G6B5_UNORM_PACK16: - case DATA_FORMAT_B5G6R5_UNORM_PACK16: - case DATA_FORMAT_R5G5B5A1_UNORM_PACK16: - case DATA_FORMAT_B5G5R5A1_UNORM_PACK16: - case DATA_FORMAT_A1R5G5B5_UNORM_PACK16: - return 2; - case DATA_FORMAT_R8_UNORM: - case DATA_FORMAT_R8_SNORM: - case DATA_FORMAT_R8_USCALED: - case DATA_FORMAT_R8_SSCALED: - case DATA_FORMAT_R8_UINT: - case DATA_FORMAT_R8_SINT: - case DATA_FORMAT_R8_SRGB: - return 1; - case DATA_FORMAT_R8G8_UNORM: - case DATA_FORMAT_R8G8_SNORM: - case DATA_FORMAT_R8G8_USCALED: - case DATA_FORMAT_R8G8_SSCALED: - case DATA_FORMAT_R8G8_UINT: - case DATA_FORMAT_R8G8_SINT: - case DATA_FORMAT_R8G8_SRGB: - return 2; - case DATA_FORMAT_R8G8B8_UNORM: - case DATA_FORMAT_R8G8B8_SNORM: - case DATA_FORMAT_R8G8B8_USCALED: - case DATA_FORMAT_R8G8B8_SSCALED: - case DATA_FORMAT_R8G8B8_UINT: - case DATA_FORMAT_R8G8B8_SINT: - case DATA_FORMAT_R8G8B8_SRGB: - case DATA_FORMAT_B8G8R8_UNORM: - case DATA_FORMAT_B8G8R8_SNORM: - case DATA_FORMAT_B8G8R8_USCALED: - case DATA_FORMAT_B8G8R8_SSCALED: - case DATA_FORMAT_B8G8R8_UINT: - case DATA_FORMAT_B8G8R8_SINT: - case DATA_FORMAT_B8G8R8_SRGB: - return 3; - case DATA_FORMAT_R8G8B8A8_UNORM: - case DATA_FORMAT_R8G8B8A8_SNORM: - case DATA_FORMAT_R8G8B8A8_USCALED: - case DATA_FORMAT_R8G8B8A8_SSCALED: - case DATA_FORMAT_R8G8B8A8_UINT: - case DATA_FORMAT_R8G8B8A8_SINT: - case DATA_FORMAT_R8G8B8A8_SRGB: - case DATA_FORMAT_B8G8R8A8_UNORM: - case DATA_FORMAT_B8G8R8A8_SNORM: - case DATA_FORMAT_B8G8R8A8_USCALED: - case DATA_FORMAT_B8G8R8A8_SSCALED: - case DATA_FORMAT_B8G8R8A8_UINT: - case DATA_FORMAT_B8G8R8A8_SINT: - case DATA_FORMAT_B8G8R8A8_SRGB: - return 4; - case DATA_FORMAT_A8B8G8R8_UNORM_PACK32: - case DATA_FORMAT_A8B8G8R8_SNORM_PACK32: - case DATA_FORMAT_A8B8G8R8_USCALED_PACK32: - case DATA_FORMAT_A8B8G8R8_SSCALED_PACK32: - case DATA_FORMAT_A8B8G8R8_UINT_PACK32: - case DATA_FORMAT_A8B8G8R8_SINT_PACK32: - case DATA_FORMAT_A8B8G8R8_SRGB_PACK32: - case DATA_FORMAT_A2R10G10B10_UNORM_PACK32: - case DATA_FORMAT_A2R10G10B10_SNORM_PACK32: - case DATA_FORMAT_A2R10G10B10_USCALED_PACK32: - case DATA_FORMAT_A2R10G10B10_SSCALED_PACK32: - case DATA_FORMAT_A2R10G10B10_UINT_PACK32: - case DATA_FORMAT_A2R10G10B10_SINT_PACK32: - case DATA_FORMAT_A2B10G10R10_UNORM_PACK32: - case DATA_FORMAT_A2B10G10R10_SNORM_PACK32: - case DATA_FORMAT_A2B10G10R10_USCALED_PACK32: - case DATA_FORMAT_A2B10G10R10_SSCALED_PACK32: - case DATA_FORMAT_A2B10G10R10_UINT_PACK32: - case DATA_FORMAT_A2B10G10R10_SINT_PACK32: - return 4; - case DATA_FORMAT_R16_UNORM: - case DATA_FORMAT_R16_SNORM: - case DATA_FORMAT_R16_USCALED: - case DATA_FORMAT_R16_SSCALED: - case DATA_FORMAT_R16_UINT: - case DATA_FORMAT_R16_SINT: - case DATA_FORMAT_R16_SFLOAT: - return 2; - case DATA_FORMAT_R16G16_UNORM: - case DATA_FORMAT_R16G16_SNORM: - case DATA_FORMAT_R16G16_USCALED: - case DATA_FORMAT_R16G16_SSCALED: - case DATA_FORMAT_R16G16_UINT: - case DATA_FORMAT_R16G16_SINT: - case DATA_FORMAT_R16G16_SFLOAT: - return 4; - case DATA_FORMAT_R16G16B16_UNORM: - case DATA_FORMAT_R16G16B16_SNORM: - case DATA_FORMAT_R16G16B16_USCALED: - case DATA_FORMAT_R16G16B16_SSCALED: - case DATA_FORMAT_R16G16B16_UINT: - case DATA_FORMAT_R16G16B16_SINT: - case DATA_FORMAT_R16G16B16_SFLOAT: - return 6; - case DATA_FORMAT_R16G16B16A16_UNORM: - case DATA_FORMAT_R16G16B16A16_SNORM: - case DATA_FORMAT_R16G16B16A16_USCALED: - case DATA_FORMAT_R16G16B16A16_SSCALED: - case DATA_FORMAT_R16G16B16A16_UINT: - case DATA_FORMAT_R16G16B16A16_SINT: - case DATA_FORMAT_R16G16B16A16_SFLOAT: - return 8; - case DATA_FORMAT_R32_UINT: - case DATA_FORMAT_R32_SINT: - case DATA_FORMAT_R32_SFLOAT: - return 4; - case DATA_FORMAT_R32G32_UINT: - case DATA_FORMAT_R32G32_SINT: - case DATA_FORMAT_R32G32_SFLOAT: - return 8; - case DATA_FORMAT_R32G32B32_UINT: - case DATA_FORMAT_R32G32B32_SINT: - case DATA_FORMAT_R32G32B32_SFLOAT: - return 12; - case DATA_FORMAT_R32G32B32A32_UINT: - case DATA_FORMAT_R32G32B32A32_SINT: - case DATA_FORMAT_R32G32B32A32_SFLOAT: - return 16; - case DATA_FORMAT_R64_UINT: - case DATA_FORMAT_R64_SINT: - case DATA_FORMAT_R64_SFLOAT: - return 8; - case DATA_FORMAT_R64G64_UINT: - case DATA_FORMAT_R64G64_SINT: - case DATA_FORMAT_R64G64_SFLOAT: - return 16; - case DATA_FORMAT_R64G64B64_UINT: - case DATA_FORMAT_R64G64B64_SINT: - case DATA_FORMAT_R64G64B64_SFLOAT: - return 24; - case DATA_FORMAT_R64G64B64A64_UINT: - case DATA_FORMAT_R64G64B64A64_SINT: - case DATA_FORMAT_R64G64B64A64_SFLOAT: - return 32; - case DATA_FORMAT_B10G11R11_UFLOAT_PACK32: - case DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32: - return 4; - case DATA_FORMAT_D16_UNORM: - return 2; - case DATA_FORMAT_X8_D24_UNORM_PACK32: - return 4; - case DATA_FORMAT_D32_SFLOAT: - return 4; - case DATA_FORMAT_S8_UINT: - return 1; - case DATA_FORMAT_D16_UNORM_S8_UINT: - return 4; - case DATA_FORMAT_D24_UNORM_S8_UINT: - return 4; - case DATA_FORMAT_D32_SFLOAT_S8_UINT: - return 5; // ? - case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: - case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: - case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: - case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: - case DATA_FORMAT_BC2_UNORM_BLOCK: - case DATA_FORMAT_BC2_SRGB_BLOCK: - case DATA_FORMAT_BC3_UNORM_BLOCK: - case DATA_FORMAT_BC3_SRGB_BLOCK: - case DATA_FORMAT_BC4_UNORM_BLOCK: - case DATA_FORMAT_BC4_SNORM_BLOCK: - case DATA_FORMAT_BC5_UNORM_BLOCK: - case DATA_FORMAT_BC5_SNORM_BLOCK: - case DATA_FORMAT_BC6H_UFLOAT_BLOCK: - case DATA_FORMAT_BC6H_SFLOAT_BLOCK: - case DATA_FORMAT_BC7_UNORM_BLOCK: - case DATA_FORMAT_BC7_SRGB_BLOCK: - return 1; - case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: - return 1; - case DATA_FORMAT_EAC_R11_UNORM_BLOCK: - case DATA_FORMAT_EAC_R11_SNORM_BLOCK: - case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: - case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: - return 1; - case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: - case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: - case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: - case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: - case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK: - case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK: - case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: - case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK: - case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK: - case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: - case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: - case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: - return 1; - case DATA_FORMAT_G8B8G8R8_422_UNORM: - case DATA_FORMAT_B8G8R8G8_422_UNORM: - return 4; - case DATA_FORMAT_G8_B8_R8_3PLANE_420_UNORM: - case DATA_FORMAT_G8_B8R8_2PLANE_420_UNORM: - case DATA_FORMAT_G8_B8_R8_3PLANE_422_UNORM: - case DATA_FORMAT_G8_B8R8_2PLANE_422_UNORM: - case DATA_FORMAT_G8_B8_R8_3PLANE_444_UNORM: - return 4; - case DATA_FORMAT_R10X6_UNORM_PACK16: - case DATA_FORMAT_R10X6G10X6_UNORM_2PACK16: - case DATA_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: - case DATA_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16: - case DATA_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16: - case DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16: - case DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: - case DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16: - case DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16: - case DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16: - case DATA_FORMAT_R12X4_UNORM_PACK16: - case DATA_FORMAT_R12X4G12X4_UNORM_2PACK16: - case DATA_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: - case DATA_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16: - case DATA_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16: - case DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16: - case DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16: - case DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16: - case DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16: - case DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16: - return 2; - case DATA_FORMAT_G16B16G16R16_422_UNORM: - case DATA_FORMAT_B16G16R16G16_422_UNORM: - case DATA_FORMAT_G16_B16_R16_3PLANE_420_UNORM: - case DATA_FORMAT_G16_B16R16_2PLANE_420_UNORM: - case DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM: - case DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM: - case DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM: - return 8; - default: { - ERR_PRINT("Format not handled, bug"); - } - } - - return 1; -} - -// https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.pdf - -void RenderingDeviceVulkan::get_compressed_image_format_block_dimensions(DataFormat p_format, uint32_t &r_w, uint32_t &r_h) { - switch (p_format) { - case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: - case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: - case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: - case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: - case DATA_FORMAT_BC2_UNORM_BLOCK: - case DATA_FORMAT_BC2_SRGB_BLOCK: - case DATA_FORMAT_BC3_UNORM_BLOCK: - case DATA_FORMAT_BC3_SRGB_BLOCK: - case DATA_FORMAT_BC4_UNORM_BLOCK: - case DATA_FORMAT_BC4_SNORM_BLOCK: - case DATA_FORMAT_BC5_UNORM_BLOCK: - case DATA_FORMAT_BC5_SNORM_BLOCK: - case DATA_FORMAT_BC6H_UFLOAT_BLOCK: - case DATA_FORMAT_BC6H_SFLOAT_BLOCK: - case DATA_FORMAT_BC7_UNORM_BLOCK: - case DATA_FORMAT_BC7_SRGB_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: - case DATA_FORMAT_EAC_R11_UNORM_BLOCK: - case DATA_FORMAT_EAC_R11_SNORM_BLOCK: - case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: - case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: - case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc. - case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: { - r_w = 4; - r_h = 4; - } break; - case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: // Unsupported - case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: - case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK: - case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK: - case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: { - r_w = 4; - r_h = 4; - } break; - case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: { - r_w = 8; - r_h = 8; - } break; - case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: // Unsupported - case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK: - case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK: - case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: - case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: - case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: - r_w = 4; - r_h = 4; - return; - default: { - r_w = 1; - r_h = 1; - } - } -} - -uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(DataFormat p_format) { - switch (p_format) { - case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: - case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: - case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: - case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: - return 8; - case DATA_FORMAT_BC2_UNORM_BLOCK: - case DATA_FORMAT_BC2_SRGB_BLOCK: - return 16; - case DATA_FORMAT_BC3_UNORM_BLOCK: - case DATA_FORMAT_BC3_SRGB_BLOCK: - return 16; - case DATA_FORMAT_BC4_UNORM_BLOCK: - case DATA_FORMAT_BC4_SNORM_BLOCK: - return 8; - case DATA_FORMAT_BC5_UNORM_BLOCK: - case DATA_FORMAT_BC5_SNORM_BLOCK: - return 16; - case DATA_FORMAT_BC6H_UFLOAT_BLOCK: - case DATA_FORMAT_BC6H_SFLOAT_BLOCK: - return 16; - case DATA_FORMAT_BC7_UNORM_BLOCK: - case DATA_FORMAT_BC7_SRGB_BLOCK: - return 16; - case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: - return 8; - case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: - return 8; - case DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: - return 16; - case DATA_FORMAT_EAC_R11_UNORM_BLOCK: - case DATA_FORMAT_EAC_R11_SNORM_BLOCK: - return 8; - case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: - case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: - return 16; - case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc. - case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: - case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: - case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: - case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK: - case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK: - case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: - case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK: - case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK: - case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK: - case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: - case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: - case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: - return 16; - default: { - } - } - return 1; -} - -uint32_t RenderingDeviceVulkan::get_compressed_image_format_pixel_rshift(DataFormat p_format) { - switch (p_format) { - case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: // These formats are half byte size, so rshift is 1. - case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: - case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: - case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: - case DATA_FORMAT_BC4_UNORM_BLOCK: - case DATA_FORMAT_BC4_SNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: - case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: - case DATA_FORMAT_EAC_R11_UNORM_BLOCK: - case DATA_FORMAT_EAC_R11_SNORM_BLOCK: - return 1; - case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: - case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: { - return 2; - } - default: { - } - } - - return 0; -} - -bool RenderingDeviceVulkan::format_has_stencil(DataFormat p_format) { - switch (p_format) { - case DATA_FORMAT_S8_UINT: - case DATA_FORMAT_D16_UNORM_S8_UINT: - case DATA_FORMAT_D24_UNORM_S8_UINT: - case DATA_FORMAT_D32_SFLOAT_S8_UINT: { - return true; - } - default: { - } - } - return false; -} - -uint32_t RenderingDeviceVulkan::get_image_format_required_size(DataFormat p_format, uint32_t p_width, uint32_t p_height, uint32_t p_depth, uint32_t p_mipmaps, uint32_t *r_blockw, uint32_t *r_blockh, uint32_t *r_depth) { - ERR_FAIL_COND_V(p_mipmaps == 0, 0); - uint32_t w = p_width; - uint32_t h = p_height; - uint32_t d = p_depth; - - uint32_t size = 0; - - uint32_t pixel_size = get_image_format_pixel_size(p_format); - uint32_t pixel_rshift = get_compressed_image_format_pixel_rshift(p_format); - uint32_t blockw, blockh; - get_compressed_image_format_block_dimensions(p_format, blockw, blockh); - - for (uint32_t i = 0; i < p_mipmaps; i++) { - uint32_t bw = w % blockw != 0 ? w + (blockw - w % blockw) : w; - uint32_t bh = h % blockh != 0 ? h + (blockh - h % blockh) : h; - - uint32_t s = bw * bh; - - s *= pixel_size; - s >>= pixel_rshift; - size += s * d; - if (r_blockw) { - *r_blockw = bw; - } - if (r_blockh) { - *r_blockh = bh; - } - if (r_depth) { - *r_depth = d; - } - w = MAX(blockw, w >> 1); - h = MAX(blockh, h >> 1); - d = MAX(1u, d >> 1); - } - - return size; -} - -uint32_t RenderingDeviceVulkan::get_image_required_mipmaps(uint32_t p_width, uint32_t p_height, uint32_t p_depth) { - // Formats and block size don't really matter here since they can all go down to 1px (even if block is larger). - uint32_t w = p_width; - uint32_t h = p_height; - uint32_t d = p_depth; - - uint32_t mipmaps = 1; - - while (true) { - if (w == 1 && h == 1 && d == 1) { - break; - } - - w = MAX(1u, w >> 1); - h = MAX(1u, h >> 1); - d = MAX(1u, d >> 1); - - mipmaps++; - } - - return mipmaps; -} - -/////////////////////// - -const VkCompareOp RenderingDeviceVulkan::compare_operators[RenderingDevice::COMPARE_OP_MAX] = { - VK_COMPARE_OP_NEVER, - VK_COMPARE_OP_LESS, - VK_COMPARE_OP_EQUAL, - VK_COMPARE_OP_LESS_OR_EQUAL, - VK_COMPARE_OP_GREATER, - VK_COMPARE_OP_NOT_EQUAL, - VK_COMPARE_OP_GREATER_OR_EQUAL, - VK_COMPARE_OP_ALWAYS -}; - -const VkStencilOp RenderingDeviceVulkan::stencil_operations[RenderingDevice::STENCIL_OP_MAX] = { - VK_STENCIL_OP_KEEP, - VK_STENCIL_OP_ZERO, - VK_STENCIL_OP_REPLACE, - VK_STENCIL_OP_INCREMENT_AND_CLAMP, - VK_STENCIL_OP_DECREMENT_AND_CLAMP, - VK_STENCIL_OP_INVERT, - VK_STENCIL_OP_INCREMENT_AND_WRAP, - VK_STENCIL_OP_DECREMENT_AND_WRAP -}; - -const VkSampleCountFlagBits RenderingDeviceVulkan::rasterization_sample_count[RenderingDevice::TEXTURE_SAMPLES_MAX] = { - VK_SAMPLE_COUNT_1_BIT, - VK_SAMPLE_COUNT_2_BIT, - VK_SAMPLE_COUNT_4_BIT, - VK_SAMPLE_COUNT_8_BIT, - VK_SAMPLE_COUNT_16_BIT, - VK_SAMPLE_COUNT_32_BIT, - VK_SAMPLE_COUNT_64_BIT, -}; - -const VkLogicOp RenderingDeviceVulkan::logic_operations[RenderingDevice::LOGIC_OP_MAX] = { - VK_LOGIC_OP_CLEAR, - VK_LOGIC_OP_AND, - VK_LOGIC_OP_AND_REVERSE, - VK_LOGIC_OP_COPY, - VK_LOGIC_OP_AND_INVERTED, - VK_LOGIC_OP_NO_OP, - VK_LOGIC_OP_XOR, - VK_LOGIC_OP_OR, - VK_LOGIC_OP_NOR, - VK_LOGIC_OP_EQUIVALENT, - VK_LOGIC_OP_INVERT, - VK_LOGIC_OP_OR_REVERSE, - VK_LOGIC_OP_COPY_INVERTED, - VK_LOGIC_OP_OR_INVERTED, - VK_LOGIC_OP_NAND, - VK_LOGIC_OP_SET -}; - -const VkBlendFactor RenderingDeviceVulkan::blend_factors[RenderingDevice::BLEND_FACTOR_MAX] = { - VK_BLEND_FACTOR_ZERO, - VK_BLEND_FACTOR_ONE, - VK_BLEND_FACTOR_SRC_COLOR, - VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, - VK_BLEND_FACTOR_DST_COLOR, - VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, - VK_BLEND_FACTOR_SRC_ALPHA, - VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, - VK_BLEND_FACTOR_DST_ALPHA, - VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, - VK_BLEND_FACTOR_CONSTANT_COLOR, - VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, - VK_BLEND_FACTOR_CONSTANT_ALPHA, - VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, - VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, - VK_BLEND_FACTOR_SRC1_COLOR, - VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, - VK_BLEND_FACTOR_SRC1_ALPHA, - VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA -}; -const VkBlendOp RenderingDeviceVulkan::blend_operations[RenderingDevice::BLEND_OP_MAX] = { - VK_BLEND_OP_ADD, - VK_BLEND_OP_SUBTRACT, - VK_BLEND_OP_REVERSE_SUBTRACT, - VK_BLEND_OP_MIN, - VK_BLEND_OP_MAX -}; - -const VkSamplerAddressMode RenderingDeviceVulkan::address_modes[RenderingDevice::SAMPLER_REPEAT_MODE_MAX] = { - VK_SAMPLER_ADDRESS_MODE_REPEAT, - VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE -}; - -const VkBorderColor RenderingDeviceVulkan::sampler_border_colors[RenderingDevice::SAMPLER_BORDER_COLOR_MAX] = { - VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, - VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, - VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, - VK_BORDER_COLOR_INT_OPAQUE_BLACK, - VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, - VK_BORDER_COLOR_INT_OPAQUE_WHITE -}; - -const VkImageType RenderingDeviceVulkan::vulkan_image_type[RenderingDevice::TEXTURE_TYPE_MAX] = { - VK_IMAGE_TYPE_1D, - VK_IMAGE_TYPE_2D, - VK_IMAGE_TYPE_3D, - VK_IMAGE_TYPE_2D, - VK_IMAGE_TYPE_1D, - VK_IMAGE_TYPE_2D, - VK_IMAGE_TYPE_2D -}; - -/***************************/ -/**** BUFFER MANAGEMENT ****/ -/***************************/ - -Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mem_usage, VmaAllocationCreateFlags p_mem_flags) { - VkBufferCreateInfo bufferInfo; - bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.pNext = nullptr; - bufferInfo.flags = 0; - bufferInfo.size = p_size; - bufferInfo.usage = p_usage; - bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - bufferInfo.queueFamilyIndexCount = 0; - bufferInfo.pQueueFamilyIndices = nullptr; - - VmaAllocationCreateInfo allocInfo; - allocInfo.flags = p_mem_flags; - allocInfo.usage = p_mem_usage; - allocInfo.requiredFlags = 0; - allocInfo.preferredFlags = 0; - allocInfo.memoryTypeBits = 0; - allocInfo.pool = nullptr; - allocInfo.pUserData = nullptr; - if (p_size <= SMALL_ALLOCATION_MAX_SIZE) { - uint32_t mem_type_index = 0; - vmaFindMemoryTypeIndexForBufferInfo(allocator, &bufferInfo, &allocInfo, &mem_type_index); - allocInfo.pool = _find_or_create_small_allocs_pool(mem_type_index); - } - - VkResult err = vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &p_buffer->buffer, &p_buffer->allocation, nullptr); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Can't create buffer of size: " + itos(p_size) + ", error " + itos(err) + "."); - p_buffer->size = p_size; - p_buffer->buffer_info.buffer = p_buffer->buffer; - p_buffer->buffer_info.offset = 0; - p_buffer->buffer_info.range = p_size; - p_buffer->usage = p_usage; - - buffer_memory += p_size; - - return OK; -} - -Error RenderingDeviceVulkan::_buffer_free(Buffer *p_buffer) { - ERR_FAIL_COND_V(p_buffer->size == 0, ERR_INVALID_PARAMETER); - - buffer_memory -= p_buffer->size; - vmaDestroyBuffer(allocator, p_buffer->buffer, p_buffer->allocation); - p_buffer->buffer = VK_NULL_HANDLE; - p_buffer->allocation = nullptr; - p_buffer->size = 0; - - return OK; -} - -Error RenderingDeviceVulkan::_insert_staging_block() { - VkBufferCreateInfo bufferInfo; - bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.pNext = nullptr; - bufferInfo.flags = 0; - bufferInfo.size = staging_buffer_block_size; - bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - bufferInfo.queueFamilyIndexCount = 0; - bufferInfo.pQueueFamilyIndices = nullptr; - - VmaAllocationCreateInfo allocInfo; - allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; - allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; - allocInfo.requiredFlags = 0; - allocInfo.preferredFlags = 0; - allocInfo.memoryTypeBits = 0; - allocInfo.pool = nullptr; - allocInfo.pUserData = nullptr; - - StagingBufferBlock block; - - VkResult err = vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &block.buffer, &block.allocation, nullptr); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vmaCreateBuffer failed with error " + itos(err) + "."); - - block.frame_used = 0; - block.fill_amount = 0; - - staging_buffer_blocks.insert(staging_buffer_current, block); - return OK; -} - -Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment) { - // Determine a block to use. - - r_alloc_size = p_amount; - - while (true) { - r_alloc_offset = 0; - - // See if we can use current block. - if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { - // We used this block this frame, let's see if there is still room. - - uint32_t write_from = staging_buffer_blocks[staging_buffer_current].fill_amount; - - { - uint32_t align_remainder = write_from % p_required_align; - if (align_remainder != 0) { - write_from += p_required_align - align_remainder; - } - } - - int32_t available_bytes = int32_t(staging_buffer_block_size) - int32_t(write_from); - - if ((int32_t)p_amount < available_bytes) { - // All is good, we should be ok, all will fit. - r_alloc_offset = write_from; - } else if (p_can_segment && available_bytes >= (int32_t)p_required_align) { - // Ok all won't fit but at least we can fit a chunkie. - // All is good, update what needs to be written to. - r_alloc_offset = write_from; - r_alloc_size = available_bytes - (available_bytes % p_required_align); - - } else { - // Can't fit it into this buffer. - // Will need to try next buffer. - - staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); - - // Before doing anything, though, let's check that we didn't manage to fill all blocks. - // Possible in a single frame. - if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { - // Guess we did.. ok, let's see if we can insert a new block. - if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { - // We can, so we are safe. - Error err = _insert_staging_block(); - if (err) { - return err; - } - // Claim for this frame. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; - } else { - // Ok, worst case scenario, all the staging buffers belong to this frame - // 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). - - if (false) { // Separate thread from render. - - //block_until_next_frame() - continue; - } else { - // Flush EVERYTHING including setup commands. IF not immediate, also need to flush the draw commands. - _flush(true); - - // Clear the whole staging buffer. - for (int i = 0; i < staging_buffer_blocks.size(); i++) { - staging_buffer_blocks.write[i].frame_used = 0; - staging_buffer_blocks.write[i].fill_amount = 0; - } - // Claim current. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; - } - } - - } else { - // Not from current frame, so continue and try again. - continue; - } - } - - } else if (staging_buffer_blocks[staging_buffer_current].frame_used <= frames_drawn - frame_count) { - // 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; - } else { - // This block may still be in use, let's not touch it unless we have to, so.. can we create a new one? - if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { - // We are still allowed to create a new block, so let's do that and insert it for current pos. - Error err = _insert_staging_block(); - if (err) { - return err; - } - // Claim for this frame. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; - } else { - // Oops, we are out of room and we can't create more. - // 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). - - if (false) { - // Separate thread from render. - //block_until_next_frame() - continue; // And try again. - } else { - _flush(false); - - for (int i = 0; i < staging_buffer_blocks.size(); i++) { - // Clear all blocks but the ones from this frame. - int block_idx = (i + staging_buffer_current) % staging_buffer_blocks.size(); - if (staging_buffer_blocks[block_idx].frame_used == frames_drawn) { - break; // Ok, we reached something from this frame, abort. - } - - staging_buffer_blocks.write[block_idx].frame_used = 0; - staging_buffer_blocks.write[block_idx].fill_amount = 0; - } - - // Claim for current frame. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; - } - } - } - - // All was good, break. - break; - } - - staging_buffer_used = true; - - return OK; -} - -Error RenderingDeviceVulkan::_buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_buffer, uint32_t p_required_align) { - // Submitting may get chunked for various reasons, so convert this to a task. - size_t to_submit = p_data_size; - size_t submit_from = 0; - - while (to_submit > 0) { - uint32_t block_write_offset; - uint32_t block_write_amount; - - Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), p_required_align, block_write_offset, block_write_amount); - if (err) { - return err; - } - - // Map staging buffer (It's CPU and coherent). - - void *data_ptr = nullptr; - { - VkResult vkerr = vmaMapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation, &data_ptr); - ERR_FAIL_COND_V_MSG(vkerr, ERR_CANT_CREATE, "vmaMapMemory failed with error " + itos(vkerr) + "."); - } - - // Copy to staging buffer. - memcpy(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount); - - // Unmap. - vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation); - // Insert a command to copy this. - - VkBufferCopy region; - region.srcOffset = block_write_offset; - region.dstOffset = submit_from + p_offset; - region.size = block_write_amount; - - vkCmdCopyBuffer(p_use_draw_command_buffer ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, staging_buffer_blocks[staging_buffer_current].buffer, p_buffer->buffer, 1, ®ion); - - staging_buffer_blocks.write[staging_buffer_current].fill_amount = block_write_offset + block_write_amount; - - to_submit -= block_write_amount; - submit_from += block_write_amount; - } - - return OK; -} - -void RenderingDeviceVulkan::_memory_barrier(VkPipelineStageFlags p_src_stage_mask, VkPipelineStageFlags p_dst_stage_mask, VkAccessFlags p_src_access, VkAccessFlags p_dst_access, bool p_sync_with_draw) { - VkMemoryBarrier mem_barrier; - mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; - mem_barrier.pNext = nullptr; - mem_barrier.srcAccessMask = p_src_access; - mem_barrier.dstAccessMask = p_dst_access; - - if (p_src_stage_mask == 0 || p_dst_stage_mask == 0) { - return; // No barrier, since this is invalid. - } - vkCmdPipelineBarrier(p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, p_src_stage_mask, p_dst_stage_mask, 0, 1, &mem_barrier, 0, nullptr, 0, nullptr); -} - -void RenderingDeviceVulkan::_full_barrier(bool p_sync_with_draw) { - // Used for debug. - _memory_barrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_ACCESS_INDIRECT_COMMAND_READ_BIT | - VK_ACCESS_INDEX_READ_BIT | - VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - VK_ACCESS_UNIFORM_READ_BIT | - VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | - VK_ACCESS_SHADER_READ_BIT | - VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT | - VK_ACCESS_HOST_READ_BIT | - VK_ACCESS_HOST_WRITE_BIT, - VK_ACCESS_INDIRECT_COMMAND_READ_BIT | - VK_ACCESS_INDEX_READ_BIT | - VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - VK_ACCESS_UNIFORM_READ_BIT | - VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | - VK_ACCESS_SHADER_READ_BIT | - VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT | - VK_ACCESS_HOST_READ_BIT | - VK_ACCESS_HOST_WRITE_BIT, - p_sync_with_draw); -} - -void RenderingDeviceVulkan::_buffer_memory_barrier(VkBuffer buffer, uint64_t p_from, uint64_t p_size, VkPipelineStageFlags p_src_stage_mask, VkPipelineStageFlags p_dst_stage_mask, VkAccessFlags p_src_access, VkAccessFlags p_dst_access, bool p_sync_with_draw) { - VkBufferMemoryBarrier buffer_mem_barrier; - buffer_mem_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; - buffer_mem_barrier.pNext = nullptr; - buffer_mem_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - buffer_mem_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - buffer_mem_barrier.srcAccessMask = p_src_access; - buffer_mem_barrier.dstAccessMask = p_dst_access; - buffer_mem_barrier.buffer = buffer; - buffer_mem_barrier.offset = p_from; - buffer_mem_barrier.size = p_size; - - vkCmdPipelineBarrier(p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, p_src_stage_mask, p_dst_stage_mask, 0, 0, nullptr, 1, &buffer_mem_barrier, 0, nullptr); -} - -/*****************/ -/**** TEXTURE ****/ -/*****************/ - -RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data) { - _THREAD_SAFE_METHOD_ - - VkImageCreateInfo image_create_info; - image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_create_info.pNext = nullptr; - image_create_info.flags = 0; - - VkImageFormatListCreateInfoKHR format_list_create_info; // Keep out of the if, needed for creation. - Vector<VkFormat> allowed_formats; // Keep out of the if, needed for creation. - if (p_format.shareable_formats.size()) { - image_create_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; - - if (context->is_device_extension_enabled(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME)) { - for (int i = 0; i < p_format.shareable_formats.size(); i++) { - allowed_formats.push_back(vulkan_formats[p_format.shareable_formats[i]]); - } - - format_list_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR; - format_list_create_info.pNext = nullptr; - format_list_create_info.viewFormatCount = allowed_formats.size(); - format_list_create_info.pViewFormats = allowed_formats.ptr(); - image_create_info.pNext = &format_list_create_info; - - ERR_FAIL_COND_V_MSG(p_format.shareable_formats.find(p_format.format) == -1, RID(), - "If supplied a list of shareable formats, the current format must be present in the list"); - ERR_FAIL_COND_V_MSG(p_view.format_override != DATA_FORMAT_MAX && p_format.shareable_formats.find(p_view.format_override) == -1, RID(), - "If supplied a list of shareable formats, the current view format override must be present in the list"); - } - } - - if (p_format.texture_type == TEXTURE_TYPE_CUBE || p_format.texture_type == TEXTURE_TYPE_CUBE_ARRAY) { - image_create_info.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; - } - /*if (p_format.type == TEXTURE_TYPE_2D || p_format.type == TEXTURE_TYPE_2D_ARRAY) { - image_create_info.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; - }*/ - - ERR_FAIL_INDEX_V(p_format.texture_type, TEXTURE_TYPE_MAX, RID()); - - image_create_info.imageType = vulkan_image_type[p_format.texture_type]; - - ERR_FAIL_COND_V_MSG(p_format.width < 1, RID(), "Width must be equal or greater than 1 for all textures"); - - image_create_info.format = vulkan_formats[p_format.format]; - - image_create_info.extent.width = p_format.width; - if (image_create_info.imageType == VK_IMAGE_TYPE_3D || image_create_info.imageType == VK_IMAGE_TYPE_2D) { - ERR_FAIL_COND_V_MSG(p_format.height < 1, RID(), "Height must be equal or greater than 1 for 2D and 3D textures"); - image_create_info.extent.height = p_format.height; - } else { - image_create_info.extent.height = 1; - } - - if (image_create_info.imageType == VK_IMAGE_TYPE_3D) { - ERR_FAIL_COND_V_MSG(p_format.depth < 1, RID(), "Depth must be equal or greater than 1 for 3D textures"); - image_create_info.extent.depth = p_format.depth; - } else { - image_create_info.extent.depth = 1; - } - - ERR_FAIL_COND_V(p_format.mipmaps < 1, RID()); - - image_create_info.mipLevels = p_format.mipmaps; - - if (p_format.texture_type == TEXTURE_TYPE_1D_ARRAY || p_format.texture_type == TEXTURE_TYPE_2D_ARRAY || p_format.texture_type == TEXTURE_TYPE_CUBE_ARRAY || p_format.texture_type == TEXTURE_TYPE_CUBE) { - ERR_FAIL_COND_V_MSG(p_format.array_layers < 1, RID(), - "Amount of layers must be equal or greater than 1 for arrays and cubemaps."); - ERR_FAIL_COND_V_MSG((p_format.texture_type == TEXTURE_TYPE_CUBE_ARRAY || p_format.texture_type == TEXTURE_TYPE_CUBE) && (p_format.array_layers % 6) != 0, RID(), - "Cubemap and cubemap array textures must provide a layer number that is multiple of 6"); - image_create_info.arrayLayers = p_format.array_layers; - } else { - image_create_info.arrayLayers = 1; - } - - ERR_FAIL_INDEX_V(p_format.samples, TEXTURE_SAMPLES_MAX, RID()); - - image_create_info.samples = _ensure_supported_sample_count(p_format.samples); - image_create_info.tiling = (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; - - // Usage. - image_create_info.usage = 0; - - if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { - image_create_info.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; - } - - if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT) { - image_create_info.usage |= VK_IMAGE_USAGE_STORAGE_BIT; - } - - if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - image_create_info.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - } - - if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - image_create_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - } - - if (p_format.usage_bits & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT) { - image_create_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; - } - - if (p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { - image_create_info.usage |= VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR; - } - - if (p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT) { - image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - if (p_format.usage_bits & TEXTURE_USAGE_CAN_COPY_FROM_BIT) { - image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - } - - if (p_format.usage_bits & TEXTURE_USAGE_CAN_COPY_TO_BIT) { - image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - - image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_create_info.queueFamilyIndexCount = 0; - image_create_info.pQueueFamilyIndices = nullptr; - image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - uint32_t required_mipmaps = get_image_required_mipmaps(image_create_info.extent.width, image_create_info.extent.height, image_create_info.extent.depth); - - ERR_FAIL_COND_V_MSG(required_mipmaps < image_create_info.mipLevels, RID(), - "Too many mipmaps requested for texture format and dimensions (" + itos(image_create_info.mipLevels) + "), maximum allowed: (" + itos(required_mipmaps) + ")."); - - if (p_data.size()) { - ERR_FAIL_COND_V_MSG(!(p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT), RID(), - "Texture needs the TEXTURE_USAGE_CAN_UPDATE_BIT usage flag in order to be updated at initialization or later"); - - int expected_images = image_create_info.arrayLayers; - ERR_FAIL_COND_V_MSG(p_data.size() != expected_images, RID(), - "Default supplied data for image format is of invalid length (" + itos(p_data.size()) + "), should be (" + itos(expected_images) + ")."); - - for (uint32_t i = 0; i < image_create_info.arrayLayers; i++) { - uint32_t required_size = get_image_format_required_size(p_format.format, image_create_info.extent.width, image_create_info.extent.height, image_create_info.extent.depth, image_create_info.mipLevels); - ERR_FAIL_COND_V_MSG((uint32_t)p_data[i].size() != required_size, RID(), - "Data for slice index " + itos(i) + " (mapped to layer " + itos(i) + ") differs in size (supplied: " + itos(p_data[i].size()) + ") than what is required by the format (" + itos(required_size) + ")."); - } - } - - { - // Validate that this image is supported for the intended use. - VkFormatProperties properties; - vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), image_create_info.format, &properties); - VkFormatFeatureFlags flags; - - String format_text = "'" + String(named_formats[p_format.format]) + "'"; - - if (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) { - flags = properties.linearTilingFeatures; - format_text += " (with CPU read bit)"; - } else { - flags = properties.optimalTilingFeatures; - } - - if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT && !(flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as sampling texture."); - } - - if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT && !(flags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as color attachment."); - } - - if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT && !(flags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as depth-stencil attachment."); - } - - if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT && !(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as storage image."); - } - - if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_ATOMIC_BIT && !(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT)) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as atomic storage image."); - } - - // Validation via VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR fails if VRS attachment is not supported. - if (p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && p_format.format != DATA_FORMAT_R8_UINT) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as VRS attachment."); - } - } - - // Some view validation. - - if (p_view.format_override != DATA_FORMAT_MAX) { - ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); - } - ERR_FAIL_INDEX_V(p_view.swizzle_r, TEXTURE_SWIZZLE_MAX, RID()); - ERR_FAIL_INDEX_V(p_view.swizzle_g, TEXTURE_SWIZZLE_MAX, RID()); - ERR_FAIL_INDEX_V(p_view.swizzle_b, TEXTURE_SWIZZLE_MAX, RID()); - ERR_FAIL_INDEX_V(p_view.swizzle_a, TEXTURE_SWIZZLE_MAX, RID()); - - // Allocate memory. - - uint32_t width, height; - uint32_t image_size = get_image_format_required_size(p_format.format, p_format.width, p_format.height, p_format.depth, p_format.mipmaps, &width, &height); - - VmaAllocationCreateInfo allocInfo; - allocInfo.flags = (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) ? VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT : 0; - allocInfo.pool = nullptr; - allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; - allocInfo.requiredFlags = 0; - allocInfo.preferredFlags = 0; - allocInfo.memoryTypeBits = 0; - allocInfo.pUserData = nullptr; - if (image_size <= SMALL_ALLOCATION_MAX_SIZE) { - uint32_t mem_type_index = 0; - vmaFindMemoryTypeIndexForImageInfo(allocator, &image_create_info, &allocInfo, &mem_type_index); - allocInfo.pool = _find_or_create_small_allocs_pool(mem_type_index); - } - - Texture texture; - - VkResult err = vmaCreateImage(allocator, &image_create_info, &allocInfo, &texture.image, &texture.allocation, &texture.allocation_info); - ERR_FAIL_COND_V_MSG(err, RID(), "vmaCreateImage failed with error " + itos(err) + "."); - image_memory += texture.allocation_info.size; - texture.type = p_format.texture_type; - texture.format = p_format.format; - texture.width = image_create_info.extent.width; - texture.height = image_create_info.extent.height; - texture.depth = image_create_info.extent.depth; - texture.layers = image_create_info.arrayLayers; - texture.mipmaps = image_create_info.mipLevels; - texture.base_mipmap = 0; - texture.base_layer = 0; - texture.is_resolve_buffer = p_format.is_resolve_buffer; - texture.usage_flags = p_format.usage_bits; - texture.samples = p_format.samples; - texture.allowed_shared_formats = p_format.shareable_formats; - - // Set base layout based on usage priority. - - if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { - // First priority, readable. - texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - } else if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT) { - // Second priority, storage. - - texture.layout = VK_IMAGE_LAYOUT_GENERAL; - - } else if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - // Third priority, color or depth. - - texture.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - } else if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - texture.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - } else { - texture.layout = VK_IMAGE_LAYOUT_GENERAL; - } - - if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - texture.read_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; - texture.barrier_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; - - if (format_has_stencil(p_format.format)) { - texture.barrier_aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - } else { - texture.read_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; - texture.barrier_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; - } - - texture.bound = false; - - // Create view. - - VkImageViewCreateInfo image_view_create_info; - image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - image_view_create_info.pNext = nullptr; - image_view_create_info.flags = 0; - image_view_create_info.image = texture.image; - - static const VkImageViewType view_types[TEXTURE_TYPE_MAX] = { - VK_IMAGE_VIEW_TYPE_1D, - VK_IMAGE_VIEW_TYPE_2D, - VK_IMAGE_VIEW_TYPE_3D, - VK_IMAGE_VIEW_TYPE_CUBE, - VK_IMAGE_VIEW_TYPE_1D_ARRAY, - VK_IMAGE_VIEW_TYPE_2D_ARRAY, - VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, - }; - - image_view_create_info.viewType = view_types[p_format.texture_type]; - if (p_view.format_override == DATA_FORMAT_MAX) { - image_view_create_info.format = image_create_info.format; - } else { - image_view_create_info.format = vulkan_formats[p_view.format_override]; - } - - static const VkComponentSwizzle component_swizzles[TEXTURE_SWIZZLE_MAX] = { - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_ZERO, - VK_COMPONENT_SWIZZLE_ONE, - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A - }; - - image_view_create_info.components.r = component_swizzles[p_view.swizzle_r]; - image_view_create_info.components.g = component_swizzles[p_view.swizzle_g]; - image_view_create_info.components.b = component_swizzles[p_view.swizzle_b]; - image_view_create_info.components.a = component_swizzles[p_view.swizzle_a]; - - image_view_create_info.subresourceRange.baseMipLevel = 0; - image_view_create_info.subresourceRange.levelCount = image_create_info.mipLevels; - image_view_create_info.subresourceRange.baseArrayLayer = 0; - image_view_create_info.subresourceRange.layerCount = image_create_info.arrayLayers; - if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - } else { - image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - } - - err = vkCreateImageView(device, &image_view_create_info, nullptr, &texture.view); - - if (err) { - vmaDestroyImage(allocator, texture.image, texture.allocation); - ERR_FAIL_V_MSG(RID(), "vkCreateImageView failed with error " + itos(err) + "."); - } - - // Barrier to set layout. - { - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - image_memory_barrier.newLayout = texture.layout; - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = texture.image; - image_memory_barrier.subresourceRange.aspectMask = texture.barrier_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = image_create_info.mipLevels; - image_memory_barrier.subresourceRange.baseArrayLayer = 0; - image_memory_barrier.subresourceRange.layerCount = image_create_info.arrayLayers; - - vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - RID id = texture_owner.make_rid(texture); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - - if (p_data.size()) { - for (uint32_t i = 0; i < image_create_info.arrayLayers; i++) { - _texture_update(id, i, p_data[i], RD::BARRIER_MASK_ALL_BARRIERS, true); - } - } - return id; -} - -RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID p_with_texture) { - _THREAD_SAFE_METHOD_ - - Texture *src_texture = texture_owner.get_or_null(p_with_texture); - ERR_FAIL_NULL_V(src_texture, RID()); - - if (src_texture->owner.is_valid()) { // Ahh this is a share. - p_with_texture = src_texture->owner; - src_texture = texture_owner.get_or_null(src_texture->owner); - ERR_FAIL_NULL_V(src_texture, RID()); // This is a bug. - } - - // Create view. - - Texture texture = *src_texture; - - VkImageViewCreateInfo image_view_create_info; - image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - image_view_create_info.pNext = nullptr; - image_view_create_info.flags = 0; - image_view_create_info.image = texture.image; - - static const VkImageViewType view_types[TEXTURE_TYPE_MAX] = { - VK_IMAGE_VIEW_TYPE_1D, - VK_IMAGE_VIEW_TYPE_2D, - VK_IMAGE_VIEW_TYPE_3D, - VK_IMAGE_VIEW_TYPE_CUBE, - VK_IMAGE_VIEW_TYPE_1D_ARRAY, - VK_IMAGE_VIEW_TYPE_2D_ARRAY, - VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, - }; - - image_view_create_info.viewType = view_types[texture.type]; - if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) { - image_view_create_info.format = vulkan_formats[texture.format]; - } else { - ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); - - ERR_FAIL_COND_V_MSG(texture.allowed_shared_formats.find(p_view.format_override) == -1, RID(), - "Format override is not in the list of allowed shareable formats for original texture."); - image_view_create_info.format = vulkan_formats[p_view.format_override]; - } - - static const VkComponentSwizzle component_swizzles[TEXTURE_SWIZZLE_MAX] = { - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_ZERO, - VK_COMPONENT_SWIZZLE_ONE, - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A - }; - - image_view_create_info.components.r = component_swizzles[p_view.swizzle_r]; - image_view_create_info.components.g = component_swizzles[p_view.swizzle_g]; - image_view_create_info.components.b = component_swizzles[p_view.swizzle_b]; - image_view_create_info.components.a = component_swizzles[p_view.swizzle_a]; - - image_view_create_info.subresourceRange.baseMipLevel = 0; - image_view_create_info.subresourceRange.levelCount = texture.mipmaps; - image_view_create_info.subresourceRange.layerCount = texture.layers; - image_view_create_info.subresourceRange.baseArrayLayer = 0; - - if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - } else { - image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - } - - VkImageViewUsageCreateInfo usage_info; - if (context->is_device_extension_enabled(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.. - - usage_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO; - usage_info.pNext = nullptr; - if (p_view.format_override != DATA_FORMAT_MAX) { - // Need to validate usage with vulkan. - - usage_info.usage = 0; - - if (texture.usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { - usage_info.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; - } - - if (texture.usage_flags & TEXTURE_USAGE_STORAGE_BIT) { - if (texture_is_format_supported_for_usage(p_view.format_override, TEXTURE_USAGE_STORAGE_BIT)) { - usage_info.usage |= VK_IMAGE_USAGE_STORAGE_BIT; - } - } - - if (texture.usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - if (texture_is_format_supported_for_usage(p_view.format_override, TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - usage_info.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - } - } - - if (texture.usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT) { - usage_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; - } - - if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - usage_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - } - - if (texture.usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT) { - usage_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - if (texture.usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT) { - usage_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - } - - if (texture.usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT) { - usage_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - - image_view_create_info.pNext = &usage_info; - } - } - - VkResult err = vkCreateImageView(device, &image_view_create_info, nullptr, &texture.view); - ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateImageView failed with error " + itos(err) + "."); - - texture.owner = p_with_texture; - RID id = texture_owner.make_rid(texture); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - _add_dependency(id, p_with_texture); - - return id; -} - -RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, BitField<RenderingDevice::TextureUsageBits> p_flags, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers) { - _THREAD_SAFE_METHOD_ - // This method creates a texture object using a VkImage created by an extension, module or other external source (OpenXR uses this). - VkImage image = (VkImage)p_image; - - Texture texture; - texture.image = image; - // If we leave texture.allocation as a nullptr, would that be enough to detect we don't "own" the image? - // Also leave texture.allocation_info alone. - // We'll set texture.view later on. - texture.type = p_type; - texture.format = p_format; - texture.samples = p_samples; - texture.width = p_width; - texture.height = p_height; - texture.depth = p_depth; - texture.layers = p_layers; - texture.mipmaps = 1; - texture.usage_flags = p_flags; - texture.base_mipmap = 0; - texture.base_layer = 0; - texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_UNORM); - texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_SRGB); - - // Set base layout based on usage priority. - - if (texture.usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { - // First priority, readable. - texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - } else if (texture.usage_flags & TEXTURE_USAGE_STORAGE_BIT) { - // Second priority, storage. - - texture.layout = VK_IMAGE_LAYOUT_GENERAL; - - } else if (texture.usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - // Third priority, color or depth. - - texture.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - } else if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - texture.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - } else { - texture.layout = VK_IMAGE_LAYOUT_GENERAL; - } - - if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - texture.read_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; - texture.barrier_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; - - // if (format_has_stencil(p_format.format)) { - // texture.barrier_aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; - // } - } else { - texture.read_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; - texture.barrier_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; - } - - // Create a view for us to use. - - VkImageViewCreateInfo image_view_create_info; - image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - image_view_create_info.pNext = nullptr; - image_view_create_info.flags = 0; - image_view_create_info.image = texture.image; - - static const VkImageViewType view_types[TEXTURE_TYPE_MAX] = { - VK_IMAGE_VIEW_TYPE_1D, - VK_IMAGE_VIEW_TYPE_2D, - VK_IMAGE_VIEW_TYPE_3D, - VK_IMAGE_VIEW_TYPE_CUBE, - VK_IMAGE_VIEW_TYPE_1D_ARRAY, - VK_IMAGE_VIEW_TYPE_2D_ARRAY, - VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, - }; - - image_view_create_info.viewType = view_types[texture.type]; - image_view_create_info.format = vulkan_formats[texture.format]; - - static const VkComponentSwizzle component_swizzles[TEXTURE_SWIZZLE_MAX] = { - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_ZERO, - VK_COMPONENT_SWIZZLE_ONE, - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A - }; - - // Hardcode for now, maybe make this settable from outside. - image_view_create_info.components.r = component_swizzles[TEXTURE_SWIZZLE_R]; - image_view_create_info.components.g = component_swizzles[TEXTURE_SWIZZLE_G]; - image_view_create_info.components.b = component_swizzles[TEXTURE_SWIZZLE_B]; - image_view_create_info.components.a = component_swizzles[TEXTURE_SWIZZLE_A]; - - image_view_create_info.subresourceRange.baseMipLevel = 0; - image_view_create_info.subresourceRange.levelCount = texture.mipmaps; - image_view_create_info.subresourceRange.baseArrayLayer = 0; - image_view_create_info.subresourceRange.layerCount = texture.layers; - if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - } else { - image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - } - - VkResult err = vkCreateImageView(device, &image_view_create_info, nullptr, &texture.view); - - if (err) { - // vmaDestroyImage(allocator, texture.image, texture.allocation); - ERR_FAIL_V_MSG(RID(), "vkCreateImageView failed with error " + itos(err) + "."); - } - - // Barrier to set layout. - { - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - image_memory_barrier.newLayout = texture.layout; - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = texture.image; - image_memory_barrier.subresourceRange.aspectMask = texture.barrier_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = texture.mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = 0; - image_memory_barrier.subresourceRange.layerCount = texture.layers; - - vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - RID id = texture_owner.make_rid(texture); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - - return id; -} - -RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps, TextureSliceType p_slice_type, uint32_t p_layers) { - _THREAD_SAFE_METHOD_ - - Texture *src_texture = texture_owner.get_or_null(p_with_texture); - ERR_FAIL_NULL_V(src_texture, RID()); - - if (src_texture->owner.is_valid()) { // Ahh this is a share. - p_with_texture = src_texture->owner; - src_texture = texture_owner.get_or_null(src_texture->owner); - ERR_FAIL_NULL_V(src_texture, RID()); // This is a bug. - } - - ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_CUBEMAP && (src_texture->type != TEXTURE_TYPE_CUBE && src_texture->type != TEXTURE_TYPE_CUBE_ARRAY), RID(), - "Can only create a cubemap slice from a cubemap or cubemap array mipmap"); - - ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_3D && src_texture->type != TEXTURE_TYPE_3D, RID(), - "Can only create a 3D slice from a 3D texture"); - - ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_2D_ARRAY && (src_texture->type != TEXTURE_TYPE_2D_ARRAY), RID(), - "Can only create an array slice from a 2D array mipmap"); - - // Create view. - - ERR_FAIL_UNSIGNED_INDEX_V(p_mipmap, src_texture->mipmaps, RID()); - ERR_FAIL_COND_V(p_mipmap + p_mipmaps > src_texture->mipmaps, RID()); - ERR_FAIL_UNSIGNED_INDEX_V(p_layer, src_texture->layers, RID()); - - int slice_layers = 1; - if (p_layers != 0) { - ERR_FAIL_COND_V_MSG(p_layers > 1 && p_slice_type != TEXTURE_SLICE_2D_ARRAY, RID(), "layer slicing only supported for 2D arrays"); - ERR_FAIL_COND_V_MSG(p_layer + p_layers > src_texture->layers, RID(), "layer slice is out of bounds"); - slice_layers = p_layers; - } else if (p_slice_type == TEXTURE_SLICE_2D_ARRAY) { - ERR_FAIL_COND_V_MSG(p_layer != 0, RID(), "layer must be 0 when obtaining a 2D array mipmap slice"); - slice_layers = src_texture->layers; - } else if (p_slice_type == TEXTURE_SLICE_CUBEMAP) { - slice_layers = 6; - } - - Texture texture = *src_texture; - get_image_format_required_size(texture.format, texture.width, texture.height, texture.depth, p_mipmap + 1, &texture.width, &texture.height); - texture.mipmaps = p_mipmaps; - texture.layers = slice_layers; - texture.base_mipmap = p_mipmap; - texture.base_layer = p_layer; - - VkImageViewCreateInfo image_view_create_info; - image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - image_view_create_info.pNext = nullptr; - image_view_create_info.flags = 0; - image_view_create_info.image = texture.image; - - static const VkImageViewType view_types[TEXTURE_TYPE_MAX] = { - VK_IMAGE_VIEW_TYPE_1D, - VK_IMAGE_VIEW_TYPE_2D, - VK_IMAGE_VIEW_TYPE_2D, - VK_IMAGE_VIEW_TYPE_2D, - VK_IMAGE_VIEW_TYPE_1D, - VK_IMAGE_VIEW_TYPE_2D, - VK_IMAGE_VIEW_TYPE_2D, - }; - - image_view_create_info.viewType = view_types[texture.type]; - - if (p_slice_type == TEXTURE_SLICE_CUBEMAP) { - image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE; - } else if (p_slice_type == TEXTURE_SLICE_3D) { - image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_3D; - } else if (p_slice_type == TEXTURE_SLICE_2D_ARRAY) { - image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; - } - - if (p_slice_type == TEXTURE_SLICE_2D) { - texture.type = TEXTURE_TYPE_2D; - } else if (p_slice_type == TEXTURE_SLICE_3D) { - texture.type = TEXTURE_TYPE_3D; - } - - if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) { - image_view_create_info.format = vulkan_formats[texture.format]; - } else { - ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); - - ERR_FAIL_COND_V_MSG(texture.allowed_shared_formats.find(p_view.format_override) == -1, RID(), - "Format override is not in the list of allowed shareable formats for original texture."); - image_view_create_info.format = vulkan_formats[p_view.format_override]; - } - - static const VkComponentSwizzle component_swizzles[TEXTURE_SWIZZLE_MAX] = { - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_ZERO, - VK_COMPONENT_SWIZZLE_ONE, - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A - }; - - image_view_create_info.components.r = component_swizzles[p_view.swizzle_r]; - image_view_create_info.components.g = component_swizzles[p_view.swizzle_g]; - image_view_create_info.components.b = component_swizzles[p_view.swizzle_b]; - image_view_create_info.components.a = component_swizzles[p_view.swizzle_a]; - - if (p_slice_type == TEXTURE_SLICE_CUBEMAP) { - ERR_FAIL_COND_V_MSG(p_layer >= src_texture->layers, RID(), - "Specified layer is invalid for cubemap"); - ERR_FAIL_COND_V_MSG((p_layer % 6) != 0, RID(), - "Specified layer must be a multiple of 6."); - } - image_view_create_info.subresourceRange.baseMipLevel = p_mipmap; - image_view_create_info.subresourceRange.levelCount = p_mipmaps; - image_view_create_info.subresourceRange.layerCount = slice_layers; - image_view_create_info.subresourceRange.baseArrayLayer = p_layer; - - if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - } else { - image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - } - - VkResult err = vkCreateImageView(device, &image_view_create_info, nullptr, &texture.view); - ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateImageView failed with error " + itos(err) + "."); - - texture.owner = p_with_texture; - RID id = texture_owner.make_rid(texture); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - _add_dependency(id, p_with_texture); - - return id; -} - -Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier) { - return _texture_update(p_texture, p_layer, p_data, p_post_barrier, false); -} - -static _ALWAYS_INLINE_ void _copy_region(uint8_t const *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_x, uint32_t p_src_y, uint32_t p_src_w, uint32_t p_src_h, uint32_t p_src_full_w, uint32_t p_unit_size) { - uint32_t src_offset = (p_src_y * p_src_full_w + p_src_x) * p_unit_size; - uint32_t dst_offset = 0; - for (uint32_t y = p_src_h; y > 0; y--) { - uint8_t const *__restrict src = p_src + src_offset; - uint8_t *__restrict dst = p_dst + dst_offset; - for (uint32_t x = p_src_w * p_unit_size; x > 0; x--) { - *dst = *src; - src++; - dst++; - } - src_offset += p_src_full_w * p_unit_size; - dst_offset += p_src_w * p_unit_size; - } -} - -Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier, bool p_use_setup_queue) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V_MSG((draw_list || compute_list) && !p_use_setup_queue, ERR_INVALID_PARAMETER, - "Updating textures is forbidden during creation of a draw or compute list"); - - Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_NULL_V(texture, ERR_INVALID_PARAMETER); - - if (texture->owner != RID()) { - p_texture = texture->owner; - texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_NULL_V(texture, ERR_BUG); // This is a bug. - } - - ERR_FAIL_COND_V_MSG(texture->bound, ERR_CANT_ACQUIRE_RESOURCE, - "Texture can't be updated while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to update this texture."); - - ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT), ERR_INVALID_PARAMETER, - "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT` to be set to be updatable."); - - uint32_t layer_count = texture->layers; - if (texture->type == TEXTURE_TYPE_CUBE || texture->type == TEXTURE_TYPE_CUBE_ARRAY) { - layer_count *= 6; - } - ERR_FAIL_COND_V(p_layer >= layer_count, ERR_INVALID_PARAMETER); - - uint32_t width, height; - uint32_t image_size = get_image_format_required_size(texture->format, texture->width, texture->height, texture->depth, texture->mipmaps, &width, &height); - uint32_t required_size = image_size; - uint32_t required_align = get_compressed_image_format_block_byte_size(texture->format); - if (required_align == 1) { - required_align = get_image_format_pixel_size(texture->format); - } - if ((required_align % 4) != 0) { // Alignment rules are really strange. - required_align *= 4; - } - - ERR_FAIL_COND_V_MSG(required_size != (uint32_t)p_data.size(), ERR_INVALID_PARAMETER, - "Required size for texture update (" + itos(required_size) + ") does not match data supplied size (" + itos(p_data.size()) + ")."); - - uint32_t region_size = texture_upload_region_size_px; - - const uint8_t *r = p_data.ptr(); - - VkCommandBuffer command_buffer = p_use_setup_queue ? frames[frame].setup_command_buffer : frames[frame].draw_command_buffer; - - // Barrier to transfer. - { - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.oldLayout = texture->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = texture->image; - image_memory_barrier.subresourceRange.aspectMask = texture->barrier_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = texture->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - uint32_t mipmap_offset = 0; - - uint32_t logic_width = texture->width; - uint32_t logic_height = texture->height; - - for (uint32_t mm_i = 0; mm_i < texture->mipmaps; mm_i++) { - uint32_t depth; - uint32_t image_total = get_image_format_required_size(texture->format, texture->width, texture->height, texture->depth, mm_i + 1, &width, &height, &depth); - - const uint8_t *read_ptr_mipmap = r + mipmap_offset; - image_size = image_total - mipmap_offset; - - for (uint32_t z = 0; z < depth; z++) { // For 3D textures, depth may be > 0. - - const uint8_t *read_ptr = read_ptr_mipmap + (image_size / depth) * z; - - for (uint32_t y = 0; y < height; y += region_size) { - for (uint32_t x = 0; x < width; x += region_size) { - uint32_t region_w = MIN(region_size, width - x); - uint32_t region_h = MIN(region_size, height - y); - - uint32_t region_logic_w = MIN(region_size, logic_width - x); - uint32_t region_logic_h = MIN(region_size, logic_height - y); - - uint32_t pixel_size = get_image_format_pixel_size(texture->format); - uint32_t to_allocate = region_w * region_h * pixel_size; - to_allocate >>= get_compressed_image_format_pixel_rshift(texture->format); - - uint32_t alloc_offset, alloc_size; - Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - - uint8_t *write_ptr; - - { // Map. - void *data_ptr = nullptr; - VkResult vkerr = vmaMapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation, &data_ptr); - ERR_FAIL_COND_V_MSG(vkerr, ERR_CANT_CREATE, "vmaMapMemory failed with error " + itos(vkerr) + "."); - write_ptr = (uint8_t *)data_ptr; - write_ptr += alloc_offset; - } - - uint32_t block_w, block_h; - get_compressed_image_format_block_dimensions(texture->format, block_w, block_h); - - ERR_FAIL_COND_V(region_w % block_w, ERR_BUG); - ERR_FAIL_COND_V(region_h % block_h, ERR_BUG); - - if (block_w != 1 || block_h != 1) { - // Compressed image (blocks). - // Must copy a block region. - - uint32_t block_size = get_compressed_image_format_block_byte_size(texture->format); - // Re-create current variables in blocky format. - uint32_t xb = x / block_w; - uint32_t yb = y / block_h; - uint32_t wb = width / block_w; - //uint32_t hb = height / block_h; - uint32_t region_wb = region_w / block_w; - uint32_t region_hb = region_h / block_h; - _copy_region(read_ptr, write_ptr, xb, yb, region_wb, region_hb, wb, block_size); - } else { - // Regular image (pixels). - // Must copy a pixel region. - _copy_region(read_ptr, write_ptr, x, y, region_w, region_h, width, pixel_size); - } - - { // Unmap. - vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation); - } - - VkBufferImageCopy buffer_image_copy; - buffer_image_copy.bufferOffset = alloc_offset; - buffer_image_copy.bufferRowLength = 0; // Tightly packed. - buffer_image_copy.bufferImageHeight = 0; // Tightly packed. - - buffer_image_copy.imageSubresource.aspectMask = texture->read_aspect_mask; - buffer_image_copy.imageSubresource.mipLevel = mm_i; - buffer_image_copy.imageSubresource.baseArrayLayer = p_layer; - buffer_image_copy.imageSubresource.layerCount = 1; - - buffer_image_copy.imageOffset.x = x; - buffer_image_copy.imageOffset.y = y; - buffer_image_copy.imageOffset.z = z; - - buffer_image_copy.imageExtent.width = region_logic_w; - buffer_image_copy.imageExtent.height = region_logic_h; - buffer_image_copy.imageExtent.depth = 1; - - vkCmdCopyBufferToImage(command_buffer, staging_buffer_blocks[staging_buffer_current].buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_image_copy); - - staging_buffer_blocks.write[staging_buffer_current].fill_amount = alloc_offset + alloc_size; - } - } - } - - mipmap_offset = image_total; - logic_width = MAX(1u, logic_width >> 1); - logic_height = MAX(1u, logic_height >> 1); - } - - // Barrier to restore layout. - { - uint32_t barrier_flags = 0; - uint32_t access_flags = 0; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; - access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT; - } - - if (barrier_flags == 0) { - barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.dstAccessMask = access_flags; - image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - image_memory_barrier.newLayout = texture->layout; - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = texture->image; - image_memory_barrier.subresourceRange.aspectMask = texture->barrier_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = texture->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - if (texture->used_in_frame != frames_drawn) { - texture->used_in_raster = false; - texture->used_in_compute = false; - texture->used_in_frame = frames_drawn; - } - texture->used_in_transfer = true; - - return OK; -} - -Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex, VkImage p_image, VmaAllocation p_allocation, uint32_t p_layer, bool p_2d) { - uint32_t width, height, depth; - uint32_t image_size = get_image_format_required_size(tex->format, tex->width, tex->height, p_2d ? 1 : tex->depth, tex->mipmaps, &width, &height, &depth); - - Vector<uint8_t> image_data; - image_data.resize(image_size); - - void *img_mem; - vmaMapMemory(allocator, p_allocation, &img_mem); - - uint32_t blockw, blockh; - get_compressed_image_format_block_dimensions(tex->format, blockw, blockh); - uint32_t block_size = get_compressed_image_format_block_byte_size(tex->format); - uint32_t pixel_size = get_image_format_pixel_size(tex->format); - - { - uint8_t *w = image_data.ptrw(); - - uint32_t mipmap_offset = 0; - for (uint32_t mm_i = 0; mm_i < tex->mipmaps; mm_i++) { - uint32_t image_total = get_image_format_required_size(tex->format, tex->width, tex->height, p_2d ? 1 : tex->depth, mm_i + 1, &width, &height, &depth); - - uint8_t *write_ptr_mipmap = w + mipmap_offset; - image_size = image_total - mipmap_offset; - - VkImageSubresource image_sub_resorce; - image_sub_resorce.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - image_sub_resorce.arrayLayer = p_layer; - image_sub_resorce.mipLevel = mm_i; - VkSubresourceLayout layout; - vkGetImageSubresourceLayout(device, p_image, &image_sub_resorce, &layout); - - for (uint32_t z = 0; z < depth; z++) { - uint8_t *write_ptr = write_ptr_mipmap + z * image_size / depth; - const uint8_t *slice_read_ptr = ((uint8_t *)img_mem) + layout.offset + z * layout.depthPitch; - - if (block_size > 1) { - // Compressed. - uint32_t line_width = (block_size * (width / blockw)); - for (uint32_t y = 0; y < height / blockh; y++) { - const uint8_t *rptr = slice_read_ptr + y * layout.rowPitch; - uint8_t *wptr = write_ptr + y * line_width; - - memcpy(wptr, rptr, line_width); - } - - } else { - // Uncompressed. - for (uint32_t y = 0; y < height; y++) { - const uint8_t *rptr = slice_read_ptr + y * layout.rowPitch; - uint8_t *wptr = write_ptr + y * pixel_size * width; - memcpy(wptr, rptr, (uint64_t)pixel_size * width); - } - } - } - - mipmap_offset = image_total; - } - } - - vmaUnmapMemory(allocator, p_allocation); - - return image_data; -} - -Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t p_layer) { - _THREAD_SAFE_METHOD_ - - Texture *tex = texture_owner.get_or_null(p_texture); - ERR_FAIL_NULL_V(tex, Vector<uint8_t>()); - - ERR_FAIL_COND_V_MSG(tex->bound, Vector<uint8_t>(), - "Texture can't be retrieved while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to retrieve this texture."); - ERR_FAIL_COND_V_MSG(!(tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), Vector<uint8_t>(), - "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); - - uint32_t layer_count = tex->layers; - if (tex->type == TEXTURE_TYPE_CUBE || tex->type == TEXTURE_TYPE_CUBE_ARRAY) { - layer_count *= 6; - } - ERR_FAIL_COND_V(p_layer >= layer_count, Vector<uint8_t>()); - - if (tex->usage_flags & TEXTURE_USAGE_CPU_READ_BIT) { - // Does not need anything fancy, map and read. - return _texture_get_data_from_image(tex, tex->image, tex->allocation, p_layer); - } else { - // Compute total image size. - uint32_t width, height, depth; - uint32_t buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps, &width, &height, &depth); - - // Allocate buffer. - VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; // Makes more sense to retrieve. - Buffer tmp_buffer; - _buffer_allocate(&tmp_buffer, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT); - - { // Source image barrier. - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - image_memory_barrier.oldLayout = tex->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = tex->image; - image_memory_barrier.subresourceRange.aspectMask = tex->barrier_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = tex->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - uint32_t computed_w = tex->width; - uint32_t computed_h = tex->height; - uint32_t computed_d = tex->depth; - - uint32_t prev_size = 0; - uint32_t offset = 0; - for (uint32_t i = 0; i < tex->mipmaps; i++) { - VkBufferImageCopy buffer_image_copy; - - uint32_t image_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, i + 1); - uint32_t size = image_size - prev_size; - prev_size = image_size; - - buffer_image_copy.bufferOffset = offset; - buffer_image_copy.bufferImageHeight = 0; - buffer_image_copy.bufferRowLength = 0; - buffer_image_copy.imageSubresource.aspectMask = tex->read_aspect_mask; - buffer_image_copy.imageSubresource.baseArrayLayer = p_layer; - buffer_image_copy.imageSubresource.layerCount = 1; - buffer_image_copy.imageSubresource.mipLevel = i; - buffer_image_copy.imageOffset.x = 0; - buffer_image_copy.imageOffset.y = 0; - buffer_image_copy.imageOffset.z = 0; - buffer_image_copy.imageExtent.width = computed_w; - buffer_image_copy.imageExtent.height = computed_h; - buffer_image_copy.imageExtent.depth = computed_d; - - vkCmdCopyImageToBuffer(command_buffer, tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, tmp_buffer.buffer, 1, &buffer_image_copy); - - computed_w = MAX(1u, computed_w >> 1); - computed_h = MAX(1u, computed_h >> 1); - computed_d = MAX(1u, computed_d >> 1); - offset += size; - } - - { // Restore src. - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - if (tex->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { - image_memory_barrier.dstAccessMask |= VK_ACCESS_SHADER_WRITE_BIT; - } - image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - image_memory_barrier.newLayout = tex->layout; - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = tex->image; - image_memory_barrier.subresourceRange.aspectMask = tex->barrier_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = tex->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - _flush(true); - - void *buffer_mem; - VkResult vkerr = vmaMapMemory(allocator, tmp_buffer.allocation, &buffer_mem); - ERR_FAIL_COND_V_MSG(vkerr, Vector<uint8_t>(), "vmaMapMemory failed with error " + itos(vkerr) + "."); - - Vector<uint8_t> buffer_data; - { - buffer_data.resize(buffer_size); - uint8_t *w = buffer_data.ptrw(); - memcpy(w, buffer_mem, buffer_size); - } - - vmaUnmapMemory(allocator, tmp_buffer.allocation); - - _buffer_free(&tmp_buffer); - - return buffer_data; - } -} - -bool RenderingDeviceVulkan::texture_is_shared(RID p_texture) { - _THREAD_SAFE_METHOD_ - - Texture *tex = texture_owner.get_or_null(p_texture); - ERR_FAIL_NULL_V(tex, false); - return tex->owner.is_valid(); -} - -bool RenderingDeviceVulkan::texture_is_valid(RID p_texture) { - return texture_owner.owns(p_texture); -} - -RD::TextureFormat RenderingDeviceVulkan::texture_get_format(RID p_texture) { - _THREAD_SAFE_METHOD_ - - Texture *tex = texture_owner.get_or_null(p_texture); - ERR_FAIL_NULL_V(tex, TextureFormat()); - - TextureFormat tf; - - tf.format = tex->format; - tf.width = tex->width; - tf.height = tex->height; - tf.depth = tex->depth; - tf.array_layers = tex->layers; - tf.mipmaps = tex->mipmaps; - tf.texture_type = tex->type; - tf.samples = tex->samples; - tf.usage_bits = tex->usage_flags; - tf.shareable_formats = tex->allowed_shared_formats; - tf.is_resolve_buffer = tex->is_resolve_buffer; - - return tf; -} - -Size2i RenderingDeviceVulkan::texture_size(RID p_texture) { - _THREAD_SAFE_METHOD_ - - Texture *tex = texture_owner.get_or_null(p_texture); - ERR_FAIL_NULL_V(tex, Size2i()); - return Size2i(tex->width, tex->height); -} - -uint64_t RenderingDeviceVulkan::texture_get_native_handle(RID p_texture) { - _THREAD_SAFE_METHOD_ - - Texture *tex = texture_owner.get_or_null(p_texture); - ERR_FAIL_NULL_V(tex, 0); - - return (uint64_t)tex->image; -} - -Error RenderingDeviceVulkan::texture_copy(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<BarrierMask> p_post_barrier) { - _THREAD_SAFE_METHOD_ - - Texture *src_tex = texture_owner.get_or_null(p_from_texture); - ERR_FAIL_NULL_V(src_tex, ERR_INVALID_PARAMETER); - - ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, - "Source texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); - ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, - "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); - - uint32_t src_layer_count = src_tex->layers; - uint32_t src_width, src_height, src_depth; - get_image_format_required_size(src_tex->format, src_tex->width, src_tex->height, src_tex->depth, p_src_mipmap + 1, &src_width, &src_height, &src_depth); - if (src_tex->type == TEXTURE_TYPE_CUBE || src_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { - src_layer_count *= 6; - } - - ERR_FAIL_COND_V(p_from.x < 0 || p_from.x + p_size.x > src_width, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_from.y < 0 || p_from.y + p_size.y > src_height, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_from.z < 0 || p_from.z + p_size.z > src_depth, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_src_mipmap >= src_tex->mipmaps, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_src_layer >= src_layer_count, ERR_INVALID_PARAMETER); - - Texture *dst_tex = texture_owner.get_or_null(p_to_texture); - ERR_FAIL_NULL_V(dst_tex, ERR_INVALID_PARAMETER); - - ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER, - "Destination texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); - ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, - "Destination texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be retrieved."); - - uint32_t dst_layer_count = dst_tex->layers; - uint32_t dst_width, dst_height, dst_depth; - get_image_format_required_size(dst_tex->format, dst_tex->width, dst_tex->height, dst_tex->depth, p_dst_mipmap + 1, &dst_width, &dst_height, &dst_depth); - if (dst_tex->type == TEXTURE_TYPE_CUBE || dst_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { - dst_layer_count *= 6; - } - - ERR_FAIL_COND_V(p_to.x < 0 || p_to.x + p_size.x > dst_width, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_to.y < 0 || p_to.y + p_size.y > dst_height, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_to.z < 0 || p_to.z + p_size.z > dst_depth, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_dst_mipmap >= dst_tex->mipmaps, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_dst_layer >= dst_layer_count, ERR_INVALID_PARAMETER); - - ERR_FAIL_COND_V_MSG(src_tex->read_aspect_mask != dst_tex->read_aspect_mask, ERR_INVALID_PARAMETER, - "Source and destination texture must be of the same type (color or depth)."); - - VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - - { - // PRE Copy the image. - - { // Source. - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - image_memory_barrier.oldLayout = src_tex->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = src_tex->image; - image_memory_barrier.subresourceRange.aspectMask = src_tex->barrier_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = p_src_mipmap; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - { // Dest. - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.oldLayout = dst_tex->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = dst_tex->image; - image_memory_barrier.subresourceRange.aspectMask = dst_tex->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = p_dst_mipmap; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.baseArrayLayer = p_dst_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - // COPY. - - { - VkImageCopy image_copy_region; - image_copy_region.srcSubresource.aspectMask = src_tex->read_aspect_mask; - image_copy_region.srcSubresource.baseArrayLayer = p_src_layer; - image_copy_region.srcSubresource.layerCount = 1; - image_copy_region.srcSubresource.mipLevel = p_src_mipmap; - image_copy_region.srcOffset.x = p_from.x; - image_copy_region.srcOffset.y = p_from.y; - image_copy_region.srcOffset.z = p_from.z; - - image_copy_region.dstSubresource.aspectMask = dst_tex->read_aspect_mask; - image_copy_region.dstSubresource.baseArrayLayer = p_dst_layer; - image_copy_region.dstSubresource.layerCount = 1; - image_copy_region.dstSubresource.mipLevel = p_dst_mipmap; - image_copy_region.dstOffset.x = p_to.x; - image_copy_region.dstOffset.y = p_to.y; - image_copy_region.dstOffset.z = p_to.z; - - image_copy_region.extent.width = p_size.x; - image_copy_region.extent.height = p_size.y; - image_copy_region.extent.depth = p_size.z; - - vkCmdCopyImage(command_buffer, src_tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_tex->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy_region); - } - - // RESTORE LAYOUT for SRC and DST. - - uint32_t barrier_flags = 0; - uint32_t access_flags = 0; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; - access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT; - } - - if (barrier_flags == 0) { - barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - { // Restore src. - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - image_memory_barrier.dstAccessMask = access_flags; - image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - image_memory_barrier.newLayout = src_tex->layout; - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = src_tex->image; - image_memory_barrier.subresourceRange.aspectMask = src_tex->barrier_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = p_src_mipmap; - image_memory_barrier.subresourceRange.levelCount = src_tex->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - { // Make dst readable. - - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.dstAccessMask = access_flags; - image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - image_memory_barrier.newLayout = dst_tex->layout; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = dst_tex->image; - image_memory_barrier.subresourceRange.aspectMask = dst_tex->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = p_src_mipmap; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - } - - if (dst_tex->used_in_frame != frames_drawn) { - dst_tex->used_in_raster = false; - dst_tex->used_in_compute = false; - dst_tex->used_in_frame = frames_drawn; - } - dst_tex->used_in_transfer = true; - - return OK; -} - -Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID p_to_texture, BitField<BarrierMask> p_post_barrier) { - _THREAD_SAFE_METHOD_ - - Texture *src_tex = texture_owner.get_or_null(p_from_texture); - ERR_FAIL_NULL_V(src_tex, ERR_INVALID_PARAMETER); - - ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, - "Source texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); - ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, - "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); - - ERR_FAIL_COND_V_MSG(src_tex->type != TEXTURE_TYPE_2D, ERR_INVALID_PARAMETER, "Source texture must be 2D (or a slice of a 3D/Cube texture)"); - ERR_FAIL_COND_V_MSG(src_tex->samples == TEXTURE_SAMPLES_1, ERR_INVALID_PARAMETER, "Source texture must be multisampled."); - - Texture *dst_tex = texture_owner.get_or_null(p_to_texture); - ERR_FAIL_NULL_V(dst_tex, ERR_INVALID_PARAMETER); - - ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER, - "Destination texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); - ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, - "Destination texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be retrieved."); - - ERR_FAIL_COND_V_MSG(dst_tex->type != TEXTURE_TYPE_2D, ERR_INVALID_PARAMETER, "Destination texture must be 2D (or a slice of a 3D/Cube texture)."); - ERR_FAIL_COND_V_MSG(dst_tex->samples != TEXTURE_SAMPLES_1, ERR_INVALID_PARAMETER, "Destination texture must not be multisampled."); - - ERR_FAIL_COND_V_MSG(src_tex->format != dst_tex->format, ERR_INVALID_PARAMETER, "Source and Destination textures must be the same format."); - ERR_FAIL_COND_V_MSG(src_tex->width != dst_tex->width && src_tex->height != dst_tex->height && src_tex->depth != dst_tex->depth, ERR_INVALID_PARAMETER, "Source and Destination textures must have the same dimensions."); - - ERR_FAIL_COND_V_MSG(src_tex->read_aspect_mask != dst_tex->read_aspect_mask, ERR_INVALID_PARAMETER, - "Source and destination texture must be of the same type (color or depth)."); - - VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - - { - // PRE Copy the image. - - { // Source. - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - image_memory_barrier.oldLayout = src_tex->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = src_tex->image; - image_memory_barrier.subresourceRange.aspectMask = src_tex->barrier_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = src_tex->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.baseArrayLayer = src_tex->base_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - { // Dest. - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.oldLayout = dst_tex->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = dst_tex->image; - image_memory_barrier.subresourceRange.aspectMask = dst_tex->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = dst_tex->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.baseArrayLayer = dst_tex->base_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - // COPY. - - { - VkImageResolve image_copy_region; - image_copy_region.srcSubresource.aspectMask = src_tex->read_aspect_mask; - image_copy_region.srcSubresource.baseArrayLayer = src_tex->base_layer; - image_copy_region.srcSubresource.layerCount = 1; - image_copy_region.srcSubresource.mipLevel = src_tex->base_mipmap; - image_copy_region.srcOffset.x = 0; - image_copy_region.srcOffset.y = 0; - image_copy_region.srcOffset.z = 0; - - image_copy_region.dstSubresource.aspectMask = dst_tex->read_aspect_mask; - image_copy_region.dstSubresource.baseArrayLayer = dst_tex->base_layer; - image_copy_region.dstSubresource.layerCount = 1; - image_copy_region.dstSubresource.mipLevel = dst_tex->base_mipmap; - image_copy_region.dstOffset.x = 0; - image_copy_region.dstOffset.y = 0; - image_copy_region.dstOffset.z = 0; - - image_copy_region.extent.width = src_tex->width; - image_copy_region.extent.height = src_tex->height; - image_copy_region.extent.depth = src_tex->depth; - - vkCmdResolveImage(command_buffer, src_tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_tex->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy_region); - } - - // RESTORE LAYOUT for SRC and DST. - - uint32_t barrier_flags = 0; - uint32_t access_flags = 0; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; - access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT; - } - - if (barrier_flags == 0) { - barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - { // Restore src. - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - image_memory_barrier.dstAccessMask = access_flags; - image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - image_memory_barrier.newLayout = src_tex->layout; - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = src_tex->image; - image_memory_barrier.subresourceRange.aspectMask = src_tex->barrier_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = src_tex->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.baseArrayLayer = src_tex->base_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - { // Make dst readable. - - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.dstAccessMask = access_flags; - image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - image_memory_barrier.newLayout = dst_tex->layout; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = dst_tex->image; - image_memory_barrier.subresourceRange.aspectMask = dst_tex->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = dst_tex->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.baseArrayLayer = dst_tex->base_layer; - image_memory_barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - } - - return OK; -} - -Error RenderingDeviceVulkan::texture_clear(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<BarrierMask> p_post_barrier) { - _THREAD_SAFE_METHOD_ - - Texture *src_tex = texture_owner.get_or_null(p_texture); - ERR_FAIL_NULL_V(src_tex, ERR_INVALID_PARAMETER); - - ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, - "Source texture can't be cleared while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to clear this texture."); - - ERR_FAIL_COND_V(p_layers == 0, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_mipmaps == 0, ERR_INVALID_PARAMETER); - - ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, - "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be cleared."); - - uint32_t src_layer_count = src_tex->layers; - if (src_tex->type == TEXTURE_TYPE_CUBE || src_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { - src_layer_count *= 6; - } - - ERR_FAIL_COND_V(p_base_mipmap + p_mipmaps > src_tex->mipmaps, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_base_layer + p_layers > src_layer_count, ERR_INVALID_PARAMETER); - - VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - - VkImageLayout clear_layout = (src_tex->layout == VK_IMAGE_LAYOUT_GENERAL) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - - // NOTE: Perhaps the valid stages/accesses for a given owner should be a property of the owner. (Here and places like _get_buffer_from_owner.) - const VkPipelineStageFlags valid_texture_stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - constexpr VkAccessFlags read_access = VK_ACCESS_SHADER_READ_BIT; - constexpr VkAccessFlags read_write_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - const VkAccessFlags valid_texture_access = (src_tex->usage_flags & TEXTURE_USAGE_STORAGE_BIT) ? read_write_access : read_access; - - { // Barrier from previous access with optional layout change (see clear_layout logic above). - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = valid_texture_access; - image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.oldLayout = src_tex->layout; - image_memory_barrier.newLayout = clear_layout; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = src_tex->image; - image_memory_barrier.subresourceRange.aspectMask = src_tex->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = src_tex->base_mipmap + p_base_mipmap; - image_memory_barrier.subresourceRange.levelCount = p_mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = src_tex->base_layer + p_base_layer; - image_memory_barrier.subresourceRange.layerCount = p_layers; - - vkCmdPipelineBarrier(command_buffer, valid_texture_stages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - VkClearColorValue clear_color; - clear_color.float32[0] = p_color.r; - clear_color.float32[1] = p_color.g; - clear_color.float32[2] = p_color.b; - clear_color.float32[3] = p_color.a; - - VkImageSubresourceRange range; - range.aspectMask = src_tex->read_aspect_mask; - range.baseArrayLayer = src_tex->base_layer + p_base_layer; - range.layerCount = p_layers; - range.baseMipLevel = src_tex->base_mipmap + p_base_mipmap; - range.levelCount = p_mipmaps; - - vkCmdClearColorImage(command_buffer, src_tex->image, clear_layout, &clear_color, 1, &range); - - { // Barrier to post clear accesses (changing back the layout if needed). - - uint32_t barrier_flags = 0; - uint32_t access_flags = 0; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; - access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT; - } - - if (barrier_flags == 0) { - barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.dstAccessMask = access_flags; - image_memory_barrier.oldLayout = clear_layout; - image_memory_barrier.newLayout = src_tex->layout; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = src_tex->image; - image_memory_barrier.subresourceRange.aspectMask = src_tex->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = src_tex->base_mipmap + p_base_mipmap; - image_memory_barrier.subresourceRange.levelCount = p_mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = src_tex->base_layer + p_base_layer; - image_memory_barrier.subresourceRange.layerCount = p_layers; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - } - - if (src_tex->used_in_frame != frames_drawn) { - src_tex->used_in_raster = false; - src_tex->used_in_compute = false; - src_tex->used_in_frame = frames_drawn; - } - src_tex->used_in_transfer = true; - - return OK; -} - -bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_format, BitField<RenderingDevice::TextureUsageBits> p_usage) const { - ERR_FAIL_INDEX_V(p_format, DATA_FORMAT_MAX, false); - - _THREAD_SAFE_METHOD_ - - // Validate that this image is supported for the intended use. - VkFormatProperties properties; - vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), vulkan_formats[p_format], &properties); - VkFormatFeatureFlags flags; - - if (p_usage.has_flag(TEXTURE_USAGE_CPU_READ_BIT)) { - flags = properties.linearTilingFeatures; - } else { - flags = properties.optimalTilingFeatures; - } - - if (p_usage.has_flag(TEXTURE_USAGE_SAMPLING_BIT) && !(flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { - return false; - } - - if (p_usage.has_flag(TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) && !(flags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { - return false; - } - - if (p_usage.has_flag(TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !(flags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - return false; - } - - if (p_usage.has_flag(TEXTURE_USAGE_STORAGE_BIT) && !(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)) { - return false; - } - - if (p_usage.has_flag(TEXTURE_USAGE_STORAGE_ATOMIC_BIT) && !(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT)) { - return false; - } - - // Validation via VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR fails if VRS attachment is not supported. - if (p_usage.has_flag(TEXTURE_USAGE_VRS_ATTACHMENT_BIT) && p_format != DATA_FORMAT_R8_UINT) { - return false; - } - - return true; -} - -/********************/ -/**** ATTACHMENT ****/ -/********************/ - -VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count, Vector<TextureSamples> *r_samples) { - // Set up dependencies from/to external equivalent to the default (implicit) one, and then amend them. - const VkPipelineStageFlags default_access_mask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | // From Section 7.1 of Vulkan API Spec v1.1.148. - VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR; - - VkPipelineStageFlags reading_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; - VkSubpassDependency2KHR dependencies[2] = { - { VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, nullptr, VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, default_access_mask, 0, 0 }, - { VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, nullptr, 0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, default_access_mask, 0, 0, 0 } - }; - VkSubpassDependency2KHR &dependency_from_external = dependencies[0]; - VkSubpassDependency2KHR &dependency_to_external = dependencies[1]; - LocalVector<int32_t> attachment_last_pass; - attachment_last_pass.resize(p_attachments.size()); - - if (p_view_count > 1) { - const VulkanContext::MultiviewCapabilities capabilities = context->get_multiview_capabilities(); - - // This only works with multiview! - ERR_FAIL_COND_V_MSG(!capabilities.is_supported, VK_NULL_HANDLE, "Multiview not supported"); - - // Make sure we limit this to the number of views we support. - ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, VK_NULL_HANDLE, "Hardware does not support requested number of views for Multiview render pass"); - } - - // These are only used if we use multiview but we need to define them in scope. - const uint32_t view_mask = (1 << p_view_count) - 1; - const uint32_t correlation_mask = (1 << p_view_count) - 1; - - Vector<VkAttachmentDescription2KHR> attachments; - Vector<int> attachment_remap; - - for (int i = 0; i < p_attachments.size(); i++) { - if (p_attachments[i].usage_flags == AttachmentFormat::UNUSED_ATTACHMENT) { - attachment_remap.push_back(VK_ATTACHMENT_UNUSED); - continue; - } - - ERR_FAIL_INDEX_V(p_attachments[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); - ERR_FAIL_INDEX_V(p_attachments[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); - ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT | TEXTURE_USAGE_VRS_ATTACHMENT_BIT)), - VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth-stencil, input or VRS) bit set."); - - VkAttachmentDescription2KHR description = {}; - description.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR; - description.pNext = nullptr; - description.flags = 0; - description.format = vulkan_formats[p_attachments[i].format]; - description.samples = _ensure_supported_sample_count(p_attachments[i].samples); - - bool is_sampled = p_attachments[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT; - bool is_storage = p_attachments[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT; - bool is_depth = p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - - // We can setup a framebuffer where we write to our VRS texture to set it up. - // We make the assumption here that if our texture is actually used as our VRS attachment. - // It is used as such for each subpass. This is fairly certain seeing the restrictions on subpasses. - bool is_vrs = p_attachments[i].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && i == p_passes[0].vrs_attachment; - - if (is_vrs) { - // For VRS we only read, there is no writing to this texture. - description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - description.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - } else { - // For each UNDEFINED, assume the prior use was a *read*, as we'd be discarding the output of a write. - // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution synchronization vs - // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that - // stage. - - switch (is_depth ? p_initial_depth_action : p_initial_action) { - case INITIAL_ACTION_CLEAR_REGION: - case INITIAL_ACTION_CLEAR: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - dependency_from_external.srcStageMask |= reading_stages; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. - dependency_from_external.srcStageMask |= reading_stages; - } - } break; - case INITIAL_ACTION_KEEP: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - dependency_from_external.srcStageMask |= reading_stages; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. - dependency_from_external.srcStageMask |= reading_stages; - } - } break; - case INITIAL_ACTION_DROP: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - dependency_from_external.srcStageMask |= reading_stages; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. - dependency_from_external.srcStageMask |= reading_stages; - } - } break; - case INITIAL_ACTION_CLEAR_REGION_CONTINUE: - case INITIAL_ACTION_CONTINUE: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - description.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - description.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. - dependency_from_external.srcStageMask |= reading_stages; - } - } break; - default: { - ERR_FAIL_V(VK_NULL_HANDLE); // Should never reach here. - } - } - } - - bool used_last = false; - - { - int last_pass = p_passes.size() - 1; - - if (is_depth) { - // Likely missing depth resolve? - if (p_passes[last_pass].depth_attachment == i) { - used_last = true; - } - } else if (is_vrs) { - if (p_passes[last_pass].vrs_attachment == i) { - used_last = true; - } - } else { - if (p_passes[last_pass].resolve_attachments.size()) { - // If using resolve attachments, check resolve attachments. - for (int j = 0; j < p_passes[last_pass].resolve_attachments.size(); j++) { - if (p_passes[last_pass].resolve_attachments[j] == i) { - used_last = true; - break; - } - } - } - if (!used_last) { - for (int j = 0; j < p_passes[last_pass].color_attachments.size(); j++) { - if (p_passes[last_pass].color_attachments[j] == i) { - used_last = true; - break; - } - } - } - } - - if (!used_last) { - for (int j = 0; j < p_passes[last_pass].preserve_attachments.size(); j++) { - if (p_passes[last_pass].preserve_attachments[j] == i) { - used_last = true; - break; - } - } - } - } - - FinalAction final_action = p_final_action; - FinalAction final_depth_action = p_final_depth_action; - - if (!used_last) { - if (is_depth) { - final_depth_action = FINAL_ACTION_DISCARD; - - } else { - final_action = FINAL_ACTION_DISCARD; - } - } - - if (is_vrs) { - // We don't change our VRS texture during this process. - - description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - // TODO: Do we need to update our external dependency? - // update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, false); - } else { - switch (is_depth ? final_depth_action : final_action) { - case FINAL_ACTION_READ: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, false); - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, true); - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. - // TODO: What does this mean about the next usage (and thus appropriate dependency masks. - } - } break; - case FINAL_ACTION_DISCARD: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - } else { - description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. - } - } break; - case FINAL_ACTION_CONTINUE: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - description.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } else { - description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. - } - - } break; - default: { - ERR_FAIL_V(VK_NULL_HANDLE); // Should never reach here. - } - } - } - - attachment_last_pass[i] = -1; - attachment_remap.push_back(attachments.size()); - attachments.push_back(description); - } - - LocalVector<VkSubpassDescription2KHR> subpasses; - LocalVector<LocalVector<VkAttachmentReference2KHR>> color_reference_array; - LocalVector<LocalVector<VkAttachmentReference2KHR>> input_reference_array; - LocalVector<LocalVector<VkAttachmentReference2KHR>> resolve_reference_array; - LocalVector<LocalVector<uint32_t>> preserve_reference_array; - LocalVector<VkAttachmentReference2KHR> depth_reference_array; - LocalVector<VkAttachmentReference2KHR> vrs_reference_array; - LocalVector<VkFragmentShadingRateAttachmentInfoKHR> vrs_attachment_info_array; - - subpasses.resize(p_passes.size()); - color_reference_array.resize(p_passes.size()); - input_reference_array.resize(p_passes.size()); - resolve_reference_array.resize(p_passes.size()); - preserve_reference_array.resize(p_passes.size()); - depth_reference_array.resize(p_passes.size()); - vrs_reference_array.resize(p_passes.size()); - vrs_attachment_info_array.resize(p_passes.size()); - - LocalVector<VkSubpassDependency2KHR> subpass_dependencies; - - for (int i = 0; i < p_passes.size(); i++) { - const FramebufferPass *pass = &p_passes[i]; - - LocalVector<VkAttachmentReference2KHR> &color_references = color_reference_array[i]; - - TextureSamples texture_samples = TEXTURE_SAMPLES_1; - bool is_multisample_first = true; - void *subpass_nextptr = nullptr; - - for (int j = 0; j < pass->color_attachments.size(); j++) { - int32_t attachment = pass->color_attachments[j]; - VkAttachmentReference2KHR reference; - reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; - reference.pNext = nullptr; - if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { - reference.attachment = VK_ATTACHMENT_UNUSED; - reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; - } else { - ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), color attachment (" + itos(j) + ")."); - ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as color attachment."); - ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - - if (is_multisample_first) { - texture_samples = p_attachments[attachment].samples; - is_multisample_first = false; - } else { - ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples."); - } - reference.attachment = attachment_remap[attachment]; - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachment_last_pass[attachment] = i; - } - reference.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - color_references.push_back(reference); - } - - LocalVector<VkAttachmentReference2KHR> &input_references = input_reference_array[i]; - - for (int j = 0; j < pass->input_attachments.size(); j++) { - int32_t attachment = pass->input_attachments[j]; - VkAttachmentReference2KHR reference; - reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; - reference.pNext = nullptr; - if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { - reference.attachment = VK_ATTACHMENT_UNUSED; - reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; - } else { - ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), input attachment (" + itos(j) + ")."); - ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it isn't marked as an input texture."); - ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - reference.attachment = attachment_remap[attachment]; - reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - attachment_last_pass[attachment] = i; - } - reference.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - input_references.push_back(reference); - } - - LocalVector<VkAttachmentReference2KHR> &resolve_references = resolve_reference_array[i]; - - if (pass->resolve_attachments.size() > 0) { - ERR_FAIL_COND_V_MSG(pass->resolve_attachments.size() != pass->color_attachments.size(), VK_NULL_HANDLE, "The amount of resolve attachments (" + itos(pass->resolve_attachments.size()) + ") must match the number of color attachments (" + itos(pass->color_attachments.size()) + ")."); - ERR_FAIL_COND_V_MSG(texture_samples == TEXTURE_SAMPLES_1, VK_NULL_HANDLE, "Resolve attachments specified, but color attachments are not multisample."); - } - for (int j = 0; j < pass->resolve_attachments.size(); j++) { - int32_t attachment = pass->resolve_attachments[j]; - VkAttachmentReference2KHR reference; - reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; - reference.pNext = nullptr; - if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { - reference.attachment = VK_ATTACHMENT_UNUSED; - reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; - } else { - ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + ")."); - ERR_FAIL_COND_V_MSG(pass->color_attachments[j] == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + "), the respective color attachment is marked as unused."); - ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment, it isn't marked as a color texture."); - ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - bool multisample = p_attachments[attachment].samples > TEXTURE_SAMPLES_1; - ERR_FAIL_COND_V_MSG(multisample, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachments can't be multisample."); - reference.attachment = attachment_remap[attachment]; - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - attachment_last_pass[attachment] = i; - } - reference.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - resolve_references.push_back(reference); - } - - VkAttachmentReference2KHR &depth_stencil_reference = depth_reference_array[i]; - depth_stencil_reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; - depth_stencil_reference.pNext = nullptr; - - if (pass->depth_attachment != FramebufferPass::ATTACHMENT_UNUSED) { - int32_t attachment = pass->depth_attachment; - ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), depth attachment."); - ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not a depth attachment."); - ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - depth_stencil_reference.attachment = attachment_remap[attachment]; - depth_stencil_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - depth_stencil_reference.aspectMask = VK_IMAGE_ASPECT_NONE; - attachment_last_pass[attachment] = i; - - if (is_multisample_first) { - texture_samples = p_attachments[attachment].samples; - is_multisample_first = false; - } else { - ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples including the depth."); - } - - } else { - depth_stencil_reference.attachment = VK_ATTACHMENT_UNUSED; - depth_stencil_reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; - } - - if (context->get_vrs_capabilities().attachment_vrs_supported && pass->vrs_attachment != FramebufferPass::ATTACHMENT_UNUSED) { - int32_t attachment = pass->vrs_attachment; - ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), VRS attachment."); - ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as VRS, but it's not a VRS attachment."); - ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer VRS attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - - VkAttachmentReference2KHR &vrs_reference = vrs_reference_array[i]; - vrs_reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; - vrs_reference.pNext = nullptr; - vrs_reference.attachment = attachment_remap[attachment]; - vrs_reference.layout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR; - vrs_reference.aspectMask = VK_IMAGE_ASPECT_NONE; - - Size2i texel_size = context->get_vrs_capabilities().texel_size; - - VkFragmentShadingRateAttachmentInfoKHR &vrs_attachment_info = vrs_attachment_info_array[i]; - vrs_attachment_info.sType = VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR; - vrs_attachment_info.pNext = nullptr; - vrs_attachment_info.pFragmentShadingRateAttachment = &vrs_reference; - vrs_attachment_info.shadingRateAttachmentTexelSize = { uint32_t(texel_size.x), uint32_t(texel_size.y) }; - - attachment_last_pass[attachment] = i; - - subpass_nextptr = &vrs_attachment_info; - } - - LocalVector<uint32_t> &preserve_references = preserve_reference_array[i]; - - for (int j = 0; j < pass->preserve_attachments.size(); j++) { - int32_t attachment = pass->preserve_attachments[j]; - - ERR_FAIL_COND_V_MSG(attachment == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + "). Preserve attachments can't be unused."); - - ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + ")."); - - if (attachment_last_pass[attachment] != i) { - // Preserve can still be used to keep depth or color from being discarded after use. - attachment_last_pass[attachment] = i; - preserve_references.push_back(attachment); - } - } - - VkSubpassDescription2KHR &subpass = subpasses[i]; - subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; - subpass.pNext = subpass_nextptr; - subpass.flags = 0; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - if (p_view_count == 1) { - // VUID-VkSubpassDescription2-multiview-06558: If the multiview feature is not enabled, viewMask must be 0. - subpass.viewMask = 0; - } else { - subpass.viewMask = view_mask; - } - subpass.inputAttachmentCount = input_references.size(); - if (input_references.size()) { - subpass.pInputAttachments = input_references.ptr(); - } else { - subpass.pInputAttachments = nullptr; - } - subpass.colorAttachmentCount = color_references.size(); - if (color_references.size()) { - subpass.pColorAttachments = color_references.ptr(); - } else { - subpass.pColorAttachments = nullptr; - } - if (depth_stencil_reference.attachment != VK_ATTACHMENT_UNUSED) { - subpass.pDepthStencilAttachment = &depth_stencil_reference; - } else { - subpass.pDepthStencilAttachment = nullptr; - } - - if (resolve_references.size()) { - subpass.pResolveAttachments = resolve_references.ptr(); - } else { - subpass.pResolveAttachments = nullptr; - } - - subpass.preserveAttachmentCount = preserve_references.size(); - if (preserve_references.size()) { - subpass.pPreserveAttachments = preserve_references.ptr(); - } else { - subpass.pPreserveAttachments = nullptr; - } - - if (r_samples) { - r_samples->push_back(texture_samples); - } - - if (i > 0) { - VkSubpassDependency2KHR dependency; - dependency.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR; - dependency.pNext = nullptr; - dependency.srcSubpass = i - 1; - dependency.dstSubpass = i; - dependency.srcStageMask = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - - dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; - dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - dependency.viewOffset = 0; - subpass_dependencies.push_back(dependency); - } - /* - // NOTE: Big Mallet Approach -- any layout transition causes a full barrier. - if (reference.layout != description.initialLayout) { - // NOTE: This should be smarter based on the texture's knowledge of its previous role. - dependency_from_external.srcStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - dependency_from_external.srcAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; - } - if (reference.layout != description.finalLayout) { - // NOTE: This should be smarter based on the texture's knowledge of its subsequent role. - dependency_to_external.dstStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - dependency_to_external.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; - } - */ - } - - VkRenderPassCreateInfo2KHR render_pass_create_info; - render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; - render_pass_create_info.pNext = nullptr; - render_pass_create_info.flags = 0; - - render_pass_create_info.attachmentCount = attachments.size(); - render_pass_create_info.pAttachments = attachments.ptr(); - render_pass_create_info.subpassCount = subpasses.size(); - render_pass_create_info.pSubpasses = subpasses.ptr(); - // Commenting this because it seems it just avoids raster and compute to work at the same time. - // Other barriers seem to be protecting the render pass fine. - // render_pass_create_info.dependencyCount = 2; - // render_pass_create_info.pDependencies = dependencies; - - render_pass_create_info.dependencyCount = subpass_dependencies.size(); - if (subpass_dependencies.size()) { - render_pass_create_info.pDependencies = subpass_dependencies.ptr(); - } else { - render_pass_create_info.pDependencies = nullptr; - } - - if (p_view_count == 1) { - // VUID-VkRenderPassCreateInfo2-viewMask-03057: If the VkSubpassDescription2::viewMask member of all elements of pSubpasses is 0, correlatedViewMaskCount must be 0. - render_pass_create_info.correlatedViewMaskCount = 0; - render_pass_create_info.pCorrelatedViewMasks = nullptr; - } else { - render_pass_create_info.correlatedViewMaskCount = 1; - render_pass_create_info.pCorrelatedViewMasks = &correlation_mask; - } - - Vector<uint32_t> view_masks; - VkRenderPassMultiviewCreateInfo render_pass_multiview_create_info; - - if ((p_view_count > 1) && !context->supports_renderpass2()) { - // This is only required when using vkCreateRenderPass, we add it if vkCreateRenderPass2KHR is not supported - // resulting this in being passed to our vkCreateRenderPass fallback. - - // Set view masks for each subpass. - for (uint32_t i = 0; i < subpasses.size(); i++) { - view_masks.push_back(view_mask); - } - - render_pass_multiview_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO; - render_pass_multiview_create_info.pNext = nullptr; - render_pass_multiview_create_info.subpassCount = subpasses.size(); - render_pass_multiview_create_info.pViewMasks = view_masks.ptr(); - render_pass_multiview_create_info.dependencyCount = 0; - render_pass_multiview_create_info.pViewOffsets = nullptr; - render_pass_multiview_create_info.correlationMaskCount = 1; - render_pass_multiview_create_info.pCorrelationMasks = &correlation_mask; - - render_pass_create_info.pNext = &render_pass_multiview_create_info; - } - - VkRenderPass render_pass; - VkResult res = context->vkCreateRenderPass2KHR(device, &render_pass_create_info, nullptr, &render_pass); - ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass2KHR failed with error " + itos(res) + "."); - - return render_pass; -} - -RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count) { - FramebufferPass pass; - for (int i = 0; i < p_format.size(); i++) { - if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - pass.depth_attachment = i; - } else { - pass.color_attachments.push_back(i); - } - } - - Vector<FramebufferPass> passes; - passes.push_back(pass); - return framebuffer_format_create_multipass(p_format, passes, p_view_count); -} -RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { - _THREAD_SAFE_METHOD_ - - FramebufferFormatKey key; - key.attachments = p_attachments; - key.passes = p_passes; - key.view_count = p_view_count; - - const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); - if (E) { - // Exists, return. - return E->get(); - } - - Vector<TextureSamples> samples; - VkRenderPass render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, p_view_count, &samples); // Actions don't matter for this use case. - - if (render_pass == VK_NULL_HANDLE) { // Was likely invalid. - return INVALID_ID; - } - FramebufferFormatID id = FramebufferFormatID(framebuffer_format_cache.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT)); - - E = framebuffer_format_cache.insert(key, id); - FramebufferFormat fb_format; - fb_format.E = E; - fb_format.render_pass = render_pass; - fb_format.pass_samples = samples; - fb_format.view_count = p_view_count; - framebuffer_formats[id] = fb_format; - return id; -} - -RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_empty(TextureSamples p_samples) { - FramebufferFormatKey key; - key.passes.push_back(FramebufferPass()); - - const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); - if (E) { - // Exists, return. - return E->get(); - } - - VkSubpassDescription2KHR subpass; - subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; - subpass.pNext = nullptr; - subpass.flags = 0; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.viewMask = 0; - subpass.inputAttachmentCount = 0; // Unsupported for now. - subpass.pInputAttachments = nullptr; - subpass.colorAttachmentCount = 0; - subpass.pColorAttachments = nullptr; - subpass.pDepthStencilAttachment = nullptr; - subpass.pResolveAttachments = nullptr; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = nullptr; - - VkRenderPassCreateInfo2KHR render_pass_create_info; - render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; - render_pass_create_info.pNext = nullptr; - render_pass_create_info.flags = 0; - render_pass_create_info.attachmentCount = 0; - render_pass_create_info.pAttachments = nullptr; - render_pass_create_info.subpassCount = 1; - render_pass_create_info.pSubpasses = &subpass; - render_pass_create_info.dependencyCount = 0; - render_pass_create_info.pDependencies = nullptr; - render_pass_create_info.correlatedViewMaskCount = 0; - render_pass_create_info.pCorrelatedViewMasks = nullptr; - - VkRenderPass render_pass; - VkResult res = context->vkCreateRenderPass2KHR(device, &render_pass_create_info, nullptr, &render_pass); - - ERR_FAIL_COND_V_MSG(res, 0, "vkCreateRenderPass2KHR for empty fb failed with error " + itos(res) + "."); - - if (render_pass == VK_NULL_HANDLE) { // Was likely invalid. - return INVALID_ID; - } - - FramebufferFormatID id = FramebufferFormatID(framebuffer_format_cache.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT)); - - E = framebuffer_format_cache.insert(key, id); - - FramebufferFormat fb_format; - fb_format.E = E; - fb_format.render_pass = render_pass; - fb_format.pass_samples.push_back(p_samples); - framebuffer_formats[id] = fb_format; - return id; -} - -RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass) { - HashMap<FramebufferFormatID, FramebufferFormat>::Iterator E = framebuffer_formats.find(p_format); - ERR_FAIL_COND_V(!E, TEXTURE_SAMPLES_1); - ERR_FAIL_COND_V(p_pass >= uint32_t(E->value.pass_samples.size()), TEXTURE_SAMPLES_1); - - return E->value.pass_samples[p_pass]; -} - -/***********************/ -/**** RENDER TARGET ****/ -/***********************/ - -RID RenderingDeviceVulkan::framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples, FramebufferFormatID p_format_check) { - _THREAD_SAFE_METHOD_ - Framebuffer framebuffer; - framebuffer.format_id = framebuffer_format_create_empty(p_samples); - ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID()); - framebuffer.size = p_size; - framebuffer.view_count = 1; - - RID id = framebuffer_owner.make_rid(framebuffer); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - return id; -} - -RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check, uint32_t p_view_count) { - _THREAD_SAFE_METHOD_ - - FramebufferPass pass; - - for (int i = 0; i < p_texture_attachments.size(); i++) { - Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]); - - ERR_FAIL_COND_V_MSG(texture && texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); - - if (texture && texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - pass.depth_attachment = i; - } else if (texture && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { - pass.vrs_attachment = i; - } else { - if (texture && texture->is_resolve_buffer) { - pass.resolve_attachments.push_back(i); - } else { - pass.color_attachments.push_back(texture ? i : FramebufferPass::ATTACHMENT_UNUSED); - } - } - } - - Vector<FramebufferPass> passes; - passes.push_back(pass); - - return framebuffer_create_multipass(p_texture_attachments, passes, p_format_check, p_view_count); -} - -RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { - _THREAD_SAFE_METHOD_ - - Vector<AttachmentFormat> attachments; - attachments.resize(p_texture_attachments.size()); - Size2i size; - bool size_set = false; - for (int i = 0; i < p_texture_attachments.size(); i++) { - AttachmentFormat af; - Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]); - if (!texture) { - af.usage_flags = AttachmentFormat::UNUSED_ATTACHMENT; - } else { - ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); - - if (!size_set) { - size.width = texture->width; - size.height = texture->height; - size_set = true; - } else if (texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { - // If this is not the first attachment we assume this is used as the VRS attachment. - // In this case this texture will be 1/16th the size of the color attachment. - // So we skip the size check. - } else { - ERR_FAIL_COND_V_MSG((uint32_t)size.width != texture->width || (uint32_t)size.height != texture->height, RID(), - "All textures in a framebuffer should be the same size."); - } - - af.format = texture->format; - af.samples = texture->samples; - af.usage_flags = texture->usage_flags; - } - attachments.write[i] = af; - } - - ERR_FAIL_COND_V_MSG(!size_set, RID(), "All attachments unused."); - - FramebufferFormatID format_id = framebuffer_format_create_multipass(attachments, p_passes, p_view_count); - if (format_id == INVALID_ID) { - return RID(); - } - - ERR_FAIL_COND_V_MSG(p_format_check != INVALID_ID && format_id != p_format_check, RID(), - "The format used to check this framebuffer differs from the intended framebuffer format."); - - Framebuffer framebuffer; - framebuffer.format_id = format_id; - framebuffer.texture_ids = p_texture_attachments; - framebuffer.size = size; - framebuffer.view_count = p_view_count; - - RID id = framebuffer_owner.make_rid(framebuffer); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - - for (int i = 0; i < p_texture_attachments.size(); i++) { - if (p_texture_attachments[i].is_valid()) { - _add_dependency(id, p_texture_attachments[i]); - } - } - - return id; -} - -RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_get_format(RID p_framebuffer) { - _THREAD_SAFE_METHOD_ - - Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); - ERR_FAIL_NULL_V(framebuffer, INVALID_ID); - - return framebuffer->format_id; -} - -bool RenderingDeviceVulkan::framebuffer_is_valid(RID p_framebuffer) const { - _THREAD_SAFE_METHOD_ - - return framebuffer_owner.owns(p_framebuffer); -} - -void RenderingDeviceVulkan::framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata) { - _THREAD_SAFE_METHOD_ - - Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); - ERR_FAIL_NULL(framebuffer); - - framebuffer->invalidated_callback = p_callback; - framebuffer->invalidated_callback_userdata = p_userdata; -} - -/*****************/ -/**** SAMPLER ****/ -/*****************/ - -RID RenderingDeviceVulkan::sampler_create(const SamplerState &p_state) { - _THREAD_SAFE_METHOD_ - - VkSamplerCreateInfo sampler_create_info; - sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - sampler_create_info.pNext = nullptr; - sampler_create_info.flags = 0; - sampler_create_info.magFilter = p_state.mag_filter == SAMPLER_FILTER_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; - sampler_create_info.minFilter = p_state.min_filter == SAMPLER_FILTER_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; - sampler_create_info.mipmapMode = p_state.mip_filter == SAMPLER_FILTER_LINEAR ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST; - - ERR_FAIL_INDEX_V(p_state.repeat_u, SAMPLER_REPEAT_MODE_MAX, RID()); - sampler_create_info.addressModeU = address_modes[p_state.repeat_u]; - ERR_FAIL_INDEX_V(p_state.repeat_v, SAMPLER_REPEAT_MODE_MAX, RID()); - sampler_create_info.addressModeV = address_modes[p_state.repeat_v]; - ERR_FAIL_INDEX_V(p_state.repeat_w, SAMPLER_REPEAT_MODE_MAX, RID()); - sampler_create_info.addressModeW = address_modes[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.maxAnisotropy = p_state.anisotropy_max; - sampler_create_info.compareEnable = p_state.enable_compare; - - ERR_FAIL_INDEX_V(p_state.compare_op, COMPARE_OP_MAX, RID()); - sampler_create_info.compareOp = compare_operators[p_state.compare_op]; - - sampler_create_info.minLod = p_state.min_lod; - sampler_create_info.maxLod = p_state.max_lod; - - ERR_FAIL_INDEX_V(p_state.border_color, SAMPLER_BORDER_COLOR_MAX, RID()); - sampler_create_info.borderColor = sampler_border_colors[p_state.border_color]; - - sampler_create_info.unnormalizedCoordinates = p_state.unnormalized_uvw; - - VkSampler sampler; - VkResult res = vkCreateSampler(device, &sampler_create_info, nullptr, &sampler); - ERR_FAIL_COND_V_MSG(res, RID(), "vkCreateSampler failed with error " + itos(res) + "."); - - RID id = sampler_owner.make_rid(sampler); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - return id; -} - -bool RenderingDeviceVulkan::sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const { - ERR_FAIL_INDEX_V(p_format, DATA_FORMAT_MAX, false); - - _THREAD_SAFE_METHOD_ - - // Validate that this image is supported for the intended filtering. - VkFormatProperties properties; - vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), vulkan_formats[p_format], &properties); - - return p_sampler_filter == RD::SAMPLER_FILTER_NEAREST || (p_sampler_filter == RD::SAMPLER_FILTER_LINEAR && (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)); -} - -/**********************/ -/**** VERTEX ARRAY ****/ -/**********************/ - -RID RenderingDeviceVulkan::vertex_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, bool p_use_as_storage) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); - - uint32_t usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - if (p_use_as_storage) { - usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - } - Buffer buffer; - _buffer_allocate(&buffer, p_size_bytes, usage, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); - if (p_data.size()) { - uint64_t data_size = p_data.size(); - const uint8_t *r = p_data.ptr(); - _buffer_update(&buffer, 0, r, data_size); - _buffer_memory_barrier(buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, false); - } - - RID id = vertex_buffer_owner.make_rid(buffer); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - return id; -} - -// Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated. -RenderingDevice::VertexFormatID RenderingDeviceVulkan::vertex_format_create(const Vector<VertexAttribute> &p_vertex_formats) { - _THREAD_SAFE_METHOD_ - - VertexDescriptionKey key; - key.vertex_formats = p_vertex_formats; - - VertexFormatID *idptr = vertex_format_cache.getptr(key); - if (idptr) { - return *idptr; - } - - // Does not exist, create one and cache it. - VertexDescriptionCache vdcache; - vdcache.bindings = memnew_arr(VkVertexInputBindingDescription, p_vertex_formats.size()); - vdcache.attributes = memnew_arr(VkVertexInputAttributeDescription, p_vertex_formats.size()); - - HashSet<int> used_locations; - for (int i = 0; i < p_vertex_formats.size(); i++) { - ERR_CONTINUE(p_vertex_formats[i].format >= DATA_FORMAT_MAX); - ERR_FAIL_COND_V(used_locations.has(p_vertex_formats[i].location), INVALID_ID); - - ERR_FAIL_COND_V_MSG(get_format_vertex_size(p_vertex_formats[i].format) == 0, INVALID_ID, - "Data format for attachment (" + itos(i) + "), '" + named_formats[p_vertex_formats[i].format] + "', is not valid for a vertex array."); - - vdcache.bindings[i].binding = i; - vdcache.bindings[i].stride = p_vertex_formats[i].stride; - vdcache.bindings[i].inputRate = p_vertex_formats[i].frequency == VERTEX_FREQUENCY_INSTANCE ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; - vdcache.attributes[i].binding = i; - vdcache.attributes[i].location = p_vertex_formats[i].location; - vdcache.attributes[i].format = vulkan_formats[p_vertex_formats[i].format]; - vdcache.attributes[i].offset = p_vertex_formats[i].offset; - used_locations.insert(p_vertex_formats[i].location); - } - - vdcache.create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vdcache.create_info.pNext = nullptr; - vdcache.create_info.flags = 0; - - vdcache.create_info.vertexAttributeDescriptionCount = p_vertex_formats.size(); - vdcache.create_info.pVertexAttributeDescriptions = vdcache.attributes; - - vdcache.create_info.vertexBindingDescriptionCount = p_vertex_formats.size(); - vdcache.create_info.pVertexBindingDescriptions = vdcache.bindings; - vdcache.vertex_formats = p_vertex_formats; - - VertexFormatID id = VertexFormatID(vertex_format_cache.size()) | (VertexFormatID(ID_TYPE_VERTEX_FORMAT) << ID_BASE_SHIFT); - vertex_format_cache[key] = id; - vertex_formats[id] = vdcache; - return id; -} - -RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const Vector<RID> &p_src_buffers, const Vector<uint64_t> &p_offsets) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!vertex_formats.has(p_vertex_format), RID()); - const VertexDescriptionCache &vd = vertex_formats[p_vertex_format]; - - ERR_FAIL_COND_V(vd.vertex_formats.size() != p_src_buffers.size(), RID()); - - for (int i = 0; i < p_src_buffers.size(); i++) { - ERR_FAIL_COND_V(!vertex_buffer_owner.owns(p_src_buffers[i]), RID()); - } - - VertexArray vertex_array; - - if (p_offsets.is_empty()) { - vertex_array.offsets.resize_zeroed(p_src_buffers.size()); - } else { - ERR_FAIL_COND_V(p_offsets.size() != p_src_buffers.size(), RID()); - vertex_array.offsets = p_offsets; - } - - vertex_array.vertex_count = p_vertex_count; - vertex_array.description = p_vertex_format; - vertex_array.max_instances_allowed = 0xFFFFFFFF; // By default as many as you want. - for (int i = 0; i < p_src_buffers.size(); i++) { - Buffer *buffer = vertex_buffer_owner.get_or_null(p_src_buffers[i]); - - // Validate with buffer. - { - const VertexAttribute &atf = vd.vertex_formats[i]; - - uint32_t element_size = get_format_vertex_size(atf.format); - ERR_FAIL_COND_V(element_size == 0, RID()); // Should never happens since this was prevalidated. - - if (atf.frequency == VERTEX_FREQUENCY_VERTEX) { - // Validate size for regular drawing. - uint64_t total_size = uint64_t(atf.stride) * (p_vertex_count - 1) + atf.offset + element_size; - - ERR_FAIL_COND_V_MSG(total_size > buffer->size, RID(), - "Attachment (" + itos(i) + ") will read past the end of the buffer."); - - } else { - // Validate size for instances drawing. - uint64_t available = buffer->size - atf.offset; - ERR_FAIL_COND_V_MSG(available < element_size, RID(), - "Attachment (" + itos(i) + ") uses instancing, but it's just too small."); - - uint32_t instances_allowed = available / atf.stride; - vertex_array.max_instances_allowed = MIN(instances_allowed, vertex_array.max_instances_allowed); - } - } - - vertex_array.buffers.push_back(buffer->buffer); - } - - RID id = vertex_array_owner.make_rid(vertex_array); - for (int i = 0; i < p_src_buffers.size(); i++) { - _add_dependency(id, p_src_buffers[i]); - } - - return id; -} - -RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBufferFormat p_format, const Vector<uint8_t> &p_data, bool p_use_restart_indices) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(p_index_count == 0, RID()); - - IndexBuffer index_buffer; - index_buffer.index_type = (p_format == INDEX_BUFFER_FORMAT_UINT16) ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32; - index_buffer.supports_restart_indices = p_use_restart_indices; - index_buffer.index_count = p_index_count; - uint32_t size_bytes = p_index_count * ((p_format == INDEX_BUFFER_FORMAT_UINT16) ? 2 : 4); -#ifdef DEBUG_ENABLED - if (p_data.size()) { - index_buffer.max_index = 0; - ERR_FAIL_COND_V_MSG((uint32_t)p_data.size() != size_bytes, RID(), - "Default index buffer initializer array size (" + itos(p_data.size()) + ") does not match format required size (" + itos(size_bytes) + ")."); - const uint8_t *r = p_data.ptr(); - if (p_format == INDEX_BUFFER_FORMAT_UINT16) { - const uint16_t *index16 = (const uint16_t *)r; - for (uint32_t i = 0; i < p_index_count; i++) { - if (p_use_restart_indices && index16[i] == 0xFFFF) { - continue; // Restart index, ignore. - } - index_buffer.max_index = MAX(index16[i], index_buffer.max_index); - } - } else { - const uint32_t *index32 = (const uint32_t *)r; - for (uint32_t i = 0; i < p_index_count; i++) { - if (p_use_restart_indices && index32[i] == 0xFFFFFFFF) { - continue; // Restart index, ignore. - } - index_buffer.max_index = MAX(index32[i], index_buffer.max_index); - } - } - } else { - index_buffer.max_index = 0xFFFFFFFF; - } -#else - index_buffer.max_index = 0xFFFFFFFF; -#endif - _buffer_allocate(&index_buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); - if (p_data.size()) { - uint64_t data_size = p_data.size(); - const uint8_t *r = p_data.ptr(); - _buffer_update(&index_buffer, 0, r, data_size); - _buffer_memory_barrier(index_buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_INDEX_READ_BIT, false); - } - RID id = index_buffer_owner.make_rid(index_buffer); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - return id; -} - -RID RenderingDeviceVulkan::index_array_create(RID p_index_buffer, uint32_t p_index_offset, uint32_t p_index_count) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!index_buffer_owner.owns(p_index_buffer), RID()); - - IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_index_buffer); - - ERR_FAIL_COND_V(p_index_count == 0, RID()); - ERR_FAIL_COND_V(p_index_offset + p_index_count > index_buffer->index_count, RID()); - - IndexArray index_array; - index_array.max_index = index_buffer->max_index; - index_array.buffer = index_buffer->buffer; - index_array.offset = p_index_offset; - index_array.indices = p_index_count; - index_array.index_type = index_buffer->index_type; - index_array.supports_restart_indices = index_buffer->supports_restart_indices; - - RID id = index_array_owner.make_rid(index_array); - _add_dependency(id, p_index_buffer); - return id; -} - -/****************/ -/**** SHADER ****/ -/****************/ - -static const char *shader_uniform_names[RenderingDevice::UNIFORM_TYPE_MAX] = { - "Sampler", "CombinedSampler", "Texture", "Image", "TextureBuffer", "SamplerTextureBuffer", "ImageBuffer", "UniformBuffer", "StorageBuffer", "InputAttachment" -}; - -static VkShaderStageFlagBits shader_stage_masks[RenderingDevice::SHADER_STAGE_MAX] = { - VK_SHADER_STAGE_VERTEX_BIT, - VK_SHADER_STAGE_FRAGMENT_BIT, - VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, - VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, - VK_SHADER_STAGE_COMPUTE_BIT, -}; - -String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) { - String ret; - const Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_NULL_V(shader, String()); - for (int i = 0; i < shader->sets.size(); i++) { - if (p_set >= 0 && i != p_set) { - continue; - } - for (int j = 0; j < shader->sets[i].uniform_info.size(); j++) { - const UniformInfo &ui = shader->sets[i].uniform_info[j]; - if (!ret.is_empty()) { - ret += "\n"; - } - ret += "Set: " + itos(i) + " Binding: " + itos(ui.binding) + " Type: " + shader_uniform_names[ui.type] + " Writable: " + (ui.writable ? "Y" : "N") + " Length: " + itos(ui.length); - } - } - return ret; -} - -// Version 1: initial. -// Version 2: Added shader name. -// Version 3: Added writable. -// Version 4: 64-bit vertex input mask. - -#define SHADER_BINARY_VERSION 4 - -String RenderingDeviceVulkan::shader_get_binary_cache_key() const { - return "Vulkan-SV" + itos(SHADER_BINARY_VERSION); -} - -struct RenderingDeviceVulkanShaderBinaryDataBinding { - uint32_t type; - uint32_t binding; - uint32_t stages; - uint32_t length; // Size of arrays (in total elements), or ubos (in bytes * total elements). - uint32_t writable; -}; - -struct RenderingDeviceVulkanShaderBinarySpecializationConstant { - uint32_t type; - uint32_t constant_id; - union { - uint32_t int_value; - float float_value; - bool bool_value; - }; - uint32_t stage_flags; -}; - -struct RenderingDeviceVulkanShaderBinaryData { - uint64_t vertex_input_mask; - uint32_t fragment_output_mask; - uint32_t specialization_constants_count; - uint32_t is_compute; - uint32_t compute_local_size[3]; - uint32_t set_count; - uint32_t push_constant_size; - uint32_t push_constant_vk_stages_mask; - uint32_t stage_count; - uint32_t shader_name_len; -}; - -Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name) { - SpirvReflectionData spirv_data; - if (_reflect_spirv(p_spirv, spirv_data) != OK) { - return Vector<uint8_t>(); - } - - ERR_FAIL_COND_V_MSG((uint32_t)spirv_data.uniforms.size() > limits.maxBoundDescriptorSets, Vector<uint8_t>(), - "Number of uniform sets is larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."); - - // Collect reflection data into binary data. - RenderingDeviceVulkanShaderBinaryData binary_data{}; - Vector<Vector<RenderingDeviceVulkanShaderBinaryDataBinding>> uniform_info; // Set bindings. - Vector<RenderingDeviceVulkanShaderBinarySpecializationConstant> specialization_constants; - { - binary_data.vertex_input_mask = spirv_data.vertex_input_mask; - binary_data.fragment_output_mask = spirv_data.fragment_output_mask; - binary_data.specialization_constants_count = spirv_data.specialization_constants.size(); - binary_data.is_compute = spirv_data.is_compute; - binary_data.compute_local_size[0] = spirv_data.compute_local_size[0]; - binary_data.compute_local_size[1] = spirv_data.compute_local_size[1]; - binary_data.compute_local_size[2] = spirv_data.compute_local_size[2]; - binary_data.set_count = spirv_data.uniforms.size(); - binary_data.push_constant_size = spirv_data.push_constant_size; - for (uint32_t i = 0; i < SHADER_STAGE_MAX; i++) { - if (spirv_data.push_constant_stages_mask.has_flag((ShaderStage)(1 << i))) { - binary_data.push_constant_vk_stages_mask |= shader_stage_masks[i]; - } - } - - for (const Vector<SpirvReflectionData::Uniform> &spirv_set : spirv_data.uniforms) { - Vector<RenderingDeviceVulkanShaderBinaryDataBinding> set_bindings; - for (const SpirvReflectionData::Uniform &spirv_uniform : spirv_set) { - RenderingDeviceVulkanShaderBinaryDataBinding binding{}; - binding.type = (uint32_t)spirv_uniform.type; - binding.binding = spirv_uniform.binding; - binding.stages = (uint32_t)spirv_uniform.stages_mask; - binding.length = spirv_uniform.length; - binding.writable = (uint32_t)spirv_uniform.writable; - set_bindings.push_back(binding); - } - uniform_info.push_back(set_bindings); - } - - for (const SpirvReflectionData::SpecializationConstant &spirv_sc : spirv_data.specialization_constants) { - RenderingDeviceVulkanShaderBinarySpecializationConstant spec_constant{}; - spec_constant.type = (uint32_t)spirv_sc.type; - spec_constant.constant_id = spirv_sc.constant_id; - spec_constant.int_value = spirv_sc.int_value; - spec_constant.stage_flags = (uint32_t)spirv_sc.stages_mask; - specialization_constants.push_back(spec_constant); - } - } - - Vector<Vector<uint8_t>> compressed_stages; - Vector<uint32_t> smolv_size; - Vector<uint32_t> zstd_size; // If 0, zstd not used. - - uint32_t stages_binary_size = 0; - - bool strip_debug = false; - - for (int i = 0; i < p_spirv.size(); i++) { - smolv::ByteArray smolv; - if (!smolv::Encode(p_spirv[i].spir_v.ptr(), p_spirv[i].spir_v.size(), smolv, strip_debug ? smolv::kEncodeFlagStripDebugInfo : 0)) { - ERR_FAIL_V_MSG(Vector<uint8_t>(), "Error compressing shader stage :" + String(shader_stage_names[p_spirv[i].shader_stage])); - } else { - smolv_size.push_back(smolv.size()); - { // zstd. - Vector<uint8_t> zstd; - zstd.resize(Compression::get_max_compressed_buffer_size(smolv.size(), Compression::MODE_ZSTD)); - int dst_size = Compression::compress(zstd.ptrw(), &smolv[0], smolv.size(), Compression::MODE_ZSTD); - - if (dst_size > 0 && (uint32_t)dst_size < smolv.size()) { - zstd_size.push_back(dst_size); - zstd.resize(dst_size); - compressed_stages.push_back(zstd); - } else { - Vector<uint8_t> smv; - smv.resize(smolv.size()); - memcpy(smv.ptrw(), &smolv[0], smolv.size()); - zstd_size.push_back(0); // Not using zstd. - compressed_stages.push_back(smv); - } - } - } - uint32_t s = compressed_stages[i].size(); - if (s % 4 != 0) { - s += 4 - (s % 4); - } - stages_binary_size += s; - } - - binary_data.specialization_constants_count = specialization_constants.size(); - binary_data.set_count = uniform_info.size(); - binary_data.stage_count = p_spirv.size(); - - CharString shader_name_utf = p_shader_name.utf8(); - - binary_data.shader_name_len = shader_name_utf.length(); - - uint32_t total_size = sizeof(uint32_t) * 3; // Header + version + main datasize;. - total_size += sizeof(RenderingDeviceVulkanShaderBinaryData); - - total_size += binary_data.shader_name_len; - - if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. - total_size += 4 - (binary_data.shader_name_len % 4); - } - - for (int i = 0; i < uniform_info.size(); i++) { - total_size += sizeof(uint32_t); - total_size += uniform_info[i].size() * sizeof(RenderingDeviceVulkanShaderBinaryDataBinding); - } - - total_size += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size(); - - total_size += compressed_stages.size() * sizeof(uint32_t) * 3; // Sizes. - total_size += stages_binary_size; - - Vector<uint8_t> ret; - ret.resize(total_size); - { - uint32_t offset = 0; - uint8_t *binptr = ret.ptrw(); - binptr[0] = 'G'; - binptr[1] = 'S'; - binptr[2] = 'B'; - binptr[3] = 'D'; // Godot Shader Binary Data. - offset += 4; - encode_uint32(SHADER_BINARY_VERSION, binptr + offset); - offset += sizeof(uint32_t); - encode_uint32(sizeof(RenderingDeviceVulkanShaderBinaryData), binptr + offset); - offset += sizeof(uint32_t); - memcpy(binptr + offset, &binary_data, sizeof(RenderingDeviceVulkanShaderBinaryData)); - offset += sizeof(RenderingDeviceVulkanShaderBinaryData); - - if (binary_data.shader_name_len > 0) { - memcpy(binptr + offset, shader_name_utf.ptr(), binary_data.shader_name_len); - offset += binary_data.shader_name_len; - - if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. - offset += 4 - (binary_data.shader_name_len % 4); - } - } - - for (int i = 0; i < uniform_info.size(); i++) { - int count = uniform_info[i].size(); - encode_uint32(count, binptr + offset); - offset += sizeof(uint32_t); - if (count > 0) { - memcpy(binptr + offset, uniform_info[i].ptr(), sizeof(RenderingDeviceVulkanShaderBinaryDataBinding) * count); - offset += sizeof(RenderingDeviceVulkanShaderBinaryDataBinding) * count; - } - } - - if (specialization_constants.size()) { - memcpy(binptr + offset, specialization_constants.ptr(), sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size()); - offset += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size(); - } - - for (int i = 0; i < compressed_stages.size(); i++) { - encode_uint32(p_spirv[i].shader_stage, binptr + offset); - offset += sizeof(uint32_t); - encode_uint32(smolv_size[i], binptr + offset); - offset += sizeof(uint32_t); - encode_uint32(zstd_size[i], binptr + offset); - offset += sizeof(uint32_t); - memcpy(binptr + offset, compressed_stages[i].ptr(), compressed_stages[i].size()); - - uint32_t s = compressed_stages[i].size(); - - if (s % 4 != 0) { - s += 4 - (s % 4); - } - - offset += s; - } - - ERR_FAIL_COND_V(offset != (uint32_t)ret.size(), Vector<uint8_t>()); - } - - return ret; -} - -RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder) { - const uint8_t *binptr = p_shader_binary.ptr(); - uint32_t binsize = p_shader_binary.size(); - - uint32_t read_offset = 0; - // Consistency check. - ERR_FAIL_COND_V(binsize < sizeof(uint32_t) * 3 + sizeof(RenderingDeviceVulkanShaderBinaryData), RID()); - ERR_FAIL_COND_V(binptr[0] != 'G' || binptr[1] != 'S' || binptr[2] != 'B' || binptr[3] != 'D', RID()); - - uint32_t bin_version = decode_uint32(binptr + 4); - ERR_FAIL_COND_V(bin_version != SHADER_BINARY_VERSION, RID()); - - uint32_t bin_data_size = decode_uint32(binptr + 8); - - const RenderingDeviceVulkanShaderBinaryData &binary_data = *(reinterpret_cast<const RenderingDeviceVulkanShaderBinaryData *>(binptr + 12)); - - Shader::PushConstant push_constant; - push_constant.size = binary_data.push_constant_size; - push_constant.vk_stages_mask = binary_data.push_constant_vk_stages_mask; - - uint64_t vertex_input_mask = binary_data.vertex_input_mask; - - uint32_t fragment_output_mask = binary_data.fragment_output_mask; - - bool is_compute = binary_data.is_compute; - - const uint32_t compute_local_size[3] = { binary_data.compute_local_size[0], binary_data.compute_local_size[1], binary_data.compute_local_size[2] }; - - read_offset += sizeof(uint32_t) * 3 + bin_data_size; - - String name; - - if (binary_data.shader_name_len) { - name.parse_utf8((const char *)(binptr + read_offset), binary_data.shader_name_len); - read_offset += binary_data.shader_name_len; - if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. - read_offset += 4 - (binary_data.shader_name_len % 4); - } - } - - Vector<Vector<VkDescriptorSetLayoutBinding>> set_bindings; - Vector<Vector<UniformInfo>> uniform_info; - - set_bindings.resize(binary_data.set_count); - uniform_info.resize(binary_data.set_count); - - for (uint32_t i = 0; i < binary_data.set_count; i++) { - ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) >= binsize, RID()); - uint32_t set_count = decode_uint32(binptr + read_offset); - read_offset += sizeof(uint32_t); - const RenderingDeviceVulkanShaderBinaryDataBinding *set_ptr = reinterpret_cast<const RenderingDeviceVulkanShaderBinaryDataBinding *>(binptr + read_offset); - uint32_t set_size = set_count * sizeof(RenderingDeviceVulkanShaderBinaryDataBinding); - ERR_FAIL_COND_V(read_offset + set_size >= binsize, RID()); - - for (uint32_t j = 0; j < set_count; j++) { - UniformInfo info; - info.type = UniformType(set_ptr[j].type); - info.writable = set_ptr[j].writable; - info.length = set_ptr[j].length; - info.binding = set_ptr[j].binding; - info.stages = set_ptr[j].stages; - - VkDescriptorSetLayoutBinding layout_binding; - layout_binding.pImmutableSamplers = nullptr; - layout_binding.binding = set_ptr[j].binding; - layout_binding.descriptorCount = 1; - layout_binding.stageFlags = 0; - for (uint32_t k = 0; k < SHADER_STAGE_MAX; k++) { - if (set_ptr[j].stages & (1 << k)) { - layout_binding.stageFlags |= shader_stage_masks[k]; - } - } - - switch (info.type) { - case UNIFORM_TYPE_SAMPLER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; - layout_binding.descriptorCount = set_ptr[j].length; - } break; - case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - layout_binding.descriptorCount = set_ptr[j].length; - } break; - case UNIFORM_TYPE_TEXTURE: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - layout_binding.descriptorCount = set_ptr[j].length; - } break; - case UNIFORM_TYPE_IMAGE: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - layout_binding.descriptorCount = set_ptr[j].length; - } break; - case UNIFORM_TYPE_TEXTURE_BUFFER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; - layout_binding.descriptorCount = set_ptr[j].length; - } break; - case UNIFORM_TYPE_IMAGE_BUFFER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; - } break; - case UNIFORM_TYPE_UNIFORM_BUFFER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - } break; - case UNIFORM_TYPE_STORAGE_BUFFER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - } break; - case UNIFORM_TYPE_INPUT_ATTACHMENT: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - } break; - default: { - ERR_FAIL_V(RID()); - } - } - - set_bindings.write[i].push_back(layout_binding); - uniform_info.write[i].push_back(info); - } - - read_offset += set_size; - } - - ERR_FAIL_COND_V(read_offset + binary_data.specialization_constants_count * sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) >= binsize, RID()); - - Vector<Shader::SpecializationConstant> specialization_constants; - - for (uint32_t i = 0; i < binary_data.specialization_constants_count; i++) { - const RenderingDeviceVulkanShaderBinarySpecializationConstant &src_sc = *(reinterpret_cast<const RenderingDeviceVulkanShaderBinarySpecializationConstant *>(binptr + read_offset)); - Shader::SpecializationConstant sc; - sc.constant.int_value = src_sc.int_value; - sc.constant.type = PipelineSpecializationConstantType(src_sc.type); - sc.constant.constant_id = src_sc.constant_id; - sc.stage_flags = src_sc.stage_flags; - specialization_constants.push_back(sc); - - read_offset += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant); - } - - Vector<Vector<uint8_t>> stage_spirv_data; - Vector<ShaderStage> stage_type; - - for (uint32_t i = 0; i < binary_data.stage_count; i++) { - ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) * 3 >= binsize, RID()); - uint32_t stage = decode_uint32(binptr + read_offset); - read_offset += sizeof(uint32_t); - uint32_t smolv_size = decode_uint32(binptr + read_offset); - read_offset += sizeof(uint32_t); - uint32_t zstd_size = decode_uint32(binptr + read_offset); - read_offset += sizeof(uint32_t); - - uint32_t buf_size = (zstd_size > 0) ? zstd_size : smolv_size; - - Vector<uint8_t> smolv; - const uint8_t *src_smolv = nullptr; - - if (zstd_size > 0) { - // Decompress to smolv. - smolv.resize(smolv_size); - int dec_smolv_size = Compression::decompress(smolv.ptrw(), smolv.size(), binptr + read_offset, zstd_size, Compression::MODE_ZSTD); - ERR_FAIL_COND_V(dec_smolv_size != (int32_t)smolv_size, RID()); - src_smolv = smolv.ptr(); - } else { - src_smolv = binptr + read_offset; - } - - Vector<uint8_t> spirv; - uint32_t spirv_size = smolv::GetDecodedBufferSize(src_smolv, smolv_size); - spirv.resize(spirv_size); - if (!smolv::Decode(src_smolv, smolv_size, spirv.ptrw(), spirv_size)) { - ERR_FAIL_V_MSG(RID(), "Malformed smolv input uncompressing shader stage:" + String(shader_stage_names[stage])); - } - stage_spirv_data.push_back(spirv); - stage_type.push_back(ShaderStage(stage)); - - if (buf_size % 4 != 0) { - buf_size += 4 - (buf_size % 4); - } - - ERR_FAIL_COND_V(read_offset + buf_size > binsize, RID()); - - read_offset += buf_size; - } - - ERR_FAIL_COND_V(read_offset != binsize, RID()); - - // All good, let's create modules. - - _THREAD_SAFE_METHOD_ - - RID id; - if (p_placeholder.is_null()) { - id = shader_owner.make_rid(); - } else { - id = p_placeholder; - } - - Shader *shader = shader_owner.get_or_null(id); - ERR_FAIL_NULL_V(shader, RID()); - - shader->vertex_input_mask = vertex_input_mask; - shader->fragment_output_mask = fragment_output_mask; - shader->push_constant = push_constant; - shader->is_compute = is_compute; - shader->compute_local_size[0] = compute_local_size[0]; - shader->compute_local_size[1] = compute_local_size[1]; - shader->compute_local_size[2] = compute_local_size[2]; - shader->specialization_constants = specialization_constants; - shader->name = name; - - String error_text; - - bool success = true; - for (int i = 0; i < stage_spirv_data.size(); i++) { - VkShaderModuleCreateInfo shader_module_create_info; - shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - shader_module_create_info.pNext = nullptr; - shader_module_create_info.flags = 0; - shader_module_create_info.codeSize = stage_spirv_data[i].size(); - const uint8_t *r = stage_spirv_data[i].ptr(); - - shader_module_create_info.pCode = (const uint32_t *)r; - - VkShaderModule module; - VkResult res = vkCreateShaderModule(device, &shader_module_create_info, nullptr, &module); - if (res) { - success = false; - error_text = "Error (" + itos(res) + ") creating shader module for stage: " + String(shader_stage_names[stage_type[i]]); - break; - } - - VkPipelineShaderStageCreateInfo shader_stage; - shader_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shader_stage.pNext = nullptr; - shader_stage.flags = 0; - shader_stage.stage = shader_stage_masks[stage_type[i]]; - shader_stage.module = module; - shader_stage.pName = "main"; - shader_stage.pSpecializationInfo = nullptr; - - shader->pipeline_stages.push_back(shader_stage); - } - // Proceed to create descriptor sets. - - if (success) { - for (int i = 0; i < set_bindings.size(); i++) { - // Empty ones are fine if they were not used according to spec (binding count will be 0). - VkDescriptorSetLayoutCreateInfo layout_create_info; - layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layout_create_info.pNext = nullptr; - layout_create_info.flags = 0; - layout_create_info.bindingCount = set_bindings[i].size(); - layout_create_info.pBindings = set_bindings[i].ptr(); - - VkDescriptorSetLayout layout; - VkResult res = vkCreateDescriptorSetLayout(device, &layout_create_info, nullptr, &layout); - if (res) { - error_text = "Error (" + itos(res) + ") creating descriptor set layout for set " + itos(i); - success = false; - break; - } - - Shader::Set set; - set.descriptor_set_layout = layout; - set.uniform_info = uniform_info[i]; - // Sort and hash. - set.uniform_info.sort(); - - uint32_t format = 0; // No format, default. - - if (set.uniform_info.size()) { - // Has data, needs an actual format. - UniformSetFormat usformat; - usformat.uniform_info = set.uniform_info; - RBMap<UniformSetFormat, uint32_t>::Element *E = uniform_set_format_cache.find(usformat); - if (E) { - format = E->get(); - } else { - format = uniform_set_format_cache.size() + 1; - uniform_set_format_cache.insert(usformat, format); - } - } - - shader->sets.push_back(set); - shader->set_formats.push_back(format); - } - } - - if (success) { - // Create pipeline layout. - VkPipelineLayoutCreateInfo pipeline_layout_create_info; - pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipeline_layout_create_info.pNext = nullptr; - pipeline_layout_create_info.flags = 0; - pipeline_layout_create_info.setLayoutCount = shader->sets.size(); - - Vector<VkDescriptorSetLayout> layouts; - layouts.resize(shader->sets.size()); - - for (int i = 0; i < layouts.size(); i++) { - layouts.write[i] = shader->sets[i].descriptor_set_layout; - } - - pipeline_layout_create_info.pSetLayouts = layouts.ptr(); - // Needs to be declared in this outer scope, otherwise it may not outlive its assignment - // to pipeline_layout_create_info. - VkPushConstantRange push_constant_range; - if (push_constant.size) { - push_constant_range.stageFlags = push_constant.vk_stages_mask; - push_constant_range.offset = 0; - push_constant_range.size = push_constant.size; - - pipeline_layout_create_info.pushConstantRangeCount = 1; - pipeline_layout_create_info.pPushConstantRanges = &push_constant_range; - } else { - pipeline_layout_create_info.pushConstantRangeCount = 0; - pipeline_layout_create_info.pPushConstantRanges = nullptr; - } - - VkResult err = vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, &shader->pipeline_layout); - - if (err) { - error_text = "Error (" + itos(err) + ") creating pipeline layout."; - success = false; - } - } - - if (!success) { - // Clean up if failed. - for (int i = 0; i < shader->pipeline_stages.size(); i++) { - vkDestroyShaderModule(device, shader->pipeline_stages[i].module, nullptr); - } - - for (int i = 0; i < shader->sets.size(); i++) { - vkDestroyDescriptorSetLayout(device, shader->sets[i].descriptor_set_layout, nullptr); - } - - shader_owner.free(id); - - ERR_FAIL_V_MSG(RID(), error_text); - } - -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - return id; -} - -RID RenderingDeviceVulkan::shader_create_placeholder() { - Shader shader; - return shader_owner.make_rid(shader); -} - -uint64_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) { - _THREAD_SAFE_METHOD_ - - const Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_NULL_V(shader, 0); - return shader->vertex_input_mask; -} - -/******************/ -/**** UNIFORMS ****/ -/******************/ - -RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); - - Buffer buffer; - Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); - ERR_FAIL_COND_V(err != OK, RID()); - if (p_data.size()) { - uint64_t data_size = p_data.size(); - const uint8_t *r = p_data.ptr(); - _buffer_update(&buffer, 0, r, data_size); - _buffer_memory_barrier(buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_UNIFORM_READ_BIT, false); - } - RID id = uniform_buffer_owner.make_rid(buffer); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - return id; -} - -RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, BitField<StorageBufferUsage> p_usage) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); - - Buffer buffer; - uint32_t flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - if (p_usage.has_flag(STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT)) { - flags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; - } - Error err = _buffer_allocate(&buffer, p_size_bytes, flags, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); - ERR_FAIL_COND_V(err != OK, RID()); - - if (p_data.size()) { - uint64_t data_size = p_data.size(); - const uint8_t *r = p_data.ptr(); - _buffer_update(&buffer, 0, r, data_size); - _buffer_memory_barrier(buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, false); - } - return storage_buffer_owner.make_rid(buffer); -} - -RID RenderingDeviceVulkan::texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data) { - _THREAD_SAFE_METHOD_ - - uint32_t element_size = get_format_vertex_size(p_format); - ERR_FAIL_COND_V_MSG(element_size == 0, RID(), "Format requested is not supported for texture buffers"); - uint64_t size_bytes = uint64_t(element_size) * p_size_elements; - - ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != size_bytes, RID()); - - TextureBuffer texture_buffer; - Error err = _buffer_allocate(&texture_buffer.buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); - ERR_FAIL_COND_V(err != OK, RID()); - - if (p_data.size()) { - uint64_t data_size = p_data.size(); - const uint8_t *r = p_data.ptr(); - _buffer_update(&texture_buffer.buffer, 0, r, data_size); - _buffer_memory_barrier(texture_buffer.buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, false); - } - - VkBufferViewCreateInfo view_create_info; - view_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; - view_create_info.pNext = nullptr; - view_create_info.flags = 0; - view_create_info.buffer = texture_buffer.buffer.buffer; - view_create_info.format = vulkan_formats[p_format]; - view_create_info.offset = 0; - view_create_info.range = size_bytes; - - texture_buffer.view = VK_NULL_HANDLE; - - VkResult res = vkCreateBufferView(device, &view_create_info, nullptr, &texture_buffer.view); - if (res) { - _buffer_free(&texture_buffer.buffer); - ERR_FAIL_V_MSG(RID(), "Unable to create buffer view, error " + itos(res) + "."); - } - - // Allocate the view. - RID id = texture_buffer_owner.make_rid(texture_buffer); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - return id; -} - -RenderingDeviceVulkan::DescriptorPool *RenderingDeviceVulkan::_descriptor_pool_allocate(const DescriptorPoolKey &p_key) { - if (!descriptor_pools.has(p_key)) { - descriptor_pools[p_key] = HashSet<DescriptorPool *>(); - } - - DescriptorPool *pool = nullptr; - - for (DescriptorPool *E : descriptor_pools[p_key]) { - if (E->usage < max_descriptors_per_pool) { - pool = E; - break; - } - } - - if (!pool) { - // Create a new one. - pool = memnew(DescriptorPool); - pool->usage = 0; - - VkDescriptorPoolCreateInfo descriptor_pool_create_info; - descriptor_pool_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - descriptor_pool_create_info.pNext = nullptr; - descriptor_pool_create_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; // Can't think how somebody may NOT need this flag. - descriptor_pool_create_info.maxSets = max_descriptors_per_pool; - Vector<VkDescriptorPoolSize> sizes; - // Here comes more vulkan API strangeness. - - if (p_key.uniform_type[UNIFORM_TYPE_SAMPLER]) { - VkDescriptorPoolSize s; - s.type = VK_DESCRIPTOR_TYPE_SAMPLER; - s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_SAMPLER] * max_descriptors_per_pool; - sizes.push_back(s); - } - if (p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE]) { - VkDescriptorPoolSize s; - s.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE] * max_descriptors_per_pool; - sizes.push_back(s); - } - if (p_key.uniform_type[UNIFORM_TYPE_TEXTURE]) { - VkDescriptorPoolSize s; - s.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_TEXTURE] * max_descriptors_per_pool; - sizes.push_back(s); - } - if (p_key.uniform_type[UNIFORM_TYPE_IMAGE]) { - VkDescriptorPoolSize s; - s.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_IMAGE] * max_descriptors_per_pool; - sizes.push_back(s); - } - if (p_key.uniform_type[UNIFORM_TYPE_TEXTURE_BUFFER] || p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER]) { - VkDescriptorPoolSize s; - s.type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; - s.descriptorCount = (p_key.uniform_type[UNIFORM_TYPE_TEXTURE_BUFFER] + p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER]) * max_descriptors_per_pool; - sizes.push_back(s); - } - if (p_key.uniform_type[UNIFORM_TYPE_IMAGE_BUFFER]) { - VkDescriptorPoolSize s; - s.type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; - s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_IMAGE_BUFFER] * max_descriptors_per_pool; - sizes.push_back(s); - } - if (p_key.uniform_type[UNIFORM_TYPE_UNIFORM_BUFFER]) { - VkDescriptorPoolSize s; - s.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_UNIFORM_BUFFER] * max_descriptors_per_pool; - sizes.push_back(s); - } - - if (p_key.uniform_type[UNIFORM_TYPE_STORAGE_BUFFER]) { - VkDescriptorPoolSize s; - s.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_STORAGE_BUFFER] * max_descriptors_per_pool; - sizes.push_back(s); - } - - if (p_key.uniform_type[UNIFORM_TYPE_INPUT_ATTACHMENT]) { - VkDescriptorPoolSize s; - s.type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_INPUT_ATTACHMENT] * max_descriptors_per_pool; - sizes.push_back(s); - } - - descriptor_pool_create_info.poolSizeCount = sizes.size(); - descriptor_pool_create_info.pPoolSizes = sizes.ptr(); - VkResult res = vkCreateDescriptorPool(device, &descriptor_pool_create_info, nullptr, &pool->pool); - if (res) { - memdelete(pool); - ERR_FAIL_COND_V_MSG(res, nullptr, "vkCreateDescriptorPool failed with error " + itos(res) + "."); - } - descriptor_pools[p_key].insert(pool); - } - - pool->usage++; - - return pool; -} - -void RenderingDeviceVulkan::_descriptor_pool_free(const DescriptorPoolKey &p_key, DescriptorPool *p_pool) { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(!descriptor_pools[p_key].has(p_pool)); -#endif - ERR_FAIL_COND(p_pool->usage == 0); - p_pool->usage--; - if (p_pool->usage == 0) { - vkDestroyDescriptorPool(device, p_pool->pool, nullptr); - descriptor_pools[p_key].erase(p_pool); - memdelete(p_pool); - if (descriptor_pools[p_key].is_empty()) { - descriptor_pools.erase(p_key); - } - } -} - -RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(p_uniforms.size() == 0, RID()); - - Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_NULL_V(shader, RID()); - - ERR_FAIL_COND_V_MSG(p_shader_set >= (uint32_t)shader->sets.size() || shader->sets[p_shader_set].uniform_info.size() == 0, RID(), - "Desired set (" + itos(p_shader_set) + ") not used by shader."); - // See that all sets in shader are satisfied. - - const Shader::Set &set = shader->sets[p_shader_set]; - - uint32_t uniform_count = p_uniforms.size(); - const Uniform *uniforms = p_uniforms.ptr(); - - uint32_t set_uniform_count = set.uniform_info.size(); - const UniformInfo *set_uniforms = set.uniform_info.ptr(); - - Vector<VkWriteDescriptorSet> writes; - DescriptorPoolKey pool_key; - - // To keep them alive until update call. - List<Vector<VkDescriptorBufferInfo>> buffer_infos; - List<Vector<VkBufferView>> buffer_views; - List<Vector<VkDescriptorImageInfo>> image_infos; - // Used for verification to make sure a uniform set does not use a framebuffer bound texture. - LocalVector<UniformSet::AttachableTexture> attachable_textures; - Vector<Texture *> mutable_sampled_textures; - Vector<Texture *> mutable_storage_textures; - - for (uint32_t i = 0; i < set_uniform_count; i++) { - const UniformInfo &set_uniform = set_uniforms[i]; - int uniform_idx = -1; - for (int j = 0; j < (int)uniform_count; j++) { - if (uniforms[j].binding == set_uniform.binding) { - uniform_idx = j; - } - } - ERR_FAIL_COND_V_MSG(uniform_idx == -1, RID(), - "All the shader bindings for the given set must be covered by the uniforms provided. Binding (" + itos(set_uniform.binding) + "), set (" + itos(p_shader_set) + ") was not provided."); - - const Uniform &uniform = uniforms[uniform_idx]; - - ERR_FAIL_COND_V_MSG(uniform.uniform_type != set_uniform.type, RID(), - "Mismatch uniform type for binding (" + itos(set_uniform.binding) + "), set (" + itos(p_shader_set) + "). Expected '" + shader_uniform_names[set_uniform.type] + "', supplied: '" + shader_uniform_names[uniform.uniform_type] + "'."); - - VkWriteDescriptorSet write; // Common header. - write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write.pNext = nullptr; - write.dstSet = VK_NULL_HANDLE; // Will assign afterwards when everything is valid. - write.dstBinding = set_uniform.binding; - write.dstArrayElement = 0; - write.descriptorCount = 0; - write.descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM; // Invalid value. - write.pImageInfo = nullptr; - write.pBufferInfo = nullptr; - write.pTexelBufferView = nullptr; - uint32_t type_size = 1; - - switch (uniform.uniform_type) { - case UNIFORM_TYPE_SAMPLER: { - if (uniform.get_id_count() != (uint32_t)set_uniform.length) { - if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler elements, so it should be provided equal number of sampler IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } else { - ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") should provide one ID referencing a sampler (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } - } - - Vector<VkDescriptorImageInfo> image_info; - - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - VkSampler *sampler = sampler_owner.get_or_null(uniform.get_id(j)); - ERR_FAIL_NULL_V_MSG(sampler, RID(), "Sampler (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid sampler."); - - VkDescriptorImageInfo img_info; - img_info.sampler = *sampler; - img_info.imageView = VK_NULL_HANDLE; - img_info.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - image_info.push_back(img_info); - } - - write.dstArrayElement = 0; - write.descriptorCount = uniform.get_id_count(); - write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; - write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); - write.pBufferInfo = nullptr; - write.pTexelBufferView = nullptr; - - type_size = uniform.get_id_count(); - - } break; - case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { - if (uniform.get_id_count() != (uint32_t)set_uniform.length * 2) { - if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler&texture elements, so it should provided twice the amount of IDs (sampler,texture pairs) to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } else { - ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } - } - - Vector<VkDescriptorImageInfo> image_info; - - for (uint32_t j = 0; j < uniform.get_id_count(); j += 2) { - VkSampler *sampler = sampler_owner.get_or_null(uniform.get_id(j + 0)); - ERR_FAIL_NULL_V_MSG(sampler, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); - - Texture *texture = texture_owner.get_or_null(uniform.get_id(j + 1)); - ERR_FAIL_NULL_V_MSG(texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); - - ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), - "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); - - VkDescriptorImageInfo img_info; - img_info.sampler = *sampler; - img_info.imageView = texture->view; - - if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) { - UniformSet::AttachableTexture attachable_texture; - attachable_texture.bind = set_uniform.binding; - attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.get_id(j + 1); - attachable_textures.push_back(attachable_texture); - } - - if (texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { - // Can also be used as storage, add to mutable sampled. - mutable_sampled_textures.push_back(texture); - } - - DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); - - img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - image_info.push_back(img_info); - } - - write.dstArrayElement = 0; - write.descriptorCount = uniform.get_id_count() / 2; - write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); - write.pBufferInfo = nullptr; - write.pTexelBufferView = nullptr; - - type_size = uniform.get_id_count() / 2; - - } break; - case UNIFORM_TYPE_TEXTURE: { - if (uniform.get_id_count() != (uint32_t)set_uniform.length) { - if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } else { - ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } - } - - Vector<VkDescriptorImageInfo> image_info; - - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); - ERR_FAIL_NULL_V_MSG(texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); - - ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), - "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); - - VkDescriptorImageInfo img_info; - img_info.sampler = VK_NULL_HANDLE; - img_info.imageView = texture->view; - - if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) { - UniformSet::AttachableTexture attachable_texture; - attachable_texture.bind = set_uniform.binding; - attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.get_id(j); - attachable_textures.push_back(attachable_texture); - } - - if (texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { - // Can also be used as storage, add to mutable sampled. - mutable_sampled_textures.push_back(texture); - } - - DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); - - img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - image_info.push_back(img_info); - } - - write.dstArrayElement = 0; - write.descriptorCount = uniform.get_id_count(); - write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); - write.pBufferInfo = nullptr; - write.pTexelBufferView = nullptr; - - type_size = uniform.get_id_count(); - } break; - case UNIFORM_TYPE_IMAGE: { - if (uniform.get_id_count() != (uint32_t)set_uniform.length) { - if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } else { - ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } - } - - Vector<VkDescriptorImageInfo> image_info; - - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); - - ERR_FAIL_NULL_V_MSG(texture, RID(), - "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); - - ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), RID(), - "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_STORAGE_BIT usage flag set in order to be used as uniform."); - - VkDescriptorImageInfo img_info; - img_info.sampler = VK_NULL_HANDLE; - img_info.imageView = texture->view; - - if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { - // Can also be used as storage, add to mutable sampled. - mutable_storage_textures.push_back(texture); - } - - DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); - - img_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - - image_info.push_back(img_info); - } - - write.dstArrayElement = 0; - write.descriptorCount = uniform.get_id_count(); - write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); - write.pBufferInfo = nullptr; - write.pTexelBufferView = nullptr; - - type_size = uniform.get_id_count(); - - } break; - case UNIFORM_TYPE_TEXTURE_BUFFER: { - if (uniform.get_id_count() != (uint32_t)set_uniform.length) { - if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") texture buffer elements, so it should be provided equal number of texture buffer IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } else { - ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture buffer (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } - } - - Vector<VkDescriptorBufferInfo> buffer_info; - Vector<VkBufferView> buffer_view; - - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - TextureBuffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j)); - ERR_FAIL_NULL_V_MSG(buffer, RID(), "Texture Buffer (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture buffer."); - - buffer_info.push_back(buffer->buffer.buffer_info); - buffer_view.push_back(buffer->view); - } - - write.dstArrayElement = 0; - write.descriptorCount = uniform.get_id_count(); - write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; - write.pImageInfo = nullptr; - write.pBufferInfo = buffer_infos.push_back(buffer_info)->get().ptr(); - write.pTexelBufferView = buffer_views.push_back(buffer_view)->get().ptr(); - - type_size = uniform.get_id_count(); - - } break; - case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: { - if (uniform.get_id_count() != (uint32_t)set_uniform.length * 2) { - if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler buffer elements, so it should provided twice the amount of IDs (sampler,buffer pairs) to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } else { - ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture buffer (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } - } - - Vector<VkDescriptorImageInfo> image_info; - Vector<VkDescriptorBufferInfo> buffer_info; - Vector<VkBufferView> buffer_view; - - for (uint32_t j = 0; j < uniform.get_id_count(); j += 2) { - VkSampler *sampler = sampler_owner.get_or_null(uniform.get_id(j + 0)); - ERR_FAIL_NULL_V_MSG(sampler, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); - - TextureBuffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j + 1)); - - VkDescriptorImageInfo img_info; - img_info.sampler = *sampler; - img_info.imageView = VK_NULL_HANDLE; - img_info.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - image_info.push_back(img_info); - - ERR_FAIL_NULL_V_MSG(buffer, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid texture buffer."); - - buffer_info.push_back(buffer->buffer.buffer_info); - buffer_view.push_back(buffer->view); - } - - write.dstArrayElement = 0; - write.descriptorCount = uniform.get_id_count() / 2; - write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; - write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); - write.pBufferInfo = buffer_infos.push_back(buffer_info)->get().ptr(); - write.pTexelBufferView = buffer_views.push_back(buffer_view)->get().ptr(); - - type_size = uniform.get_id_count() / 2; - } break; - case UNIFORM_TYPE_IMAGE_BUFFER: { - // Todo. - - } break; - case UNIFORM_TYPE_UNIFORM_BUFFER: { - ERR_FAIL_COND_V_MSG(uniform.get_id_count() != 1, RID(), - "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.get_id_count()) + " provided)."); - - Buffer *buffer = uniform_buffer_owner.get_or_null(uniform.get_id(0)); - ERR_FAIL_NULL_V_MSG(buffer, RID(), "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); - - ERR_FAIL_COND_V_MSG(buffer->size < (uint32_t)set_uniform.length, RID(), - "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " is smaller than size of shader uniform: (" + itos(set_uniform.length) + ")."); - - write.dstArrayElement = 0; - write.descriptorCount = 1; - write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - write.pImageInfo = nullptr; - write.pBufferInfo = &buffer->buffer_info; - write.pTexelBufferView = nullptr; - - } break; - case UNIFORM_TYPE_STORAGE_BUFFER: { - ERR_FAIL_COND_V_MSG(uniform.get_id_count() != 1, RID(), - "Storage buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.get_id_count()) + " provided)."); - - Buffer *buffer = nullptr; - - if (storage_buffer_owner.owns(uniform.get_id(0))) { - buffer = storage_buffer_owner.get_or_null(uniform.get_id(0)); - } else if (vertex_buffer_owner.owns(uniform.get_id(0))) { - buffer = vertex_buffer_owner.get_or_null(uniform.get_id(0)); - - ERR_FAIL_COND_V_MSG(!(buffer->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), RID(), "Vertex buffer supplied (binding: " + itos(uniform.binding) + ") was not created with storage flag."); - } - ERR_FAIL_NULL_V_MSG(buffer, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); - - // If 0, then it's sized on link time. - ERR_FAIL_COND_V_MSG(set_uniform.length > 0 && buffer->size != (uint32_t)set_uniform.length, RID(), - "Storage buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " does not match size of shader uniform: (" + itos(set_uniform.length) + ")."); - - write.dstArrayElement = 0; - write.descriptorCount = 1; - write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - write.pImageInfo = nullptr; - write.pBufferInfo = &buffer->buffer_info; - write.pTexelBufferView = nullptr; - } break; - case UNIFORM_TYPE_INPUT_ATTACHMENT: { - ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") supplied for compute shader (this is not allowed)."); - - if (uniform.get_id_count() != (uint32_t)set_uniform.length) { - if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } else { - ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); - } - } - - Vector<VkDescriptorImageInfo> image_info; - - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); - - ERR_FAIL_NULL_V_MSG(texture, RID(), - "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); - - ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), - "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); - - VkDescriptorImageInfo img_info; - img_info.sampler = VK_NULL_HANDLE; - img_info.imageView = texture->view; - - DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); - - img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - image_info.push_back(img_info); - } - - write.dstArrayElement = 0; - write.descriptorCount = uniform.get_id_count(); - write.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); - write.pBufferInfo = nullptr; - write.pTexelBufferView = nullptr; - - type_size = uniform.get_id_count(); - } break; - default: { - } - } - - writes.push_back(write); - - ERR_FAIL_COND_V_MSG(pool_key.uniform_type[set_uniform.type] == MAX_DESCRIPTOR_POOL_ELEMENT, RID(), - "Uniform set reached the limit of bindings for the same type (" + itos(MAX_DESCRIPTOR_POOL_ELEMENT) + ")."); - pool_key.uniform_type[set_uniform.type] += type_size; - } - - // Need a descriptor pool. - DescriptorPool *pool = _descriptor_pool_allocate(pool_key); - - ERR_FAIL_NULL_V(pool, RID()); - - VkDescriptorSetAllocateInfo descriptor_set_allocate_info; - - descriptor_set_allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - descriptor_set_allocate_info.pNext = nullptr; - descriptor_set_allocate_info.descriptorPool = pool->pool; - descriptor_set_allocate_info.descriptorSetCount = 1; - descriptor_set_allocate_info.pSetLayouts = &shader->sets[p_shader_set].descriptor_set_layout; - - VkDescriptorSet descriptor_set; - - VkResult res = vkAllocateDescriptorSets(device, &descriptor_set_allocate_info, &descriptor_set); - if (res) { - _descriptor_pool_free(pool_key, pool); // Meh. - ERR_FAIL_V_MSG(RID(), "Cannot allocate descriptor sets, error " + itos(res) + "."); - } - - UniformSet uniform_set; - uniform_set.pool = pool; - uniform_set.pool_key = pool_key; - uniform_set.descriptor_set = descriptor_set; - uniform_set.format = shader->set_formats[p_shader_set]; - uniform_set.attachable_textures = attachable_textures; - uniform_set.mutable_sampled_textures = mutable_sampled_textures; - uniform_set.mutable_storage_textures = mutable_storage_textures; - uniform_set.shader_set = p_shader_set; - uniform_set.shader_id = p_shader; - - RID id = uniform_set_owner.make_rid(uniform_set); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - // Add dependencies. - _add_dependency(id, p_shader); - for (uint32_t i = 0; i < uniform_count; i++) { - const Uniform &uniform = uniforms[i]; - int id_count = uniform.get_id_count(); - for (int j = 0; j < id_count; j++) { - _add_dependency(id, uniform.get_id(j)); - } - } - - // Write the contents. - if (writes.size()) { - for (int i = 0; i < writes.size(); i++) { - writes.write[i].dstSet = descriptor_set; - } - vkUpdateDescriptorSets(device, writes.size(), writes.ptr(), 0, nullptr); - } - - return id; -} - -bool RenderingDeviceVulkan::uniform_set_is_valid(RID p_uniform_set) { - return uniform_set_owner.owns(p_uniform_set); -} - -void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) { - UniformSet *us = uniform_set_owner.get_or_null(p_uniform_set); - ERR_FAIL_NULL(us); - us->invalidated_callback = p_callback; - us->invalidated_callback_userdata = p_userdata; -} - -Error RenderingDeviceVulkan::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, - "Copying buffers is forbidden during creation of a draw list"); - ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, - "Copying buffers is forbidden during creation of a compute list"); - - // This method assumes the barriers have been pushed prior to being called, therefore no barriers are pushed - // for the source or destination buffers before performing the copy. These masks are effectively ignored. - VkPipelineStageFlags src_stage_mask = 0; - VkAccessFlags src_access_mask = 0; - Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer, src_stage_mask, src_access_mask, BARRIER_MASK_NO_BARRIER); - if (!src_buffer) { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Source buffer argument is not a valid buffer of any type."); - } - - VkPipelineStageFlags dst_stage_mask = 0; - VkAccessFlags dst_access = 0; - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - // If the post barrier mask defines it, we indicate the destination buffer will require a barrier with these flags set - // after the copy command is queued. - dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; - dst_access = VK_ACCESS_TRANSFER_WRITE_BIT; - } - - Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer, dst_stage_mask, dst_access, p_post_barrier); - if (!dst_buffer) { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Destination buffer argument is not a valid buffer of any type."); - } - - // Validate the copy's dimensions for both buffers. - ERR_FAIL_COND_V_MSG((p_size + p_src_offset) > src_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the source buffer."); - ERR_FAIL_COND_V_MSG((p_size + p_dst_offset) > dst_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the destination buffer."); - - // Perform the copy. - VkBufferCopy region; - region.srcOffset = p_src_offset; - region.dstOffset = p_dst_offset; - region.size = p_size; - vkCmdCopyBuffer(frames[frame].draw_command_buffer, src_buffer->buffer, dst_buffer->buffer, 1, ®ion); - -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else - if (dst_stage_mask == 0) { - dst_stage_mask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - // As indicated by the post barrier mask, push a new barrier. - if (p_post_barrier != RD::BARRIER_MASK_NO_BARRIER) { - _buffer_memory_barrier(dst_buffer->buffer, p_dst_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, true); - } -#endif - - return OK; -} - -Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, - "Updating buffers is forbidden during creation of a draw list"); - ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, - "Updating buffers is forbidden during creation of a compute list"); - - VkPipelineStageFlags dst_stage_mask = 0; - VkAccessFlags dst_access = 0; - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - // Protect subsequent updates. - dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; - dst_access = VK_ACCESS_TRANSFER_WRITE_BIT; - } - Buffer *buffer = _get_buffer_from_owner(p_buffer, dst_stage_mask, dst_access, p_post_barrier); - if (!buffer) { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Buffer argument is not a valid buffer of any type."); - } - - ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, - "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); - - // No barrier should be needed here. - // _buffer_memory_barrier(buffer->buffer, p_offset, p_size, dst_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_access, VK_ACCESS_TRANSFER_WRITE_BIT, true); - - Error err = _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, p_post_barrier); - if (err) { - return err; - } - -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else - if (dst_stage_mask == 0) { - dst_stage_mask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - if (p_post_barrier != RD::BARRIER_MASK_NO_BARRIER) { - _buffer_memory_barrier(buffer->buffer, p_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, true); - } - -#endif - return err; -} - -Error RenderingDeviceVulkan::buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V_MSG((p_size % 4) != 0, ERR_INVALID_PARAMETER, - "Size must be a multiple of four"); - ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, - "Updating buffers in is forbidden during creation of a draw list"); - ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, - "Updating buffers is forbidden during creation of a compute list"); - - VkPipelineStageFlags dst_stage_mask = 0; - VkAccessFlags dst_access = 0; - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - // Protect subsequent updates. - dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; - dst_access = VK_ACCESS_TRANSFER_WRITE_BIT; - } - - Buffer *buffer = _get_buffer_from_owner(p_buffer, dst_stage_mask, dst_access, p_post_barrier); - if (!buffer) { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Buffer argument is not a valid buffer of any type."); - } - - ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, - "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); - - vkCmdFillBuffer(frames[frame].draw_command_buffer, buffer->buffer, p_offset, p_size, 0); - -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else - if (dst_stage_mask == 0) { - dst_stage_mask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - _buffer_memory_barrier(buffer->buffer, p_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, true); - -#endif - return OK; -} - -Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer, uint32_t p_offset, uint32_t p_size) { - _THREAD_SAFE_METHOD_ - - // It could be this buffer was just created. - VkPipelineShaderStageCreateFlags src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; - VkAccessFlags src_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT; - // Get the vulkan buffer and the potential stage/access possible. - Buffer *buffer = _get_buffer_from_owner(p_buffer, src_stage_mask, src_access_mask, BARRIER_MASK_ALL_BARRIERS); - if (!buffer) { - ERR_FAIL_V_MSG(Vector<uint8_t>(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving."); - } - - // Make sure no one is using the buffer -- the "true" gets us to the same command buffer as below. - _buffer_memory_barrier(buffer->buffer, 0, buffer->size, src_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, src_access_mask, VK_ACCESS_TRANSFER_READ_BIT, true); - - VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - - // Size of buffer to retrieve. - if (!p_size) { - p_size = buffer->size; - } else { - ERR_FAIL_COND_V_MSG(p_size + p_offset > buffer->size, Vector<uint8_t>(), - "Size is larger than the buffer."); - } - - Buffer tmp_buffer; - _buffer_allocate(&tmp_buffer, p_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT); - VkBufferCopy region; - region.srcOffset = p_offset; - region.dstOffset = 0; - region.size = p_size; - vkCmdCopyBuffer(command_buffer, buffer->buffer, tmp_buffer.buffer, 1, ®ion); // Dst buffer is in CPU, but I wonder if src buffer needs a barrier for this. - // Flush everything so memory can be safely mapped. - _flush(true); - - void *buffer_mem; - VkResult vkerr = vmaMapMemory(allocator, tmp_buffer.allocation, &buffer_mem); - ERR_FAIL_COND_V_MSG(vkerr, Vector<uint8_t>(), "vmaMapMemory failed with error " + itos(vkerr) + "."); - - Vector<uint8_t> buffer_data; - { - buffer_data.resize(p_size); - uint8_t *w = buffer_data.ptrw(); - memcpy(w, buffer_mem, p_size); - } - - vmaUnmapMemory(allocator, tmp_buffer.allocation); - - _buffer_free(&tmp_buffer); - - return buffer_data; -} - -/*************************/ -/**** RENDER PIPELINE ****/ -/*************************/ - -RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags, uint32_t p_for_render_pass, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { - _THREAD_SAFE_METHOD_ - - // Needs a shader. - Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_NULL_V(shader, RID()); - - ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), - "Compute shaders can't be used in render pipelines"); - - if (p_framebuffer_format == INVALID_ID) { - // If nothing provided, use an empty one (no attachments). - p_framebuffer_format = framebuffer_format_create(Vector<AttachmentFormat>()); - } - ERR_FAIL_COND_V(!framebuffer_formats.has(p_framebuffer_format), RID()); - const FramebufferFormat &fb_format = framebuffer_formats[p_framebuffer_format]; - - { // Validate shader vs framebuffer. - - ERR_FAIL_COND_V_MSG(p_for_render_pass >= uint32_t(fb_format.E->key().passes.size()), RID(), "Render pass requested for pipeline creation (" + itos(p_for_render_pass) + ") is out of bounds"); - const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; - uint32_t output_mask = 0; - for (int i = 0; i < pass.color_attachments.size(); i++) { - if (pass.color_attachments[i] != FramebufferPass::ATTACHMENT_UNUSED) { - output_mask |= 1 << i; - } - } - ERR_FAIL_COND_V_MSG(shader->fragment_output_mask != output_mask, RID(), - "Mismatch fragment shader output mask (" + itos(shader->fragment_output_mask) + ") and framebuffer color output mask (" + itos(output_mask) + ") when binding both in render pipeline."); - } - // Vertex. - VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info; - - if (p_vertex_format != INVALID_ID) { - // Uses vertices, else it does not. - ERR_FAIL_COND_V(!vertex_formats.has(p_vertex_format), RID()); - const VertexDescriptionCache &vd = vertex_formats[p_vertex_format]; - - pipeline_vertex_input_state_create_info = vd.create_info; - - // Validate with inputs. - for (uint64_t i = 0; i < 64; i++) { - if (!(shader->vertex_input_mask & (1ULL << i))) { - continue; - } - bool found = false; - for (int j = 0; j < vd.vertex_formats.size(); j++) { - if (vd.vertex_formats[j].location == i) { - found = true; - } - } - - ERR_FAIL_COND_V_MSG(!found, RID(), - "Shader vertex input location (" + itos(i) + ") not provided in vertex input description for pipeline creation."); - } - - } else { - // Does not use vertices. - pipeline_vertex_input_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - pipeline_vertex_input_state_create_info.pNext = nullptr; - pipeline_vertex_input_state_create_info.flags = 0; - pipeline_vertex_input_state_create_info.vertexBindingDescriptionCount = 0; - pipeline_vertex_input_state_create_info.pVertexBindingDescriptions = nullptr; - pipeline_vertex_input_state_create_info.vertexAttributeDescriptionCount = 0; - pipeline_vertex_input_state_create_info.pVertexAttributeDescriptions = nullptr; - - ERR_FAIL_COND_V_MSG(shader->vertex_input_mask != 0, RID(), - "Shader contains vertex inputs, but no vertex input description was provided for pipeline creation."); - } - // Input assembly. - - ERR_FAIL_INDEX_V(p_render_primitive, RENDER_PRIMITIVE_MAX, RID()); - - VkPipelineInputAssemblyStateCreateInfo input_assembly_create_info; - input_assembly_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - input_assembly_create_info.pNext = nullptr; - input_assembly_create_info.flags = 0; - - static const VkPrimitiveTopology topology_list[RENDER_PRIMITIVE_MAX] = { - VK_PRIMITIVE_TOPOLOGY_POINT_LIST, - VK_PRIMITIVE_TOPOLOGY_LINE_LIST, - VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, - VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, - VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, - VK_PRIMITIVE_TOPOLOGY_PATCH_LIST - }; - - input_assembly_create_info.topology = topology_list[p_render_primitive]; - input_assembly_create_info.primitiveRestartEnable = (p_render_primitive == RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_RESTART_INDEX); - - // Tessellation. - VkPipelineTessellationStateCreateInfo tessellation_create_info; - tessellation_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; - tessellation_create_info.pNext = nullptr; - tessellation_create_info.flags = 0; - ERR_FAIL_COND_V(limits.maxTessellationPatchSize > 0 && (p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > limits.maxTessellationPatchSize), RID()); - tessellation_create_info.patchControlPoints = p_rasterization_state.patch_control_points; - - VkPipelineViewportStateCreateInfo viewport_state_create_info; - viewport_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewport_state_create_info.pNext = nullptr; - viewport_state_create_info.flags = 0; - viewport_state_create_info.viewportCount = 1; // If VR extensions are supported at some point, this will have to be customizable in the framebuffer format. - viewport_state_create_info.pViewports = nullptr; - viewport_state_create_info.scissorCount = 1; - viewport_state_create_info.pScissors = nullptr; - - // Rasterization. - VkPipelineRasterizationStateCreateInfo rasterization_state_create_info; - rasterization_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterization_state_create_info.pNext = nullptr; - rasterization_state_create_info.flags = 0; - rasterization_state_create_info.depthClampEnable = p_rasterization_state.enable_depth_clamp; - rasterization_state_create_info.rasterizerDiscardEnable = p_rasterization_state.discard_primitives; - rasterization_state_create_info.polygonMode = (p_rasterization_state.wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL); - static const VkCullModeFlags cull_mode[3] = { - VK_CULL_MODE_NONE, - VK_CULL_MODE_FRONT_BIT, - VK_CULL_MODE_BACK_BIT - }; - - ERR_FAIL_INDEX_V(p_rasterization_state.cull_mode, 3, RID()); - rasterization_state_create_info.cullMode = cull_mode[p_rasterization_state.cull_mode]; - rasterization_state_create_info.frontFace = (p_rasterization_state.front_face == POLYGON_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE); - rasterization_state_create_info.depthBiasEnable = p_rasterization_state.depth_bias_enabled; - rasterization_state_create_info.depthBiasConstantFactor = p_rasterization_state.depth_bias_constant_factor; - rasterization_state_create_info.depthBiasClamp = p_rasterization_state.depth_bias_clamp; - rasterization_state_create_info.depthBiasSlopeFactor = p_rasterization_state.depth_bias_slope_factor; - rasterization_state_create_info.lineWidth = p_rasterization_state.line_width; - - // Multisample. - VkPipelineMultisampleStateCreateInfo multisample_state_create_info; - multisample_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisample_state_create_info.pNext = nullptr; - multisample_state_create_info.flags = 0; - - multisample_state_create_info.rasterizationSamples = _ensure_supported_sample_count(p_multisample_state.sample_count); - multisample_state_create_info.sampleShadingEnable = p_multisample_state.enable_sample_shading; - multisample_state_create_info.minSampleShading = p_multisample_state.min_sample_shading; - Vector<VkSampleMask> sample_mask; - if (p_multisample_state.sample_mask.size()) { - // Use sample mask. - const int rasterization_sample_mask_expected_size[TEXTURE_SAMPLES_MAX] = { - 1, 2, 4, 8, 16, 32, 64 - }; - ERR_FAIL_COND_V(rasterization_sample_mask_expected_size[p_multisample_state.sample_count] != p_multisample_state.sample_mask.size(), RID()); - sample_mask.resize(p_multisample_state.sample_mask.size()); - for (int i = 0; i < p_multisample_state.sample_mask.size(); i++) { - VkSampleMask mask = p_multisample_state.sample_mask[i]; - sample_mask.push_back(mask); - } - multisample_state_create_info.pSampleMask = sample_mask.ptr(); - } else { - multisample_state_create_info.pSampleMask = nullptr; - } - - multisample_state_create_info.alphaToCoverageEnable = p_multisample_state.enable_alpha_to_coverage; - multisample_state_create_info.alphaToOneEnable = p_multisample_state.enable_alpha_to_one; - - // Depth stencil. - - VkPipelineDepthStencilStateCreateInfo depth_stencil_state_create_info; - depth_stencil_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depth_stencil_state_create_info.pNext = nullptr; - depth_stencil_state_create_info.flags = 0; - depth_stencil_state_create_info.depthTestEnable = p_depth_stencil_state.enable_depth_test; - depth_stencil_state_create_info.depthWriteEnable = p_depth_stencil_state.enable_depth_write; - ERR_FAIL_INDEX_V(p_depth_stencil_state.depth_compare_operator, COMPARE_OP_MAX, RID()); - depth_stencil_state_create_info.depthCompareOp = compare_operators[p_depth_stencil_state.depth_compare_operator]; - depth_stencil_state_create_info.depthBoundsTestEnable = p_depth_stencil_state.enable_depth_range; - depth_stencil_state_create_info.stencilTestEnable = p_depth_stencil_state.enable_stencil; - - ERR_FAIL_INDEX_V(p_depth_stencil_state.front_op.fail, STENCIL_OP_MAX, RID()); - depth_stencil_state_create_info.front.failOp = stencil_operations[p_depth_stencil_state.front_op.fail]; - ERR_FAIL_INDEX_V(p_depth_stencil_state.front_op.pass, STENCIL_OP_MAX, RID()); - depth_stencil_state_create_info.front.passOp = stencil_operations[p_depth_stencil_state.front_op.pass]; - ERR_FAIL_INDEX_V(p_depth_stencil_state.front_op.depth_fail, STENCIL_OP_MAX, RID()); - depth_stencil_state_create_info.front.depthFailOp = stencil_operations[p_depth_stencil_state.front_op.depth_fail]; - ERR_FAIL_INDEX_V(p_depth_stencil_state.front_op.compare, COMPARE_OP_MAX, RID()); - depth_stencil_state_create_info.front.compareOp = compare_operators[p_depth_stencil_state.front_op.compare]; - depth_stencil_state_create_info.front.compareMask = p_depth_stencil_state.front_op.compare_mask; - depth_stencil_state_create_info.front.writeMask = p_depth_stencil_state.front_op.write_mask; - depth_stencil_state_create_info.front.reference = p_depth_stencil_state.front_op.reference; - - ERR_FAIL_INDEX_V(p_depth_stencil_state.back_op.fail, STENCIL_OP_MAX, RID()); - depth_stencil_state_create_info.back.failOp = stencil_operations[p_depth_stencil_state.back_op.fail]; - ERR_FAIL_INDEX_V(p_depth_stencil_state.back_op.pass, STENCIL_OP_MAX, RID()); - depth_stencil_state_create_info.back.passOp = stencil_operations[p_depth_stencil_state.back_op.pass]; - ERR_FAIL_INDEX_V(p_depth_stencil_state.back_op.depth_fail, STENCIL_OP_MAX, RID()); - depth_stencil_state_create_info.back.depthFailOp = stencil_operations[p_depth_stencil_state.back_op.depth_fail]; - ERR_FAIL_INDEX_V(p_depth_stencil_state.back_op.compare, COMPARE_OP_MAX, RID()); - depth_stencil_state_create_info.back.compareOp = compare_operators[p_depth_stencil_state.back_op.compare]; - depth_stencil_state_create_info.back.compareMask = p_depth_stencil_state.back_op.compare_mask; - depth_stencil_state_create_info.back.writeMask = p_depth_stencil_state.back_op.write_mask; - depth_stencil_state_create_info.back.reference = p_depth_stencil_state.back_op.reference; - - depth_stencil_state_create_info.minDepthBounds = p_depth_stencil_state.depth_range_min; - depth_stencil_state_create_info.maxDepthBounds = p_depth_stencil_state.depth_range_max; - - // Blend state. - VkPipelineColorBlendStateCreateInfo color_blend_state_create_info; - color_blend_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - color_blend_state_create_info.pNext = nullptr; - color_blend_state_create_info.flags = 0; - color_blend_state_create_info.logicOpEnable = p_blend_state.enable_logic_op; - ERR_FAIL_INDEX_V(p_blend_state.logic_op, LOGIC_OP_MAX, RID()); - color_blend_state_create_info.logicOp = logic_operations[p_blend_state.logic_op]; - - Vector<VkPipelineColorBlendAttachmentState> attachment_states; - { - const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; - attachment_states.resize(pass.color_attachments.size()); - ERR_FAIL_COND_V(p_blend_state.attachments.size() < pass.color_attachments.size(), RID()); - for (int i = 0; i < pass.color_attachments.size(); i++) { - VkPipelineColorBlendAttachmentState state; - if (pass.color_attachments[i] == FramebufferPass::ATTACHMENT_UNUSED) { - state.blendEnable = false; - - state.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO; - state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; - state.colorBlendOp = VK_BLEND_OP_ADD; - - state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - state.alphaBlendOp = VK_BLEND_OP_ADD; - - state.colorWriteMask = 0; - } else { - state.blendEnable = p_blend_state.attachments[i].enable_blend; - - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); - state.srcColorBlendFactor = blend_factors[p_blend_state.attachments[i].src_color_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); - state.dstColorBlendFactor = blend_factors[p_blend_state.attachments[i].dst_color_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].color_blend_op, BLEND_OP_MAX, RID()); - state.colorBlendOp = blend_operations[p_blend_state.attachments[i].color_blend_op]; - - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - state.srcAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].src_alpha_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - state.dstAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].dst_alpha_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].alpha_blend_op, BLEND_OP_MAX, RID()); - state.alphaBlendOp = blend_operations[p_blend_state.attachments[i].alpha_blend_op]; - - state.colorWriteMask = 0; - if (p_blend_state.attachments[i].write_r) { - state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; - } - if (p_blend_state.attachments[i].write_g) { - state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; - } - if (p_blend_state.attachments[i].write_b) { - state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; - } - if (p_blend_state.attachments[i].write_a) { - state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; - } - } - attachment_states.write[i] = state; - } - } - - color_blend_state_create_info.attachmentCount = attachment_states.size(); - color_blend_state_create_info.pAttachments = attachment_states.ptr(); - - color_blend_state_create_info.blendConstants[0] = p_blend_state.blend_constant.r; - color_blend_state_create_info.blendConstants[1] = p_blend_state.blend_constant.g; - color_blend_state_create_info.blendConstants[2] = p_blend_state.blend_constant.b; - color_blend_state_create_info.blendConstants[3] = p_blend_state.blend_constant.a; - - // Dynamic state. - - VkPipelineDynamicStateCreateInfo dynamic_state_create_info; - dynamic_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamic_state_create_info.pNext = nullptr; - dynamic_state_create_info.flags = 0; - Vector<VkDynamicState> dynamic_states; // Vulkan is weird. - - dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); // Viewport and scissor are always dynamic. - dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); - - if (p_dynamic_state_flags.has_flag(DYNAMIC_STATE_LINE_WIDTH)) { - dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH); - } - - if (p_dynamic_state_flags.has_flag(DYNAMIC_STATE_DEPTH_BIAS)) { - dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS); - } - - if (p_dynamic_state_flags.has_flag(DYNAMIC_STATE_BLEND_CONSTANTS)) { - dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS); - } - - if (p_dynamic_state_flags.has_flag(DYNAMIC_STATE_DEPTH_BOUNDS)) { - dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS); - } - - if (p_dynamic_state_flags.has_flag(DYNAMIC_STATE_STENCIL_COMPARE_MASK)) { - dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); - } - - if (p_dynamic_state_flags.has_flag(DYNAMIC_STATE_STENCIL_WRITE_MASK)) { - dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); - } - - if (p_dynamic_state_flags.has_flag(DYNAMIC_STATE_STENCIL_REFERENCE)) { - dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); - } - - dynamic_state_create_info.dynamicStateCount = dynamic_states.size(); - dynamic_state_create_info.pDynamicStates = dynamic_states.ptr(); - - void *graphics_pipeline_nextptr = nullptr; - - VkPipelineFragmentShadingRateStateCreateInfoKHR vrs_create_info; - if (context->get_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. - - vrs_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR; - vrs_create_info.pNext = nullptr; - vrs_create_info.fragmentSize = { 4, 4 }; - vrs_create_info.combinerOps[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR; // We don't use pipeline/primitive VRS so this really doesn't matter. - vrs_create_info.combinerOps[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR; // Always use the outcome of attachment VRS if enabled. - - graphics_pipeline_nextptr = &vrs_create_info; - } - - // Finally, pipeline create info. - VkGraphicsPipelineCreateInfo graphics_pipeline_create_info; - - graphics_pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - graphics_pipeline_create_info.pNext = graphics_pipeline_nextptr; - graphics_pipeline_create_info.flags = 0; - - Vector<VkPipelineShaderStageCreateInfo> pipeline_stages = shader->pipeline_stages; - Vector<VkSpecializationInfo> specialization_info; - Vector<Vector<VkSpecializationMapEntry>> specialization_map_entries; - Vector<uint32_t> specialization_constant_data; - - if (shader->specialization_constants.size()) { - specialization_constant_data.resize(shader->specialization_constants.size()); - uint32_t *data_ptr = specialization_constant_data.ptrw(); - specialization_info.resize(pipeline_stages.size()); - specialization_map_entries.resize(pipeline_stages.size()); - for (int i = 0; i < shader->specialization_constants.size(); i++) { - // See if overridden. - const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; - data_ptr[i] = sc.constant.int_value; // Just copy the 32 bits. - - for (int j = 0; j < p_specialization_constants.size(); j++) { - const PipelineSpecializationConstant &psc = p_specialization_constants[j]; - if (psc.constant_id == sc.constant.constant_id) { - ERR_FAIL_COND_V_MSG(psc.type != sc.constant.type, RID(), "Specialization constant provided for id (" + itos(sc.constant.constant_id) + ") is of the wrong type."); - data_ptr[i] = psc.int_value; - break; - } - } - - VkSpecializationMapEntry entry; - - entry.constantID = sc.constant.constant_id; - entry.offset = i * sizeof(uint32_t); - entry.size = sizeof(uint32_t); - - for (int j = 0; j < SHADER_STAGE_MAX; j++) { - if (sc.stage_flags & (1 << j)) { - VkShaderStageFlagBits stage = shader_stage_masks[j]; - for (int k = 0; k < pipeline_stages.size(); k++) { - if (pipeline_stages[k].stage == stage) { - specialization_map_entries.write[k].push_back(entry); - } - } - } - } - } - - for (int i = 0; i < pipeline_stages.size(); i++) { - if (specialization_map_entries[i].size()) { - specialization_info.write[i].dataSize = specialization_constant_data.size() * sizeof(uint32_t); - specialization_info.write[i].pData = data_ptr; - specialization_info.write[i].mapEntryCount = specialization_map_entries[i].size(); - specialization_info.write[i].pMapEntries = specialization_map_entries[i].ptr(); - pipeline_stages.write[i].pSpecializationInfo = specialization_info.ptr() + i; - } - } - } - - graphics_pipeline_create_info.stageCount = pipeline_stages.size(); - graphics_pipeline_create_info.pStages = pipeline_stages.ptr(); - - graphics_pipeline_create_info.pVertexInputState = &pipeline_vertex_input_state_create_info; - graphics_pipeline_create_info.pInputAssemblyState = &input_assembly_create_info; - graphics_pipeline_create_info.pTessellationState = &tessellation_create_info; - graphics_pipeline_create_info.pViewportState = &viewport_state_create_info; - graphics_pipeline_create_info.pRasterizationState = &rasterization_state_create_info; - graphics_pipeline_create_info.pMultisampleState = &multisample_state_create_info; - graphics_pipeline_create_info.pDepthStencilState = &depth_stencil_state_create_info; - graphics_pipeline_create_info.pColorBlendState = &color_blend_state_create_info; - graphics_pipeline_create_info.pDynamicState = &dynamic_state_create_info; - graphics_pipeline_create_info.layout = shader->pipeline_layout; - graphics_pipeline_create_info.renderPass = fb_format.render_pass; - - graphics_pipeline_create_info.subpass = p_for_render_pass; - graphics_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; - graphics_pipeline_create_info.basePipelineIndex = 0; - - RenderPipeline pipeline; - VkResult err = vkCreateGraphicsPipelines(device, pipelines_cache.cache_object, 1, &graphics_pipeline_create_info, nullptr, &pipeline.pipeline); - ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateGraphicsPipelines failed with error " + itos(err) + " for shader '" + shader->name + "'."); - - if (pipelines_cache.cache_object != VK_NULL_HANDLE) { - _update_pipeline_cache(); - } - - pipeline.set_formats = shader->set_formats; - pipeline.push_constant_stages_mask = shader->push_constant.vk_stages_mask; - pipeline.pipeline_layout = shader->pipeline_layout; - pipeline.shader = p_shader; - pipeline.push_constant_size = shader->push_constant.size; - -#ifdef DEBUG_ENABLED - pipeline.validation.dynamic_state = p_dynamic_state_flags; - pipeline.validation.framebuffer_format = p_framebuffer_format; - pipeline.validation.render_pass = p_for_render_pass; - pipeline.validation.vertex_format = p_vertex_format; - pipeline.validation.uses_restart_indices = input_assembly_create_info.primitiveRestartEnable; - - static const uint32_t primitive_divisor[RENDER_PRIMITIVE_MAX] = { - 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1 - }; - pipeline.validation.primitive_divisor = primitive_divisor[p_render_primitive]; - static const uint32_t primitive_minimum[RENDER_PRIMITIVE_MAX] = { - 1, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 3, - 1, - }; - pipeline.validation.primitive_minimum = primitive_minimum[p_render_primitive]; -#endif - // Create ID to associate with this pipeline. - RID id = render_pipeline_owner.make_rid(pipeline); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - // Now add all the dependencies. - _add_dependency(id, p_shader); - return id; -} - -bool RenderingDeviceVulkan::render_pipeline_is_valid(RID p_pipeline) { - _THREAD_SAFE_METHOD_ - return render_pipeline_owner.owns(p_pipeline); -} - -/**************************/ -/**** COMPUTE PIPELINE ****/ -/**************************/ - -RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { - _THREAD_SAFE_METHOD_ - - // Needs a shader. - Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_NULL_V(shader, RID()); - - ERR_FAIL_COND_V_MSG(!shader->is_compute, RID(), - "Non-compute shaders can't be used in compute pipelines"); - - // Finally, pipeline create info. - VkComputePipelineCreateInfo compute_pipeline_create_info; - - compute_pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; - compute_pipeline_create_info.pNext = nullptr; - compute_pipeline_create_info.flags = 0; - - compute_pipeline_create_info.stage = shader->pipeline_stages[0]; - compute_pipeline_create_info.layout = shader->pipeline_layout; - compute_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; - compute_pipeline_create_info.basePipelineIndex = 0; - - VkSpecializationInfo specialization_info; - Vector<VkSpecializationMapEntry> specialization_map_entries; - Vector<uint32_t> specialization_constant_data; - - if (shader->specialization_constants.size()) { - specialization_constant_data.resize(shader->specialization_constants.size()); - uint32_t *data_ptr = specialization_constant_data.ptrw(); - for (int i = 0; i < shader->specialization_constants.size(); i++) { - // See if overridden. - const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; - data_ptr[i] = sc.constant.int_value; // Just copy the 32 bits. - - for (int j = 0; j < p_specialization_constants.size(); j++) { - const PipelineSpecializationConstant &psc = p_specialization_constants[j]; - if (psc.constant_id == sc.constant.constant_id) { - ERR_FAIL_COND_V_MSG(psc.type != sc.constant.type, RID(), "Specialization constant provided for id (" + itos(sc.constant.constant_id) + ") is of the wrong type."); - data_ptr[i] = psc.int_value; - break; - } - } - - VkSpecializationMapEntry entry; - - entry.constantID = sc.constant.constant_id; - entry.offset = i * sizeof(uint32_t); - entry.size = sizeof(uint32_t); - - specialization_map_entries.push_back(entry); - } - - specialization_info.dataSize = specialization_constant_data.size() * sizeof(uint32_t); - specialization_info.pData = data_ptr; - specialization_info.mapEntryCount = specialization_map_entries.size(); - specialization_info.pMapEntries = specialization_map_entries.ptr(); - - compute_pipeline_create_info.stage.pSpecializationInfo = &specialization_info; - } - - ComputePipeline pipeline; - VkResult err = vkCreateComputePipelines(device, pipelines_cache.cache_object, 1, &compute_pipeline_create_info, nullptr, &pipeline.pipeline); - ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateComputePipelines failed with error " + itos(err) + "."); - - if (pipelines_cache.cache_object != VK_NULL_HANDLE) { - _update_pipeline_cache(); - } - - pipeline.set_formats = shader->set_formats; - pipeline.push_constant_stages_mask = shader->push_constant.vk_stages_mask; - pipeline.pipeline_layout = shader->pipeline_layout; - pipeline.shader = p_shader; - pipeline.push_constant_size = shader->push_constant.size; - pipeline.local_group_size[0] = shader->compute_local_size[0]; - pipeline.local_group_size[1] = shader->compute_local_size[1]; - pipeline.local_group_size[2] = shader->compute_local_size[2]; - - // Create ID to associate with this pipeline. - RID id = compute_pipeline_owner.make_rid(pipeline); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - // Now add all the dependencies. - _add_dependency(id, p_shader); - return id; -} - -bool RenderingDeviceVulkan::compute_pipeline_is_valid(RID p_pipeline) { - return compute_pipeline_owner.owns(p_pipeline); -} - -/****************/ -/**** SCREEN ****/ -/****************/ - -int RenderingDeviceVulkan::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); -} - -int RenderingDeviceVulkan::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); -} - -RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::screen_get_framebuffer_format() const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(local_device.is_valid(), INVALID_ID, "Local devices have no screen"); - - // Very hacky, but not used often per frame so I guess ok. - VkFormat vkformat = context->get_screen_format(); - DataFormat format = DATA_FORMAT_MAX; - for (int i = 0; i < DATA_FORMAT_MAX; i++) { - if (vkformat == vulkan_formats[i]) { - format = DataFormat(i); - break; - } - } - - ERR_FAIL_COND_V(format == DATA_FORMAT_MAX, INVALID_ID); - - AttachmentFormat attachment; - attachment.format = format; - attachment.samples = TEXTURE_SAMPLES_1; - attachment.usage_flags = TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; - Vector<AttachmentFormat> screen_attachment; - screen_attachment.push_back(attachment); - return const_cast<RenderingDeviceVulkan *>(this)->framebuffer_format_create(screen_attachment); -} - -/*******************/ -/**** DRAW LIST ****/ -/*******************/ - -RenderingDevice::DrawListID RenderingDeviceVulkan::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."); - - VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - - if (!context->window_is_valid_swapchain(p_screen)) { - return INVALID_ID; - } - - Size2i size = Size2i(context->window_get_width(p_screen), context->window_get_height(p_screen)); - - _draw_list_allocate(Rect2i(Vector2i(), size), 0, 0); -#ifdef DEBUG_ENABLED - draw_list_framebuffer_format = screen_get_framebuffer_format(); -#endif - draw_list_subpass_count = 1; - - VkRenderPassBeginInfo render_pass_begin; - render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - render_pass_begin.pNext = nullptr; - render_pass_begin.renderPass = context->window_get_render_pass(p_screen); - render_pass_begin.framebuffer = context->window_get_framebuffer(p_screen); - - render_pass_begin.renderArea.extent.width = size.width; - render_pass_begin.renderArea.extent.height = size.height; - render_pass_begin.renderArea.offset.x = 0; - render_pass_begin.renderArea.offset.y = 0; - - render_pass_begin.clearValueCount = 1; - - VkClearValue clear_value; - clear_value.color.float32[0] = p_clear_color.r; - clear_value.color.float32[1] = p_clear_color.g; - clear_value.color.float32[2] = p_clear_color.b; - clear_value.color.float32[3] = p_clear_color.a; - - render_pass_begin.pClearValues = &clear_value; - - vkCmdBeginRenderPass(command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); - - uint32_t size_x = screen_get_width(p_screen); - uint32_t size_y = screen_get_height(p_screen); - - VkViewport viewport; - viewport.x = 0; - viewport.y = 0; - viewport.width = size_x; - viewport.height = size_y; - viewport.minDepth = 0; - viewport.maxDepth = 1.0; - - vkCmdSetViewport(command_buffer, 0, 1, &viewport); - - VkRect2D scissor; - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent.width = size_x; - scissor.extent.height = size_y; - - vkCmdSetScissor(command_buffer, 0, 1, &scissor); - - return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; -} - -Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count) { - Framebuffer::VersionKey vk; - vk.initial_color_action = p_initial_color_action; - vk.final_color_action = p_final_color_action; - vk.initial_depth_action = p_initial_depth_action; - vk.final_depth_action = p_final_depth_action; - vk.view_count = p_framebuffer->view_count; - - if (!p_framebuffer->framebuffers.has(vk)) { - // Need to create this version. - Framebuffer::Version version; - - version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, framebuffer_formats[p_framebuffer->format_id].E->key().passes, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_framebuffer->view_count); - - VkFramebufferCreateInfo framebuffer_create_info; - framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebuffer_create_info.pNext = nullptr; - framebuffer_create_info.flags = 0; - framebuffer_create_info.renderPass = version.render_pass; - Vector<VkImageView> attachments; - for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); - if (texture) { - attachments.push_back(texture->view); - if (!(texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) { // VRS attachment will be a different size. - ERR_FAIL_COND_V(texture->width != p_framebuffer->size.width, ERR_BUG); - ERR_FAIL_COND_V(texture->height != p_framebuffer->size.height, ERR_BUG); - } - } - } - framebuffer_create_info.attachmentCount = attachments.size(); - framebuffer_create_info.pAttachments = attachments.ptr(); - framebuffer_create_info.width = p_framebuffer->size.width; - framebuffer_create_info.height = p_framebuffer->size.height; - framebuffer_create_info.layers = 1; - - VkResult err = vkCreateFramebuffer(device, &framebuffer_create_info, nullptr, &version.framebuffer); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkCreateFramebuffer failed with error " + itos(err) + "."); - - version.subpass_count = framebuffer_formats[p_framebuffer->format_id].E->key().passes.size(); - - p_framebuffer->framebuffers.insert(vk, version); - } - const Framebuffer::Version &version = p_framebuffer->framebuffers[vk]; - *r_framebuffer = version.framebuffer; - *r_render_pass = version.render_pass; - *r_subpass_count = version.subpass_count; - - return OK; -} - -Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures) { - VkRenderPassBeginInfo render_pass_begin; - render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - render_pass_begin.pNext = nullptr; - render_pass_begin.renderPass = render_pass; - render_pass_begin.framebuffer = vkframebuffer; - /* - * Given how API works, it makes sense to always fully operate on the whole framebuffer. - * This allows better continue operations for operations like shadowmapping. - render_pass_begin.renderArea.extent.width = viewport_size.width; - render_pass_begin.renderArea.extent.height = viewport_size.height; - render_pass_begin.renderArea.offset.x = viewport_offset.x; - render_pass_begin.renderArea.offset.y = viewport_offset.y; - */ - render_pass_begin.renderArea.extent.width = framebuffer->size.width; - render_pass_begin.renderArea.extent.height = framebuffer->size.height; - render_pass_begin.renderArea.offset.x = 0; - render_pass_begin.renderArea.offset.y = 0; - - Vector<VkClearValue> clear_values; - clear_values.resize(framebuffer->texture_ids.size()); - int clear_values_count = 0; - { - int color_index = 0; - for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - VkClearValue clear_value; - - Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - if (!texture) { - color_index++; - continue; - } - - if (color_index < p_clear_colors.size() && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); // A bug. - Color clear_color = p_clear_colors[color_index]; - clear_value.color.float32[0] = clear_color.r; - clear_value.color.float32[1] = clear_color.g; - clear_value.color.float32[2] = clear_color.b; - clear_value.color.float32[3] = clear_color.a; - color_index++; - } else if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - clear_value.depthStencil.depth = p_clear_depth; - clear_value.depthStencil.stencil = p_clear_stencil; - } else { - clear_value.color.float32[0] = 0; - clear_value.color.float32[1] = 0; - clear_value.color.float32[2] = 0; - clear_value.color.float32[3] = 0; - } - clear_values.write[clear_values_count++] = clear_value; - } - } - - render_pass_begin.clearValueCount = clear_values_count; - render_pass_begin.pClearValues = clear_values.ptr(); - - for (int i = 0; i < p_storage_textures.size(); i++) { - Texture *texture = texture_owner.get_or_null(p_storage_textures[i]); - if (!texture) { - continue; - } - ERR_CONTINUE_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), "Supplied storage texture " + itos(i) + " for draw list is not set to be used for storage."); - - if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { - // Must change layout to general. - VkImageMemoryBarrier image_memory_barrier; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - image_memory_barrier.oldLayout = texture->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = texture->image; - image_memory_barrier.subresourceRange.aspectMask = texture->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = texture->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = texture->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = texture->base_layer; - image_memory_barrier.subresourceRange.layerCount = texture->layers; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); - - texture->layout = VK_IMAGE_LAYOUT_GENERAL; - - draw_list_storage_textures.push_back(p_storage_textures[i]); - } - } - - vkCmdBeginRenderPass(command_buffer, &render_pass_begin, subpass_contents); - - // Mark textures as bound. - draw_list_bound_textures.clear(); - draw_list_unbind_color_textures = p_final_color_action != FINAL_ACTION_CONTINUE; - draw_list_unbind_depth_textures = p_final_depth_action != FINAL_ACTION_CONTINUE; - - for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - if (!texture) { - continue; - } - texture->bound = true; - draw_list_bound_textures.push_back(framebuffer->texture_ids[i]); - } - - return OK; -} - -void RenderingDeviceVulkan::_draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil) { - Vector<VkClearAttachment> clear_attachments; - int color_index = 0; - int texture_index = 0; - for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); - - if (!texture) { - texture_index++; - continue; - } - - VkClearAttachment clear_at = {}; - if (p_clear_color && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - Color clear_color = p_clear_colors[texture_index++]; - clear_at.clearValue.color.float32[0] = clear_color.r; - clear_at.clearValue.color.float32[1] = clear_color.g; - clear_at.clearValue.color.float32[2] = clear_color.b; - clear_at.clearValue.color.float32[3] = clear_color.a; - clear_at.colorAttachment = color_index++; - clear_at.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - } else if (p_clear_depth && texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - clear_at.clearValue.depthStencil.depth = p_depth; - clear_at.clearValue.depthStencil.stencil = p_stencil; - clear_at.colorAttachment = 0; - clear_at.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - if (format_has_stencil(texture->format)) { - clear_at.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - } else { - ERR_CONTINUE(true); - } - clear_attachments.push_back(clear_at); - } - - VkClearRect cr; - cr.baseArrayLayer = 0; - cr.layerCount = 1; - cr.rect.offset.x = p_viewport_offset.x; - cr.rect.offset.y = p_viewport_offset.y; - cr.rect.extent.width = p_viewport_size.width; - cr.rect.extent.height = p_viewport_size.height; - - vkCmdClearAttachments(p_draw_list->command_buffer, clear_attachments.size(), clear_attachments.ptr(), 1, &cr); -} - -RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { - _THREAD_SAFE_METHOD_ - - 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 && !compute_list->state.allow_draw_overlap, INVALID_ID, "Only one draw/compute list can be active at the same time."); - - Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); - ERR_FAIL_NULL_V(framebuffer, INVALID_ID); - - Point2i viewport_offset; - Point2i viewport_size = framebuffer->size; - bool needs_clear_color = false; - bool needs_clear_depth = false; - - if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. - Rect2i viewport(viewport_offset, viewport_size); - Rect2i regioni = p_region; - if (!(regioni.position.x >= viewport.position.x) && (regioni.position.y >= viewport.position.y) && - ((regioni.position.x + regioni.size.x) <= (viewport.position.x + viewport.size.x)) && - ((regioni.position.y + regioni.size.y) <= (viewport.position.y + viewport.size.y))) { - ERR_FAIL_V_MSG(INVALID_ID, "When supplying a custom region, it must be contained within the framebuffer rectangle"); - } - - viewport_offset = regioni.position; - viewport_size = regioni.size; - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_CONTINUE; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_CONTINUE; - } - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_KEEP; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_KEEP; - } - } - - if (p_initial_color_action == INITIAL_ACTION_CLEAR || needs_clear_color) { // Check clear values. - int color_count = 0; - for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - // We only check for our VRS usage bit if this is not the first texture id. - // If it is the first we're likely populating our VRS texture. - // Bit dirty but... - if (!texture || (!(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !(i != 0 && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT))) { - if (!texture || !texture->is_resolve_buffer) { - color_count++; - } - } - } - ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, INVALID_ID, "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(color_count) + ")."); - } - - VkFramebuffer vkframebuffer; - VkRenderPass render_pass; - - Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass, &draw_list_subpass_count); - ERR_FAIL_COND_V(err != OK, INVALID_ID); - - VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, command_buffer, VK_SUBPASS_CONTENTS_INLINE, p_storage_textures); - - if (err != OK) { - return INVALID_ID; - } - - draw_list_render_pass = render_pass; - draw_list_vkframebuffer = vkframebuffer; - - _draw_list_allocate(Rect2i(viewport_offset, viewport_size), 0, 0); -#ifdef DEBUG_ENABLED - draw_list_framebuffer_format = framebuffer->format_id; -#endif - draw_list_current_subpass = 0; - - if (needs_clear_color || needs_clear_depth) { - _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); - } - - VkViewport viewport; - viewport.x = viewport_offset.x; - viewport.y = viewport_offset.y; - viewport.width = viewport_size.width; - viewport.height = viewport_size.height; - viewport.minDepth = 0; - viewport.maxDepth = 1.0; - - vkCmdSetViewport(command_buffer, 0, 1, &viewport); - - VkRect2D scissor; - scissor.offset.x = viewport_offset.x; - scissor.offset.y = viewport_offset.y; - scissor.extent.width = viewport_size.width; - scissor.extent.height = viewport_size.height; - - vkCmdSetScissor(command_buffer, 0, 1, &scissor); - - return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; -} - -Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V_MSG(draw_list != nullptr, ERR_BUSY, "Only one draw list can be active at the same time."); - ERR_FAIL_COND_V_MSG(compute_list != nullptr && !compute_list->state.allow_draw_overlap, ERR_BUSY, "Only one draw/compute list can be active at the same time."); - - ERR_FAIL_COND_V(p_splits < 1, ERR_INVALID_DECLARATION); - - Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); - ERR_FAIL_NULL_V(framebuffer, ERR_INVALID_DECLARATION); - - Point2i viewport_offset; - Point2i viewport_size = framebuffer->size; - - bool needs_clear_color = false; - bool needs_clear_depth = false; - - if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. - Rect2i viewport(viewport_offset, viewport_size); - Rect2i regioni = p_region; - if (!(regioni.position.x >= viewport.position.x) && (regioni.position.y >= viewport.position.y) && - ((regioni.position.x + regioni.size.x) <= (viewport.position.x + viewport.size.x)) && - ((regioni.position.y + regioni.size.y) <= (viewport.position.y + viewport.size.y))) { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "When supplying a custom region, it must be contained within the framebuffer rectangle"); - } - - viewport_offset = regioni.position; - viewport_size = regioni.size; - - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_KEEP; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_KEEP; - } - } - - if (p_initial_color_action == INITIAL_ACTION_CLEAR || needs_clear_color) { // Check clear values. - - int color_count = 0; - for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - - if (!texture || !(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - color_count++; - } - } - - ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, ERR_INVALID_PARAMETER, - "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_count) + ")."); - } - - VkFramebuffer vkframebuffer; - VkRenderPass render_pass; - - Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass, &draw_list_subpass_count); - ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - - VkCommandBuffer frame_command_buffer = frames[frame].draw_command_buffer; - err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, frame_command_buffer, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, p_storage_textures); - - if (err != OK) { - return ERR_CANT_CREATE; - } - - draw_list_current_subpass = 0; - -#ifdef DEBUG_ENABLED - draw_list_framebuffer_format = framebuffer->format_id; -#endif - draw_list_render_pass = render_pass; - draw_list_vkframebuffer = vkframebuffer; - - err = _draw_list_allocate(Rect2i(viewport_offset, viewport_size), p_splits, 0); - if (err != OK) { - return err; - } - - if (needs_clear_color || needs_clear_depth) { - _draw_list_insert_clear_region(&draw_list[0], framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); - } - - for (uint32_t i = 0; i < p_splits; i++) { - VkViewport viewport; - viewport.x = viewport_offset.x; - viewport.y = viewport_offset.y; - viewport.width = viewport_size.width; - viewport.height = viewport_size.height; - viewport.minDepth = 0; - viewport.maxDepth = 1.0; - - vkCmdSetViewport(draw_list[i].command_buffer, 0, 1, &viewport); - - VkRect2D scissor; - scissor.offset.x = viewport_offset.x; - scissor.offset.y = viewport_offset.y; - scissor.extent.width = viewport_size.width; - scissor.extent.height = viewport_size.height; - - vkCmdSetScissor(draw_list[i].command_buffer, 0, 1, &scissor); - r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; - } - - return OK; -} - -RenderingDeviceVulkan::DrawList *RenderingDeviceVulkan::_get_draw_list_ptr(DrawListID p_id) { - if (p_id < 0) { - return nullptr; - } - - if (!draw_list) { - return nullptr; - } else if (p_id == (int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT)) { - if (draw_list_split) { - return nullptr; - } - return draw_list; - } else if (p_id >> DrawListID(ID_BASE_SHIFT) == ID_TYPE_SPLIT_DRAW_LIST) { - if (!draw_list_split) { - return nullptr; - } - - uint64_t index = p_id & ((DrawListID(1) << DrawListID(ID_BASE_SHIFT)) - 1); // Mask. - - if (index >= draw_list_count) { - return nullptr; - } - - return &draw_list[index]; - } else { - return nullptr; - } -} - -void RenderingDeviceVulkan::draw_list_set_blend_constants(DrawListID p_list, const Color &p_color) { - DrawList *dl = _get_draw_list_ptr(p_list); - ERR_FAIL_NULL(dl); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); -#endif - - vkCmdSetBlendConstants(dl->command_buffer, p_color.components); -} - -void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline) { - DrawList *dl = _get_draw_list_ptr(p_list); - ERR_FAIL_NULL(dl); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); -#endif - - const RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_render_pipeline); - ERR_FAIL_NULL(pipeline); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(pipeline->validation.framebuffer_format != draw_list_framebuffer_format && pipeline->validation.render_pass != draw_list_current_subpass); -#endif - - if (p_render_pipeline == dl->state.pipeline) { - return; // Redundant state, return. - } - - dl->state.pipeline = p_render_pipeline; - dl->state.pipeline_layout = pipeline->pipeline_layout; - - vkCmdBindPipeline(dl->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); - - if (dl->state.pipeline_shader != pipeline->shader) { - // Shader changed, so descriptor sets may become incompatible. - - // Go through ALL sets, and unbind them (and all those above) if the format is different. - - uint32_t pcount = pipeline->set_formats.size(); // Formats count in this pipeline. - dl->state.set_count = MAX(dl->state.set_count, pcount); - const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats. - - bool sets_valid = true; // Once invalid, all above become invalid. - for (uint32_t i = 0; i < pcount; i++) { - // If a part of the format is different, invalidate it (and the rest). - if (!sets_valid || dl->state.sets[i].pipeline_expected_format != pformats[i]) { - dl->state.sets[i].bound = false; - dl->state.sets[i].pipeline_expected_format = pformats[i]; - sets_valid = false; - } - } - - for (uint32_t i = pcount; i < dl->state.set_count; i++) { - // Unbind the ones above (not used) if exist. - dl->state.sets[i].bound = false; - } - - dl->state.set_count = pcount; // Update set count. - - if (pipeline->push_constant_size) { - dl->state.pipeline_push_constant_stages = pipeline->push_constant_stages_mask; -#ifdef DEBUG_ENABLED - dl->validation.pipeline_push_constant_supplied = false; -#endif - } - - dl->state.pipeline_shader = pipeline->shader; - } - -#ifdef DEBUG_ENABLED - // Update render pass pipeline info. - dl->validation.pipeline_active = true; - dl->validation.pipeline_dynamic_state = pipeline->validation.dynamic_state; - dl->validation.pipeline_vertex_format = pipeline->validation.vertex_format; - dl->validation.pipeline_uses_restart_indices = pipeline->validation.uses_restart_indices; - dl->validation.pipeline_primitive_divisor = pipeline->validation.primitive_divisor; - dl->validation.pipeline_primitive_minimum = pipeline->validation.primitive_minimum; - dl->validation.pipeline_push_constant_size = pipeline->push_constant_size; -#endif -} - -void RenderingDeviceVulkan::draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index) { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(p_index >= limits.maxBoundDescriptorSets || p_index >= MAX_UNIFORM_SETS, - "Attempting to bind a descriptor set (" + itos(p_index) + ") greater than what the hardware supports (" + itos(limits.maxBoundDescriptorSets) + ")."); -#endif - DrawList *dl = _get_draw_list_ptr(p_list); - ERR_FAIL_NULL(dl); - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); -#endif - - const UniformSet *uniform_set = uniform_set_owner.get_or_null(p_uniform_set); - ERR_FAIL_NULL(uniform_set); - - if (p_index > dl->state.set_count) { - dl->state.set_count = p_index; - } - - dl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; // Update set pointer. - dl->state.sets[p_index].bound = false; // Needs rebind. - dl->state.sets[p_index].uniform_set_format = uniform_set->format; - dl->state.sets[p_index].uniform_set = p_uniform_set; - - uint32_t mst_count = uniform_set->mutable_storage_textures.size(); - if (mst_count) { - Texture **mst_textures = const_cast<UniformSet *>(uniform_set)->mutable_storage_textures.ptrw(); - for (uint32_t i = 0; i < mst_count; i++) { - if (mst_textures[i]->used_in_frame != frames_drawn) { - mst_textures[i]->used_in_frame = frames_drawn; - mst_textures[i]->used_in_transfer = false; - mst_textures[i]->used_in_compute = false; - } - mst_textures[i]->used_in_raster = true; - } - } - -#ifdef DEBUG_ENABLED - { // Validate that textures bound are not attached as framebuffer bindings. - uint32_t attachable_count = uniform_set->attachable_textures.size(); - const UniformSet::AttachableTexture *attachable_ptr = uniform_set->attachable_textures.ptr(); - uint32_t bound_count = draw_list_bound_textures.size(); - const RID *bound_ptr = draw_list_bound_textures.ptr(); - for (uint32_t i = 0; i < attachable_count; i++) { - for (uint32_t j = 0; j < bound_count; j++) { - ERR_FAIL_COND_MSG(attachable_ptr[i].texture == bound_ptr[j], - "Attempted to use the same texture in framebuffer attachment and a uniform (set: " + itos(p_index) + ", binding: " + itos(attachable_ptr[i].bind) + "), this is not allowed."); - } - } - } -#endif -} - -void RenderingDeviceVulkan::draw_list_bind_vertex_array(DrawListID p_list, RID p_vertex_array) { - DrawList *dl = _get_draw_list_ptr(p_list); - ERR_FAIL_NULL(dl); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); -#endif - - const VertexArray *vertex_array = vertex_array_owner.get_or_null(p_vertex_array); - ERR_FAIL_NULL(vertex_array); - - if (dl->state.vertex_array == p_vertex_array) { - return; // Already set. - } - - dl->state.vertex_array = p_vertex_array; - -#ifdef DEBUG_ENABLED - dl->validation.vertex_format = vertex_array->description; - dl->validation.vertex_max_instances_allowed = vertex_array->max_instances_allowed; -#endif - dl->validation.vertex_array_size = vertex_array->vertex_count; - vkCmdBindVertexBuffers(dl->command_buffer, 0, vertex_array->buffers.size(), vertex_array->buffers.ptr(), vertex_array->offsets.ptr()); -} - -void RenderingDeviceVulkan::draw_list_bind_index_array(DrawListID p_list, RID p_index_array) { - DrawList *dl = _get_draw_list_ptr(p_list); - ERR_FAIL_NULL(dl); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); -#endif - - const IndexArray *index_array = index_array_owner.get_or_null(p_index_array); - ERR_FAIL_NULL(index_array); - - if (dl->state.index_array == p_index_array) { - return; // Already set. - } - - dl->state.index_array = p_index_array; -#ifdef DEBUG_ENABLED - dl->validation.index_array_max_index = index_array->max_index; -#endif - dl->validation.index_array_size = index_array->indices; - dl->validation.index_array_offset = index_array->offset; - - vkCmdBindIndexBuffer(dl->command_buffer, index_array->buffer, 0, index_array->index_type); -} - -void RenderingDeviceVulkan::draw_list_set_line_width(DrawListID p_list, float p_width) { - DrawList *dl = _get_draw_list_ptr(p_list); - ERR_FAIL_NULL(dl); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); -#endif - - vkCmdSetLineWidth(dl->command_buffer, p_width); -} - -void RenderingDeviceVulkan::draw_list_set_push_constant(DrawListID p_list, const void *p_data, uint32_t p_data_size) { - DrawList *dl = _get_draw_list_ptr(p_list); - ERR_FAIL_NULL(dl); - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); -#endif - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(p_data_size != dl->validation.pipeline_push_constant_size, - "This render pipeline requires (" + itos(dl->validation.pipeline_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); -#endif - vkCmdPushConstants(dl->command_buffer, dl->state.pipeline_layout, dl->state.pipeline_push_constant_stages, 0, p_data_size, p_data); -#ifdef DEBUG_ENABLED - dl->validation.pipeline_push_constant_supplied = true; -#endif -} - -void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices, uint32_t p_instances, uint32_t p_procedural_vertices) { - DrawList *dl = _get_draw_list_ptr(p_list); - ERR_FAIL_NULL(dl); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); -#endif - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.pipeline_active, - "No render pipeline was set before attempting to draw."); - if (dl->validation.pipeline_vertex_format != INVALID_ID) { - // Pipeline uses vertices, validate format. - ERR_FAIL_COND_MSG(dl->validation.vertex_format == INVALID_ID, - "No vertex array was bound, and render pipeline expects vertices."); - // Make sure format is right. - ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format != dl->validation.vertex_format, - "The vertex format used to create the pipeline does not match the vertex format bound."); - // Make sure number of instances is valid. - ERR_FAIL_COND_MSG(p_instances > dl->validation.vertex_max_instances_allowed, - "Number of instances requested (" + itos(p_instances) + " is larger than the maximum number supported by the bound vertex array (" + itos(dl->validation.vertex_max_instances_allowed) + ")."); - } - - if (dl->validation.pipeline_push_constant_size > 0) { - // Using push constants, check that they were supplied. - ERR_FAIL_COND_MSG(!dl->validation.pipeline_push_constant_supplied, - "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); - } - -#endif - - // Bind descriptor sets. - - for (uint32_t i = 0; i < dl->state.set_count; i++) { - if (dl->state.sets[i].pipeline_expected_format == 0) { - continue; // Nothing expected by this pipeline. - } -#ifdef DEBUG_ENABLED - if (dl->state.sets[i].pipeline_expected_format != dl->state.sets[i].uniform_set_format) { - if (dl->state.sets[i].uniform_set_format == 0) { - ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); - } else if (uniform_set_owner.owns(dl->state.sets[i].uniform_set)) { - UniformSet *us = uniform_set_owner.get_or_null(dl->state.sets[i].uniform_set); - ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader)); - } else { - ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader)); - } - } -#endif - if (!dl->state.sets[i].bound) { - // All good, see if this requires re-binding. - vkCmdBindDescriptorSets(dl->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, dl->state.pipeline_layout, i, 1, &dl->state.sets[i].descriptor_set, 0, nullptr); - dl->state.sets[i].bound = true; - } - } - - if (p_use_indices) { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(p_procedural_vertices > 0, - "Procedural vertices can't be used together with indices."); - - ERR_FAIL_COND_MSG(!dl->validation.index_array_size, - "Draw command requested indices, but no index buffer was set."); - - ERR_FAIL_COND_MSG(dl->validation.pipeline_uses_restart_indices != dl->validation.index_buffer_uses_restart_indices, - "The usage of restart indices in index buffer does not match the render primitive in the pipeline."); -#endif - uint32_t to_draw = dl->validation.index_array_size; - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(to_draw < dl->validation.pipeline_primitive_minimum, - "Too few indices (" + itos(to_draw) + ") for the render primitive set in the render pipeline (" + itos(dl->validation.pipeline_primitive_minimum) + ")."); - - ERR_FAIL_COND_MSG((to_draw % dl->validation.pipeline_primitive_divisor) != 0, - "Index amount (" + itos(to_draw) + ") must be a multiple of the amount of indices required by the render primitive (" + itos(dl->validation.pipeline_primitive_divisor) + ")."); -#endif - vkCmdDrawIndexed(dl->command_buffer, to_draw, p_instances, dl->validation.index_array_offset, 0, 0); - } else { - uint32_t to_draw; - - if (p_procedural_vertices > 0) { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format != INVALID_ID, - "Procedural vertices requested, but pipeline expects a vertex array."); -#endif - to_draw = p_procedural_vertices; - } else { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format == INVALID_ID, - "Draw command lacks indices, but pipeline format does not use vertices."); -#endif - to_draw = dl->validation.vertex_array_size; - } - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(to_draw < dl->validation.pipeline_primitive_minimum, - "Too few vertices (" + itos(to_draw) + ") for the render primitive set in the render pipeline (" + itos(dl->validation.pipeline_primitive_minimum) + ")."); - - ERR_FAIL_COND_MSG((to_draw % dl->validation.pipeline_primitive_divisor) != 0, - "Vertex amount (" + itos(to_draw) + ") must be a multiple of the amount of vertices required by the render primitive (" + itos(dl->validation.pipeline_primitive_divisor) + ")."); -#endif - - vkCmdDraw(dl->command_buffer, to_draw, p_instances, 0, 0); - } -} - -void RenderingDeviceVulkan::draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect) { - DrawList *dl = _get_draw_list_ptr(p_list); - - ERR_FAIL_NULL(dl); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); -#endif - Rect2i rect = p_rect; - rect.position += dl->viewport.position; - - rect = dl->viewport.intersection(rect); - - if (rect.get_area() == 0) { - return; - } - VkRect2D scissor; - scissor.offset.x = rect.position.x; - scissor.offset.y = rect.position.y; - scissor.extent.width = rect.size.width; - scissor.extent.height = rect.size.height; - - vkCmdSetScissor(dl->command_buffer, 0, 1, &scissor); -} - -void RenderingDeviceVulkan::draw_list_disable_scissor(DrawListID p_list) { - DrawList *dl = _get_draw_list_ptr(p_list); - ERR_FAIL_NULL(dl); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); -#endif - - VkRect2D scissor; - scissor.offset.x = dl->viewport.position.x; - scissor.offset.y = dl->viewport.position.y; - scissor.extent.width = dl->viewport.size.width; - scissor.extent.height = dl->viewport.size.height; - vkCmdSetScissor(dl->command_buffer, 0, 1, &scissor); -} - -uint32_t RenderingDeviceVulkan::draw_list_get_current_pass() { - return draw_list_current_subpass; -} - -RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_switch_to_next_pass() { - _THREAD_SAFE_METHOD_ - ERR_FAIL_NULL_V(draw_list, INVALID_ID); - ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID); - - draw_list_current_subpass++; - - Rect2i viewport; - _draw_list_free(&viewport); - - vkCmdNextSubpass(frames[frame].draw_command_buffer, VK_SUBPASS_CONTENTS_INLINE); - - _draw_list_allocate(viewport, 0, draw_list_current_subpass); - - return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; -} -Error RenderingDeviceVulkan::draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_NULL_V(draw_list, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, ERR_INVALID_PARAMETER); - - draw_list_current_subpass++; - - Rect2i viewport; - _draw_list_free(&viewport); - - vkCmdNextSubpass(frames[frame].draw_command_buffer, VK_SUBPASS_CONTENTS_INLINE); - - _draw_list_allocate(viewport, p_splits, draw_list_current_subpass); - - for (uint32_t i = 0; i < p_splits; i++) { - r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; - } - - return OK; -} - -Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) { - // Lock while draw_list is active. - _THREAD_SAFE_LOCK_ - - if (p_splits == 0) { - draw_list = memnew(DrawList); - draw_list->command_buffer = frames[frame].draw_command_buffer; - draw_list->viewport = p_viewport; - draw_list_count = 0; - draw_list_split = false; - } else { - if (p_splits > (uint32_t)split_draw_list_allocators.size()) { - uint32_t from = split_draw_list_allocators.size(); - split_draw_list_allocators.resize(p_splits); - for (uint32_t i = from; i < p_splits; i++) { - VkCommandPoolCreateInfo cmd_pool_info; - cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmd_pool_info.pNext = nullptr; - cmd_pool_info.queueFamilyIndex = context->get_graphics_queue_family_index(); - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - - VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &split_draw_list_allocators.write[i].command_pool); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "vkCreateCommandPool failed with error " + itos(res) + "."); - - for (int j = 0; j < frame_count; j++) { - VkCommandBuffer command_buffer; - - VkCommandBufferAllocateInfo cmdbuf; - // No command buffer exists, create it. - cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmdbuf.pNext = nullptr; - cmdbuf.commandPool = split_draw_list_allocators[i].command_pool; - cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; - cmdbuf.commandBufferCount = 1; - - VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &command_buffer); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkAllocateCommandBuffers failed with error " + itos(err) + "."); - - split_draw_list_allocators.write[i].command_buffers.push_back(command_buffer); - } - } - } - draw_list = memnew_arr(DrawList, p_splits); - draw_list_count = p_splits; - draw_list_split = true; - - for (uint32_t i = 0; i < p_splits; i++) { - // Take a command buffer and initialize it. - VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame]; - - VkCommandBufferInheritanceInfo inheritance_info; - inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; - inheritance_info.pNext = nullptr; - inheritance_info.renderPass = draw_list_render_pass; - inheritance_info.subpass = p_subpass; - inheritance_info.framebuffer = draw_list_vkframebuffer; - inheritance_info.occlusionQueryEnable = false; - inheritance_info.queryFlags = 0; // ? - inheritance_info.pipelineStatistics = 0; - - VkCommandBufferBeginInfo cmdbuf_begin; - cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmdbuf_begin.pNext = nullptr; - cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; - cmdbuf_begin.pInheritanceInfo = &inheritance_info; - - VkResult res = vkResetCommandBuffer(command_buffer, 0); - if (res) { - memdelete_arr(draw_list); - draw_list = nullptr; - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkResetCommandBuffer failed with error " + itos(res) + "."); - } - - res = vkBeginCommandBuffer(command_buffer, &cmdbuf_begin); - if (res) { - memdelete_arr(draw_list); - draw_list = nullptr; - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkBeginCommandBuffer failed with error " + itos(res) + "."); - } - - draw_list[i].command_buffer = command_buffer; - draw_list[i].viewport = p_viewport; - } - } - - return OK; -} - -void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) { - if (draw_list_split) { - // Send all command buffers. - VkCommandBuffer *command_buffers = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer) * draw_list_count); - for (uint32_t i = 0; i < draw_list_count; i++) { - vkEndCommandBuffer(draw_list[i].command_buffer); - command_buffers[i] = draw_list[i].command_buffer; - if (r_last_viewport) { - if (i == 0 || draw_list[i].viewport_set) { - *r_last_viewport = draw_list[i].viewport; - } - } - } - - vkCmdExecuteCommands(frames[frame].draw_command_buffer, draw_list_count, command_buffers); - memdelete_arr(draw_list); - draw_list = nullptr; - - } else { - if (r_last_viewport) { - *r_last_viewport = draw_list->viewport; - } - // Just end the list. - memdelete(draw_list); - draw_list = nullptr; - } - - // Draw_list is no longer active. - _THREAD_SAFE_UNLOCK_ -} - -void RenderingDeviceVulkan::draw_list_end(BitField<BarrierMask> p_post_barrier) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_NULL_MSG(draw_list, "Immediate draw list is already inactive."); - - _draw_list_free(); - - vkCmdEndRenderPass(frames[frame].draw_command_buffer); - - for (int i = 0; i < draw_list_bound_textures.size(); i++) { - Texture *texture = texture_owner.get_or_null(draw_list_bound_textures[i]); - ERR_CONTINUE(!texture); // Wtf. - if (draw_list_unbind_color_textures && (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - texture->bound = false; - } - if (draw_list_unbind_depth_textures && (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - texture->bound = false; - } - } - - uint32_t barrier_flags = 0; - uint32_t access_flags = 0; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT /*| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT*/; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT /*| VK_ACCESS_INDIRECT_COMMAND_READ_BIT*/; - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT /*| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT*/; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT /*| VK_ACCESS_INDIRECT_COMMAND_READ_BIT*/; - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; - access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; - } - - if (barrier_flags == 0) { - barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - draw_list_bound_textures.clear(); - - VkImageMemoryBarrier *image_barriers = nullptr; - - uint32_t image_barrier_count = draw_list_storage_textures.size(); - - if (image_barrier_count) { - image_barriers = (VkImageMemoryBarrier *)alloca(sizeof(VkImageMemoryBarrier) * draw_list_storage_textures.size()); - } - - uint32_t src_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - uint32_t src_access = - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - if (image_barrier_count) { - src_stage |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - src_access |= VK_ACCESS_SHADER_WRITE_BIT; - } - - for (uint32_t i = 0; i < image_barrier_count; i++) { - Texture *texture = texture_owner.get_or_null(draw_list_storage_textures[i]); - - VkImageMemoryBarrier &image_memory_barrier = image_barriers[i]; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = src_access; - image_memory_barrier.dstAccessMask = access_flags; - image_memory_barrier.oldLayout = texture->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = texture->image; - image_memory_barrier.subresourceRange.aspectMask = texture->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = texture->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = texture->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = texture->base_layer; - image_memory_barrier.subresourceRange.layerCount = texture->layers; - - texture->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } - - draw_list_storage_textures.clear(); - - // To ensure proper synchronization, we must make sure rendering is done before: - // * Some buffer is copied. - // * Another render pass happens (since we may be done). - - VkMemoryBarrier mem_barrier; - mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; - mem_barrier.pNext = nullptr; - mem_barrier.srcAccessMask = src_access; - mem_barrier.dstAccessMask = access_flags; - - if (image_barrier_count > 0 || p_post_barrier != BARRIER_MASK_NO_BARRIER) { - vkCmdPipelineBarrier(frames[frame].draw_command_buffer, src_stage, barrier_flags, 0, 1, &mem_barrier, 0, nullptr, image_barrier_count, image_barriers); - } - -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#endif -} - -/***********************/ -/**** COMPUTE LISTS ****/ -/***********************/ - -RenderingDevice::ComputeListID RenderingDeviceVulkan::compute_list_begin(bool p_allow_draw_overlap) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V_MSG(!p_allow_draw_overlap && 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."); - - // Lock while compute_list is active. - _THREAD_SAFE_LOCK_ - - compute_list = memnew(ComputeList); - compute_list->command_buffer = frames[frame].draw_command_buffer; - compute_list->state.allow_draw_overlap = p_allow_draw_overlap; - - return ID_TYPE_COMPUTE_LIST; -} - -void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline) { - // Must be called within a compute list, the class mutex is locked during that time - - ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); - ERR_FAIL_NULL(compute_list); - - ComputeList *cl = compute_list; - - const ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_compute_pipeline); - ERR_FAIL_NULL(pipeline); - - if (p_compute_pipeline == cl->state.pipeline) { - return; // Redundant state, return. - } - - cl->state.pipeline = p_compute_pipeline; - cl->state.pipeline_layout = pipeline->pipeline_layout; - - vkCmdBindPipeline(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); - - if (cl->state.pipeline_shader != pipeline->shader) { - // Shader changed, so descriptor sets may become incompatible. - - // Go through ALL sets, and unbind them (and all those above) if the format is different. - - uint32_t pcount = pipeline->set_formats.size(); // Formats count in this pipeline. - cl->state.set_count = MAX(cl->state.set_count, pcount); - const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats. - - bool sets_valid = true; // Once invalid, all above become invalid. - for (uint32_t i = 0; i < pcount; i++) { - // If a part of the format is different, invalidate it (and the rest). - if (!sets_valid || cl->state.sets[i].pipeline_expected_format != pformats[i]) { - cl->state.sets[i].bound = false; - cl->state.sets[i].pipeline_expected_format = pformats[i]; - sets_valid = false; - } - } - - for (uint32_t i = pcount; i < cl->state.set_count; i++) { - // Unbind the ones above (not used) if exist. - cl->state.sets[i].bound = false; - } - - cl->state.set_count = pcount; // Update set count. - - if (pipeline->push_constant_size) { - cl->state.pipeline_push_constant_stages = pipeline->push_constant_stages_mask; -#ifdef DEBUG_ENABLED - cl->validation.pipeline_push_constant_supplied = false; -#endif - } - - cl->state.pipeline_shader = pipeline->shader; - cl->state.local_group_size[0] = pipeline->local_group_size[0]; - cl->state.local_group_size[1] = pipeline->local_group_size[1]; - cl->state.local_group_size[2] = pipeline->local_group_size[2]; - } - -#ifdef DEBUG_ENABLED - // Update compute pass pipeline info. - cl->validation.pipeline_active = true; - cl->validation.pipeline_push_constant_size = pipeline->push_constant_size; -#endif -} - -void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index) { - // Must be called within a compute list, the class mutex is locked during that time - - ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); - ERR_FAIL_NULL(compute_list); - - ComputeList *cl = compute_list; - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(p_index >= limits.maxBoundDescriptorSets || p_index >= MAX_UNIFORM_SETS, - "Attempting to bind a descriptor set (" + itos(p_index) + ") greater than what the hardware supports (" + itos(limits.maxBoundDescriptorSets) + ")."); -#endif - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); -#endif - - UniformSet *uniform_set = uniform_set_owner.get_or_null(p_uniform_set); - ERR_FAIL_NULL(uniform_set); - - if (p_index > cl->state.set_count) { - cl->state.set_count = p_index; - } - - cl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; // Update set pointer. - cl->state.sets[p_index].bound = false; // Needs rebind. - cl->state.sets[p_index].uniform_set_format = uniform_set->format; - cl->state.sets[p_index].uniform_set = p_uniform_set; - - uint32_t textures_to_sampled_count = uniform_set->mutable_sampled_textures.size(); - uint32_t textures_to_storage_count = uniform_set->mutable_storage_textures.size(); - - Texture **textures_to_sampled = uniform_set->mutable_sampled_textures.ptrw(); - - VkImageMemoryBarrier *texture_barriers = nullptr; - - if (textures_to_sampled_count + textures_to_storage_count) { - texture_barriers = (VkImageMemoryBarrier *)alloca(sizeof(VkImageMemoryBarrier) * (textures_to_sampled_count + textures_to_storage_count)); - } - uint32_t texture_barrier_count = 0; - - uint32_t src_stage_flags = 0; - - for (uint32_t i = 0; i < textures_to_sampled_count; i++) { - if (textures_to_sampled[i]->layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - src_stage_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - - VkImageMemoryBarrier &image_memory_barrier = texture_barriers[texture_barrier_count++]; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - image_memory_barrier.oldLayout = textures_to_sampled[i]->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = textures_to_sampled[i]->image; - image_memory_barrier.subresourceRange.aspectMask = textures_to_sampled[i]->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = textures_to_sampled[i]->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = textures_to_sampled[i]->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = textures_to_sampled[i]->base_layer; - image_memory_barrier.subresourceRange.layerCount = textures_to_sampled[i]->layers; - - textures_to_sampled[i]->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - cl->state.textures_to_sampled_layout.erase(textures_to_sampled[i]); - } - - if (textures_to_sampled[i]->used_in_frame != frames_drawn) { - textures_to_sampled[i]->used_in_frame = frames_drawn; - textures_to_sampled[i]->used_in_transfer = false; - textures_to_sampled[i]->used_in_raster = false; - } - textures_to_sampled[i]->used_in_compute = true; - } - - Texture **textures_to_storage = uniform_set->mutable_storage_textures.ptrw(); - - for (uint32_t i = 0; i < textures_to_storage_count; i++) { - if (textures_to_storage[i]->layout != VK_IMAGE_LAYOUT_GENERAL) { - uint32_t src_access_flags = 0; - - if (textures_to_storage[i]->used_in_frame == frames_drawn) { - if (textures_to_storage[i]->used_in_compute) { - src_stage_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - src_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (textures_to_storage[i]->used_in_raster) { - src_stage_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - src_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (textures_to_storage[i]->used_in_transfer) { - src_stage_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; - src_access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; - } - - textures_to_storage[i]->used_in_compute = false; - textures_to_storage[i]->used_in_raster = false; - textures_to_storage[i]->used_in_transfer = false; - - } else { - src_access_flags = 0; - textures_to_storage[i]->used_in_compute = false; - textures_to_storage[i]->used_in_raster = false; - textures_to_storage[i]->used_in_transfer = false; - textures_to_storage[i]->used_in_frame = frames_drawn; - } - - VkImageMemoryBarrier &image_memory_barrier = texture_barriers[texture_barrier_count++]; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = src_access_flags; - image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - image_memory_barrier.oldLayout = textures_to_storage[i]->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = textures_to_storage[i]->image; - image_memory_barrier.subresourceRange.aspectMask = textures_to_storage[i]->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = textures_to_storage[i]->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = textures_to_storage[i]->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = textures_to_storage[i]->base_layer; - image_memory_barrier.subresourceRange.layerCount = textures_to_storage[i]->layers; - - textures_to_storage[i]->layout = VK_IMAGE_LAYOUT_GENERAL; - - cl->state.textures_to_sampled_layout.insert(textures_to_storage[i]); // Needs to go back to sampled layout afterwards. - } - } - - if (texture_barrier_count) { - if (src_stage_flags == 0) { - src_stage_flags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - } - - vkCmdPipelineBarrier(cl->command_buffer, src_stage_flags, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, texture_barrier_count, texture_barriers); - } - -#if 0 - { // Validate that textures bound are not attached as framebuffer bindings. - uint32_t attachable_count = uniform_set->attachable_textures.size(); - const RID *attachable_ptr = uniform_set->attachable_textures.ptr(); - uint32_t bound_count = draw_list_bound_textures.size(); - const RID *bound_ptr = draw_list_bound_textures.ptr(); - for (uint32_t i = 0; i < attachable_count; i++) { - for (uint32_t j = 0; j < bound_count; j++) { - ERR_FAIL_COND_MSG(attachable_ptr[i] == bound_ptr[j], - "Attempted to use the same texture in framebuffer attachment and a uniform set, this is not allowed."); - } - } - } -#endif -} - -void RenderingDeviceVulkan::compute_list_set_push_constant(ComputeListID p_list, const void *p_data, uint32_t p_data_size) { - ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); - ERR_FAIL_NULL(compute_list); - - ComputeList *cl = compute_list; - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); -#endif - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(p_data_size != cl->validation.pipeline_push_constant_size, - "This compute pipeline requires (" + itos(cl->validation.pipeline_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); -#endif - vkCmdPushConstants(cl->command_buffer, cl->state.pipeline_layout, cl->state.pipeline_push_constant_stages, 0, p_data_size, p_data); -#ifdef DEBUG_ENABLED - cl->validation.pipeline_push_constant_supplied = true; -#endif -} - -void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) { - // Must be called within a compute list, the class mutex is locked during that time - - ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); - ERR_FAIL_NULL(compute_list); - - ComputeList *cl = compute_list; - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(p_x_groups == 0, "Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is zero."); - ERR_FAIL_COND_MSG(p_z_groups == 0, "Dispatch amount of Z compute groups (" + itos(p_z_groups) + ") is zero."); - ERR_FAIL_COND_MSG(p_y_groups == 0, "Dispatch amount of Y compute groups (" + itos(p_y_groups) + ") is zero."); - ERR_FAIL_COND_MSG(p_x_groups > limits.maxComputeWorkGroupCount[0], - "Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")"); - ERR_FAIL_COND_MSG(p_y_groups > limits.maxComputeWorkGroupCount[1], - "Dispatch amount of Y compute groups (" + itos(p_y_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[1]) + ")"); - ERR_FAIL_COND_MSG(p_z_groups > limits.maxComputeWorkGroupCount[2], - "Dispatch amount of Z compute groups (" + itos(p_z_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[2]) + ")"); - - ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); -#endif - -#ifdef DEBUG_ENABLED - - ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); - - if (cl->validation.pipeline_push_constant_size > 0) { - // Using push constants, check that they were supplied. - ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, - "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); - } - -#endif - - // Bind descriptor sets. - - for (uint32_t i = 0; i < cl->state.set_count; i++) { - if (cl->state.sets[i].pipeline_expected_format == 0) { - continue; // Nothing expected by this pipeline. - } -#ifdef DEBUG_ENABLED - if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { - if (cl->state.sets[i].uniform_set_format == 0) { - ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); - } else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) { - UniformSet *us = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); - ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); - } else { - ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); - } - } -#endif - if (!cl->state.sets[i].bound) { - // All good, see if this requires re-binding. - vkCmdBindDescriptorSets(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, cl->state.pipeline_layout, i, 1, &cl->state.sets[i].descriptor_set, 0, nullptr); - cl->state.sets[i].bound = true; - } - } - - vkCmdDispatch(cl->command_buffer, p_x_groups, p_y_groups, p_z_groups); -} - -void RenderingDeviceVulkan::compute_list_dispatch_threads(ComputeListID p_list, uint32_t p_x_threads, uint32_t p_y_threads, uint32_t p_z_threads) { - ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); - ERR_FAIL_NULL(compute_list); - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(p_x_threads == 0, "Dispatch amount of X compute threads (" + itos(p_x_threads) + ") is zero."); - ERR_FAIL_COND_MSG(p_y_threads == 0, "Dispatch amount of Y compute threads (" + itos(p_y_threads) + ") is zero."); - ERR_FAIL_COND_MSG(p_z_threads == 0, "Dispatch amount of Z compute threads (" + itos(p_z_threads) + ") is zero."); -#endif - - ComputeList *cl = compute_list; - -#ifdef DEBUG_ENABLED - - ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); - - if (cl->validation.pipeline_push_constant_size > 0) { - // Using push constants, check that they were supplied. - ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, - "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); - } - -#endif - - compute_list_dispatch(p_list, (p_x_threads - 1) / cl->state.local_group_size[0] + 1, (p_y_threads - 1) / cl->state.local_group_size[1] + 1, (p_z_threads - 1) / cl->state.local_group_size[2] + 1); -} - -void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, RID p_buffer, uint32_t p_offset) { - ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); - ERR_FAIL_NULL(compute_list); - - ComputeList *cl = compute_list; - Buffer *buffer = storage_buffer_owner.get_or_null(p_buffer); - ERR_FAIL_NULL(buffer); - - ERR_FAIL_COND_MSG(!(buffer->usage & VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT), "Buffer provided was not created to do indirect dispatch."); - - ERR_FAIL_COND_MSG(p_offset + 12 > buffer->size, "Offset provided (+12) is past the end of buffer."); - -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); -#endif - -#ifdef DEBUG_ENABLED - - ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); - - if (cl->validation.pipeline_push_constant_size > 0) { - // Using push constants, check that they were supplied. - ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, - "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); - } - -#endif - - // Bind descriptor sets. - - for (uint32_t i = 0; i < cl->state.set_count; i++) { - if (cl->state.sets[i].pipeline_expected_format == 0) { - continue; // Nothing expected by this pipeline. - } -#ifdef DEBUG_ENABLED - if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { - if (cl->state.sets[i].uniform_set_format == 0) { - ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); - } else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) { - UniformSet *us = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); - ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); - } else { - ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); - } - } -#endif - if (!cl->state.sets[i].bound) { - // All good, see if this requires re-binding. - vkCmdBindDescriptorSets(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, cl->state.pipeline_layout, i, 1, &cl->state.sets[i].descriptor_set, 0, nullptr); - cl->state.sets[i].bound = true; - } - } - - vkCmdDispatchIndirect(cl->command_buffer, buffer->buffer, p_offset); -} - -void RenderingDeviceVulkan::compute_list_add_barrier(ComputeListID p_list) { - // Must be called within a compute list, the class mutex is locked during that time - - uint32_t barrier_flags = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - uint32_t access_flags = VK_ACCESS_SHADER_READ_BIT; - _compute_list_add_barrier(BARRIER_MASK_COMPUTE, barrier_flags, access_flags); -} - -void RenderingDeviceVulkan::_compute_list_add_barrier(BitField<BarrierMask> p_post_barrier, uint32_t p_barrier_flags, uint32_t p_access_flags) { - ERR_FAIL_NULL(compute_list); - - VkImageMemoryBarrier *image_barriers = nullptr; - - uint32_t image_barrier_count = compute_list->state.textures_to_sampled_layout.size(); - - if (image_barrier_count) { - image_barriers = (VkImageMemoryBarrier *)alloca(sizeof(VkImageMemoryBarrier) * image_barrier_count); - } - - image_barrier_count = 0; // We'll count how many we end up issuing. - - for (Texture *E : compute_list->state.textures_to_sampled_layout) { - if (E->layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - VkImageMemoryBarrier &image_memory_barrier = image_barriers[image_barrier_count++]; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - image_memory_barrier.dstAccessMask = p_access_flags; - image_memory_barrier.oldLayout = E->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = E->image; - image_memory_barrier.subresourceRange.aspectMask = E->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = E->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = E->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = E->base_layer; - image_memory_barrier.subresourceRange.layerCount = E->layers; - - E->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } - - if (E->used_in_frame != frames_drawn) { - E->used_in_transfer = false; - E->used_in_raster = false; - E->used_in_compute = false; - E->used_in_frame = frames_drawn; - } - } - - if (p_barrier_flags) { - VkMemoryBarrier mem_barrier; - mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; - mem_barrier.pNext = nullptr; - mem_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - mem_barrier.dstAccessMask = p_access_flags; - vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, p_barrier_flags, 0, 1, &mem_barrier, 0, nullptr, image_barrier_count, image_barriers); - } else if (image_barrier_count) { - vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, image_barrier_count, image_barriers); - } - -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#endif -} - -void RenderingDeviceVulkan::compute_list_end(BitField<BarrierMask> p_post_barrier) { - ERR_FAIL_NULL(compute_list); - - uint32_t barrier_flags = 0; - uint32_t access_flags = 0; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; - access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; - } - _compute_list_add_barrier(p_post_barrier, barrier_flags, access_flags); - - memdelete(compute_list); - compute_list = nullptr; - - // Compute_list is no longer active. - _THREAD_SAFE_UNLOCK_ -} - -void RenderingDeviceVulkan::barrier(BitField<BarrierMask> p_from, BitField<BarrierMask> p_to) { - uint32_t src_barrier_flags = 0; - uint32_t src_access_flags = 0; - - if (p_from == 0) { - src_barrier_flags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - } else { - if (p_from.has_flag(BARRIER_MASK_COMPUTE)) { - src_barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - src_access_flags |= VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_from.has_flag(BARRIER_MASK_FRAGMENT)) { - src_barrier_flags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - src_access_flags |= - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - } - if (p_from.has_flag(BARRIER_MASK_TRANSFER)) { - src_barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; - src_access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT; - } - } - - uint32_t dst_barrier_flags = 0; - uint32_t dst_access_flags = 0; - - if (p_to == 0) { - dst_barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } else { - if (p_to.has_flag(BARRIER_MASK_COMPUTE)) { - dst_barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - dst_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_to.has_flag(BARRIER_MASK_VERTEX)) { - dst_barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; - dst_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - } - if (p_to.has_flag(BARRIER_MASK_FRAGMENT)) { - dst_barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; - dst_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - } - if (p_to.has_flag(BARRIER_MASK_TRANSFER)) { - dst_barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; - dst_access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; - } - } - - _memory_barrier(src_barrier_flags, dst_barrier_flags, src_access_flags, dst_access_flags, true); -} - -void RenderingDeviceVulkan::full_barrier() { -#ifndef DEBUG_ENABLED - ERR_PRINT("Full barrier is debug-only, should not be used in production"); -#endif - _full_barrier(true); -} - -#if 0 -void RenderingDeviceVulkan::draw_list_render_secondary_to_framebuffer(ID p_framebuffer, ID *p_draw_lists, uint32_t p_draw_list_count, InitialAction p_initial_action, FinalAction p_final_action, const Vector<Variant> &p_clear_colors) { - VkCommandBuffer frame_cmdbuf = frames[frame].frame_buffer; - ERR_FAIL_NULL(frame_cmdbuf); - - VkRenderPassBeginInfo render_pass_begin; - render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - render_pass_begin.pNext = nullptr; - render_pass_begin.renderPass = context->get_render_pass(); - render_pass_begin.framebuffer = context->get_frame_framebuffer(frame); - - render_pass_begin.renderArea.extent.width = context->get_screen_width(p_screen); - render_pass_begin.renderArea.extent.height = context->get_screen_height(p_screen); - render_pass_begin.renderArea.offset.x = 0; - render_pass_begin.renderArea.offset.y = 0; - - render_pass_begin.clearValueCount = 1; - - VkClearValue clear_value; - clear_value.color.float32[0] = p_clear_color.r; - clear_value.color.float32[1] = p_clear_color.g; - clear_value.color.float32[2] = p_clear_color.b; - clear_value.color.float32[3] = p_clear_color.a; - - render_pass_begin.pClearValues = &clear_value; - - vkCmdBeginRenderPass(frame_cmdbuf, &render_pass_begin, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); - - ID screen_format = screen_get_framebuffer_format(); - { - VkCommandBuffer *command_buffers = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer) * p_draw_list_count); - uint32_t command_buffer_count = 0; - - for (uint32_t i = 0; i < p_draw_list_count; i++) { - DrawList *dl = _get_draw_list_ptr(p_draw_lists[i]); - ERR_CONTINUE_MSG(!dl, "Draw list index (" + itos(i) + ") is not a valid draw list ID."); - ERR_CONTINUE_MSG(dl->validation.framebuffer_format != p_format_check, - "Draw list index (" + itos(i) + ") is created with a framebuffer format incompatible with this render pass."); - - if (dl->validation.active) { - // Needs to be closed, so close it. - vkEndCommandBuffer(dl->command_buffer); - dl->validation.active = false; - } - - command_buffers[command_buffer_count++] = dl->command_buffer; - } - - print_line("to draw: " + itos(command_buffer_count)); - vkCmdExecuteCommands(p_primary, command_buffer_count, command_buffers); - } - - vkCmdEndRenderPass(frame_cmdbuf); -} -#endif - -void RenderingDeviceVulkan::_free_internal(RID p_id) { -#ifdef DEV_ENABLED - String resource_name; - if (resource_names.has(p_id)) { - resource_name = resource_names[p_id]; - resource_names.erase(p_id); - } -#endif - - // Push everything so it's disposed of next time this frame index is processed (means, it's safe to do it). - if (texture_owner.owns(p_id)) { - Texture *texture = texture_owner.get_or_null(p_id); - frames[frame].textures_to_dispose_of.push_back(*texture); - texture_owner.free(p_id); - } else if (framebuffer_owner.owns(p_id)) { - Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); - frames[frame].framebuffers_to_dispose_of.push_back(*framebuffer); - - if (framebuffer->invalidated_callback != nullptr) { - framebuffer->invalidated_callback(framebuffer->invalidated_callback_userdata); - } - - framebuffer_owner.free(p_id); - } else if (sampler_owner.owns(p_id)) { - VkSampler *sampler = sampler_owner.get_or_null(p_id); - frames[frame].samplers_to_dispose_of.push_back(*sampler); - sampler_owner.free(p_id); - } else if (vertex_buffer_owner.owns(p_id)) { - Buffer *vertex_buffer = vertex_buffer_owner.get_or_null(p_id); - frames[frame].buffers_to_dispose_of.push_back(*vertex_buffer); - vertex_buffer_owner.free(p_id); - } else if (vertex_array_owner.owns(p_id)) { - vertex_array_owner.free(p_id); - } else if (index_buffer_owner.owns(p_id)) { - IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_id); - Buffer b; - b.allocation = index_buffer->allocation; - b.buffer = index_buffer->buffer; - b.size = index_buffer->size; - b.buffer_info = {}; - frames[frame].buffers_to_dispose_of.push_back(b); - index_buffer_owner.free(p_id); - } else if (index_array_owner.owns(p_id)) { - index_array_owner.free(p_id); - } else if (shader_owner.owns(p_id)) { - Shader *shader = shader_owner.get_or_null(p_id); - frames[frame].shaders_to_dispose_of.push_back(*shader); - shader_owner.free(p_id); - } else if (uniform_buffer_owner.owns(p_id)) { - Buffer *uniform_buffer = uniform_buffer_owner.get_or_null(p_id); - frames[frame].buffers_to_dispose_of.push_back(*uniform_buffer); - uniform_buffer_owner.free(p_id); - } else if (texture_buffer_owner.owns(p_id)) { - TextureBuffer *texture_buffer = texture_buffer_owner.get_or_null(p_id); - frames[frame].buffers_to_dispose_of.push_back(texture_buffer->buffer); - frames[frame].buffer_views_to_dispose_of.push_back(texture_buffer->view); - texture_buffer_owner.free(p_id); - } else if (storage_buffer_owner.owns(p_id)) { - Buffer *storage_buffer = storage_buffer_owner.get_or_null(p_id); - frames[frame].buffers_to_dispose_of.push_back(*storage_buffer); - storage_buffer_owner.free(p_id); - } else if (uniform_set_owner.owns(p_id)) { - UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id); - frames[frame].uniform_sets_to_dispose_of.push_back(*uniform_set); - uniform_set_owner.free(p_id); - - if (uniform_set->invalidated_callback != nullptr) { - uniform_set->invalidated_callback(uniform_set->invalidated_callback_userdata); - } - } else if (render_pipeline_owner.owns(p_id)) { - RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id); - frames[frame].render_pipelines_to_dispose_of.push_back(*pipeline); - render_pipeline_owner.free(p_id); - } else if (compute_pipeline_owner.owns(p_id)) { - ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_id); - frames[frame].compute_pipelines_to_dispose_of.push_back(*pipeline); - compute_pipeline_owner.free(p_id); - } else { -#ifdef DEV_ENABLED - ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id()) + " " + resource_name); -#else - ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id())); -#endif - } -} - -void RenderingDeviceVulkan::free(RID p_id) { - _THREAD_SAFE_METHOD_ - - _free_dependencies(p_id); // Recursively erase dependencies first, to avoid potential API problems. - _free_internal(p_id); -} - -// The full list of resources that can be named is in the VkObjectType enum. -// We just expose the resources that are owned and can be accessed easily. -void RenderingDeviceVulkan::set_resource_name(RID p_id, const String p_name) { - if (texture_owner.owns(p_id)) { - Texture *texture = texture_owner.get_or_null(p_id); - if (texture->owner.is_null()) { - // Don't set the source texture's name when calling on a texture view. - context->set_object_name(VK_OBJECT_TYPE_IMAGE, uint64_t(texture->image), p_name); - } - context->set_object_name(VK_OBJECT_TYPE_IMAGE_VIEW, uint64_t(texture->view), p_name + " View"); - } else if (framebuffer_owner.owns(p_id)) { - //Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); - // Not implemented for now as the relationship between Framebuffer and RenderPass is very complex. - } else if (sampler_owner.owns(p_id)) { - VkSampler *sampler = sampler_owner.get_or_null(p_id); - context->set_object_name(VK_OBJECT_TYPE_SAMPLER, uint64_t(*sampler), p_name); - } else if (vertex_buffer_owner.owns(p_id)) { - Buffer *vertex_buffer = vertex_buffer_owner.get_or_null(p_id); - context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(vertex_buffer->buffer), p_name); - } else if (index_buffer_owner.owns(p_id)) { - IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_id); - context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(index_buffer->buffer), p_name); - } else if (shader_owner.owns(p_id)) { - Shader *shader = shader_owner.get_or_null(p_id); - context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(shader->pipeline_layout), p_name + " Pipeline Layout"); - for (int i = 0; i < shader->sets.size(); i++) { - context->set_object_name(VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, uint64_t(shader->sets[i].descriptor_set_layout), p_name); - } - } else if (uniform_buffer_owner.owns(p_id)) { - Buffer *uniform_buffer = uniform_buffer_owner.get_or_null(p_id); - context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(uniform_buffer->buffer), p_name); - } else if (texture_buffer_owner.owns(p_id)) { - TextureBuffer *texture_buffer = texture_buffer_owner.get_or_null(p_id); - context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(texture_buffer->buffer.buffer), p_name); - context->set_object_name(VK_OBJECT_TYPE_BUFFER_VIEW, uint64_t(texture_buffer->view), p_name + " View"); - } else if (storage_buffer_owner.owns(p_id)) { - Buffer *storage_buffer = storage_buffer_owner.get_or_null(p_id); - context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(storage_buffer->buffer), p_name); - } else if (uniform_set_owner.owns(p_id)) { - UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id); - context->set_object_name(VK_OBJECT_TYPE_DESCRIPTOR_SET, uint64_t(uniform_set->descriptor_set), p_name); - } else if (render_pipeline_owner.owns(p_id)) { - RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id); - context->set_object_name(VK_OBJECT_TYPE_PIPELINE, uint64_t(pipeline->pipeline), p_name); - context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(pipeline->pipeline_layout), p_name + " Layout"); - } else if (compute_pipeline_owner.owns(p_id)) { - ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_id); - context->set_object_name(VK_OBJECT_TYPE_PIPELINE, uint64_t(pipeline->pipeline), p_name); - context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(pipeline->pipeline_layout), p_name + " Layout"); - } else { - ERR_PRINT("Attempted to name invalid ID: " + itos(p_id.get_id())); - return; - } -#ifdef DEV_ENABLED - resource_names[p_id] = p_name; -#endif -} - -void RenderingDeviceVulkan::draw_command_begin_label(String p_label_name, const Color p_color) { - _THREAD_SAFE_METHOD_ - context->command_begin_label(frames[frame].draw_command_buffer, p_label_name, p_color); -} - -void RenderingDeviceVulkan::draw_command_insert_label(String p_label_name, const Color p_color) { - _THREAD_SAFE_METHOD_ - context->command_insert_label(frames[frame].draw_command_buffer, p_label_name, p_color); -} - -void RenderingDeviceVulkan::draw_command_end_label() { - _THREAD_SAFE_METHOD_ - context->command_end_label(frames[frame].draw_command_buffer); -} - -String RenderingDeviceVulkan::get_device_vendor_name() const { - return context->get_device_vendor_name(); -} - -String RenderingDeviceVulkan::get_device_name() const { - return context->get_device_name(); -} - -RenderingDevice::DeviceType RenderingDeviceVulkan::get_device_type() const { - return context->get_device_type(); -} - -String RenderingDeviceVulkan::get_device_api_version() const { - return context->get_device_api_version(); -} - -String RenderingDeviceVulkan::get_device_pipeline_cache_uuid() const { - return context->get_device_pipeline_cache_uuid(); -} - -void RenderingDeviceVulkan::_finalize_command_bufers() { - 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). - vkEndCommandBuffer(frames[frame].setup_command_buffer); - vkEndCommandBuffer(frames[frame].draw_command_buffer); - } -} - -void RenderingDeviceVulkan::_begin_frame() { - // Erase pending resources. - _free_pending_resources(frame); - - // Create setup command buffer and set as the setup buffer. - - { - VkCommandBufferBeginInfo cmdbuf_begin; - cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmdbuf_begin.pNext = nullptr; - cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - cmdbuf_begin.pInheritanceInfo = nullptr; - - VkResult err = vkResetCommandBuffer(frames[frame].setup_command_buffer, 0); - ERR_FAIL_COND_MSG(err, "vkResetCommandBuffer failed with error " + itos(err) + "."); - - err = vkBeginCommandBuffer(frames[frame].setup_command_buffer, &cmdbuf_begin); - ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); - err = vkBeginCommandBuffer(frames[frame].draw_command_buffer, &cmdbuf_begin); - ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); - - 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. - } - } - - // 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) { - vkGetQueryPoolResults(device, frames[frame].timestamp_pool, 0, frames[frame].timestamp_count, sizeof(uint64_t) * max_timestamp_query_elements, frames[frame].timestamp_result_values.ptr(), sizeof(uint64_t), VK_QUERY_RESULT_64_BIT); - vkCmdResetQueryPool(frames[frame].setup_command_buffer, frames[frame].timestamp_pool, 0, 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(); -} - -VkSampleCountFlagBits RenderingDeviceVulkan::_ensure_supported_sample_count(TextureSamples p_requested_sample_count) const { - VkSampleCountFlags sample_count_flags = limits.framebufferColorSampleCounts & limits.framebufferDepthSampleCounts; - - if (sample_count_flags & rasterization_sample_count[p_requested_sample_count]) { - // The requested sample count is supported. - return rasterization_sample_count[p_requested_sample_count]; - } else { - // Find the closest lower supported sample count. - VkSampleCountFlagBits sample_count = rasterization_sample_count[p_requested_sample_count]; - while (sample_count > VK_SAMPLE_COUNT_1_BIT) { - if (sample_count_flags & sample_count) { - return sample_count; - } - sample_count = (VkSampleCountFlagBits)(sample_count >> 1); - } - } - return VK_SAMPLE_COUNT_1_BIT; -} - -void RenderingDeviceVulkan::swap_buffers() { - ERR_FAIL_COND_MSG(local_device.is_valid(), "Local devices can't swap buffers."); - _THREAD_SAFE_METHOD_ - - _finalize_command_bufers(); - - screen_prepared = false; - // Swap buffers. - context->swap_buffers(); - - frame = (frame + 1) % frame_count; - - _begin_frame(); -} - -void RenderingDeviceVulkan::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_bufers(); - - VkCommandBuffer 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; -} - -void RenderingDeviceVulkan::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; -} - -VmaPool RenderingDeviceVulkan::_find_or_create_small_allocs_pool(uint32_t p_mem_type_index) { - if (small_allocs_pools.has(p_mem_type_index)) { - return small_allocs_pools[p_mem_type_index]; - } - - print_verbose("Creating VMA small objects pool for memory type index " + itos(p_mem_type_index)); - - VmaPoolCreateInfo pci; - pci.memoryTypeIndex = p_mem_type_index; - pci.flags = 0; - pci.blockSize = 0; - pci.minBlockCount = 0; - pci.maxBlockCount = SIZE_MAX; - pci.priority = 0.5f; - pci.minAllocationAlignment = 0; - pci.pMemoryAllocateNext = nullptr; - VmaPool pool = VK_NULL_HANDLE; - VkResult res = vmaCreatePool(allocator, &pci, &pool); - small_allocs_pools[p_mem_type_index] = pool; // Don't try to create it again if failed the first time. - ERR_FAIL_COND_V_MSG(res, pool, "vmaCreatePool failed with error " + itos(res) + "."); - - return pool; -} - -void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { - // Free in dependency usage order, so nothing weird happens. - // Pipelines. - while (frames[p_frame].render_pipelines_to_dispose_of.front()) { - RenderPipeline *pipeline = &frames[p_frame].render_pipelines_to_dispose_of.front()->get(); - - vkDestroyPipeline(device, pipeline->pipeline, nullptr); - - frames[p_frame].render_pipelines_to_dispose_of.pop_front(); - } - - while (frames[p_frame].compute_pipelines_to_dispose_of.front()) { - ComputePipeline *pipeline = &frames[p_frame].compute_pipelines_to_dispose_of.front()->get(); - - vkDestroyPipeline(device, pipeline->pipeline, nullptr); - - frames[p_frame].compute_pipelines_to_dispose_of.pop_front(); - } - - // Uniform sets. - while (frames[p_frame].uniform_sets_to_dispose_of.front()) { - UniformSet *uniform_set = &frames[p_frame].uniform_sets_to_dispose_of.front()->get(); - - vkFreeDescriptorSets(device, uniform_set->pool->pool, 1, &uniform_set->descriptor_set); - _descriptor_pool_free(uniform_set->pool_key, uniform_set->pool); - - frames[p_frame].uniform_sets_to_dispose_of.pop_front(); - } - - // Buffer views. - while (frames[p_frame].buffer_views_to_dispose_of.front()) { - VkBufferView buffer_view = frames[p_frame].buffer_views_to_dispose_of.front()->get(); - - vkDestroyBufferView(device, buffer_view, nullptr); - - frames[p_frame].buffer_views_to_dispose_of.pop_front(); - } - - // Shaders. - while (frames[p_frame].shaders_to_dispose_of.front()) { - Shader *shader = &frames[p_frame].shaders_to_dispose_of.front()->get(); - - // Descriptor set layout for each set. - for (int i = 0; i < shader->sets.size(); i++) { - vkDestroyDescriptorSetLayout(device, shader->sets[i].descriptor_set_layout, nullptr); - } - - // Pipeline layout. - vkDestroyPipelineLayout(device, shader->pipeline_layout, nullptr); - - // Shaders themselves. - for (int i = 0; i < shader->pipeline_stages.size(); i++) { - vkDestroyShaderModule(device, shader->pipeline_stages[i].module, nullptr); - } - - frames[p_frame].shaders_to_dispose_of.pop_front(); - } - - // Samplers. - while (frames[p_frame].samplers_to_dispose_of.front()) { - VkSampler sampler = frames[p_frame].samplers_to_dispose_of.front()->get(); - - vkDestroySampler(device, sampler, nullptr); - - frames[p_frame].samplers_to_dispose_of.pop_front(); - } - - // Framebuffers. - while (frames[p_frame].framebuffers_to_dispose_of.front()) { - Framebuffer *framebuffer = &frames[p_frame].framebuffers_to_dispose_of.front()->get(); - - for (const KeyValue<Framebuffer::VersionKey, Framebuffer::Version> &E : framebuffer->framebuffers) { - // First framebuffer, then render pass because it depends on it. - vkDestroyFramebuffer(device, E.value.framebuffer, nullptr); - vkDestroyRenderPass(device, E.value.render_pass, nullptr); - } - - frames[p_frame].framebuffers_to_dispose_of.pop_front(); - } - - // Textures. - while (frames[p_frame].textures_to_dispose_of.front()) { - Texture *texture = &frames[p_frame].textures_to_dispose_of.front()->get(); - - if (texture->bound) { - WARN_PRINT("Deleted a texture while it was bound."); - } - vkDestroyImageView(device, texture->view, nullptr); - if (texture->owner.is_null()) { - // Actually owns the image and the allocation too. - image_memory -= texture->allocation_info.size; - vmaDestroyImage(allocator, texture->image, texture->allocation); - } - frames[p_frame].textures_to_dispose_of.pop_front(); - } - - // Buffers. - while (frames[p_frame].buffers_to_dispose_of.front()) { - _buffer_free(&frames[p_frame].buffers_to_dispose_of.front()->get()); - - frames[p_frame].buffers_to_dispose_of.pop_front(); - } -} - -void RenderingDeviceVulkan::prepare_screen_for_drawing() { - _THREAD_SAFE_METHOD_ - context->prepare_buffers(); - screen_prepared = true; -} - -uint32_t RenderingDeviceVulkan::get_frame_delay() const { - return frame_count; -} - -uint64_t RenderingDeviceVulkan::get_memory_usage(MemoryType p_type) const { - if (p_type == MEMORY_BUFFERS) { - return buffer_memory; - } else if (p_type == MEMORY_TEXTURES) { - return image_memory; - } else { - VmaTotalStatistics stats; - vmaCalculateStatistics(allocator, &stats); - return stats.total.statistics.allocationBytes; - } -} - -void RenderingDeviceVulkan::_flush(bool p_current_frame) { - if (local_device.is_valid() && !p_current_frame) { - return; // Flushing previous frames has no effect with local device. - } - // Not doing this crashes RADV (undefined behavior). - if (p_current_frame) { - vkEndCommandBuffer(frames[frame].setup_command_buffer); - vkEndCommandBuffer(frames[frame].draw_command_buffer); - } - - if (local_device.is_valid()) { - VkCommandBuffer 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); - - VkCommandBufferBeginInfo cmdbuf_begin; - cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmdbuf_begin.pNext = nullptr; - cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - cmdbuf_begin.pInheritanceInfo = nullptr; - - VkResult err = vkBeginCommandBuffer(frames[frame].setup_command_buffer, &cmdbuf_begin); - ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); - err = vkBeginCommandBuffer(frames[frame].draw_command_buffer, &cmdbuf_begin); - ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); - - } else { - context->flush(p_current_frame, p_current_frame); - // Re-create the setup command. - if (p_current_frame) { - VkCommandBufferBeginInfo cmdbuf_begin; - cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmdbuf_begin.pNext = nullptr; - cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - cmdbuf_begin.pInheritanceInfo = nullptr; - - VkResult err = vkBeginCommandBuffer(frames[frame].setup_command_buffer, &cmdbuf_begin); - ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); - context->set_setup_buffer(frames[frame].setup_command_buffer); // Append now so it's added before everything else. - } - - if (p_current_frame) { - VkCommandBufferBeginInfo cmdbuf_begin; - cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmdbuf_begin.pNext = nullptr; - cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - cmdbuf_begin.pInheritanceInfo = nullptr; - - VkResult err = vkBeginCommandBuffer(frames[frame].draw_command_buffer, &cmdbuf_begin); - ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); - context->append_command_buffer(frames[frame].draw_command_buffer); - } - } -} - -void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_device) { - // Get our device capabilities. - { - device_capabilities.version_major = p_context->get_vulkan_major(); - device_capabilities.version_minor = p_context->get_vulkan_minor(); - } - - context = p_context; - device = p_context->get_device(); - if (p_local_device) { - frame_count = 1; - local_device = p_context->local_device_create(); - device = p_context->local_device_get_vk_device(local_device); - } else { - frame_count = p_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. - } - limits = p_context->get_device_limits(); - max_timestamp_query_elements = 256; - - { // Initialize allocator. - - VmaAllocatorCreateInfo allocatorInfo; - memset(&allocatorInfo, 0, sizeof(VmaAllocatorCreateInfo)); - allocatorInfo.physicalDevice = p_context->get_physical_device(); - allocatorInfo.device = device; - allocatorInfo.instance = p_context->get_instance(); - vmaCreateAllocator(&allocatorInfo, &allocator); - } - - frames.resize(frame_count); - frame = 0; - // Create setup and frame buffers. - for (int i = 0; i < frame_count; i++) { - frames[i].index = 0; - - { // Create command pool, one per frame is recommended. - VkCommandPoolCreateInfo cmd_pool_info; - cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmd_pool_info.pNext = nullptr; - cmd_pool_info.queueFamilyIndex = p_context->get_graphics_queue_family_index(); - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - - VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &frames[i].command_pool); - ERR_FAIL_COND_MSG(res, "vkCreateCommandPool failed with error " + itos(res) + "."); - } - - { // Create command buffers. - - VkCommandBufferAllocateInfo cmdbuf; - // No command buffer exists, create it. - cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmdbuf.pNext = nullptr; - cmdbuf.commandPool = frames[i].command_pool; - cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmdbuf.commandBufferCount = 1; - - VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &frames[i].setup_command_buffer); - ERR_CONTINUE_MSG(err, "vkAllocateCommandBuffers failed with error " + itos(err) + "."); - - err = vkAllocateCommandBuffers(device, &cmdbuf, &frames[i].draw_command_buffer); - ERR_CONTINUE_MSG(err, "vkAllocateCommandBuffers failed with error " + itos(err) + "."); - } - - { - // Create query pool. - VkQueryPoolCreateInfo query_pool_create_info; - query_pool_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; - query_pool_create_info.flags = 0; - query_pool_create_info.pNext = nullptr; - query_pool_create_info.queryType = VK_QUERY_TYPE_TIMESTAMP; - query_pool_create_info.queryCount = max_timestamp_query_elements; - query_pool_create_info.pipelineStatistics = 0; - - vkCreateQueryPool(device, &query_pool_create_info, nullptr, &frames[i].timestamp_pool); - - 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. - VkCommandBufferBeginInfo cmdbuf_begin; - cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmdbuf_begin.pNext = nullptr; - cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - cmdbuf_begin.pInheritanceInfo = nullptr; - - VkResult err = vkBeginCommandBuffer(frames[0].setup_command_buffer, &cmdbuf_begin); - ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); - - err = vkBeginCommandBuffer(frames[0].draw_command_buffer, &cmdbuf_begin); - ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); - 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); - } - } - - for (int i = 0; i < frame_count; i++) { - //Reset all queries in a query pool before doing any operations with them. - vkCmdResetQueryPool(frames[0].setup_command_buffer, frames[i].timestamp_pool, 0, max_timestamp_query_elements); - } - - 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_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; - - if (staging_buffer_max_size < staging_buffer_block_size * 4) { - // 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++) { - // Staging was never used, create a block. - Error err = _insert_staging_block(); - ERR_CONTINUE(err != OK); - } - - max_descriptors_per_pool = GLOBAL_GET("rendering/rendering_device/vulkan/max_descriptors_per_pool"); - - // Check to make sure DescriptorPoolKey is good. - static_assert(sizeof(uint64_t) * 3 >= UNIFORM_TYPE_MAX * sizeof(uint16_t)); - - draw_list = nullptr; - draw_list_count = 0; - draw_list_split = false; - - 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"; - - // Prepare most fields now. - VkPhysicalDeviceProperties props; - vkGetPhysicalDeviceProperties(context->get_physical_device(), &props); - pipelines_cache.header.magic = 868 + VK_PIPELINE_CACHE_HEADER_VERSION_ONE; - pipelines_cache.header.device_id = props.deviceID; - pipelines_cache.header.vendor_id = props.vendorID; - pipelines_cache.header.driver_version = props.driverVersion; - memcpy(pipelines_cache.header.uuid, props.pipelineCacheUUID, VK_UUID_SIZE); - pipelines_cache.header.driver_abi = sizeof(void *); - - _load_pipeline_cache(); - print_verbose(vformat("Startup PSO cache (%.1f MiB)", pipelines_cache.buffer.size() / (1024.0f * 1024.0f))); - VkPipelineCacheCreateInfo cache_info = {}; - cache_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; - cache_info.pNext = nullptr; - if (context->get_pipeline_cache_control_support()) { - cache_info.flags = VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT; - } - cache_info.initialDataSize = pipelines_cache.buffer.size(); - cache_info.pInitialData = pipelines_cache.buffer.ptr(); - VkResult err = vkCreatePipelineCache(device, &cache_info, nullptr, &pipelines_cache.cache_object); - - if (err != VK_SUCCESS) { - WARN_PRINT("vkCreatePipelinecache failed with error " + itos(err) + "."); - } -} - -void RenderingDeviceVulkan::_load_pipeline_cache() { - DirAccess::make_dir_recursive_absolute(pipelines_cache.file_path.get_base_dir()); - - if (FileAccess::exists(pipelines_cache.file_path)) { - Error file_error; - Vector<uint8_t> file_data = FileAccess::get_file_as_bytes(pipelines_cache.file_path, &file_error); - if (file_error != OK || file_data.size() <= (int)sizeof(PipelineCacheHeader)) { - WARN_PRINT("Invalid/corrupt pipelines cache."); - return; - } - const PipelineCacheHeader *header = reinterpret_cast<const PipelineCacheHeader *>(file_data.ptr()); - if (header->magic != 868 + VK_PIPELINE_CACHE_HEADER_VERSION_ONE) { - WARN_PRINT("Invalid pipelines cache magic number."); - return; - } - const uint8_t *loaded_buffer_start = file_data.ptr() + sizeof(PipelineCacheHeader); - uint32_t loaded_buffer_size = file_data.size() - sizeof(PipelineCacheHeader); - if (header->data_hash != hash_murmur3_buffer(loaded_buffer_start, loaded_buffer_size) || - header->data_size != loaded_buffer_size || - header->vendor_id != pipelines_cache.header.vendor_id || - header->device_id != pipelines_cache.header.device_id || - header->driver_version != pipelines_cache.header.driver_version || - memcmp(header->uuid, pipelines_cache.header.uuid, VK_UUID_SIZE) != 0 || - header->driver_abi != pipelines_cache.header.driver_abi) { - WARN_PRINT("Invalid pipelines cache header."); - pipelines_cache.current_size = 0; - pipelines_cache.buffer.clear(); - } else { - pipelines_cache.current_size = loaded_buffer_size; - pipelines_cache.buffer.resize(loaded_buffer_size); - memcpy(pipelines_cache.buffer.ptr(), loaded_buffer_start, pipelines_cache.buffer.size()); - } - } -} - -void RenderingDeviceVulkan::_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); - 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; - } 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; - } - } - } - - { - // FIXME: - // We're letting the cache grow unboundedly. We may want to set at limit and see if implementations use LRU or the like. - // If we do, we won't be able to assume any longer that the cache is dirty if, and only if, it has grown. - size_t pso_blob_size = 0; - VkResult vr = vkGetPipelineCacheData(device, pipelines_cache.cache_object, &pso_blob_size, nullptr); - ERR_FAIL_COND(vr); - size_t difference = pso_blob_size - pipelines_cache.current_size; - - bool must_save = false; - - if (p_closing) { - must_save = difference > 0; - } else { - float save_interval = GLOBAL_GET("rendering/rendering_device/pipeline_cache/save_chunk_size_mb"); - must_save = difference > 0 && difference / (1024.0f * 1024.0f) >= save_interval; - } - - if (must_save) { - pipelines_cache.current_size = pso_blob_size; - } else { - return; - } - } - - if (p_closing) { - _save_pipeline_cache(this); - } else { - pipelines_cache_save_task = WorkerThreadPool::get_singleton()->add_native_task(&_save_pipeline_cache, this, false, "PipelineCacheSave"); - } -} - -void RenderingDeviceVulkan::_save_pipeline_cache(void *p_data) { - RenderingDeviceVulkan *self = static_cast<RenderingDeviceVulkan *>(p_data); - - self->pipelines_cache.buffer.resize(self->pipelines_cache.current_size); - - self->_thread_safe_.lock(); - VkResult vr = vkGetPipelineCacheData(self->device, self->pipelines_cache.cache_object, &self->pipelines_cache.current_size, self->pipelines_cache.buffer.ptr()); - self->_thread_safe_.unlock(); - ERR_FAIL_COND(vr != VK_SUCCESS && vr != VK_INCOMPLETE); // Incomplete is OK because the cache may have grown since the size was queried (unless when exiting). - print_verbose(vformat("Updated PSO cache (%.1f MiB)", self->pipelines_cache.current_size / (1024.0f * 1024.0f))); - - // The real buffer size may now be bigger than the updated current_size. - // We take into account the new size but keep the buffer resized in a worst-case fashion. - - self->pipelines_cache.header.data_size = self->pipelines_cache.current_size; - self->pipelines_cache.header.data_hash = hash_murmur3_buffer(self->pipelines_cache.buffer.ptr(), self->pipelines_cache.current_size); - Ref<FileAccess> f = FileAccess::open(self->pipelines_cache.file_path, FileAccess::WRITE, nullptr); - if (f.is_valid()) { - f->store_buffer((const uint8_t *)&self->pipelines_cache.header, sizeof(PipelineCacheHeader)); - f->store_buffer(self->pipelines_cache.buffer.ptr(), self->pipelines_cache.current_size); - } -} - -template <class T> -void RenderingDeviceVulkan::_free_rids(T &p_owner, const char *p_type) { - List<RID> owned; - p_owner.get_owned_list(&owned); - if (owned.size()) { - if (owned.size() == 1) { - WARN_PRINT(vformat("1 RID of type \"%s\" was leaked.", p_type)); - } else { - WARN_PRINT(vformat("%d RIDs of type \"%s\" were leaked.", owned.size(), p_type)); - } - for (const RID &E : owned) { -#ifdef DEV_ENABLED - if (resource_names.has(E)) { - print_line(String(" - ") + resource_names[E]); - } -#endif - free(E); - } - } -} - -void RenderingDeviceVulkan::capture_timestamp(const String &p_name) { - ERR_FAIL_COND_MSG(draw_list != nullptr, "Capturing timestamps during draw list creation is not allowed. Offending timestamp was: " + p_name); - ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements); - - // This should be optional for profiling, else it will slow things down. - { - VkMemoryBarrier memoryBarrier; - - memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; - memoryBarrier.pNext = nullptr; - memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT | - VK_ACCESS_INDEX_READ_BIT | - VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - VK_ACCESS_UNIFORM_READ_BIT | - VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | - VK_ACCESS_SHADER_READ_BIT | - VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT | - VK_ACCESS_HOST_READ_BIT | - VK_ACCESS_HOST_WRITE_BIT; - memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT | - VK_ACCESS_INDEX_READ_BIT | - VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - VK_ACCESS_UNIFORM_READ_BIT | - VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | - VK_ACCESS_SHADER_READ_BIT | - VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT | - VK_ACCESS_HOST_READ_BIT | - VK_ACCESS_HOST_WRITE_BIT; - - vkCmdPipelineBarrier(frames[frame].draw_command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &memoryBarrier, 0, nullptr, 0, nullptr); - } - - vkCmdWriteTimestamp(frames[frame].draw_command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frames[frame].timestamp_pool, frames[frame].timestamp_count); - frames[frame].timestamp_names[frames[frame].timestamp_count] = p_name; - frames[frame].timestamp_cpu_values[frames[frame].timestamp_count] = OS::get_singleton()->get_ticks_usec(); - frames[frame].timestamp_count++; -} - -uint64_t RenderingDeviceVulkan::get_driver_resource(DriverResource p_resource, RID p_rid, uint64_t p_index) { - _THREAD_SAFE_METHOD_ - - switch (p_resource) { - case DRIVER_RESOURCE_VULKAN_DEVICE: { - return (uint64_t)context->get_device(); - } break; - case DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE: { - return (uint64_t)context->get_physical_device(); - } break; - case DRIVER_RESOURCE_VULKAN_INSTANCE: { - return (uint64_t)context->get_instance(); - } break; - case DRIVER_RESOURCE_VULKAN_QUEUE: { - return (uint64_t)context->get_graphics_queue(); - } break; - case DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX: { - return context->get_graphics_queue_family_index(); - } break; - case DRIVER_RESOURCE_VULKAN_IMAGE: { - Texture *tex = texture_owner.get_or_null(p_rid); - ERR_FAIL_NULL_V(tex, 0); - - return (uint64_t)tex->image; - } break; - case DRIVER_RESOURCE_VULKAN_IMAGE_VIEW: { - Texture *tex = texture_owner.get_or_null(p_rid); - ERR_FAIL_NULL_V(tex, 0); - - return (uint64_t)tex->view; - } break; - case DRIVER_RESOURCE_VULKAN_IMAGE_NATIVE_TEXTURE_FORMAT: { - Texture *tex = texture_owner.get_or_null(p_rid); - ERR_FAIL_NULL_V(tex, 0); - - return vulkan_formats[tex->format]; - } break; - case DRIVER_RESOURCE_VULKAN_SAMPLER: { - VkSampler *sampler = sampler_owner.get_or_null(p_rid); - ERR_FAIL_NULL_V(sampler, 0); - - return uint64_t(*sampler); - } break; - case DRIVER_RESOURCE_VULKAN_DESCRIPTOR_SET: { - UniformSet *uniform_set = uniform_set_owner.get_or_null(p_rid); - ERR_FAIL_NULL_V(uniform_set, 0); - - return uint64_t(uniform_set->descriptor_set); - } break; - case DRIVER_RESOURCE_VULKAN_BUFFER: { - Buffer *buffer = nullptr; - if (vertex_buffer_owner.owns(p_rid)) { - buffer = vertex_buffer_owner.get_or_null(p_rid); - } else if (index_buffer_owner.owns(p_rid)) { - buffer = index_buffer_owner.get_or_null(p_rid); - } else if (uniform_buffer_owner.owns(p_rid)) { - buffer = uniform_buffer_owner.get_or_null(p_rid); - } else if (texture_buffer_owner.owns(p_rid)) { - buffer = &texture_buffer_owner.get_or_null(p_rid)->buffer; - } else if (storage_buffer_owner.owns(p_rid)) { - buffer = storage_buffer_owner.get_or_null(p_rid); - } - - ERR_FAIL_NULL_V(buffer, 0); - - return uint64_t(buffer->buffer); - } break; - case DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE: { - ComputePipeline *compute_pipeline = compute_pipeline_owner.get_or_null(p_rid); - ERR_FAIL_NULL_V(compute_pipeline, 0); - - return uint64_t(compute_pipeline->pipeline); - } break; - case DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE: { - RenderPipeline *render_pipeline = render_pipeline_owner.get_or_null(p_rid); - ERR_FAIL_NULL_V(render_pipeline, 0); - - return uint64_t(render_pipeline->pipeline); - } break; - default: { - // Not supported for this driver. - return 0; - } break; - } -} - -uint32_t RenderingDeviceVulkan::get_captured_timestamps_count() const { - return frames[frame].timestamp_result_count; -} - -uint64_t RenderingDeviceVulkan::get_captured_timestamps_frame() const { - return frames[frame].index; -} - -static void mult64to128(uint64_t u, uint64_t v, uint64_t &h, uint64_t &l) { - uint64_t u1 = (u & 0xffffffff); - uint64_t v1 = (v & 0xffffffff); - uint64_t t = (u1 * v1); - uint64_t w3 = (t & 0xffffffff); - uint64_t k = (t >> 32); - - u >>= 32; - t = (u * v1) + k; - k = (t & 0xffffffff); - uint64_t w1 = (t >> 32); - - v >>= 32; - t = (u1 * v) + k; - k = (t >> 32); - - h = (u * v) + w1 + k; - l = (t << 32) + w3; -} - -uint64_t RenderingDeviceVulkan::get_captured_timestamp_gpu_time(uint32_t p_index) const { - ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); - - // This sucks because timestampPeriod multiplier is a float, while the timestamp is 64 bits nanosecs. - // So, in cases like nvidia which give you enormous numbers and 1 as multiplier, multiplying is next to impossible. - // Need to do 128 bits fixed point multiplication to get the right value. - - uint64_t shift_bits = 16; - - uint64_t h, l; - - mult64to128(frames[frame].timestamp_result_values[p_index], uint64_t(double(limits.timestampPeriod) * double(1 << shift_bits)), h, l); - l >>= shift_bits; - l |= h << (64 - shift_bits); - - return l; -} - -uint64_t RenderingDeviceVulkan::get_captured_timestamp_cpu_time(uint32_t p_index) const { - ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); - return frames[frame].timestamp_cpu_result_values[p_index]; -} - -String RenderingDeviceVulkan::get_captured_timestamp_name(uint32_t p_index) const { - ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, String()); - return frames[frame].timestamp_result_names[p_index]; -} - -uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) const { - switch (p_limit) { - case LIMIT_MAX_BOUND_UNIFORM_SETS: - return limits.maxBoundDescriptorSets; - case LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS: - return limits.maxColorAttachments; - case LIMIT_MAX_TEXTURES_PER_UNIFORM_SET: - return limits.maxDescriptorSetSampledImages; - case LIMIT_MAX_SAMPLERS_PER_UNIFORM_SET: - return limits.maxDescriptorSetSamplers; - case LIMIT_MAX_STORAGE_BUFFERS_PER_UNIFORM_SET: - return limits.maxDescriptorSetStorageBuffers; - case LIMIT_MAX_STORAGE_IMAGES_PER_UNIFORM_SET: - return limits.maxDescriptorSetStorageImages; - case LIMIT_MAX_UNIFORM_BUFFERS_PER_UNIFORM_SET: - return limits.maxDescriptorSetUniformBuffers; - case LIMIT_MAX_DRAW_INDEXED_INDEX: - return limits.maxDrawIndexedIndexValue; - case LIMIT_MAX_FRAMEBUFFER_HEIGHT: - return limits.maxFramebufferHeight; - case LIMIT_MAX_FRAMEBUFFER_WIDTH: - return limits.maxFramebufferWidth; - case LIMIT_MAX_TEXTURE_ARRAY_LAYERS: - return limits.maxImageArrayLayers; - case LIMIT_MAX_TEXTURE_SIZE_1D: - return limits.maxImageDimension1D; - case LIMIT_MAX_TEXTURE_SIZE_2D: - return limits.maxImageDimension2D; - case LIMIT_MAX_TEXTURE_SIZE_3D: - return limits.maxImageDimension3D; - case LIMIT_MAX_TEXTURE_SIZE_CUBE: - return limits.maxImageDimensionCube; - case LIMIT_MAX_TEXTURES_PER_SHADER_STAGE: - return limits.maxPerStageDescriptorSampledImages; - case LIMIT_MAX_SAMPLERS_PER_SHADER_STAGE: - return limits.maxPerStageDescriptorSamplers; - case LIMIT_MAX_STORAGE_BUFFERS_PER_SHADER_STAGE: - return limits.maxPerStageDescriptorStorageBuffers; - case LIMIT_MAX_STORAGE_IMAGES_PER_SHADER_STAGE: - return limits.maxPerStageDescriptorStorageImages; - case LIMIT_MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE: - return limits.maxPerStageDescriptorUniformBuffers; - case LIMIT_MAX_PUSH_CONSTANT_SIZE: - return limits.maxPushConstantsSize; - case LIMIT_MAX_UNIFORM_BUFFER_SIZE: - return limits.maxUniformBufferRange; - case LIMIT_MAX_VERTEX_INPUT_ATTRIBUTE_OFFSET: - return limits.maxVertexInputAttributeOffset; - case LIMIT_MAX_VERTEX_INPUT_ATTRIBUTES: - return limits.maxVertexInputAttributes; - case LIMIT_MAX_VERTEX_INPUT_BINDINGS: - return limits.maxVertexInputBindings; - case LIMIT_MAX_VERTEX_INPUT_BINDING_STRIDE: - return limits.maxVertexInputBindingStride; - case LIMIT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT: - return limits.minUniformBufferOffsetAlignment; - case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X: - return limits.maxComputeWorkGroupCount[0]; - case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y: - return limits.maxComputeWorkGroupCount[1]; - case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z: - return limits.maxComputeWorkGroupCount[2]; - case LIMIT_MAX_COMPUTE_WORKGROUP_INVOCATIONS: - return limits.maxComputeWorkGroupInvocations; - case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X: - return limits.maxComputeWorkGroupSize[0]; - case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y: - return limits.maxComputeWorkGroupSize[1]; - case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z: - return limits.maxComputeWorkGroupSize[2]; - case LIMIT_MAX_VIEWPORT_DIMENSIONS_X: - 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(); - return subgroup_capabilities.size; - } - case LIMIT_SUBGROUP_MIN_SIZE: { - VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); - return subgroup_capabilities.min_size; - } - case LIMIT_SUBGROUP_MAX_SIZE: { - VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); - return subgroup_capabilities.max_size; - } - case LIMIT_SUBGROUP_IN_SHADERS: { - VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); - return subgroup_capabilities.supported_stages_flags_rd(); - } - case LIMIT_SUBGROUP_OPERATIONS: { - VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); - return subgroup_capabilities.supported_operations_flags_rd(); - } - case LIMIT_VRS_TEXEL_WIDTH: { - return context->get_vrs_capabilities().texel_size.x; - } - case LIMIT_VRS_TEXEL_HEIGHT: { - return context->get_vrs_capabilities().texel_size.y; - } - default: - ERR_FAIL_V(0); - } - - return 0; -} - -void RenderingDeviceVulkan::finalize() { - // Free all resources. - - _flush(false); - - _free_rids(render_pipeline_owner, "Pipeline"); - _free_rids(compute_pipeline_owner, "Compute"); - _free_rids(uniform_set_owner, "UniformSet"); - _free_rids(texture_buffer_owner, "TextureBuffer"); - _free_rids(storage_buffer_owner, "StorageBuffer"); - _free_rids(uniform_buffer_owner, "UniformBuffer"); - _free_rids(shader_owner, "Shader"); - _free_rids(index_array_owner, "IndexArray"); - _free_rids(index_buffer_owner, "IndexBuffer"); - _free_rids(vertex_array_owner, "VertexArray"); - _free_rids(vertex_buffer_owner, "VertexBuffer"); - _free_rids(framebuffer_owner, "Framebuffer"); - _free_rids(sampler_owner, "Sampler"); - { - // For textures it's a bit more difficult because they may be shared. - List<RID> owned; - texture_owner.get_owned_list(&owned); - if (owned.size()) { - if (owned.size() == 1) { - WARN_PRINT("1 RID of type \"Texture\" was leaked."); - } else { - WARN_PRINT(vformat("%d RIDs of type \"Texture\" were leaked.", owned.size())); - } - // Free shared first. - for (List<RID>::Element *E = owned.front(); E;) { - List<RID>::Element *N = E->next(); - if (texture_is_shared(E->get())) { -#ifdef DEV_ENABLED - if (resource_names.has(E->get())) { - print_line(String(" - ") + resource_names[E->get()]); - } -#endif - free(E->get()); - owned.erase(E); - } - E = N; - } - // Free non shared second, this will avoid an error trying to free unexisting textures due to dependencies. - for (const RID &E : owned) { -#ifdef DEV_ENABLED - if (resource_names.has(E)) { - print_line(String(" - ") + resource_names[E]); - } -#endif - free(E); - } - } - } - - // Free everything pending. - for (int i = 0; i < frame_count; i++) { - int f = (frame + i) % frame_count; - _free_pending_resources(f); - vkDestroyCommandPool(device, frames[i].command_pool, nullptr); - vkDestroyQueryPool(device, frames[i].timestamp_pool, nullptr); - } - _update_pipeline_cache(true); - - vkDestroyPipelineCache(device, pipelines_cache.cache_object, nullptr); - - for (int i = 0; i < split_draw_list_allocators.size(); i++) { - vkDestroyCommandPool(device, split_draw_list_allocators[i].command_pool, nullptr); - } - - frames.clear(); - - for (int i = 0; i < staging_buffer_blocks.size(); i++) { - vmaDestroyBuffer(allocator, staging_buffer_blocks[i].buffer, staging_buffer_blocks[i].allocation); - } - while (small_allocs_pools.size()) { - HashMap<uint32_t, VmaPool>::Iterator E = small_allocs_pools.begin(); - vmaDestroyPool(allocator, E->value); - small_allocs_pools.remove(E); - } - vmaDestroyAllocator(allocator); - - while (vertex_formats.size()) { - HashMap<VertexFormatID, VertexDescriptionCache>::Iterator temp = vertex_formats.begin(); - memdelete_arr(temp->value.bindings); - memdelete_arr(temp->value.attributes); - vertex_formats.remove(temp); - } - - for (KeyValue<FramebufferFormatID, FramebufferFormat> &E : framebuffer_formats) { - vkDestroyRenderPass(device, E.value.render_pass, nullptr); - } - framebuffer_formats.clear(); - - // All these should be clear at this point. - ERR_FAIL_COND(descriptor_pools.size()); - ERR_FAIL_COND(dependency_map.size()); - ERR_FAIL_COND(reverse_dependency_map.size()); -} - -RenderingDevice *RenderingDeviceVulkan::create_local_device() { - RenderingDeviceVulkan *rd = memnew(RenderingDeviceVulkan); - rd->initialize(context, true); - return rd; -} - -bool RenderingDeviceVulkan::has_feature(const Features p_feature) const { - switch (p_feature) { - case SUPPORTS_MULTIVIEW: { - VulkanContext::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: { - return true; - } break; - default: { - return false; - } - } -} - -RenderingDeviceVulkan::RenderingDeviceVulkan() { - device_capabilities.device_family = DEVICE_VULKAN; -} - -RenderingDeviceVulkan::~RenderingDeviceVulkan() { - if (local_device.is_valid()) { - finalize(); - context->local_device_free(local_device); - } -} diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h deleted file mode 100644 index 7c514c11f8..0000000000 --- a/drivers/vulkan/rendering_device_vulkan.h +++ /dev/null @@ -1,1288 +0,0 @@ -/**************************************************************************/ -/* rendering_device_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_DEVICE_VULKAN_H -#define RENDERING_DEVICE_VULKAN_H - -#include "core/object/worker_thread_pool.h" -#include "core/os/thread_safe.h" -#include "core/templates/local_vector.h" -#include "core/templates/oa_hash_map.h" -#include "core/templates/rid_owner.h" -#include "servers/rendering/rendering_device.h" - -#ifdef DEBUG_ENABLED -#ifndef _DEBUG -#define _DEBUG -#endif -#endif -#include "vk_mem_alloc.h" - -#ifdef USE_VOLK -#include <volk.h> -#else -#include <vulkan/vulkan.h> -#endif - -class VulkanContext; - -class RenderingDeviceVulkan : public RenderingDevice { - _THREAD_SAFE_CLASS_ - - // Miscellaneous tables that map - // our enums to enums used - // by vulkan. - - VkPhysicalDeviceLimits limits; - static const VkFormat vulkan_formats[DATA_FORMAT_MAX]; - static const char *named_formats[DATA_FORMAT_MAX]; - static const VkCompareOp compare_operators[COMPARE_OP_MAX]; - static const VkStencilOp stencil_operations[STENCIL_OP_MAX]; - static const VkSampleCountFlagBits rasterization_sample_count[TEXTURE_SAMPLES_MAX]; - static const VkLogicOp logic_operations[RenderingDevice::LOGIC_OP_MAX]; - static const VkBlendFactor blend_factors[RenderingDevice::BLEND_FACTOR_MAX]; - static const VkBlendOp blend_operations[RenderingDevice::BLEND_OP_MAX]; - static const VkSamplerAddressMode address_modes[SAMPLER_REPEAT_MODE_MAX]; - static const VkBorderColor sampler_border_colors[SAMPLER_BORDER_COLOR_MAX]; - static const VkImageType vulkan_image_type[TEXTURE_TYPE_MAX]; - - // Functions used for format - // validation, and ensures the - // user passes valid data. - - static int get_format_vertex_size(DataFormat p_format); - static uint32_t get_image_format_pixel_size(DataFormat p_format); - static void get_compressed_image_format_block_dimensions(DataFormat p_format, uint32_t &r_w, uint32_t &r_h); - uint32_t get_compressed_image_format_block_byte_size(DataFormat p_format); - static uint32_t get_compressed_image_format_pixel_rshift(DataFormat p_format); - static uint32_t get_image_format_required_size(DataFormat p_format, uint32_t p_width, uint32_t p_height, uint32_t p_depth, uint32_t p_mipmaps, uint32_t *r_blockw = nullptr, uint32_t *r_blockh = nullptr, uint32_t *r_depth = nullptr); - static uint32_t get_image_required_mipmaps(uint32_t p_width, uint32_t p_height, uint32_t p_depth); - static bool format_has_stencil(DataFormat p_format); - - /***************************/ - /**** ID INFRASTRUCTURE ****/ - /***************************/ - - enum IDType { - ID_TYPE_FRAMEBUFFER_FORMAT, - ID_TYPE_VERTEX_FORMAT, - ID_TYPE_DRAW_LIST, - ID_TYPE_SPLIT_DRAW_LIST, - ID_TYPE_COMPUTE_LIST, - ID_TYPE_MAX, - ID_BASE_SHIFT = 58 // 5 bits for ID types. - }; - - VkDevice device = VK_NULL_HANDLE; - - HashMap<RID, HashSet<RID>> dependency_map; // IDs to IDs that depend on it. - HashMap<RID, HashSet<RID>> reverse_dependency_map; // Same as above, but in reverse. - - void _add_dependency(RID p_id, RID p_depends_on); - void _free_dependencies(RID p_id); - - /*****************/ - /**** TEXTURE ****/ - /*****************/ - - // In Vulkan, the concept of textures does not exist, - // instead there is the image (the memory pretty much, - // the view (how the memory is interpreted) and the - // sampler (how it's sampled from the shader). - // - // Texture here includes the first two stages, but - // It's possible to create textures sharing the image - // but with different views. The main use case for this - // is textures that can be read as both SRGB/Linear, - // or slices of a texture (a mipmap, a layer, a 3D slice) - // for a framebuffer to render into it. - - struct Texture { - VkImage image = VK_NULL_HANDLE; - VmaAllocation allocation = nullptr; - VmaAllocationInfo allocation_info; - VkImageView view = VK_NULL_HANDLE; - - TextureType type; - DataFormat format; - TextureSamples samples; - uint32_t width = 0; - uint32_t height = 0; - uint32_t depth = 0; - uint32_t layers = 0; - uint32_t mipmaps = 0; - uint32_t usage_flags = 0; - uint32_t base_mipmap = 0; - uint32_t base_layer = 0; - - Vector<DataFormat> allowed_shared_formats; - - VkImageLayout layout; - - uint64_t used_in_frame = 0; - bool used_in_transfer = false; - bool used_in_raster = false; - bool used_in_compute = false; - - bool is_resolve_buffer = false; - - uint32_t read_aspect_mask = 0; - uint32_t barrier_aspect_mask = 0; - bool bound = false; // Bound to framebffer. - RID owner; - }; - - RID_Owner<Texture, true> texture_owner; - uint32_t texture_upload_region_size_px = 0; - - Vector<uint8_t> _texture_get_data_from_image(Texture *tex, VkImage p_image, VmaAllocation p_allocation, uint32_t p_layer, bool p_2d = false); - Error _texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier, bool p_use_setup_queue); - - /*****************/ - /**** SAMPLER ****/ - /*****************/ - - RID_Owner<VkSampler> sampler_owner; - - /***************************/ - /**** BUFFER MANAGEMENT ****/ - /***************************/ - - // These are temporary buffers on CPU memory that hold - // the information until the CPU fetches it and places it - // either on GPU buffers, or images (textures). It ensures - // updates are properly synchronized with whatever the - // GPU is doing. - // - // The logic here is as follows, only 3 of these - // blocks are created at the beginning (one per frame) - // they can each belong to a frame (assigned to current when - // used) and they can only be reused after the same frame is - // recycled. - // - // When CPU requires to allocate more than what is available, - // more of these buffers are created. If a limit is reached, - // then a fence will ensure will wait for blocks allocated - // in previous frames are processed. If that fails, then - // another fence will ensure everything pending for the current - // frame is processed (effectively stalling). - // - // See the comments in the code to understand better how it works. - - struct StagingBufferBlock { - VkBuffer buffer = VK_NULL_HANDLE; - VmaAllocation allocation = nullptr; - uint64_t frame_used = 0; - uint32_t fill_amount = 0; - }; - - Vector<StagingBufferBlock> staging_buffer_blocks; - int staging_buffer_current = 0; - uint32_t staging_buffer_block_size = 0; - uint64_t staging_buffer_max_size = 0; - bool staging_buffer_used = false; - - Error _staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment = true); - Error _insert_staging_block(); - - struct Buffer { - uint32_t size = 0; - uint32_t usage = 0; - VkBuffer buffer = VK_NULL_HANDLE; - VmaAllocation allocation = nullptr; - VkDescriptorBufferInfo buffer_info; // Used for binding. - Buffer() { - } - }; - - Error _buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mem_usage, VmaAllocationCreateFlags p_mem_flags); - Error _buffer_free(Buffer *p_buffer); - Error _buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_buffer = false, uint32_t p_required_align = 32); - - void _full_barrier(bool p_sync_with_draw); - void _memory_barrier(VkPipelineStageFlags p_src_stage_mask, VkPipelineStageFlags p_dst_stage_mask, VkAccessFlags p_src_access, VkAccessFlags p_dst_access, bool p_sync_with_draw); - void _buffer_memory_barrier(VkBuffer buffer, uint64_t p_from, uint64_t p_size, VkPipelineStageFlags p_src_stage_mask, VkPipelineStageFlags p_dst_stage_mask, VkAccessFlags p_src_access, VkAccessFlags p_dst_access, bool p_sync_with_draw); - - /*********************/ - /**** FRAMEBUFFER ****/ - /*********************/ - - // In Vulkan, framebuffers work similar to how they - // do in OpenGL, with the exception that - // the "format" (vkRenderPass) is not dynamic - // and must be more or less the same as the one - // used for the render pipelines. - - struct FramebufferFormatKey { - Vector<AttachmentFormat> attachments; - Vector<FramebufferPass> passes; - uint32_t view_count = 1; - - bool operator<(const FramebufferFormatKey &p_key) const { - if (view_count != p_key.view_count) { - return view_count < p_key.view_count; - } - - uint32_t pass_size = passes.size(); - uint32_t key_pass_size = p_key.passes.size(); - if (pass_size != key_pass_size) { - return pass_size < key_pass_size; - } - const FramebufferPass *pass_ptr = passes.ptr(); - const FramebufferPass *key_pass_ptr = p_key.passes.ptr(); - - for (uint32_t i = 0; i < pass_size; i++) { - { // Compare color attachments. - uint32_t attachment_size = pass_ptr[i].color_attachments.size(); - uint32_t key_attachment_size = key_pass_ptr[i].color_attachments.size(); - if (attachment_size != key_attachment_size) { - return attachment_size < key_attachment_size; - } - const int32_t *pass_attachment_ptr = pass_ptr[i].color_attachments.ptr(); - const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].color_attachments.ptr(); - - for (uint32_t j = 0; j < attachment_size; j++) { - if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { - return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; - } - } - } - { // Compare input attachments. - uint32_t attachment_size = pass_ptr[i].input_attachments.size(); - uint32_t key_attachment_size = key_pass_ptr[i].input_attachments.size(); - if (attachment_size != key_attachment_size) { - return attachment_size < key_attachment_size; - } - const int32_t *pass_attachment_ptr = pass_ptr[i].input_attachments.ptr(); - const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].input_attachments.ptr(); - - for (uint32_t j = 0; j < attachment_size; j++) { - if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { - return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; - } - } - } - { // Compare resolve attachments. - uint32_t attachment_size = pass_ptr[i].resolve_attachments.size(); - uint32_t key_attachment_size = key_pass_ptr[i].resolve_attachments.size(); - if (attachment_size != key_attachment_size) { - return attachment_size < key_attachment_size; - } - const int32_t *pass_attachment_ptr = pass_ptr[i].resolve_attachments.ptr(); - const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].resolve_attachments.ptr(); - - for (uint32_t j = 0; j < attachment_size; j++) { - if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { - return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; - } - } - } - { // Compare preserve attachments. - uint32_t attachment_size = pass_ptr[i].preserve_attachments.size(); - uint32_t key_attachment_size = key_pass_ptr[i].preserve_attachments.size(); - if (attachment_size != key_attachment_size) { - return attachment_size < key_attachment_size; - } - const int32_t *pass_attachment_ptr = pass_ptr[i].preserve_attachments.ptr(); - const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].preserve_attachments.ptr(); - - for (uint32_t j = 0; j < attachment_size; j++) { - if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { - return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; - } - } - } - if (pass_ptr[i].depth_attachment != key_pass_ptr[i].depth_attachment) { - return pass_ptr[i].depth_attachment < key_pass_ptr[i].depth_attachment; - } - } - - int as = attachments.size(); - int bs = p_key.attachments.size(); - if (as != bs) { - return as < bs; - } - - const AttachmentFormat *af_a = attachments.ptr(); - const AttachmentFormat *af_b = p_key.attachments.ptr(); - for (int i = 0; i < as; i++) { - const AttachmentFormat &a = af_a[i]; - const AttachmentFormat &b = af_b[i]; - if (a.format != b.format) { - return a.format < b.format; - } - if (a.samples != b.samples) { - return a.samples < b.samples; - } - if (a.usage_flags != b.usage_flags) { - return a.usage_flags < b.usage_flags; - } - } - - return false; // Equal. - } - }; - - VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count = 1, Vector<TextureSamples> *r_samples = nullptr); - // This is a cache and it's never freed, it ensures - // IDs for a given format are always unique. - RBMap<FramebufferFormatKey, FramebufferFormatID> framebuffer_format_cache; - struct FramebufferFormat { - const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E; - VkRenderPass render_pass = VK_NULL_HANDLE; // Here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec). - Vector<TextureSamples> pass_samples; - uint32_t view_count = 1; // Number of views. - }; - - HashMap<FramebufferFormatID, FramebufferFormat> framebuffer_formats; - - struct Framebuffer { - FramebufferFormatID format_id = 0; - struct VersionKey { - InitialAction initial_color_action; - FinalAction final_color_action; - InitialAction initial_depth_action; - FinalAction final_depth_action; - uint32_t view_count; - - bool operator<(const VersionKey &p_key) const { - if (initial_color_action == p_key.initial_color_action) { - if (final_color_action == p_key.final_color_action) { - if (initial_depth_action == p_key.initial_depth_action) { - if (final_depth_action == p_key.final_depth_action) { - return view_count < p_key.view_count; - } else { - return final_depth_action < p_key.final_depth_action; - } - } else { - return initial_depth_action < p_key.initial_depth_action; - } - } else { - return final_color_action < p_key.final_color_action; - } - } else { - return initial_color_action < p_key.initial_color_action; - } - } - }; - - uint32_t storage_mask = 0; - Vector<RID> texture_ids; - InvalidationCallback invalidated_callback = nullptr; - void *invalidated_callback_userdata = nullptr; - - struct Version { - VkFramebuffer framebuffer = VK_NULL_HANDLE; - VkRenderPass render_pass = VK_NULL_HANDLE; // This one is owned. - uint32_t subpass_count = 1; - }; - - RBMap<VersionKey, Version> framebuffers; - Size2 size; - uint32_t view_count; - }; - - RID_Owner<Framebuffer, true> framebuffer_owner; - - /***********************/ - /**** VERTEX BUFFER ****/ - /***********************/ - - // Vertex buffers in Vulkan are similar to how - // they work in OpenGL, except that instead of - // an attribute index, there is a buffer binding - // index (for binding the buffers in real-time) - // and a location index (what is used in the shader). - // - // This mapping is done here internally, and it's not - // exposed. - - RID_Owner<Buffer, true> vertex_buffer_owner; - - struct VertexDescriptionKey { - Vector<VertexAttribute> vertex_formats; - bool operator==(const VertexDescriptionKey &p_key) const { - int vdc = vertex_formats.size(); - int vdck = p_key.vertex_formats.size(); - - if (vdc != vdck) { - return false; - } else { - const VertexAttribute *a_ptr = vertex_formats.ptr(); - const VertexAttribute *b_ptr = p_key.vertex_formats.ptr(); - for (int i = 0; i < vdc; i++) { - const VertexAttribute &a = a_ptr[i]; - const VertexAttribute &b = b_ptr[i]; - - if (a.location != b.location) { - return false; - } - if (a.offset != b.offset) { - return false; - } - if (a.format != b.format) { - return false; - } - if (a.stride != b.stride) { - return false; - } - if (a.frequency != b.frequency) { - return false; - } - } - return true; // They are equal. - } - } - - uint32_t hash() const { - int vdc = vertex_formats.size(); - uint32_t h = hash_murmur3_one_32(vdc); - const VertexAttribute *ptr = vertex_formats.ptr(); - for (int i = 0; i < vdc; i++) { - const VertexAttribute &vd = ptr[i]; - h = hash_murmur3_one_32(vd.location, h); - h = hash_murmur3_one_32(vd.offset, h); - h = hash_murmur3_one_32(vd.format, h); - h = hash_murmur3_one_32(vd.stride, h); - h = hash_murmur3_one_32(vd.frequency, h); - } - return hash_fmix32(h); - } - }; - - struct VertexDescriptionHash { - static _FORCE_INLINE_ uint32_t hash(const VertexDescriptionKey &p_key) { - return p_key.hash(); - } - }; - - // This is a cache and it's never freed, it ensures that - // ID used for a specific format always remain the same. - HashMap<VertexDescriptionKey, VertexFormatID, VertexDescriptionHash> vertex_format_cache; - - struct VertexDescriptionCache { - Vector<VertexAttribute> vertex_formats; - VkVertexInputBindingDescription *bindings = nullptr; - VkVertexInputAttributeDescription *attributes = nullptr; - VkPipelineVertexInputStateCreateInfo create_info; - }; - - HashMap<VertexFormatID, VertexDescriptionCache> vertex_formats; - - struct VertexArray { - RID buffer; - VertexFormatID description = 0; - int vertex_count = 0; - uint32_t max_instances_allowed = 0; - - Vector<VkBuffer> buffers; // Not owned, just referenced. - Vector<VkDeviceSize> offsets; - }; - - RID_Owner<VertexArray, true> vertex_array_owner; - - struct IndexBuffer : public Buffer { - uint32_t max_index = 0; // Used for validation. - uint32_t index_count = 0; - VkIndexType index_type = VK_INDEX_TYPE_NONE_NV; - bool supports_restart_indices = false; - }; - - RID_Owner<IndexBuffer, true> index_buffer_owner; - - struct IndexArray { - uint32_t max_index = 0; // Remember the maximum index here too, for validation. - VkBuffer buffer; // Not owned, inherited from index buffer. - uint32_t offset = 0; - uint32_t indices = 0; - VkIndexType index_type = VK_INDEX_TYPE_NONE_NV; - bool supports_restart_indices = false; - }; - - RID_Owner<IndexArray, true> index_array_owner; - - /****************/ - /**** SHADER ****/ - /****************/ - - // Vulkan specifies a really complex behavior for the application - // in order to tell when descriptor sets need to be re-bound (or not). - // "When binding a descriptor set (see Descriptor Set Binding) to set - // number N, if the previously bound descriptor sets for sets zero - // through N-1 were all bound using compatible pipeline layouts, - // then performing this binding does not disturb any of the lower numbered sets. - // If, additionally, the previous bound descriptor set for set N was - // bound using a pipeline layout compatible for set N, then the bindings - // in sets numbered greater than N are also not disturbed." - // As a result, we need to figure out quickly when something is no longer "compatible". - // in order to avoid costly rebinds. - - struct UniformInfo { - UniformType type = UniformType::UNIFORM_TYPE_MAX; - bool writable = false; - int binding = 0; - uint32_t stages = 0; - int length = 0; // Size of arrays (in total elements), or ubos (in bytes * total elements). - - bool operator!=(const UniformInfo &p_info) const { - return (binding != p_info.binding || type != p_info.type || writable != p_info.writable || stages != p_info.stages || length != p_info.length); - } - - bool operator<(const UniformInfo &p_info) const { - if (binding != p_info.binding) { - return binding < p_info.binding; - } - if (type != p_info.type) { - return type < p_info.type; - } - if (writable != p_info.writable) { - return writable < p_info.writable; - } - if (stages != p_info.stages) { - return stages < p_info.stages; - } - return length < p_info.length; - } - }; - - struct UniformSetFormat { - Vector<UniformInfo> uniform_info; - bool operator<(const UniformSetFormat &p_format) const { - uint32_t size = uniform_info.size(); - uint32_t psize = p_format.uniform_info.size(); - - if (size != psize) { - return size < psize; - } - - const UniformInfo *infoptr = uniform_info.ptr(); - const UniformInfo *pinfoptr = p_format.uniform_info.ptr(); - - for (uint32_t i = 0; i < size; i++) { - if (infoptr[i] != pinfoptr[i]) { - return infoptr[i] < pinfoptr[i]; - } - } - - return false; - } - }; - - // Always grows, never shrinks, ensuring unique IDs, but we assume - // the amount of formats will never be a problem, as the amount of shaders - // in a game is limited. - RBMap<UniformSetFormat, uint32_t> uniform_set_format_cache; - - // Shaders in Vulkan are just pretty much - // precompiled blocks of SPIR-V bytecode. They - // are most likely not really compiled to host - // assembly until a pipeline is created. - // - // When supplying the shaders, this implementation - // will use the reflection abilities of glslang to - // understand and cache everything required to - // create and use the descriptor sets (Vulkan's - // biggest pain). - // - // Additionally, hashes are created for every set - // to do quick validation and ensuring the user - // does not submit something invalid. - - struct Shader { - struct Set { - Vector<UniformInfo> uniform_info; - VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; - }; - - uint64_t vertex_input_mask = 0; // Inputs used, this is mostly for validation. - uint32_t fragment_output_mask = 0; - - struct PushConstant { - uint32_t size = 0; - uint32_t vk_stages_mask = 0; - }; - - PushConstant push_constant; - - uint32_t compute_local_size[3] = { 0, 0, 0 }; - - struct SpecializationConstant { - PipelineSpecializationConstant constant; - uint32_t stage_flags = 0; - }; - - bool is_compute = false; - Vector<Set> sets; - Vector<uint32_t> set_formats; - Vector<VkPipelineShaderStageCreateInfo> pipeline_stages; - Vector<SpecializationConstant> specialization_constants; - VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; - String name; // Used for debug. - }; - - String _shader_uniform_debug(RID p_shader, int p_set = -1); - - RID_Owner<Shader, true> shader_owner; - - /******************/ - /**** UNIFORMS ****/ - /******************/ - - // Descriptor sets require allocation from a pool. - // The documentation on how to use pools properly - // is scarce, and the documentation is strange. - // - // Basically, you can mix and match pools as you - // like, but you'll run into fragmentation issues. - // Because of this, the recommended approach is to - // create a pool for every descriptor set type, as - // this prevents fragmentation. - // - // This is implemented here as a having a list of - // pools (each can contain up to 64 sets) for each - // set layout. The amount of sets for each type - // is used as the key. - - enum { - MAX_DESCRIPTOR_POOL_ELEMENT = 65535 - }; - - struct DescriptorPoolKey { - union { - struct { - uint16_t uniform_type[UNIFORM_TYPE_MAX]; // Using 16 bits because, for sending arrays, each element is a pool set. - }; - struct { - uint64_t key1; - uint64_t key2; - uint64_t key3; - }; - }; - bool operator<(const DescriptorPoolKey &p_key) const { - if (key1 != p_key.key1) { - return key1 < p_key.key1; - } - if (key2 != p_key.key2) { - return key2 < p_key.key2; - } - - return key3 < p_key.key3; - } - DescriptorPoolKey() { - key1 = 0; - key2 = 0; - key3 = 0; - } - }; - - struct DescriptorPool { - VkDescriptorPool pool; - uint32_t usage; - }; - - RBMap<DescriptorPoolKey, HashSet<DescriptorPool *>> descriptor_pools; - uint32_t max_descriptors_per_pool = 0; - - DescriptorPool *_descriptor_pool_allocate(const DescriptorPoolKey &p_key); - void _descriptor_pool_free(const DescriptorPoolKey &p_key, DescriptorPool *p_pool); - - RID_Owner<Buffer, true> uniform_buffer_owner; - RID_Owner<Buffer, true> storage_buffer_owner; - - // Texture buffer needs a view. - struct TextureBuffer { - Buffer buffer; - VkBufferView view = VK_NULL_HANDLE; - }; - - RID_Owner<TextureBuffer, true> texture_buffer_owner; - - // This structure contains the descriptor set. They _need_ to be allocated - // for a shader (and will be erased when this shader is erased), but should - // work for other shaders as long as the hash matches. This covers using - // them in shader variants. - // - // Keep also in mind that you can share buffers between descriptor sets, so - // the above restriction is not too serious. - - struct UniformSet { - uint32_t format = 0; - RID shader_id; - uint32_t shader_set = 0; - DescriptorPool *pool = nullptr; - DescriptorPoolKey pool_key; - VkDescriptorSet descriptor_set = VK_NULL_HANDLE; - //VkPipelineLayout pipeline_layout; // Not owned, inherited from shader. - struct AttachableTexture { - uint32_t bind; - RID texture; - }; - - LocalVector<AttachableTexture> attachable_textures; // Used for validation. - Vector<Texture *> mutable_sampled_textures; // Used for layout change. - Vector<Texture *> mutable_storage_textures; // Used for layout change. - InvalidationCallback invalidated_callback = nullptr; - void *invalidated_callback_userdata = nullptr; - }; - - RID_Owner<UniformSet, true> uniform_set_owner; - - /*******************/ - /**** PIPELINES ****/ - /*******************/ - - // Render pipeline contains ALL the - // information required for drawing. - // This includes all the rasterizer state - // as well as shader used, framebuffer format, - // etc. - // While the pipeline is just a single object - // (VkPipeline) a lot of values are also saved - // here to do validation (vulkan does none by - // default) and warn the user if something - // was not supplied as intended. - - struct RenderPipeline { - // Cached values for validation. -#ifdef DEBUG_ENABLED - struct Validation { - FramebufferFormatID framebuffer_format = 0; - uint32_t render_pass = 0; - uint32_t dynamic_state = 0; - VertexFormatID vertex_format = 0; - bool uses_restart_indices = false; - uint32_t primitive_minimum = 0; - uint32_t primitive_divisor = 0; - } validation; -#endif - // Actual pipeline. - RID shader; - Vector<uint32_t> set_formats; - VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // Not owned, needed for push constants. - VkPipeline pipeline = VK_NULL_HANDLE; - uint32_t push_constant_size = 0; - uint32_t push_constant_stages_mask = 0; - }; - - RID_Owner<RenderPipeline, true> render_pipeline_owner; - - struct PipelineCacheHeader { - uint32_t magic; - uint32_t data_size; - uint64_t data_hash; - uint32_t vendor_id; - uint32_t device_id; - uint32_t driver_version; - uint8_t uuid[VK_UUID_SIZE]; - uint8_t driver_abi; - }; - - struct PipelineCache { - String file_path; - PipelineCacheHeader header = {}; - size_t current_size = 0; - LocalVector<uint8_t> buffer; - VkPipelineCache cache_object = VK_NULL_HANDLE; - }; - - PipelineCache pipelines_cache; - - WorkerThreadPool::TaskID pipelines_cache_save_task = WorkerThreadPool::INVALID_TASK_ID; - - void _load_pipeline_cache(); - void _update_pipeline_cache(bool p_closing = false); - static void _save_pipeline_cache(void *p_data); - - struct ComputePipeline { - RID shader; - Vector<uint32_t> set_formats; - VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // Not owned, needed for push constants. - VkPipeline pipeline = VK_NULL_HANDLE; - uint32_t push_constant_size = 0; - uint32_t push_constant_stages_mask = 0; - uint32_t local_group_size[3] = { 0, 0, 0 }; - }; - - RID_Owner<ComputePipeline, true> compute_pipeline_owner; - - /*******************/ - /**** DRAW LIST ****/ - /*******************/ - - // Draw list contains both the command buffer - // used for drawing as well as a LOT of - // information used for validation. This - // validation is cheap so most of it can - // also run in release builds. - - // When using split command lists, this is - // implemented internally using secondary command - // buffers. As they can be created in threads, - // each needs its own command pool. - - struct SplitDrawListAllocator { - VkCommandPool command_pool = VK_NULL_HANDLE; - Vector<VkCommandBuffer> command_buffers; // One for each frame. - }; - - Vector<SplitDrawListAllocator> split_draw_list_allocators; - - struct DrawList { - VkCommandBuffer command_buffer = VK_NULL_HANDLE; // If persistent, this is owned, otherwise it's shared with the ringbuffer. - Rect2i viewport; - bool viewport_set = false; - - struct SetState { - uint32_t pipeline_expected_format = 0; - uint32_t uniform_set_format = 0; - VkDescriptorSet descriptor_set = VK_NULL_HANDLE; - RID uniform_set; - bool bound = false; - }; - - struct State { - SetState sets[MAX_UNIFORM_SETS]; - uint32_t set_count = 0; - RID pipeline; - RID pipeline_shader; - VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; - RID vertex_array; - RID index_array; - uint32_t pipeline_push_constant_stages = 0; - } state; - -#ifdef DEBUG_ENABLED - struct Validation { - bool active = true; // Means command buffer was not closed, so you can keep adding things. - // Actual render pass values. - uint32_t dynamic_state = 0; - VertexFormatID vertex_format = INVALID_ID; - uint32_t vertex_array_size = 0; - uint32_t vertex_max_instances_allowed = 0xFFFFFFFF; - bool index_buffer_uses_restart_indices = false; - uint32_t index_array_size = 0; - uint32_t index_array_max_index = 0; - uint32_t index_array_offset = 0; - Vector<uint32_t> set_formats; - Vector<bool> set_bound; - Vector<RID> set_rids; - // Last pipeline set values. - bool pipeline_active = false; - uint32_t pipeline_dynamic_state = 0; - VertexFormatID pipeline_vertex_format = INVALID_ID; - RID pipeline_shader; - bool pipeline_uses_restart_indices = false; - uint32_t pipeline_primitive_divisor = 0; - uint32_t pipeline_primitive_minimum = 0; - uint32_t pipeline_push_constant_size = 0; - bool pipeline_push_constant_supplied = false; - } validation; -#else - struct Validation { - uint32_t vertex_array_size = 0; - uint32_t index_array_size = 0; - uint32_t index_array_offset; - } validation; -#endif - }; - - DrawList *draw_list = nullptr; // One for regular draw lists, multiple for split. - uint32_t draw_list_subpass_count = 0; - uint32_t draw_list_count = 0; - VkRenderPass draw_list_render_pass = VK_NULL_HANDLE; - VkFramebuffer draw_list_vkframebuffer = VK_NULL_HANDLE; -#ifdef DEBUG_ENABLED - FramebufferFormatID draw_list_framebuffer_format = INVALID_ID; -#endif - uint32_t draw_list_current_subpass = 0; - - bool draw_list_split = false; - Vector<RID> draw_list_bound_textures; - Vector<RID> draw_list_storage_textures; - bool draw_list_unbind_color_textures = false; - bool draw_list_unbind_depth_textures = false; - - void _draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); - Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count); - Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures); - _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); - Buffer *_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &dst_stage_mask, VkAccessFlags &dst_access, BitField<BarrierMask> p_post_barrier); - Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass); - void _draw_list_free(Rect2i *r_last_viewport = nullptr); - - /**********************/ - /**** COMPUTE LIST ****/ - /**********************/ - - struct ComputeList { - VkCommandBuffer command_buffer = VK_NULL_HANDLE; // If persistent, this is owned, otherwise it's shared with the ringbuffer. - - struct SetState { - uint32_t pipeline_expected_format = 0; - uint32_t uniform_set_format = 0; - VkDescriptorSet descriptor_set = VK_NULL_HANDLE; - RID uniform_set; - bool bound = false; - }; - - struct State { - HashSet<Texture *> textures_to_sampled_layout; - SetState sets[MAX_UNIFORM_SETS]; - uint32_t set_count = 0; - RID pipeline; - RID pipeline_shader; - uint32_t local_group_size[3] = { 0, 0, 0 }; - VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; - uint32_t pipeline_push_constant_stages = 0; - bool allow_draw_overlap; - } state; - -#ifdef DEBUG_ENABLED - struct Validation { - bool active = true; // Means command buffer was not closed, so you can keep adding things. - Vector<uint32_t> set_formats; - Vector<bool> set_bound; - Vector<RID> set_rids; - // Last pipeline set values. - bool pipeline_active = false; - RID pipeline_shader; - uint32_t invalid_set_from = 0; - uint32_t pipeline_push_constant_size = 0; - bool pipeline_push_constant_supplied = false; - } validation; -#endif - }; - - ComputeList *compute_list = nullptr; - - void _compute_list_add_barrier(BitField<BarrierMask> p_post_barrier, uint32_t p_barrier_flags, uint32_t p_access_flags); - - /**************************/ - /**** FRAME MANAGEMENT ****/ - /**************************/ - - // This is the frame structure. There are normally - // 3 of these (used for triple buffering), or 2 - // (double buffering). They are cycled constantly. - // - // It contains two command buffers, one that is - // used internally for setting up (creating stuff) - // and another used mostly for drawing. - // - // They also contains a list of things that need - // to be disposed of when deleted, which can't - // happen immediately due to the asynchronous - // nature of the GPU. They will get deleted - // when the frame is cycled. - - struct Frame { - // List in usage order, from last to free to first to free. - List<Buffer> buffers_to_dispose_of; - List<Texture> textures_to_dispose_of; - List<Framebuffer> framebuffers_to_dispose_of; - List<VkSampler> samplers_to_dispose_of; - List<Shader> shaders_to_dispose_of; - List<VkBufferView> buffer_views_to_dispose_of; - List<UniformSet> uniform_sets_to_dispose_of; - List<RenderPipeline> render_pipelines_to_dispose_of; - List<ComputePipeline> compute_pipelines_to_dispose_of; - - VkCommandPool command_pool = VK_NULL_HANDLE; - VkCommandBuffer setup_command_buffer = VK_NULL_HANDLE; // Used at the beginning of every frame for set-up. - VkCommandBuffer draw_command_buffer = VK_NULL_HANDLE; // Used at the beginning of every frame for set-up. - - struct Timestamp { - String description; - uint64_t value = 0; - }; - - VkQueryPool timestamp_pool; - - TightLocalVector<String> timestamp_names; - TightLocalVector<uint64_t> timestamp_cpu_values; - uint32_t timestamp_count = 0; - TightLocalVector<String> timestamp_result_names; - TightLocalVector<uint64_t> timestamp_cpu_result_values; - TightLocalVector<uint64_t> timestamp_result_values; - uint32_t timestamp_result_count = 0; - uint64_t index = 0; - }; - - uint32_t max_timestamp_query_elements = 0; - - TightLocalVector<Frame> 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. - uint64_t frames_drawn = 0; - RID local_device; - bool local_device_processing = false; - - void _free_pending_resources(int p_frame); - - VmaAllocator allocator = nullptr; - HashMap<uint32_t, VmaPool> small_allocs_pools; - VmaPool _find_or_create_small_allocs_pool(uint32_t p_mem_type_index); - - VulkanContext *context = nullptr; - - uint64_t image_memory = 0; - uint64_t buffer_memory = 0; - - void _free_internal(RID p_id); - void _flush(bool p_current_frame); - - bool screen_prepared = false; - - template <class T> - void _free_rids(T &p_owner, const char *p_type); - - void _finalize_command_bufers(); - void _begin_frame(); - -#ifdef DEV_ENABLED - HashMap<RID, String> resource_names; -#endif - - VkSampleCountFlagBits _ensure_supported_sample_count(TextureSamples p_requested_sample_count) const; - -public: - virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>()); - virtual RID texture_create_shared(const TextureView &p_view, RID p_with_texture); - virtual RID texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, BitField<RenderingDevice::TextureUsageBits> p_flags, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers); - - virtual RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D, uint32_t p_layers = 0); - virtual Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - virtual Vector<uint8_t> texture_get_data(RID p_texture, uint32_t p_layer); - - virtual bool texture_is_format_supported_for_usage(DataFormat p_format, BitField<RenderingDevice::TextureUsageBits> p_usage) const; - virtual bool texture_is_shared(RID p_texture); - virtual bool texture_is_valid(RID p_texture); - virtual TextureFormat texture_get_format(RID p_texture); - virtual Size2i texture_size(RID p_texture); - virtual uint64_t texture_get_native_handle(RID p_texture); - - virtual Error texture_copy(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<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - virtual Error texture_clear(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<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - virtual Error texture_resolve_multisample(RID p_from_texture, RID p_to_texture, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - - /*********************/ - /**** FRAMEBUFFER ****/ - /*********************/ - - virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1); - virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1); - virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1); - virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0); - - virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); - virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); - virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID); - virtual bool framebuffer_is_valid(RID p_framebuffer) const; - virtual void framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata); - - virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer); - - /*****************/ - /**** SAMPLER ****/ - /*****************/ - - virtual RID sampler_create(const SamplerState &p_state); - virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const; - - /**********************/ - /**** VERTEX ARRAY ****/ - /**********************/ - - virtual RID vertex_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>(), bool p_use_as_storage = false); - - // Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated. - virtual VertexFormatID vertex_format_create(const Vector<VertexAttribute> &p_vertex_formats); - virtual RID vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const Vector<RID> &p_src_buffers, const Vector<uint64_t> &p_offsets = Vector<uint64_t>()); - - virtual RID index_buffer_create(uint32_t p_size_indices, IndexBufferFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>(), bool p_use_restart_indices = false); - - virtual RID index_array_create(RID p_index_buffer, uint32_t p_index_offset, uint32_t p_index_count); - - /****************/ - /**** SHADER ****/ - /****************/ - - virtual String shader_get_binary_cache_key() const; - virtual Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = ""); - - virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID()); - virtual RID shader_create_placeholder(); - - virtual uint64_t shader_get_vertex_input_attribute_mask(RID p_shader); - - /*****************/ - /**** UNIFORM ****/ - /*****************/ - - virtual RID uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>()); - virtual RID storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>(), BitField<StorageBufferUsage> p_usage = 0); - virtual RID texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>()); - - virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set); - virtual bool uniform_set_is_valid(RID p_uniform_set); - virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata); - - virtual Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); // Works for any buffer. - virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - virtual Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0); - - /*************************/ - /**** RENDER PIPELINE ****/ - /*************************/ - - virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()); - virtual bool render_pipeline_is_valid(RID p_pipeline); - - /**************************/ - /**** COMPUTE PIPELINE ****/ - /**************************/ - - virtual RID compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()); - virtual bool compute_pipeline_is_valid(RID p_pipeline); - - /****************/ - /**** SCREEN ****/ - /****************/ - - virtual int screen_get_width(DisplayServer::WindowID p_screen = 0) const; - virtual int screen_get_height(DisplayServer::WindowID p_screen = 0) const; - virtual FramebufferFormatID screen_get_framebuffer_format() const; - - /********************/ - /**** DRAW LISTS ****/ - /********************/ - - virtual DrawListID draw_list_begin_for_screen(DisplayServer::WindowID p_screen = 0, const Color &p_clear_color = Color()); - - virtual DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); - virtual Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); - - virtual void draw_list_set_blend_constants(DrawListID p_list, const Color &p_color); - virtual void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline); - virtual void draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index); - virtual void draw_list_bind_vertex_array(DrawListID p_list, RID p_vertex_array); - virtual void draw_list_bind_index_array(DrawListID p_list, RID p_index_array); - virtual void draw_list_set_line_width(DrawListID p_list, float p_width); - virtual void draw_list_set_push_constant(DrawListID p_list, const void *p_data, uint32_t p_data_size); - - virtual void draw_list_draw(DrawListID p_list, bool p_use_indices, uint32_t p_instances = 1, uint32_t p_procedural_vertices = 0); - - virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect); - virtual void draw_list_disable_scissor(DrawListID p_list); - - virtual uint32_t draw_list_get_current_pass(); - virtual DrawListID draw_list_switch_to_next_pass(); - virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids); - - virtual void draw_list_end(BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - - /***********************/ - /**** COMPUTE LISTS ****/ - /***********************/ - - virtual ComputeListID compute_list_begin(bool p_allow_draw_overlap = false); - virtual void compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline); - virtual void compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index); - virtual void compute_list_set_push_constant(ComputeListID p_list, const void *p_data, uint32_t p_data_size); - virtual void compute_list_add_barrier(ComputeListID p_list); - - virtual void compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups); - virtual void compute_list_dispatch_threads(ComputeListID p_list, uint32_t p_x_threads, uint32_t p_y_threads, uint32_t p_z_threads); - virtual void compute_list_dispatch_indirect(ComputeListID p_list, RID p_buffer, uint32_t p_offset); - virtual void compute_list_end(BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - - virtual void barrier(BitField<BarrierMask> p_from = BARRIER_MASK_ALL_BARRIERS, BitField<BarrierMask> p_to = BARRIER_MASK_ALL_BARRIERS); - virtual void full_barrier(); - - /**************/ - /**** FREE ****/ - /**************/ - - virtual void free(RID p_id); - - /****************/ - /**** Timing ****/ - /****************/ - - virtual void capture_timestamp(const String &p_name); - virtual uint32_t get_captured_timestamps_count() const; - virtual uint64_t get_captured_timestamps_frame() const; - virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const; - virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const; - virtual String get_captured_timestamp_name(uint32_t p_index) const; - - /****************/ - /**** Limits ****/ - /****************/ - - virtual uint64_t limit_get(Limit p_limit) const; - - virtual void prepare_screen_for_drawing(); - void initialize(VulkanContext *p_context, bool p_local_device = false); - void finalize(); - - virtual void swap_buffers(); // For main device. - - virtual void submit(); // For local device. - virtual void sync(); // For local device. - - virtual uint32_t get_frame_delay() const; - - virtual RenderingDevice *create_local_device(); - - virtual uint64_t get_memory_usage(MemoryType p_type) const; - - virtual void set_resource_name(RID p_id, const String p_name); - - virtual void draw_command_begin_label(String p_label_name, const Color p_color = Color(1, 1, 1, 1)); - virtual void draw_command_insert_label(String p_label_name, const Color p_color = Color(1, 1, 1, 1)); - virtual void draw_command_end_label(); - - virtual String get_device_vendor_name() const; - virtual String get_device_name() const; - virtual RenderingDevice::DeviceType get_device_type() const; - virtual String get_device_api_version() const; - virtual String get_device_pipeline_cache_uuid() const; - - virtual uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0); - - virtual bool has_feature(const Features p_feature) const; - - RenderingDeviceVulkan(); - ~RenderingDeviceVulkan(); -}; - -#endif // RENDERING_DEVICE_VULKAN_H diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 7a397a170d..7db2a9cd66 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -412,7 +412,9 @@ Error VulkanContext::_initialize_instance_extensions() { // Make sure our core extensions are here register_requested_instance_extension(VK_KHR_SURFACE_EXTENSION_NAME, true); - register_requested_instance_extension(_get_platform_surface_extension(), 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); @@ -1232,21 +1234,24 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { bool present_supported = false; - uint32_t device_queue_family_count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, nullptr); - VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties)); - vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props); - for (uint32_t j = 0; j < device_queue_family_count; j++) { - 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; + 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"; @@ -1276,10 +1281,9 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { } vendor_idx++; } - free(device_queue_props); print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + dev_type); - if (present_supported) { // Select first supported device of preferred type: Discrete > Integrated > Virtual > CPU > Other. + 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) { @@ -1509,7 +1513,7 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { return OK; } -Error VulkanContext::_create_device() { +Error VulkanContext::_create_device(VkDevice &r_vk_device) { VkResult err; float queue_priorities[1] = { 0.0 }; VkDeviceQueueCreateInfo queues[2]; @@ -1624,11 +1628,11 @@ Error VulkanContext::_create_device() { } if (vulkan_hooks) { - if (!vulkan_hooks->create_vulkan_device(&sdevice, &device)) { + if (!vulkan_hooks->create_vulkan_device(&sdevice, &r_vk_device)) { return ERR_CANT_CREATE; } } else { - err = vkCreateDevice(gpu, &sdevice, nullptr, &device); + err = vkCreateDevice(gpu, &sdevice, nullptr, &r_vk_device); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); } @@ -1637,9 +1641,13 @@ Error VulkanContext::_create_device() { Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { // Iterate over each queue to learn whether it supports presenting: - VkBool32 *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]); + 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 @@ -1652,7 +1660,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { graphicsQueueFamilyIndex = i; } - if (supportsPresent[i] == VK_TRUE) { + if (p_surface && supportsPresent[i] == VK_TRUE) { graphicsQueueFamilyIndex = i; presentQueueFamilyIndex = i; break; @@ -1660,7 +1668,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { } } - if (presentQueueFamilyIndex == UINT32_MAX) { + 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) { @@ -1671,17 +1679,22 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { } } - free(supportsPresent); + 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"); + // 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); + 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(); + _create_device(device); + driver = memnew(RenderingDeviceDriverVulkan(this, device)); static PFN_vkGetDeviceProcAddr g_gdpa = nullptr; #define GET_DEVICE_PROC_ADDR(dev, entrypoint) \ @@ -1705,60 +1718,62 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { vkGetDeviceQueue(device, graphics_queue_family_index, 0, &graphics_queue); - 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 (p_surface) { + if (!separate_present_queue) { + present_queue = graphics_queue; + } else { + vkGetDeviceQueue(device, present_queue_family_index, 0, &present_queue); + } - if (formatCount < 1) { + // 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_MSG(ERR_CANT_CREATE, "formatCount less than 1"); + 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; + // 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."); + if (format == VK_FORMAT_UNDEFINED) { + free(surfFormats); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "No usable surface format found."); + } } - } - free(surfFormats); + free(surfFormats); + } Error serr = _create_semaphores(); if (serr) { @@ -1830,6 +1845,8 @@ VkExtent2D VulkanContext::_compute_swapchain_extent(const VkSurfaceCapabilitiesK } 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) { @@ -1880,22 +1897,20 @@ bool VulkanContext::window_is_valid_swapchain(DisplayServer::WindowID p_window) return w->swapchain_image_resources != VK_NULL_HANDLE; } -VkRenderPass VulkanContext::window_get_render_pass(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), 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]; - // Vulkan use of currentbuffer. - return w->render_pass; + return (RDD::RenderPassID)w->render_pass; } -VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), VK_NULL_HANDLE); - ERR_FAIL_COND_V(!buffers_prepared, VK_NULL_HANDLE); +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]; - // Vulkan use of currentbuffer. if (w->swapchain_image_resources != VK_NULL_HANDLE) { - return w->swapchain_image_resources[w->current_buffer].framebuffer; + return (RDD::FramebufferID)w->swapchain_image_resources[w->current_buffer].framebuffer; } else { - return VK_NULL_HANDLE; + return RDD::FramebufferID(); } } @@ -2235,7 +2250,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { /*pPreserveAttachments*/ nullptr, }; - const VkRenderPassCreateInfo2KHR rp_info = { + const VkRenderPassCreateInfo2KHR pass_info = { /*sType*/ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR, /*pNext*/ nullptr, /*flags*/ 0, @@ -2249,7 +2264,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { /*pCorrelatedViewMasks*/ nullptr, }; - err = vkCreateRenderPass2KHR(device, &rp_info, nullptr, &window->render_pass); + 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++) { @@ -2351,25 +2366,40 @@ Error VulkanContext::initialize() { 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(VkCommandBuffer p_command_buffer) { - command_buffer_queue.write[0] = p_command_buffer; +void VulkanContext::set_setup_buffer(RDD::CommandBufferID p_command_buffer) { + command_buffer_queue[0] = (VkCommandBuffer)p_command_buffer.id; } -void VulkanContext::append_command_buffer(VkCommandBuffer p_command_buffer) { +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.write[command_buffer_count] = p_command_buffer; + 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) { +void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending, bool p_sync) { // Ensure everything else pending is executed. - vkDeviceWaitIdle(device); + if (p_sync) { + vkDeviceWaitIdle(device); + } // Flush the pending setup buffer. @@ -2389,7 +2419,7 @@ void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending) { 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.write[0] = nullptr; + command_buffer_queue[0] = nullptr; ERR_FAIL_COND(err); } @@ -2412,10 +2442,12 @@ void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending) { ERR_FAIL_COND(err); } - vkDeviceWaitIdle(device); + if (p_sync) { + vkDeviceWaitIdle(device); + } } -Error VulkanContext::prepare_buffers() { +Error VulkanContext::prepare_buffers(RDD::CommandBufferID p_command_buffer) { if (!queues_initialized) { return OK; } @@ -2468,12 +2500,15 @@ Error VulkanContext::prepare_buffers() { return OK; } +void VulkanContext::postpare_buffers(RDD::CommandBufferID p_command_buffer) { +} + Error VulkanContext::swap_buffers() { if (!queues_initialized) { return OK; } - // print_line("swapbuffers?"); + //print_line("swap_buffers"); VkResult err; #if 0 @@ -2535,7 +2570,7 @@ Error VulkanContext::swap_buffers() { 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.write[0] = nullptr; + command_buffer_queue[0] = nullptr; command_buffer_count = 1; if (separate_present_queue) { @@ -2690,6 +2725,14 @@ Error VulkanContext::swap_buffers() { 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; } @@ -2714,61 +2757,27 @@ VkFormat VulkanContext::get_screen_format() const { return format; } -VkPhysicalDeviceLimits VulkanContext::get_device_limits() const { +const VkPhysicalDeviceLimits &VulkanContext::get_device_limits() const { return gpu_props.limits; } RID VulkanContext::local_device_create() { LocalDevice ld; - { // Create 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; - - uint32_t enabled_extension_count = 0; - const char *enabled_extension_names[MAX_EXTENSIONS]; - ERR_FAIL_COND_V(enabled_device_extension_names.size() > MAX_EXTENSIONS, RID()); - 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 */ nullptr, - /*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. - }; - err = vkCreateDevice(gpu, &sdevice, nullptr, &ld.device); - ERR_FAIL_COND_V(err, RID()); - } + 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); } - return local_device_owner.make_rid(ld); -} + ld.driver = memnew(RenderingDeviceDriverVulkan(this, ld.device)); -VkDevice VulkanContext::local_device_get_vk_device(RID p_local_device) { - LocalDevice *ld = local_device_owner.get_or_null(p_local_device); - return ld->device; + return local_device_owner.make_rid(ld); } -void VulkanContext::local_device_push_command_buffers(RID p_local_device, const VkCommandBuffer *p_buffers, int p_count) { +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); @@ -2779,7 +2788,7 @@ void VulkanContext::local_device_push_command_buffers(RID p_local_device, const submit_info.waitSemaphoreCount = 0; submit_info.pWaitSemaphores = nullptr; submit_info.commandBufferCount = p_count; - submit_info.pCommandBuffers = p_buffers; + submit_info.pCommandBuffers = (const VkCommandBuffer *)p_buffers; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = nullptr; @@ -2808,50 +2817,11 @@ void VulkanContext::local_device_sync(RID p_local_device) { 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::command_begin_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color) { - if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - return; - } - - CharString cs = p_label_name.utf8(); - VkDebugUtilsLabelEXT label; - label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; - label.pNext = nullptr; - label.pLabelName = cs.get_data(); - label.color[0] = p_color[0]; - label.color[1] = p_color[1]; - label.color[2] = p_color[2]; - label.color[3] = p_color[3]; - CmdBeginDebugUtilsLabelEXT(p_command_buffer, &label); -} - -void VulkanContext::command_insert_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color) { - if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - return; - } - CharString cs = p_label_name.utf8(); - VkDebugUtilsLabelEXT label; - label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; - label.pNext = nullptr; - label.pLabelName = cs.get_data(); - label.color[0] = p_color[0]; - label.color[1] = p_color[1]; - label.color[2] = p_color[2]; - label.color[3] = p_color[3]; - CmdInsertDebugUtilsLabelEXT(p_command_buffer, &label); -} - -void VulkanContext::command_end_label(VkCommandBuffer p_command_buffer) { - if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - return; - } - CmdEndDebugUtilsLabelEXT(p_command_buffer); -} - 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; @@ -2897,12 +2867,29 @@ void VulkanContext::set_vsync_mode(DisplayServer::WindowID p_window, DisplayServ _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.write[0] = nullptr; + command_buffer_queue[0] = nullptr; } VulkanContext::~VulkanContext() { + if (driver) { + memdelete(driver); + } if (queue_props) { free(queue_props); } diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index 2ccfd13739..cbb6cf326f 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -37,8 +37,9 @@ #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/rendering_device.h" +#include "servers/rendering/renderer_rd/api_context_rd.h" #ifdef USE_VOLK #include <volk.h> @@ -48,7 +49,7 @@ #include "vulkan_hooks.h" -class VulkanContext { +class VulkanContext : public ApiContextRD { public: struct SubgroupCapabilities { uint32_t size; @@ -65,14 +66,6 @@ public: String supported_operations_desc() const; }; - struct MultiviewCapabilities { - bool is_supported; - bool geometry_shader_is_supported; - bool tessellation_shader_is_supported; - uint32_t max_view_count; - uint32_t max_instance_count; - }; - 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. @@ -115,7 +108,7 @@ private: uint32_t instance_api_version = VK_API_VERSION_1_0; SubgroupCapabilities subgroup_capabilities; - MultiviewCapabilities multiview_capabilities; + RDD::MultiviewCapabilities multiview_capabilities; VRSCapabilities vrs_capabilities; ShaderCapabilities shader_capabilities; StorageBufferCapabilities storage_buffer_capabilities; @@ -171,10 +164,13 @@ private: bool waiting = false; VkDevice device = VK_NULL_HANDLE; VkQueue queue = VK_NULL_HANDLE; + RenderingDeviceDriverVulkan *driver = nullptr; }; RID_Owner<LocalDevice, true> local_device_owner; + RenderingDeviceDriverVulkan *driver = nullptr; + HashMap<DisplayServer::WindowID, Window> windows; uint32_t swapchainImageCount = 0; @@ -182,8 +178,8 @@ private: bool prepared = false; - Vector<VkCommandBuffer> command_buffer_queue; - int command_buffer_count = 1; + LocalVector<VkCommandBuffer> command_buffer_queue; + uint32_t command_buffer_count = 1; // Extensions. static bool instance_extensions_initialized; @@ -250,7 +246,7 @@ private: Error _initialize_queues(VkSurfaceKHR p_surface); - Error _create_device(); + Error _create_device(VkDevice &r_vk_device); Error _clean_up_swap_chain(Window *window); @@ -262,7 +258,7 @@ private: Vector<VkAttachmentReference> _convert_VkAttachmentReference2(uint32_t p_count, const VkAttachmentReference2 *p_refs); protected: - virtual const char *_get_platform_surface_extension() const = 0; + 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); @@ -277,10 +273,10 @@ public: 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); - uint32_t get_vulkan_major() const { return VK_API_VERSION_MAJOR(device_api_version); }; - uint32_t get_vulkan_minor() const { return VK_API_VERSION_MINOR(device_api_version); }; + 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; }; - const MultiviewCapabilities &get_multiview_capabilities() const { return multiview_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; }; @@ -290,7 +286,7 @@ public: VkDevice get_device(); VkPhysicalDevice get_physical_device(); VkInstance get_instance() { return inst; } - int get_swapchain_image_count() const; + virtual int get_swapchain_image_count() const override final; VkQueue get_graphics_queue() const; uint32_t get_graphics_queue_family_index() const; @@ -306,44 +302,44 @@ public: return enabled_device_extension_names.has(extension_name); } - void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); - int window_get_width(DisplayServer::WindowID p_window = 0); - int window_get_height(DisplayServer::WindowID p_window = 0); - bool window_is_valid_swapchain(DisplayServer::WindowID p_window = 0); - void window_destroy(DisplayServer::WindowID p_window_id); - VkFramebuffer window_get_framebuffer(DisplayServer::WindowID p_window = 0); - VkRenderPass window_get_render_pass(DisplayServer::WindowID p_window = 0); + 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; - RID local_device_create(); - VkDevice local_device_get_vk_device(RID p_local_device); - void local_device_push_command_buffers(RID p_local_device, const VkCommandBuffer *p_buffers, int p_count); - void local_device_sync(RID p_local_device); - void local_device_free(RID p_local_device); + 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; - VkPhysicalDeviceLimits get_device_limits() const; + const VkPhysicalDeviceLimits &get_device_limits() const; - void set_setup_buffer(VkCommandBuffer p_command_buffer); - void append_command_buffer(VkCommandBuffer p_command_buffer); + 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(); - void flush(bool p_flush_setup = false, bool p_flush_pending = false); - Error prepare_buffers(); - Error swap_buffers(); - Error initialize(); - - void command_begin_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color); - void command_insert_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color); - void command_end_label(VkCommandBuffer p_command_buffer); + 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); - String get_device_vendor_name() const; - String get_device_name() const; - RenderingDevice::DeviceType get_device_type() const; - String get_device_api_version() const; - String get_device_pipeline_cache_uuid() const; + 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; - void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode); - DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const; + virtual RenderingDeviceDriver *get_driver(RID p_local_device = RID()) override final; + virtual bool is_debug_utils_enabled() const override final; VulkanContext(); virtual ~VulkanContext(); diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index e39373e7a0..64f2d1f203 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -557,13 +557,11 @@ Error AudioDriverWASAPI::init() { target_latency_ms = Engine::get_singleton()->get_audio_output_latency(); - Error err = init_output_device(); - if (err != OK) { - ERR_PRINT("WASAPI: init_output_device error"); - } - exit_thread.clear(); + Error err = init_output_device(); + ERR_FAIL_COND_V_MSG(err != OK, err, "WASAPI: init_output_device error."); + thread.start(thread_func, this); return OK; diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 9d21073f19..1b69f5da2c 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -284,6 +284,72 @@ uint8_t FileAccessWindows::get_8() const { return b; } +uint16_t FileAccessWindows::get_16() const { + ERR_FAIL_NULL_V(f, 0); + + if (flags == READ_WRITE || flags == WRITE_READ) { + if (prev_op == WRITE) { + fflush(f); + } + prev_op = READ; + } + + uint16_t b = 0; + if (fread(&b, 1, 2, f) != 2) { + check_errors(); + } + + if (big_endian) { + b = BSWAP16(b); + } + + return b; +} + +uint32_t FileAccessWindows::get_32() const { + ERR_FAIL_NULL_V(f, 0); + + if (flags == READ_WRITE || flags == WRITE_READ) { + if (prev_op == WRITE) { + fflush(f); + } + prev_op = READ; + } + + uint32_t b = 0; + if (fread(&b, 1, 4, f) != 4) { + check_errors(); + } + + if (big_endian) { + b = BSWAP32(b); + } + + return b; +} + +uint64_t FileAccessWindows::get_64() const { + ERR_FAIL_NULL_V(f, 0); + + if (flags == READ_WRITE || flags == WRITE_READ) { + if (prev_op == WRITE) { + fflush(f); + } + prev_op = READ; + } + + uint64_t b = 0; + if (fread(&b, 1, 8, f) != 8) { + check_errors(); + } + + if (big_endian) { + b = BSWAP64(b); + } + + return b; +} + uint64_t FileAccessWindows::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); ERR_FAIL_NULL_V(f, -1); @@ -326,6 +392,63 @@ void FileAccessWindows::store_8(uint8_t p_dest) { fwrite(&p_dest, 1, 1, f); } +void FileAccessWindows::store_16(uint16_t p_dest) { + ERR_FAIL_NULL(f); + + if (flags == READ_WRITE || flags == WRITE_READ) { + if (prev_op == READ) { + if (last_error != ERR_FILE_EOF) { + fseek(f, 0, SEEK_CUR); + } + } + prev_op = WRITE; + } + + if (big_endian) { + p_dest = BSWAP16(p_dest); + } + + fwrite(&p_dest, 1, 2, f); +} + +void FileAccessWindows::store_32(uint32_t p_dest) { + ERR_FAIL_NULL(f); + + if (flags == READ_WRITE || flags == WRITE_READ) { + if (prev_op == READ) { + if (last_error != ERR_FILE_EOF) { + fseek(f, 0, SEEK_CUR); + } + } + prev_op = WRITE; + } + + if (big_endian) { + p_dest = BSWAP32(p_dest); + } + + fwrite(&p_dest, 1, 4, f); +} + +void FileAccessWindows::store_64(uint64_t p_dest) { + ERR_FAIL_NULL(f); + + if (flags == READ_WRITE || flags == WRITE_READ) { + if (prev_op == READ) { + if (last_error != ERR_FILE_EOF) { + fseek(f, 0, SEEK_CUR); + } + } + prev_op = WRITE; + } + + if (big_endian) { + p_dest = BSWAP64(p_dest); + } + + fwrite(&p_dest, 1, 8, f); +} + void FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_NULL(f); ERR_FAIL_COND(!p_src && p_length > 0); diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 73143009fc..dabb174cb2 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -69,12 +69,18 @@ public: virtual bool eof_reached() const override; ///< reading passed EOF virtual uint8_t get_8() const override; ///< get a byte + virtual uint16_t get_16() const override; + virtual uint32_t get_32() const override; + virtual uint64_t get_64() const override; virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; ///< get last error virtual void flush() override; virtual void store_8(uint8_t p_dest) override; ///< store a byte + virtual void store_16(uint16_t p_dest) override; + virtual void store_32(uint32_t p_dest) override; + virtual void store_64(uint64_t p_dest) override; virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_name) override; ///< return true if a file exists |
