diff options
| author | Pedro J. Estébanez <pedrojrulez@gmail.com> | 2023-12-19 12:48:02 +0100 |
|---|---|---|
| committer | Pedro J. Estébanez <pedrojrulez@gmail.com> | 2023-12-20 19:18:08 +0100 |
| commit | 12a519bae232e2e4a37a243a4050edaa0d802687 (patch) | |
| tree | 0a0bdd4d2bc8ecf34b7382c08c5072bf2ee8001d /drivers | |
| parent | 0567c5df9f6fbd3f3237de38b36a8474a9a9b03b (diff) | |
| download | redot-engine-12a519bae232e2e4a37a243a4050edaa0d802687.tar.gz | |
Split RenderingDevice into API-agnostic and RenderingDeviceDriver parts
Credit and thanks to @bruzvg for multiple build fixes, update of 3rd-party items and MinGW support.
Co-authored-by: bruvzg <7645683+bruvzg@users.noreply.github.com>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/d3d12/SCsub | 36 | ||||
| -rw-r--r-- | drivers/d3d12/d3d12_context.cpp | 244 | ||||
| -rw-r--r-- | drivers/d3d12/d3d12_context.h | 133 | ||||
| -rw-r--r-- | drivers/d3d12/d3d12ma.cpp | 17 | ||||
| -rw-r--r-- | drivers/d3d12/rendering_device_d3d12.cpp | 9480 | ||||
| -rw-r--r-- | drivers/d3d12/rendering_device_d3d12.h | 1277 | ||||
| -rw-r--r-- | drivers/d3d12/rendering_device_driver_d3d12.cpp | 5491 | ||||
| -rw-r--r-- | drivers/d3d12/rendering_device_driver_d3d12.h | 858 | ||||
| -rw-r--r-- | drivers/vulkan/SCsub | 2 | ||||
| -rw-r--r-- | drivers/vulkan/rendering_device_driver_vulkan.cpp | 3350 | ||||
| -rw-r--r-- | drivers/vulkan/rendering_device_driver_vulkan.h | 482 | ||||
| -rw-r--r-- | drivers/vulkan/rendering_device_vulkan.cpp | 9765 | ||||
| -rw-r--r-- | drivers/vulkan/rendering_device_vulkan.h | 1293 | ||||
| -rw-r--r-- | drivers/vulkan/vulkan_context.cpp | 307 | ||||
| -rw-r--r-- | drivers/vulkan/vulkan_context.h | 96 |
15 files changed, 10655 insertions, 22176 deletions
diff --git a/drivers/d3d12/SCsub b/drivers/d3d12/SCsub index ce7134fb77..11ae52fd40 100644 --- a/drivers/d3d12/SCsub +++ b/drivers/d3d12/SCsub @@ -5,36 +5,39 @@ from pathlib import Path Import("env") -env_d3d12_rd = env.Clone() +env.Append(CPPDEFINES=["RD_ENABLED"]) + +env_d3d12_rdd = env.Clone() thirdparty_obj = [] # DirectX Headers (must take precedence over Windows SDK's). -env.Prepend(CPPPATH=["#thirdparty/directx_headers"]) -env_d3d12_rd.Prepend(CPPPATH=["#thirdparty/directx_headers"]) +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_rd.Append(CPPPATH=["#thirdparty/d3d12ma"]) +env_d3d12_rdd.Append(CPPPATH=["#thirdparty/d3d12ma"]) # Agility SDK. if env["agility_sdk_path"] != "": - env_d3d12_rd.Append(CPPDEFINES=["AGILITY_SDK_ENABLED"]) + env_d3d12_rdd.Append(CPPDEFINES=["AGILITY_SDK_ENABLED"]) if env["agility_sdk_multiarch"]: - env_d3d12_rd.Append(CPPDEFINES=["AGILITY_SDK_MULTIARCH_ENABLED"]) + env_d3d12_rdd.Append(CPPDEFINES=["AGILITY_SDK_MULTIARCH_ENABLED"]) # PIX. if env["pix_path"] != "": - env_d3d12_rd.Append(CPPDEFINES=["PIX_ENABLED"]) - env_d3d12_rd.Append(CPPPATH=[env["pix_path"] + "/Include"]) + env_d3d12_rdd.Append(CPPDEFINES=["PIX_ENABLED"]) + env_d3d12_rdd.Append(CPPPATH=[env["pix_path"] + "/Include"]) # Mesa (SPIR-V to DXIL functionality). @@ -105,12 +108,16 @@ 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", '\\"' + Path(mesa_absdir + "/VERSION").read_text().strip() + '\\"'), + ("PACKAGE_VERSION", '\\"' + mesa_ver.read_text().strip() + '\\"'), ("PACKAGE_BUGREPORT", '\\"https://gitlab.freedesktop.org/mesa/mesa/-/issues\\"'), "PIPE_SUBSYSTEM_WINDOWS_USER", ("_Static_assert", "static_assert"), @@ -129,11 +136,16 @@ if env.msvc: "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_rd.Prepend(CPPPATH=mesa_private_inc_paths) +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_rd.Append(CPPDEFINES=extra_defines) +env_d3d12_rdd.Append(CPPDEFINES=extra_defines) # Add all. @@ -144,7 +156,7 @@ env.drivers_sources += thirdparty_obj # Godot source files. driver_obj = [] -env_d3d12_rd.add_source_files(driver_obj, "*.cpp") +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. diff --git a/drivers/d3d12/d3d12_context.cpp b/drivers/d3d12/d3d12_context.cpp index 6933089208..36492b198b 100644 --- a/drivers/d3d12/d3d12_context.cpp +++ b/drivers/d3d12/d3d12_context.cpp @@ -37,8 +37,26 @@ #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 + extern "C" { char godot_nir_arch_name[32]; @@ -47,8 +65,12 @@ __declspec(dllexport) extern const UINT D3D12SDKVersion = 610; #ifdef AGILITY_SDK_MULTIARCH_ENABLED #if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) __declspec(dllexport) extern const char *D3D12SDKPath = "\\.\\arm64"; -#else +#elif defined(__arm__) || defined(_M_ARM) +__declspec(dllexport) extern const char *D3D12SDKPath = "\\.\\arm32"; +#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) __declspec(dllexport) extern const char *D3D12SDKPath = "\\.\\x86_64"; +#elif defined(__i386) || defined(__i386__) || defined(_M_IX86) +__declspec(dllexport) extern const char *D3D12SDKPath = "\\.\\x86_32"; #endif #else __declspec(dllexport) extern const char *D3D12SDKPath = "\\."; @@ -57,11 +79,15 @@ __declspec(dllexport) extern const char *D3D12SDKPath = "\\."; } #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 ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) void D3D12Context::_debug_message_func( D3D12_MESSAGE_CATEGORY p_category, @@ -178,7 +204,7 @@ Error D3D12Context::_check_capabilities() { D3D12_FEATURE_DATA_SHADER_MODEL shader_model = {}; shader_model.HighestShaderModel = MIN(D3D_HIGHEST_SHADER_MODEL, D3D_SHADER_MODEL_6_6); HRESULT res = md.device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shader_model, sizeof(shader_model)); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); + 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:"); @@ -270,7 +296,7 @@ Error D3D12Context::_check_capabilities() { Error D3D12Context::_initialize_debug_layers() { ComPtr<ID3D12Debug> debug_controller; HRESULT res = D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)); - ERR_FAIL_COND_V(res, ERR_QUERY_FAILED); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_QUERY_FAILED); debug_controller->EnableDebugLayer(); return OK; } @@ -399,7 +425,7 @@ Error D3D12Context::_select_adapter(int &r_index) { if (SUCCEEDED(res)) { tearing_supported = result; } else { - ERR_PRINT("CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); + ERR_PRINT("CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); } } @@ -423,7 +449,7 @@ void D3D12Context::_dump_adapter_info(int p_index) { feat_levels.pFeatureLevelsRequested = FEATURE_LEVELS; HRESULT res = md.device->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &feat_levels, sizeof(feat_levels)); - ERR_FAIL_COND_MSG(res, "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); + 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; @@ -476,25 +502,25 @@ void D3D12Context::_dump_adapter_info(int p_index) { Error D3D12Context::_create_device(DeviceBasics &r_basics) { HRESULT res = D3D12CreateDevice(gpu.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(r_basics.device.GetAddressOf())); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "D3D12CreateDevice failed with error " + vformat("0x%08ux", res) + "."); + 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(res, ERR_CANT_CREATE); + 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(res, ERR_CANT_CREATE); + 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(res, ERR_CANT_CREATE); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); #if 0 // This causes crashes. Needs investigation. ComPtr<ID3D12InfoQueue1> info_queue_1; @@ -505,7 +531,7 @@ Error D3D12Context::_create_device(DeviceBasics &r_basics) { info_queue_1->SetMuteDebugOutput(TRUE); res = info_queue_1->RegisterMessageCallback(&_debug_message_func, D3D12_MESSAGE_CALLBACK_IGNORE_FILTERS, nullptr, 0); - ERR_FAIL_COND_V(res, ERR_CANT_CREATE); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); } else #endif { @@ -513,7 +539,11 @@ Error D3D12Context::_create_device(DeviceBasics &r_basics) { if (Engine::get_singleton()->is_abort_on_gpu_errors_enabled()) { res = info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE); - ERR_FAIL_COND_V(res, ERR_CANT_CREATE); + 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[] = { @@ -535,7 +565,7 @@ Error D3D12Context::_create_device(DeviceBasics &r_basics) { filter.DenyList.pIDList = messages_to_mute; res = info_queue->PushStorageFilter(&filter); - ERR_FAIL_COND_V(res, ERR_CANT_CREATE); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); } return OK; @@ -544,7 +574,7 @@ Error D3D12Context::_create_device(DeviceBasics &r_basics) { Error D3D12Context::_get_device_limits() { D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; HRESULT res = md.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)); - ERR_FAIL_COND_V_MSG(res, ERR_UNAVAILABLE, "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); + 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; @@ -567,18 +597,44 @@ bool D3D12Context::_use_validation_layers() { return Engine::get_singleton()->is_validation_layers_enabled(); } -Error D3D12Context::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) { +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 = p_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; } @@ -605,25 +661,20 @@ bool D3D12Context::window_is_valid_swapchain(DisplayServer::WindowID p_window) { return (bool)w->swapchain; } -CD3DX12_CPU_DESCRIPTOR_HANDLE D3D12Context::window_get_framebuffer_rtv_handle(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), CD3DX12_CPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT())); - ERR_FAIL_COND_V(!buffers_prepared, CD3DX12_CPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT())); +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]; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_handle( - w->rtv_heap->GetCPUDescriptorHandleForHeapStart(), - w->current_buffer, - md.device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)); - return rtv_handle; + return RDD::RenderPassID(&w->render_pass); } -ID3D12Resource *D3D12Context::window_get_framebuffer_texture(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), nullptr); - ERR_FAIL_COND_V(!buffers_prepared, nullptr); +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 w->render_targets[w->current_buffer].Get(); + return RDD::FramebufferID(&w->framebuffers[w->current_buffer]); } else { - return nullptr; + return RDD::FramebufferID(); } } @@ -699,7 +750,6 @@ Error D3D12Context::_update_swap_chain(Window *window) { for (uint32_t i = 0; i < IMAGE_COUNT; i++) { window->render_targets[i].Reset(); } - window->rtv_heap.Reset(); // D3D12 docs: "IDXGISwapChain::ResizeBuffers can't be used to add or remove this flag." bool allow_tearing_flag_changed = (swapchain_flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) != (window->swapchain_flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING); @@ -722,45 +772,37 @@ Error D3D12Context::_update_swap_chain(Window *window) { ComPtr<IDXGISwapChain1> swapchain; HRESULT res = dxgi_factory->CreateSwapChainForHwnd(md.queue.Get(), window->hwnd, &swapchain_desc, nullptr, nullptr, swapchain.GetAddressOf()); - ERR_FAIL_COND_V(res, ERR_CANT_CREATE); + 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(res, ERR_CANT_CREATE); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); res = window->swapchain->GetDesc1(&swapchain_desc); - ERR_FAIL_COND_V(res, ERR_CANT_CREATE); + 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(res, ERR_UNAVAILABLE); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_UNAVAILABLE); } window->swapchain_flags = swapchain_flags; window->current_buffer = window->swapchain->GetCurrentBackBufferIndex(); - // Describe and create a render target view (RTV) descriptor heap. - D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc = {}; - rtv_heap_desc.NumDescriptors = IMAGE_COUNT; - rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - rtv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - HRESULT res = md.device->CreateDescriptorHeap(&rtv_heap_desc, IID_PPV_ARGS(window->rtv_heap.GetAddressOf())); - ERR_FAIL_COND_V(res, ERR_CANT_CREATE); - - CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_handle(window->rtv_heap->GetCPUDescriptorHandleForHeapStart()); - for (uint32_t i = 0; i < IMAGE_COUNT; i++) { - res = window->swapchain->GetBuffer(i, IID_PPV_ARGS(&window->render_targets[i])); - ERR_FAIL_COND_V(res, ERR_CANT_CREATE); + 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, rtv_handle); - rtv_handle.Offset(1, md.device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)); + md.device->CreateRenderTargetView(window->render_targets[i].Get(), nullptr, walker.get_curr_cpu_handle()); } return OK; @@ -790,33 +832,28 @@ Error D3D12Context::initialize() { { HRESULT res = md.device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(frame_fence.GetAddressOf())); - ERR_FAIL_COND_V(res, ERR_CANT_CREATE); + 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); } - { // Initialize allocator. - D3D12MA::ALLOCATOR_DESC allocator_desc = {}; - allocator_desc.pDevice = md.device.Get(); - allocator_desc.pAdapter = gpu.Get(); - - HRESULT res = D3D12MA::CreateAllocator(&allocator_desc, &allocator); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "D3D12MA::CreateAllocator failed with error " + vformat("0x%08ux", res) + "."); - } + md.driver = memnew(RenderingDeviceDriverD3D12(this, md.device.Get(), IMAGE_COUNT + 1)); return OK; } -void D3D12Context::set_setup_list(ID3D12CommandList *p_command_list) { - command_list_queue.write[0] = p_command_list; +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_list(ID3D12CommandList *p_command_list) { +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); } - command_list_queue.write[command_list_count] = p_command_list; + 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++; } @@ -833,7 +870,7 @@ void D3D12Context::_wait_for_idle_queue(ID3D12CommandQueue *p_queue) { void D3D12Context::flush(bool p_flush_setup, bool p_flush_pending) { if (p_flush_setup && command_list_queue[0]) { md.queue->ExecuteCommandLists(1, command_list_queue.ptr()); - command_list_queue.write[0] = nullptr; + command_list_queue[0] = nullptr; } if (p_flush_pending && command_list_count > 1) { @@ -846,7 +883,7 @@ void D3D12Context::flush(bool p_flush_setup, bool p_flush_pending) { } } -void D3D12Context::prepare_buffers(ID3D12GraphicsCommandList *p_command_list) { +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; @@ -860,20 +897,21 @@ void D3D12Context::prepare_buffers(ID3D12GraphicsCommandList *p_command_list) { } D3D12_RESOURCE_BARRIER *barriers = (D3D12_RESOURCE_BARRIER *)alloca(windows.size() * sizeof(D3D12_RESOURCE_BARRIER)); - uint32_t n = 0; for (KeyValue<int, Window> &E : windows) { Window *w = &E.value; w->current_buffer = w->swapchain->GetCurrentBackBufferIndex(); barriers[n++] = CD3DX12_RESOURCE_BARRIER::Transition(w->render_targets[w->current_buffer].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET); } - - p_command_list->ResourceBarrier(n, barriers); + 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(ID3D12GraphicsCommandList *p_command_list) { +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; @@ -882,7 +920,8 @@ void D3D12Context::postpare_buffers(ID3D12GraphicsCommandList *p_command_list) { barriers[n++] = CD3DX12_RESOURCE_BARRIER::Transition(w->render_targets[w->current_buffer].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT); } - p_command_list->ResourceBarrier(n, barriers); + 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() { @@ -902,7 +941,7 @@ Error D3D12Context::swap_buffers() { md.queue->ExecuteCommandLists(commands_to_submit, commands_ptr); - command_list_queue.write[0] = nullptr; + command_list_queue[0] = nullptr; command_list_count = 1; for (KeyValue<int, Window> &E : windows) { @@ -912,8 +951,8 @@ Error D3D12Context::swap_buffers() { continue; } HRESULT res = w->swapchain->Present(w->sync_interval, w->present_flags); - if (res) { - print_verbose("D3D12: Presenting swapchain of window " + itos(E.key) + " failed with error " + vformat("0x%08ux", res) + "."); + if (!SUCCEEDED(res)) { + print_verbose("D3D12: Presenting swapchain of window " + itos(E.key) + " failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); } } @@ -927,16 +966,20 @@ Error D3D12Context::swap_buffers() { void D3D12Context::resize_notify() { } -ComPtr<ID3D12Device> D3D12Context::get_device() { - return md.device; +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; } -ComPtr<IDXGIAdapter> D3D12Context::get_adapter() { - return gpu; +ID3D12Device *D3D12Context::get_device() { + return md.device.Get(); } -D3D12MA::Allocator *D3D12Context::get_allocator() { - return allocator.Get(); +IDXGIAdapter *D3D12Context::get_adapter() { + return gpu.Get(); } int D3D12Context::get_swapchain_image_count() const { @@ -947,26 +990,22 @@ DXGI_FORMAT D3D12Context::get_screen_format() const { return format; } -D3D12Context::DeviceLimits D3D12Context::get_device_limits() const { +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); } -ComPtr<ID3D12Device> D3D12Context::local_device_get_d3d12_device(RID p_local_device) { - LocalDevice *ld = local_device_owner.get_or_null(p_local_device); - return ld->device; -} - -void D3D12Context::local_device_push_command_lists(RID p_local_device, ID3D12CommandList *const *p_lists, int p_count) { +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, p_lists); + ld->queue->ExecuteCommandLists(p_count, (ID3D12CommandList *const *)p_buffers); ld->waiting = true; } @@ -988,27 +1027,29 @@ void D3D12Context::local_device_sync(RID p_local_device) { 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::command_begin_label(ID3D12GraphicsCommandList *p_command_list, String p_label_name, const Color p_color) { +void D3D12Context::command_begin_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) { #ifdef PIX_ENABLED - PIXBeginEvent(p_command_list, p_color.to_argb32(), p_label_name.utf8().get_data()); + const RenderingDeviceDriverD3D12::CommandBufferInfo *cmd_buf_info = (const RenderingDeviceDriverD3D12::CommandBufferInfo *)p_command_buffer.id; + PIXBeginEvent(cmd_buf_info->cmd_list.Get(), p_color.to_argb32(), p_label_name.utf8().get_data()); #endif } -void D3D12Context::command_insert_label(ID3D12GraphicsCommandList *p_command_list, String p_label_name, const Color p_color) { +void D3D12Context::command_insert_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) { #ifdef PIX_ENABLED - PIXSetMarker(p_command_list, p_color.to_argb32(), p_label_name.utf8().get_data()); + const RenderingDeviceDriverD3D12::CommandBufferInfo *cmd_buf_info = (const RenderingDeviceDriverD3D12::CommandBufferInfo *)p_command_buffer.id; + PIXSetMarker(cmd_buf_info->cmd_list.Get(), p_color.to_argb32(), p_label_name.utf8().get_data()); #endif } -void D3D12Context::command_end_label(ID3D12GraphicsCommandList *p_command_list) { +void D3D12Context::command_end_label(RDD::CommandBufferID p_command_buffer) { #ifdef PIX_ENABLED - PIXEndEvent(p_command_list); + const RenderingDeviceDriverD3D12::CommandBufferInfo *cmd_buf_info = (const RenderingDeviceDriverD3D12::CommandBufferInfo *)p_command_buffer.id; + PIXEndEvent(cmd_buf_info->cmd_list.Get()); #endif } @@ -1050,11 +1091,22 @@ void D3D12Context::set_vsync_mode(DisplayServer::WindowID p_window, DisplayServe _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; + } +} + D3D12Context::D3D12Context() { command_list_queue.resize(1); // First one is always the setup command. - command_list_queue.write[0] = nullptr; + command_list_queue[0] = nullptr; - strcpy(godot_nir_arch_name, Engine::get_singleton()->get_architecture_name().ascii().get_data()); + CharString cs = Engine::get_singleton()->get_architecture_name().ascii(); + memcpy(godot_nir_arch_name, (const char *)cs.get_data(), cs.size()); } D3D12Context::~D3D12Context() { diff --git a/drivers/d3d12/d3d12_context.h b/drivers/d3d12/d3d12_context.h index 31e75affd0..ec4bc832b6 100644 --- a/drivers/d3d12/d3d12_context.h +++ b/drivers/d3d12/d3d12_context.h @@ -34,20 +34,38 @@ #include "core/error/error_list.h" #include "core/os/mutex.h" #include "core/string/ustring.h" -#include "core/templates/rb_map.h" #include "core/templates/rid_owner.h" +#include "rendering_device_driver_d3d12.h" #include "servers/display_server.h" -#include "servers/rendering/rendering_device.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> -#define D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED -#include "D3D12MemAlloc.h" #include <wrl/client.h> + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + using Microsoft::WRL::ComPtr; -class D3D12Context { +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +class D3D12Context : public ApiContextRD { public: struct DeviceLimits { uint64_t max_srvs_per_shader_stage; @@ -64,15 +82,6 @@ public: uint32_t supported_operations_flags_rd() const; }; - // Following VulkanContext definition. - 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 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. @@ -110,12 +119,13 @@ private: 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; - MultiviewCapabilities multiview_capabilities; + RDD::MultiviewCapabilities multiview_capabilities; VRSCapabilities vrs_capabilities; ShaderCapabilities shader_capabilities; StorageBufferCapabilities storage_buffer_capabilities; @@ -126,8 +136,6 @@ private: RenderingDevice::DeviceType adapter_type = {}; String pipeline_cache_id; - ComPtr<D3D12MA::Allocator> allocator; - bool buffers_prepared = false; DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; @@ -146,7 +154,8 @@ private: int width = 0; int height = 0; DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; - ComPtr<ID3D12DescriptorHeap> rtv_heap; + RenderingDeviceDriverD3D12::RenderPassInfo render_pass; + RenderingDeviceDriverD3D12::FramebufferInfo framebuffers[IMAGE_COUNT]; }; struct LocalDevice : public DeviceBasics { @@ -161,8 +170,8 @@ private: // Commands. - Vector<ID3D12CommandList *> command_list_queue; - int command_list_count = 1; + LocalVector<ID3D12CommandList *> command_list_queue; + uint32_t command_list_count = 1; static void _debug_message_func( D3D12_MESSAGE_CATEGORY p_category, @@ -187,59 +196,63 @@ protected: virtual bool _use_validation_layers(); public: - uint32_t get_feat_level_major() const { return feature_level / 10; }; - uint32_t get_feat_level_minor() const { return feature_level % 10; }; + 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; }; - 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; }; const FormatCapabilities &get_format_capabilities() const { return format_capabilities; }; - ComPtr<ID3D12Device> get_device(); - ComPtr<IDXGIAdapter> get_adapter(); - D3D12MA::Allocator *get_allocator(); - int get_swapchain_image_count() const; - Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height); - 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); - CD3DX12_CPU_DESCRIPTOR_HANDLE window_get_framebuffer_rtv_handle(DisplayServer::WindowID p_window = 0); - ID3D12Resource *window_get_framebuffer_texture(DisplayServer::WindowID p_window = 0); - - RID local_device_create(); - ComPtr<ID3D12Device> local_device_get_d3d12_device(RID p_local_device); - void local_device_push_command_lists(RID p_local_device, ID3D12CommandList *const *p_lists, int p_count); - void local_device_sync(RID p_local_device); - void local_device_free(RID p_local_device); + 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; - DeviceLimits get_device_limits() const; + const DeviceLimits &get_device_limits() const; - void set_setup_list(ID3D12CommandList *p_command_list); - void append_command_list(ID3D12CommandList *p_command_list); + 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); - void prepare_buffers(ID3D12GraphicsCommandList *p_command_list); - void postpare_buffers(ID3D12GraphicsCommandList *p_command_list); - Error swap_buffers(); - Error initialize(); - - void command_begin_label(ID3D12GraphicsCommandList *p_command_list, String p_label_name, const Color p_color); - void command_insert_label(ID3D12GraphicsCommandList *p_command_list, String p_label_name, const Color p_color); - void command_end_label(ID3D12GraphicsCommandList *p_command_list); + virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false) 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; + + virtual void command_begin_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) override final; + virtual void command_insert_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) override final; + virtual void command_end_label(RDD::CommandBufferID p_command_buffer) override final; void set_object_name(ID3D12Object *p_object, 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; D3D12Context(); virtual ~D3D12Context(); diff --git a/drivers/d3d12/d3d12ma.cpp b/drivers/d3d12/d3d12ma.cpp index 0ac2f71074..571ec952e7 100644 --- a/drivers/d3d12/d3d12ma.cpp +++ b/drivers/d3d12/d3d12ma.cpp @@ -30,5 +30,22 @@ #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_d3d12.cpp b/drivers/d3d12/rendering_device_d3d12.cpp deleted file mode 100644 index 839bcf6bc7..0000000000 --- a/drivers/d3d12/rendering_device_d3d12.cpp +++ /dev/null @@ -1,9480 +0,0 @@ -/**************************************************************************/ -/* rendering_device_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_d3d12.h" - -#include "core/config/project_settings.h" -#include "core/io/compression.h" -#include "core/io/file_access.h" -#include "core/io/marshalls.h" -#include "core/object/worker_thread_pool.h" -#include "core/os/os.h" -#include "core/templates/hashfuncs.h" -#include "d3d12_godot_nir_bridge.h" -#include "modules/regex/regex.h" -#include "thirdparty/zlib/zlib.h" - -#ifdef DEV_ENABLED -#include "core/crypto/hashing_context.h" -#endif - -// No point in fighting warnings in Mesa. -#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". -#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" -} -#pragma warning(pop) - -#define ALIGN(m_number, m_alignment) ((((m_number) + ((m_alignment)-1)) / (m_alignment)) * (m_alignment)) - -#ifdef USE_SMALL_ALLOCS_POOL -static const uint32_t SMALL_ALLOCATION_MAX_SIZE = 4096; -#endif - -static const D3D12_RANGE VOID_RANGE = {}; - -static const uint32_t MAX_VULKAN_SETS = 16; -static const uint32_t ROOT_CONSTANT_SPACE = MAX_VULKAN_SETS + 1; -static const uint32_t ROOT_CONSTANT_REGISTER = 0; -static const uint32_t RUNTIME_DATA_SPACE = MAX_VULKAN_SETS + 2; -static const uint32_t RUNTIME_DATA_REGISTER = 0; - -static const uint32_t MAX_IMAGE_FORMAT_PLANES = 2; - -#ifdef DEV_ENABLED -//#define DEBUG_COUNT_BARRIERS -#endif - -RenderingDeviceD3D12::Buffer *RenderingDeviceD3D12::_get_buffer_from_owner(RID p_buffer) { - Buffer *buffer = nullptr; - if (vertex_buffer_owner.owns(p_buffer)) { - buffer = vertex_buffer_owner.get_or_null(p_buffer); - } else if (index_buffer_owner.owns(p_buffer)) { - buffer = index_buffer_owner.get_or_null(p_buffer); - } else if (uniform_buffer_owner.owns(p_buffer)) { - buffer = uniform_buffer_owner.get_or_null(p_buffer); - } else if (texture_buffer_owner.owns(p_buffer)) { - 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); - } - return buffer; -} - -void RenderingDeviceD3D12::_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 RenderingDeviceD3D12::_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); - } -} - -// 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 RenderingDeviceD3D12::D3D12Format RenderingDeviceD3D12::d3d12_formats[RenderingDevice::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 */ {}, -}; - -const char *RenderingDeviceD3D12::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 RenderingDeviceD3D12::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 RenderingDeviceD3D12::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 RenderingDeviceD3D12::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: - 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: - r_w = 4; - r_h = 4; - return; - default: { - r_w = 1; - r_h = 1; - } - } -} - -uint32_t RenderingDeviceD3D12::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 8; // Wrong. - default: { - } - } - return 1; -} - -uint32_t RenderingDeviceD3D12::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; - default: { - } - } - - return 0; -} - -uint32_t RenderingDeviceD3D12::get_image_format_plane_count(DataFormat p_format) { - uint32_t planes = 1; - switch (p_format) { - case DATA_FORMAT_D16_UNORM_S8_UINT: - case DATA_FORMAT_D24_UNORM_S8_UINT: - case DATA_FORMAT_D32_SFLOAT_S8_UINT: { - planes = 2; - } - default: { - } - } - DEV_ASSERT(planes <= MAX_IMAGE_FORMAT_PLANES); - return planes; -} - -uint32_t RenderingDeviceD3D12::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 RenderingDeviceD3D12::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 D3D12_COMPARISON_FUNC RenderingDeviceD3D12::compare_operators[RenderingDevice::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, -}; - -const D3D12_STENCIL_OP RenderingDeviceD3D12::stencil_operations[RenderingDevice::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, -}; - -const UINT RenderingDeviceD3D12::rasterization_sample_count[RenderingDevice::TEXTURE_SAMPLES_MAX] = { - 1, - 2, - 4, - 8, - 16, - 32, - 64, -}; - -const D3D12_LOGIC_OP RenderingDeviceD3D12::logic_operations[RenderingDevice::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, -}; - -const D3D12_BLEND RenderingDeviceD3D12::blend_factors[RenderingDevice::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, -}; - -const D3D12_BLEND_OP RenderingDeviceD3D12::blend_operations[RenderingDevice::BLEND_OP_MAX] = { - D3D12_BLEND_OP_ADD, - D3D12_BLEND_OP_SUBTRACT, - D3D12_BLEND_OP_REV_SUBTRACT, - D3D12_BLEND_OP_MIN, - D3D12_BLEND_OP_MAX, -}; - -const D3D12_TEXTURE_ADDRESS_MODE RenderingDeviceD3D12::address_modes[RenderingDevice::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, -}; - -const FLOAT RenderingDeviceD3D12::sampler_border_colors[RenderingDevice::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 }, -}; - -const D3D12_RESOURCE_DIMENSION RenderingDeviceD3D12::d3d12_texture_dimension[RenderingDevice::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, -}; - -/******************/ -/**** RESOURCE ****/ -/******************/ - -static const D3D12_RESOURCE_STATES RESOURCE_READ_STATES = - D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER | - D3D12_RESOURCE_STATE_INDEX_BUFFER | - D3D12_RESOURCE_STATE_DEPTH_READ | - D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | - D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | - D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT | - D3D12_RESOURCE_STATE_COPY_SOURCE | - D3D12_RESOURCE_STATE_RESOLVE_SOURCE | - D3D12_RESOURCE_STATE_SHADING_RATE_SOURCE; - -static const D3D12_RESOURCE_STATES RESOURCE_WRITE_STATES = - D3D12_RESOURCE_STATE_RENDER_TARGET | - D3D12_RESOURCE_STATE_DEPTH_WRITE | - D3D12_RESOURCE_STATE_COPY_DEST | - D3D12_RESOURCE_STATE_RESOLVE_DEST; - -static const D3D12_RESOURCE_STATES RESOURCE_RW_STATES = - D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - -void RenderingDeviceD3D12::ResourceState::extend(D3D12_RESOURCE_STATES p_states_to_add) { - states |= p_states_to_add; - -#ifdef DEV_ENABLED - if ((states & RESOURCE_RW_STATES)) { - if ((states & RESOURCE_READ_STATES)) { - // Thanks to [[SRV_UAV_AMBIGUITY]], this is not necessarily an error. - } - if ((states & RESOURCE_WRITE_STATES)) { - ERR_PRINT("Error in new state mask: has R/W state plus some W/O state(s)."); - } - } else { - if ((states & RESOURCE_WRITE_STATES)) { - if ((states & RESOURCE_READ_STATES)) { - ERR_PRINT("Error in new state mask: mixes R/O and W/O states."); - } else { - uint32_t num_w_states = 0; - for (uint32_t i = 0; i < sizeof(D3D12_RESOURCE_STATES) * 8; i++) { - num_w_states += ((states & RESOURCE_WRITE_STATES) & (1 << i)) ? 1 : 0; - } - ERR_PRINT("Error in new state mask: has multiple W/O states."); - } - } - } -#endif -} - -void RenderingDeviceD3D12::_resource_transition_batch(Resource *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 - - Resource::States *res_states = p_resource->get_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 == ALIGN(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].state.get_state_mask() == 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].state.get_state_mask(); - 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; - ResourceState final_state; - if (subres_already_there) { - final_state = br.groups[curr_group_idx].state; - final_state.extend(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].state.get_state_mask() == BarrierRequest::DELETED_GROUP)) { - continue; - } - // There's another group with the final state; relocate to it. - if (br.groups[i].state.get_state_mask() == final_state.get_state_mask()) { - 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].state = ResourceState(BarrierRequest::DELETED_GROUP); - } - } else { - // Its current group, where it's alone, can extend its state. - br.groups[curr_group_idx].state = final_state; - } - } 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_state = p_new_state; - } - if (!subres_already_there) { - // See if it fits exactly the state 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].state.get_state_mask() == BarrierRequest::DELETED_GROUP)) { - continue; - } - if (br.groups[i].state.get_state_mask() == final_state.get_state_mask()) { - 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].state.get_state_mask() == BarrierRequest::DELETED_GROUP)) { - break; - } - } - CRASH_COND(group_to_fill == br.groups_count); - } - - br.groups[group_to_fill].state = final_state; - 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 = ALIGN(p_resource->get_states_ptr()->subresource_states.size(), 64) / 64; - CRASH_COND(p_resource->get_states_ptr()->subresource_states.size() > BarrierRequest::MAX_SUBRESOURCES); - br.planes = p_num_planes; - br.groups[0].state = 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 RenderingDeviceD3D12::_resource_transitions_flush(ID3D12GraphicsCommandList *p_command_list) { -#ifdef DEBUG_COUNT_BARRIERS - uint64_t start = OS::get_singleton()->get_ticks_usec(); -#endif - - for (const KeyValue<Resource::States *, BarrierRequest> &E : res_barriers_requests) { - Resource::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].state.get_state_mask()) { - res_barriers[res_barriers_count] = CD3DX12_RESOURCE_BARRIER::Transition(br.dx_resource, res_states->subresource_states[0], br.groups[0].state.get_state_mask(), 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].state.get_state_mask(); - } - } - } - } - - 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.state.get_state_mask() == 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.state.get_state_mask()) { - for (uint8_t k = 0; k < br.planes; k++) { - res_barriers[res_barriers_count] = CD3DX12_RESOURCE_BARRIER::Transition(br.dx_resource, *curr_state, g.state.get_state_mask(), subresource + k * num_subresources); - res_barriers_count++; - } - } - - *curr_state = g.state.get_state_mask(); - - subresource++; - } while (subresource < num_subresources); - } - } - } - - if (res_barriers_count) { - p_command_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++; -} - -/***************************/ -/**** BUFFER MANAGEMENT ****/ -/***************************/ - -Error RenderingDeviceD3D12::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, D3D12_RESOURCE_STATES p_usage, D3D12_HEAP_TYPE p_heap_type) { - ERR_FAIL_COND_V(p_heap_type != D3D12_HEAP_TYPE_DEFAULT && p_heap_type != D3D12_HEAP_TYPE_READBACK, ERR_INVALID_PARAMETER); - - // 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(ALIGN(p_size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT)); - if ((p_usage & D3D12_RESOURCE_STATE_UNORDERED_ACCESS)) { - resource_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - } - - D3D12MA::ALLOCATION_DESC allocation_desc = {}; - allocation_desc.HeapType = p_heap_type; -#ifdef USE_SMALL_ALLOCS_POOL - if (p_size <= SMALL_ALLOCATION_MAX_SIZE) { - allocation_desc.CustomPool = _find_or_create_small_allocs_pool(p_heap_type, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS); - } -#endif - - HRESULT res = context->get_allocator()->CreateResource( - &allocation_desc, - &resource_desc, - D3D12_RESOURCE_STATE_COPY_DEST, - nullptr, - &p_buffer->allocation, - IID_PPV_ARGS(&p_buffer->resource)); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "Can't create buffer of size: " + itos(p_size) + ", error " + vformat("0x%08ux", res) + "."); - - p_buffer->size = p_size; - p_buffer->usage = p_usage; - p_buffer->own_states.subresource_states.push_back(D3D12_RESOURCE_STATE_COPY_DEST); - - buffer_memory += p_size; - - return OK; -} - -Error RenderingDeviceD3D12::_buffer_free(Buffer *p_buffer) { - ERR_FAIL_COND_V(p_buffer->size == 0, ERR_INVALID_PARAMETER); - - buffer_memory -= p_buffer->size; - - p_buffer->resource->Release(); - p_buffer->resource = nullptr; - p_buffer->allocation->Release(); - p_buffer->allocation = nullptr; - p_buffer->size = 0; - - return OK; -} - -Error RenderingDeviceD3D12::_insert_staging_block() { - StagingBufferBlock block; - - D3D12_RESOURCE_DESC resource_desc = {}; - resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - resource_desc.Alignment = 0; - resource_desc.Width = staging_buffer_block_size; - resource_desc.Height = 1; - resource_desc.DepthOrArraySize = 1; - resource_desc.MipLevels = 1; - resource_desc.Format = DXGI_FORMAT_UNKNOWN; - resource_desc.SampleDesc.Count = 1; - resource_desc.SampleDesc.Quality = 0; - resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE; - - D3D12MA::ALLOCATION_DESC allocation_desc = {}; - allocation_desc.HeapType = D3D12_HEAP_TYPE_UPLOAD; - - HRESULT res = context->get_allocator()->CreateResource( - &allocation_desc, - &resource_desc, - D3D12_RESOURCE_STATE_GENERIC_READ, - NULL, - &block.allocation, - IID_PPV_ARGS(&block.resource)); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "CreateResource failed with error " + vformat("0x%08ux", res) + "."); - - staging_buffer_blocks.insert(staging_buffer_current, block); - return OK; -} - -Error RenderingDeviceD3D12::_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 functions. - // 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 functions 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 RenderingDeviceD3D12::_buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_list, 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. - - void *data_ptr = nullptr; - { - HRESULT res = staging_buffer_blocks[staging_buffer_current].resource->Map(0, &VOID_RANGE, &data_ptr); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "Map failed with error " + vformat("0x%08ux", res) + "."); - } - - // Copy to staging buffer. - memcpy(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount); - - // Unmap. - staging_buffer_blocks[staging_buffer_current].resource->Unmap(0, &VOID_RANGE); - - // Insert a command to copy this. - ID3D12GraphicsCommandList *command_list = (p_use_draw_command_list ? frames[frame].draw_command_list : frames[frame].setup_command_list).Get(); - command_list->CopyBufferRegion(p_buffer->resource, submit_from + p_offset, staging_buffer_blocks[staging_buffer_current].resource, block_write_offset, block_write_amount); - - 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; -} - -/*****************/ -/**** TEXTURE ****/ -/*****************/ - -RID RenderingDeviceD3D12::texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data) { - _THREAD_SAFE_METHOD_ - - D3D12_RESOURCE_DESC1 resource_desc = {}; // Using D3D12_RESOURCE_DESC1. Thanks to the layout, it's sliceable down to D3D12_RESOURCE_DESC if needed. - resource_desc.Alignment = 0; // D3D12MA will override this to use a smaller alignment than the default if possible. - - Vector<DataFormat> allowed_formats; - if (p_format.shareable_formats.size()) { - 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"); - allowed_formats = p_format.shareable_formats; - } else { - allowed_formats.push_back(p_format.format); - if (p_view.format_override != DATA_FORMAT_MAX) { - allowed_formats.push_back(p_view.format_override); - } - } - - ERR_FAIL_INDEX_V(p_format.texture_type, TEXTURE_TYPE_MAX, RID()); - - resource_desc.Dimension = d3d12_texture_dimension[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"); - - resource_desc.Format = d3d12_formats[p_format.format].family; - - resource_desc.Width = p_format.width; - if (resource_desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D || resource_desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D) { - ERR_FAIL_COND_V_MSG(p_format.height < 1, RID(), "Height must be equal or greater than 1 for 2D and 3D textures"); - resource_desc.Height = p_format.height; - } else { - resource_desc.Height = 1; - } - - if (resource_desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) { - ERR_FAIL_COND_V_MSG(p_format.depth < 1, RID(), "Depth must be equal or greater than 1 for 3D textures"); - resource_desc.DepthOrArraySize = p_format.depth; - } else { - resource_desc.DepthOrArraySize = 1; - } - - ERR_FAIL_COND_V(p_format.mipmaps < 1, RID()); - - resource_desc.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"); - resource_desc.DepthOrArraySize *= p_format.array_layers; - } - - ERR_FAIL_INDEX_V(p_format.samples, TEXTURE_SAMPLES_MAX, RID()); - - // 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; - } - - resource_desc.SampleDesc = {}; - DXGI_FORMAT format_to_test = (resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) ? d3d12_formats[p_format.format].dsv_format : d3d12_formats[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, 1), - rasterization_sample_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; - - uint32_t required_mipmaps = get_image_required_mipmaps(p_format.width, p_format.height, p_format.depth); - - ERR_FAIL_COND_V_MSG(required_mipmaps < p_format.mipmaps, RID(), - "Too many mipmaps requested for texture format and dimensions (" + itos(p_format.mipmaps) + "), 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 = p_format.array_layers; - 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 < p_format.array_layers; i++) { - uint32_t required_size = get_image_format_required_size(p_format.format, p_format.width, p_format.height, p_format.depth, p_format.mipmaps); - 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. - - // 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. - bool cross_family_sharing = false; - ComPtr<ID3D12Device10> device10; - device.As(&device10); - bool relaxed_casting_available = device10.Get() && context->get_format_capabilities().relaxed_casting_supported; - LocalVector<DXGI_FORMAT> castable_formats; - - HashMap<DataFormat, D3D12_RESOURCE_FLAGS> aliases_forbidden_flags; - D3D12_RESOURCE_FLAGS accum_forbidden_flags = {}; - for (DataFormat curr_format : allowed_formats) { - // For now, we'll validate usages only the main format, to match what Vulkan RD does. - // TODO: The aliasing trick assumes the main format is the only writable one. We should either validate for that or handle a different order gracefully. - bool checking_main_format = curr_format == p_format.format; - - String format_text = "'" + String(named_formats[p_format.format]) + "'"; - - ERR_FAIL_COND_V_MSG(d3d12_formats[curr_format].family == DXGI_FORMAT_UNKNOWN, RID(), "Format " + format_text + " is not supported."); - - if (d3d12_formats[curr_format].family != d3d12_formats[allowed_formats[0]].family) { - cross_family_sharing = true; - } - if (relaxed_casting_available) { - castable_formats.push_back(d3d12_formats[curr_format].general_format); - } - - D3D12_FEATURE_DATA_FORMAT_SUPPORT srv_rtv_support = {}; - srv_rtv_support.Format = d3d12_formats[curr_format].general_format; - HRESULT res = device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &srv_rtv_support, sizeof(srv_rtv_support)); - ERR_FAIL_COND_V_MSG(res, RID(), "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); - - D3D12_FEATURE_DATA_FORMAT_SUPPORT uav_support = srv_rtv_support; // Fine for now. - - D3D12_FEATURE_DATA_FORMAT_SUPPORT dsv_support = {}; - dsv_support.Format = d3d12_formats[curr_format].dsv_format; - res = device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &dsv_support, sizeof(dsv_support)); - ERR_FAIL_COND_V_MSG(res, RID(), "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); - - if (checking_main_format) { - if ((p_format.usage_bits & (TEXTURE_USAGE_SAMPLING_BIT | TEXTURE_USAGE_COLOR_ATTACHMENT_BIT))) { - if (p_format.mipmaps && !(srv_rtv_support.Support1 & D3D12_FORMAT_SUPPORT1_MIP)) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support mip.maps."); - } - } - - // 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. - // [[IMPLICIT_SAMPLE]] - if ((p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) && !(srv_rtv_support.Support1 & (D3D12_FORMAT_SUPPORT1_SHADER_LOAD | D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE))) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as a sampled texture."); - } - - if ((p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) && d3d12_formats[curr_format].general_format == DXGI_FORMAT_UNKNOWN) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as a sampled texture."); - } - - if ((p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) && !(srv_rtv_support.Support1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET)) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as color attachment."); - } - } - - if ((p_format.usage_bits & TEXTURE_USAGE_CAN_COPY_TO_BIT)) { - // We need to check if the texture can be cleared; if it's not flagged for color attachment , we have to see if it's possible via a UAV. - if (!(p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - if (!(uav_support.Support1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW)) { - if (checking_main_format) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as a copy-to texture, because clearing it is not supported."); - } else { - aliases_forbidden_flags[curr_format] |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - accum_forbidden_flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - } - } - } - } - - if ((p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !(dsv_support.Support1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL)) { - if (checking_main_format) { - printf("dxgiformat: %x\n", resource_desc.Format); - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as depth-stencil attachment."); - } else { - aliases_forbidden_flags[curr_format] |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - accum_forbidden_flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - } - } - - if ((p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT)) { - if (!(uav_support.Support1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW)) { // Maybe check LOAD/STORE, too? - if (checking_main_format) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as storage image."); - } else { - aliases_forbidden_flags[curr_format] |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - accum_forbidden_flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - } - } - } - - if (checking_main_format) { - if ((p_format.usage_bits & TEXTURE_USAGE_STORAGE_ATOMIC_BIT) && !(uav_support.Support2 & D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_ADD)) { // Check a basic atomic at least. - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as atomic storage image."); - } - - if ((p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) && d3d12_formats[curr_format].general_format != DXGI_FORMAT_R8_UINT) { - ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as VRS attachment."); - } - } - } - - 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(RID(), "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(RID(), "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(RID(), "This texture's views require aliasing, but that's not supported for a depth-stencil texture."); - } - if (d3d12_formats[p_format.format].family == DXGI_FORMAT_R32G32B32_TYPELESS) { - ERR_FAIL_V_MSG(RID(), "This texture's views require aliasing, but that's not supported for an R32G32B32 texture."); - } - } else { - resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - } - - 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; - } - - // 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. - - 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))) { - if (!(accum_forbidden_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)) { - if (!(accum_forbidden_flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)) { - allocation_desc.ExtraHeapFlags |= D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS; - } - } - -#ifdef USE_SMALL_ALLOCS_POOL - 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); - if (image_size <= SMALL_ALLOCATION_MAX_SIZE) { - allocation_desc.CustomPool = _find_or_create_small_allocs_pool(allocation_desc.HeapType, allocation_desc.ExtraHeapFlags); - } -#endif - - Texture texture; - - D3D12_RESOURCE_STATES initial_state = p_data.size() || (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) ? D3D12_RESOURCE_STATE_COPY_DEST : D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE; - FLOAT black[4] = {}; - D3D12_CLEAR_VALUE clear_value = CD3DX12_CLEAR_VALUE(d3d12_formats[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 = {}; - if (cross_family_sharing && relaxed_casting_available) { - res = context->get_allocator()->CreateResource3( - &allocation_desc, - &resource_desc, - D3D12_BARRIER_LAYOUT_COMMON, // Needed for barrier interop. - clear_value_ptr, - castable_formats.size(), - castable_formats.ptr(), - &texture.allocation, - IID_PPV_ARGS(&texture.owner_resource)); - initial_state = D3D12_RESOURCE_STATE_COMMON; // Needed for barrier interop. - } else { - res = context->get_allocator()->CreateResource( - &allocation_desc, - (D3D12_RESOURCE_DESC *)&resource_desc, - initial_state, - clear_value_ptr, - &texture.allocation, - IID_PPV_ARGS(&texture.owner_resource)); - } - ERR_FAIL_COND_V_MSG(res, RID(), "CreateResource failed with error " + vformat("0x%08ux", res) + "."); - texture.resource = texture.owner_resource; - image_memory += texture.allocation->GetSize(); - texture.type = p_format.texture_type; - texture.format = p_format.format; - texture.planes = get_image_format_plane_count(p_format.format); - texture.width = p_format.width; - texture.height = p_format.height; - texture.depth = p_format.depth; - texture.layers = p_format.array_layers; - texture.mipmaps = p_format.mipmaps; - texture.owner_layers = texture.layers; - texture.owner_mipmaps = texture.mipmaps; - 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; - texture.own_states.subresource_states.resize(texture.mipmaps * texture.layers); - for (uint32_t i = 0; i < texture.own_states.subresource_states.size(); i++) { - texture.own_states.subresource_states[i] = initial_state; - } - texture.bound = false; - - // Describe view. - - static const D3D12_SRV_DIMENSION view_dimensions[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 view_dimensions_ms[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 uav_dimensions[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, - }; - - texture.srv_desc.ViewDimension = p_format.samples == TEXTURE_SAMPLES_1 ? view_dimensions[p_format.texture_type] : view_dimensions_ms[p_format.texture_type]; - - texture.owner_uav_desc.Format = d3d12_formats[p_format.format].general_format; - texture.owner_uav_desc.ViewDimension = p_format.samples == TEXTURE_SAMPLES_1 ? uav_dimensions[p_format.texture_type] : D3D12_UAV_DIMENSION_UNKNOWN; - - UINT base_swizzle = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - if (p_view.format_override == DATA_FORMAT_MAX) { - texture.srv_desc.Format = d3d12_formats[p_format.format].general_format; - base_swizzle = d3d12_formats[p_format.format].swizzle; - } else { - texture.srv_desc.Format = d3d12_formats[p_view.format_override].general_format; - base_swizzle = d3d12_formats[p_view.format_override].swizzle; - } - - // Apply requested swizzle (component mapping) on top of the one from the format database. - - 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), - }; - - texture.srv_desc.Shader4ComponentMapping = 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]); - - switch (texture.srv_desc.ViewDimension) { - case D3D12_SRV_DIMENSION_TEXTURE1D: { - texture.srv_desc.Texture1D.MipLevels = p_format.mipmaps; - } break; - case D3D12_SRV_DIMENSION_TEXTURE1DARRAY: { - texture.srv_desc.Texture1DArray.MipLevels = p_format.mipmaps; - texture.srv_desc.Texture1DArray.ArraySize = p_format.array_layers; - } break; - case D3D12_SRV_DIMENSION_TEXTURE2D: { - texture.srv_desc.Texture2D.MipLevels = p_format.mipmaps; - } break; - case D3D12_SRV_DIMENSION_TEXTURE2DMS: { - } break; - case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: { - texture.srv_desc.Texture2DArray.MipLevels = p_format.mipmaps; - texture.srv_desc.Texture2DArray.ArraySize = p_format.array_layers; - } break; - case D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY: { - texture.srv_desc.Texture2DMSArray.ArraySize = p_format.array_layers; - } break; - case D3D12_SRV_DIMENSION_TEXTURECUBEARRAY: { - texture.srv_desc.TextureCubeArray.MipLevels = p_format.mipmaps; - texture.srv_desc.TextureCubeArray.NumCubes = p_format.array_layers / 6; - } break; - case D3D12_SRV_DIMENSION_TEXTURE3D: { - texture.srv_desc.Texture3D.MipLevels = p_format.mipmaps; - } break; - case D3D12_SRV_DIMENSION_TEXTURECUBE: { - texture.srv_desc.TextureCube.MipLevels = p_format.mipmaps; - } break; - } - - switch (texture.owner_uav_desc.ViewDimension) { - case D3D12_UAV_DIMENSION_TEXTURE1DARRAY: { - texture.owner_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. - texture.owner_uav_desc.Texture2DArray.ArraySize = p_format.array_layers; - } break; - case D3D12_UAV_DIMENSION_TEXTURE3D: { - texture.owner_uav_desc.Texture3D.WSize = p_format.depth; - } break; - default: { - } - } - - texture.uav_desc = texture.owner_uav_desc; - if (p_view.format_override != DATA_FORMAT_MAX) { - texture.uav_desc.Format = d3d12_formats[p_view.format_override].general_format; - } - - if (cross_family_sharing && !relaxed_casting_available) { - D3D12_RESOURCE_DESC resource_desc_backup = *(D3D12_RESOURCE_DESC *)&resource_desc; - D3D12MA::ALLOCATION_DESC allocation_desc_backup = allocation_desc; - - texture.aliases.resize(texture.allowed_shared_formats.size()); - for (int i = 0; i < texture.allowed_shared_formats.size(); i++) { - DataFormat curr_format = texture.allowed_shared_formats[i]; - - DXGI_FORMAT format_family = d3d12_formats[curr_format].family; - if (format_family == d3d12_formats[p_format.format].family) { - texture.aliases[i] = nullptr; - continue; - } - - D3D12_RESOURCE_DESC alias_resource_desc = *(D3D12_RESOURCE_DESC *)&resource_desc; - alias_resource_desc.Format = format_family; - if (aliases_forbidden_flags.has(curr_format)) { - alias_resource_desc.Flags &= ~aliases_forbidden_flags[curr_format]; - } - clear_value.Format = format_family; - res = context->get_allocator()->CreateAliasingResource( - texture.allocation, - 0, - &alias_resource_desc, - initial_state, - (alias_resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) ? clear_value_ptr : nullptr, - IID_PPV_ARGS(&texture.aliases[i])); - ERR_FAIL_COND_V_MSG(res, RID(), "CreateAliasingResource failed with error " + vformat("0x%08ux", res) + "."); - - if (curr_format == p_view.format_override) { - texture.resource = texture.aliases[i]; - } - } - } - - RID id = texture_owner.make_rid(texture); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - - if (p_data.size()) { - Texture *texture_ptr = texture_owner.get_or_null(id); - ERR_FAIL_NULL_V(texture_ptr, RID()); - - ID3D12GraphicsCommandList *command_list = frames[frame].setup_command_list.Get(); - - for (uint32_t i = 0; i < p_format.array_layers; i++) { - _texture_update(texture_ptr, i, p_data[i], RD::BARRIER_MASK_ALL_BARRIERS, command_list); - } - } - return id; -} - -RID RenderingDeviceD3D12::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. - } - - // Describe view. - - Texture texture = *src_texture; - texture.own_states.subresource_states.clear(); - texture.states = &src_texture->own_states; - - UINT base_swizzle = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) { - texture.srv_desc.Format = d3d12_formats[texture.format].general_format; - base_swizzle = d3d12_formats[texture.format].swizzle; - texture.uav_desc.Format = d3d12_formats[texture.format].general_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."); - texture.srv_desc.Format = d3d12_formats[p_view.format_override].general_format; - base_swizzle = d3d12_formats[p_view.format_override].swizzle; - texture.uav_desc.Format = d3d12_formats[p_view.format_override].general_format; - - if (texture.aliases.size()) { - for (int i = 0; i < texture.allowed_shared_formats.size(); i++) { - if (texture.allowed_shared_formats[i] == p_view.format_override) { - texture.resource = texture.aliases[i]; - break; - } - } - } - } - - // Apply requested swizzle (component mapping) on top of the one from the format database. - - 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), - }; - - texture.srv_desc.Shader4ComponentMapping = 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]); - - 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 RenderingDeviceD3D12::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) { - ERR_FAIL_V_MSG(RID(), "Unimplemented!"); -} - -RID RenderingDeviceD3D12::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"); - - // Describe 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; - texture.own_states.subresource_states.clear(); - texture.states = &src_texture->own_states; - - UINT base_swizzle = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) { - texture.srv_desc.Format = d3d12_formats[texture.format].general_format; - base_swizzle = d3d12_formats[texture.format].swizzle; - texture.uav_desc.Format = d3d12_formats[texture.format].general_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."); - texture.srv_desc.Format = d3d12_formats[p_view.format_override].general_format; - base_swizzle = d3d12_formats[p_view.format_override].swizzle; - texture.uav_desc.Format = d3d12_formats[p_view.format_override].general_format; - - if (texture.aliases.size()) { - for (int i = 0; i < texture.allowed_shared_formats.size(); i++) { - if (texture.allowed_shared_formats[i] == p_view.format_override) { - texture.resource = texture.aliases[i]; - break; - } - } - } - } - - // Apply requested swizzle (component mapping) on top of the one from the format database. - - 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), - }; - - texture.srv_desc.Shader4ComponentMapping = 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]); - - 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."); - } - - // Leveraging aliasing in members of the union as much as possible. - - texture.srv_desc.Texture1D.MostDetailedMip = p_mipmap; - texture.srv_desc.Texture1D.MipLevels = 1; - - texture.uav_desc.Texture1D.MipSlice = p_mipmap; - - switch (p_slice_type) { - case TEXTURE_SLICE_2D: { - if (texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2D && p_layer == 0) { - CRASH_COND(texture.uav_desc.ViewDimension != D3D12_UAV_DIMENSION_TEXTURE2D); - } else if (texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DMS && p_layer == 0) { - CRASH_COND(texture.uav_desc.ViewDimension != D3D12_UAV_DIMENSION_UNKNOWN); - } else if ((texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DARRAY || (texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2D && p_layer)) || texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBE || texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBEARRAY) { - texture.srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; - texture.srv_desc.Texture2DArray.FirstArraySlice = p_layer; - texture.srv_desc.Texture2DArray.ArraySize = 1; - texture.srv_desc.Texture2DArray.PlaneSlice = 0; - texture.srv_desc.Texture2DArray.ResourceMinLODClamp = 0.0f; - - texture.uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; - texture.uav_desc.Texture2DArray.FirstArraySlice = p_layer; - texture.uav_desc.Texture2DArray.ArraySize = 1; - texture.uav_desc.Texture2DArray.PlaneSlice = 0; - } else if ((texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY || (texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DMS && p_layer))) { - texture.srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; - texture.srv_desc.Texture2DMSArray.FirstArraySlice = p_layer; - texture.srv_desc.Texture2DMSArray.ArraySize = 1; - - texture.uav_desc.ViewDimension = D3D12_UAV_DIMENSION_UNKNOWN; - } else { - CRASH_NOW(); - } - } break; - case TEXTURE_SLICE_CUBEMAP: { - if (texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBE) { - CRASH_COND(texture.uav_desc.ViewDimension != D3D12_UAV_DIMENSION_TEXTURE2DARRAY); - } else if (texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBE || p_layer == 0) { - texture.srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; - - CRASH_COND(texture.uav_desc.ViewDimension != D3D12_UAV_DIMENSION_TEXTURE2DARRAY); - texture.uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; - texture.uav_desc.Texture2DArray.FirstArraySlice = 0; - texture.uav_desc.Texture2DArray.ArraySize = 6; - texture.uav_desc.Texture2DArray.PlaneSlice = 0; - } else if (texture.srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBEARRAY || p_layer != 0) { - texture.srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; - texture.srv_desc.TextureCubeArray.First2DArrayFace = p_layer; - texture.srv_desc.TextureCubeArray.NumCubes = 1; - texture.srv_desc.TextureCubeArray.ResourceMinLODClamp = 0.0f; - - CRASH_COND(texture.uav_desc.ViewDimension != D3D12_UAV_DIMENSION_TEXTURE2DARRAY); - texture.uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; - texture.uav_desc.Texture2DArray.FirstArraySlice = p_layer; - texture.uav_desc.Texture2DArray.ArraySize = 6; - texture.uav_desc.Texture2DArray.PlaneSlice = 0; - } else { - CRASH_NOW(); - } - } break; - case TEXTURE_SLICE_3D: { - CRASH_COND(texture.srv_desc.ViewDimension != D3D12_SRV_DIMENSION_TEXTURE3D); - CRASH_COND(texture.uav_desc.ViewDimension != D3D12_UAV_DIMENSION_TEXTURE3D); - texture.uav_desc.Texture3D.WSize = -1; - } break; - case TEXTURE_SLICE_2D_ARRAY: { - CRASH_COND(texture.srv_desc.ViewDimension != D3D12_SRV_DIMENSION_TEXTURE2DARRAY); - texture.srv_desc.Texture2DArray.FirstArraySlice = p_layer; - texture.srv_desc.Texture2DArray.ArraySize = slice_layers; - - CRASH_COND(texture.uav_desc.ViewDimension != D3D12_UAV_DIMENSION_TEXTURE2DARRAY); - texture.uav_desc.Texture2DArray.FirstArraySlice = p_layer; - texture.uav_desc.Texture2DArray.ArraySize = slice_layers; - } break; - } - - 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 RenderingDeviceD3D12::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier) { - ERR_FAIL_COND_V_MSG((draw_list || compute_list), 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()) { - texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_NULL_V(texture, ERR_BUG); // This is a bug. - } - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - uint32_t subresource = D3D12CalcSubresource(0, p_layer, 0, texture->mipmaps, texture->layers); - _resource_transition_batch(texture, subresource, texture->planes, D3D12_RESOURCE_STATE_COPY_DEST); - _resource_transitions_flush(command_list); - Error err = _texture_update(texture, p_layer, p_data, p_post_barrier, command_list); - - return err; -} - -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_dst_pitch, 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_dst_pitch; - } -} - -Error RenderingDeviceD3D12::_texture_update(Texture *p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier, ID3D12GraphicsCommandList *p_command_list) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V_MSG(p_texture->bound, ERR_CANT_ACQUIRE_RESOURCE, - "Texture can't be updated while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); - - ERR_FAIL_COND_V_MSG(!(p_texture->usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT), ERR_INVALID_PARAMETER, - "Texture requires the TEXTURE_USAGE_CAN_UPDATE_BIT in order to be updatable."); - - uint32_t layer_count = p_texture->layers; - if (p_texture->type == TEXTURE_TYPE_CUBE || p_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(p_texture->format, p_texture->width, p_texture->height, p_texture->depth, p_texture->mipmaps, &width, &height); - uint32_t required_size = image_size; - uint32_t required_align = get_compressed_image_format_block_byte_size(p_texture->format); - if (required_align == 1) { - required_align = get_image_format_pixel_size(p_texture->format); - } - if ((required_align % 4) != 0) { // Alignment rules are really strange. - required_align *= 4; - } - - required_align = ALIGN(required_align, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); - - 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(); - - uint32_t mipmap_offset = 0; - - uint32_t logic_width = p_texture->width; - uint32_t logic_height = p_texture->height; - - for (uint32_t mm_i = 0; mm_i < p_texture->mipmaps; mm_i++) { - uint32_t depth; - uint32_t image_total = get_image_format_required_size(p_texture->format, p_texture->width, p_texture->height, p_texture->depth, mm_i + 1, &width, &height, &depth); - - const uint8_t *read_ptr_mipmap = r + mipmap_offset; - image_size = image_total - mipmap_offset; - - UINT dst_subresource = D3D12CalcSubresource(mm_i, p_layer, 0, p_texture->mipmaps, p_texture->layers); - CD3DX12_TEXTURE_COPY_LOCATION copy_dst(p_texture->resource, dst_subresource); - - 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 * z / depth; - - 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 pixel_size = get_image_format_pixel_size(p_texture->format); - uint32_t block_w, block_h; - get_compressed_image_format_block_dimensions(p_texture->format, block_w, block_h); - - uint32_t region_pitch = (region_w * pixel_size * block_w) >> get_compressed_image_format_pixel_rshift(p_texture->format); - region_pitch = ALIGN(region_pitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); - uint32_t to_allocate = region_pitch * region_h; - - 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; - HRESULT res = staging_buffer_blocks[staging_buffer_current].resource->Map(0, &VOID_RANGE, &data_ptr); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "Map failed with error " + vformat("0x%08ux", res) + "."); - write_ptr = (uint8_t *)data_ptr; - write_ptr += alloc_offset; - } - - ERR_FAIL_COND_V(region_w % block_w, ERR_BUG); - ERR_FAIL_COND_V(region_pitch % block_w, ERR_BUG); - ERR_FAIL_COND_V(region_h % block_h, ERR_BUG); - - if (block_w != 1 || block_h != 1) { - // Compressed image (functions). - // Must copy a block region. - - uint32_t block_size = get_compressed_image_format_block_byte_size(p_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, region_pitch, block_size); - } else { - // Regular image (pixels). - // Must copy a pixel region. - _copy_region(read_ptr, write_ptr, x, y, region_w, region_h, width, region_pitch, pixel_size); - } - - { // Unmap. - staging_buffer_blocks[staging_buffer_current].resource->Unmap(0, &VOID_RANGE); - } - - D3D12_PLACED_SUBRESOURCE_FOOTPRINT src_footprint = {}; - src_footprint.Offset = alloc_offset; - src_footprint.Footprint = CD3DX12_SUBRESOURCE_FOOTPRINT( - d3d12_formats[p_texture->format].family, - region_w, - region_h, - 1, - region_pitch); - CD3DX12_TEXTURE_COPY_LOCATION copy_src(staging_buffer_blocks[staging_buffer_current].resource, src_footprint); - - CD3DX12_BOX src_box(0, 0, region_w, region_h); - p_command_list->CopyTextureRegion(©_dst, x, y, z, ©_src, &src_box); - - 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); - } - - return OK; -} - -Vector<uint8_t> RenderingDeviceD3D12::_texture_get_data_from_image(Texture *tex, 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); - - D3D12_RESOURCE_DESC res_desc = tex->resource->GetDesc(); - - 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; - - UINT subresource = 0; - - uint64_t image_total_src = 0; - D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout = {}; - device->GetCopyableFootprints( - &res_desc, - subresource, - 1, - 0, - &layout, - nullptr, - nullptr, - &image_total_src); - - void *img_mem; - HRESULT res = tex->resource->Map(subresource, nullptr, &img_mem); - ERR_FAIL_COND_V_MSG(res, Vector<uint8_t>(), "Map failed with error " + vformat("0x%08ux", res) + "."); - - 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 * image_total_src / depth; - - 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.Footprint.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.Footprint.RowPitch; - uint8_t *wptr = write_ptr + y * pixel_size * width; - memcpy(wptr, rptr, (uint64_t)pixel_size * width); - } - } - } - - tex->resource->Unmap(subresource, nullptr); - - mipmap_offset = image_total; - } - } - - return image_data; -} - -Vector<uint8_t> RenderingDeviceD3D12::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 render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); - ERR_FAIL_COND_V_MSG(!(tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), Vector<uint8_t>(), - "Texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order 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, p_layer); - } else { - // Compute total image size. - uint32_t width, height, depth; - uint32_t final_buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps, &width, &height, &depth); - - uint32_t block_w, block_h; - get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); - uint32_t alignment = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT; - - // We'll use a potentially bigger buffer to account for mip sizes in which we need to use a bigger pitch to keep D3D12 happy. - uint32_t buffer_size = 0; - { - uint32_t computed_h = tex->height; - uint32_t computed_d = tex->depth; - - uint32_t prev_size = 0; - for (uint32_t i = 0; i < tex->mipmaps; i++) { - uint32_t image_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, i + 1); - uint32_t inferred_row_pitch = image_size / (computed_h * computed_d) * block_h; - uint32_t adjusted_row_pitch = ALIGN(inferred_row_pitch, alignment); - uint32_t adjusted_image_size = adjusted_row_pitch / block_h * computed_h * tex->depth; - uint32_t size = adjusted_image_size - prev_size; - prev_size = image_size; - - buffer_size = ALIGN(buffer_size + size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); - - computed_h = MAX(1u, computed_h >> 1); - computed_d = MAX(1u, computed_d >> 1); - } - } - - // Allocate buffer. - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); // Makes more sense to retrieve. - - Buffer tmp_buffer; - Error err = _buffer_allocate(&tmp_buffer, buffer_size, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_HEAP_TYPE_READBACK); - ERR_FAIL_COND_V(err != OK, Vector<uint8_t>()); - - for (uint32_t i = 0; i < tex->mipmaps; i++) { - uint32_t subresource = D3D12CalcSubresource(i, p_layer, 0, tex->owner_mipmaps, tex->owner_layers); - _resource_transition_batch(tex, subresource, tex->planes, D3D12_RESOURCE_STATE_COPY_SOURCE); - } - _resource_transitions_flush(command_list); - - 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++) { - 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; - - D3D12_PLACED_SUBRESOURCE_FOOTPRINT dst_footprint = {}; - dst_footprint.Offset = offset; - dst_footprint.Footprint.Width = MAX(block_w, computed_w); - dst_footprint.Footprint.Height = MAX(block_h, computed_h); - dst_footprint.Footprint.Depth = computed_d; - uint32_t inferred_row_pitch = size / (dst_footprint.Footprint.Height * computed_d) * block_h; - dst_footprint.Footprint.RowPitch = inferred_row_pitch; - dst_footprint.Footprint.Format = d3d12_formats[tex->format].family; - CD3DX12_TEXTURE_COPY_LOCATION copy_dst(tmp_buffer.resource, dst_footprint); - - UINT src_subresource = D3D12CalcSubresource(i, p_layer, 0, tex->owner_mipmaps, tex->owner_layers); - CD3DX12_TEXTURE_COPY_LOCATION copy_src(tex->resource, src_subresource); - - if (dst_footprint.Footprint.RowPitch % alignment) { - // Dammit! Now we must copy with an imposed pitch and then adjust row by row. - copy_dst.PlacedFootprint.Offset = ALIGN(offset, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); - - uint32_t adjusted_row_pitch = ALIGN(inferred_row_pitch, alignment); - copy_dst.PlacedFootprint.Footprint.RowPitch = adjusted_row_pitch; - command_list->CopyTextureRegion(©_dst, 0, 0, 0, ©_src, nullptr); - _flush(true); - - void *buffer_mem; - uint32_t adjusted_size = adjusted_row_pitch / block_h * dst_footprint.Footprint.Height * computed_d; - CD3DX12_RANGE range(offset, copy_dst.PlacedFootprint.Offset + adjusted_size); - HRESULT res = tmp_buffer.resource->Map(0, &range, &buffer_mem); - ERR_FAIL_COND_V_MSG(res, Vector<uint8_t>(), "Map failed with error " + vformat("0x%08ux", res) + "."); - - for (uint32_t j = 0; j < dst_footprint.Footprint.Height / block_h * computed_d; j++) { - memmove((uint8_t *)buffer_mem + offset + j * inferred_row_pitch, (uint8_t *)buffer_mem + copy_dst.PlacedFootprint.Offset + j * adjusted_row_pitch, inferred_row_pitch); - } - - tmp_buffer.resource->Unmap(0, nullptr); - } else if (offset % D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT) { - // Row pitch is fine, but offset alignment is not good. - copy_dst.PlacedFootprint.Offset = ALIGN(offset, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); - - command_list->CopyTextureRegion(©_dst, 0, 0, 0, ©_src, nullptr); - _flush(true); - - void *buffer_mem; - CD3DX12_RANGE range(copy_dst.PlacedFootprint.Offset, size); - HRESULT res = tmp_buffer.resource->Map(0, &range, &buffer_mem); - ERR_FAIL_COND_V_MSG(res, Vector<uint8_t>(), "Map failed with error " + vformat("0x%08ux", res) + "."); - - memmove((uint8_t *)buffer_mem + offset, (uint8_t *)buffer_mem + copy_dst.PlacedFootprint.Offset, size); - - tmp_buffer.resource->Unmap(0, nullptr); - } else { - command_list->CopyTextureRegion(©_dst, 0, 0, 0, ©_src, nullptr); - } - - computed_w = MAX(1u, computed_w >> 1); - computed_h = MAX(1u, computed_h >> 1); - computed_d = MAX(1u, computed_d >> 1); - offset += size; - } - - _flush(true); - - void *buffer_mem; - CD3DX12_RANGE range(0, final_buffer_size); - HRESULT res = tmp_buffer.resource->Map(0, &range, &buffer_mem); - ERR_FAIL_COND_V_MSG(res, Vector<uint8_t>(), "Map failed with error " + vformat("0x%08ux", res) + "."); - - Vector<uint8_t> buffer_data; - buffer_data.resize(final_buffer_size); - { - uint8_t *w = buffer_data.ptrw(); - memcpy(w, buffer_mem, final_buffer_size); - } - - tmp_buffer.resource->Unmap(0, nullptr); - - _buffer_free(&tmp_buffer); - - return buffer_data; - } -} - -bool RenderingDeviceD3D12::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 RenderingDeviceD3D12::texture_is_valid(RID p_texture) { - return texture_owner.owns(p_texture); -} - -RenderingDevice::TextureFormat RenderingDeviceD3D12::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 RenderingDeviceD3D12::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 RenderingDeviceD3D12::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->resource; -} - -Error RenderingDeviceD3D12::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 render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); - ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, - "Source texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order 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 render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); - ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, - "Destination texture requires the TEXTURE_USAGE_CAN_COPY_TO_BIT in order 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->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != (dst_tex->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), ERR_INVALID_PARAMETER, - "Source and destination texture must be of the same type (color or depth)."); - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - uint32_t src_subresource = D3D12CalcSubresource(p_src_mipmap, p_src_layer, 0, src_tex->owner_mipmaps, src_tex->owner_layers); - _resource_transition_batch(src_tex, src_subresource, src_tex->planes, D3D12_RESOURCE_STATE_COPY_SOURCE); - - uint32_t dst_subresource = D3D12CalcSubresource(p_dst_mipmap, p_dst_layer, 0, dst_tex->owner_mipmaps, dst_tex->owner_layers); - _resource_transition_batch(dst_tex, dst_subresource, dst_tex->planes, D3D12_RESOURCE_STATE_COPY_DEST); - - _resource_transitions_flush(command_list); - - { - CD3DX12_TEXTURE_COPY_LOCATION src_location(src_tex->resource, src_subresource); - CD3DX12_BOX src_box(p_from.x, p_from.y, p_from.z, p_from.x + p_size.x, p_from.y + p_size.y, p_from.z + p_size.z); - CD3DX12_TEXTURE_COPY_LOCATION dst_location(dst_tex->resource, dst_subresource); - command_list->CopyTextureRegion( - &dst_location, - p_to.x, p_to.y, p_to.z, - &src_location, - &src_box); - } - - return OK; -} - -Error RenderingDeviceD3D12::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 render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); - ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, - "Source texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order 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 render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); - ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, - "Destination texture requires the TEXTURE_USAGE_CAN_COPY_TO_BIT in order 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->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != (dst_tex->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), ERR_INVALID_PARAMETER, - "Source and destination texture must be of the same type (color or depth)."); - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - uint32_t src_subresource = D3D12CalcSubresource(src_tex->base_mipmap, src_tex->base_layer, 0, src_tex->owner_mipmaps, src_tex->owner_layers); - _resource_transition_batch(src_tex, src_subresource, src_tex->planes, D3D12_RESOURCE_STATE_RESOLVE_SOURCE); - - uint32_t dst_subresource = D3D12CalcSubresource(dst_tex->base_mipmap, dst_tex->base_layer, 0, dst_tex->owner_mipmaps, dst_tex->owner_layers); - _resource_transition_batch(dst_tex, dst_subresource, dst_tex->planes, D3D12_RESOURCE_STATE_RESOLVE_DEST); - - _resource_transitions_flush(command_list); - - command_list->ResolveSubresource(dst_tex->resource, dst_subresource, src_tex->resource, src_subresource, d3d12_formats[src_tex->format].general_format); - - return OK; -} - -Error RenderingDeviceD3D12::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 render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind 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 TEXTURE_USAGE_CAN_COPY_TO_BIT in order 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); - - if ((src_tex->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - // Clear via RTV. - - if (frames[frame].desc_heap_walkers.rtv.is_at_eof()) { - if (!frames[frame].desc_heaps_exhausted_reported.rtv) { - frames[frame].desc_heaps_exhausted_reported.rtv = true; - ERR_FAIL_V_MSG(ERR_BUSY, - "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 ERR_BUSY; - } - } - - D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = _make_rtv_for_texture(src_tex, p_base_mipmap, p_base_layer, p_layers); - rtv_desc.Format = src_tex->owner_uav_desc.Format; - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - for (uint32_t i = 0; i < p_layers; i++) { - for (uint32_t j = 0; j < p_mipmaps; j++) { - uint32_t subresource = D3D12CalcSubresource(src_tex->base_mipmap + p_base_mipmap + j, src_tex->base_layer + p_base_layer + i, 0, src_tex->owner_mipmaps, src_tex->owner_layers); - _resource_transition_batch(src_tex, subresource, src_tex->planes, D3D12_RESOURCE_STATE_RENDER_TARGET, src_tex->owner_resource); - } - } - _resource_transitions_flush(command_list); - - device->CreateRenderTargetView( - src_tex->owner_resource, - &rtv_desc, - frames[frame].desc_heap_walkers.rtv.get_curr_cpu_handle()); - command_list->ClearRenderTargetView( - frames[frame].desc_heap_walkers.rtv.get_curr_cpu_handle(), - p_color.components, - 0, - nullptr); - frames[frame].desc_heap_walkers.rtv.advance(); - } else { - // Clear via UAV. - - if (frames[frame].desc_heap_walkers.resources.is_at_eof()) { - if (!frames[frame].desc_heaps_exhausted_reported.resources) { - frames[frame].desc_heaps_exhausted_reported.resources = true; - ERR_FAIL_V_MSG(ERR_BUSY, - "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 ERR_BUSY; - } - } - if (frames[frame].desc_heap_walkers.aux.is_at_eof()) { - if (!frames[frame].desc_heaps_exhausted_reported.aux) { - frames[frame].desc_heaps_exhausted_reported.aux = true; - ERR_FAIL_V_MSG(ERR_BUSY, - "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 ERR_BUSY; - } - } - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - for (uint32_t i = 0; i < p_layers; i++) { - for (uint32_t j = 0; j < p_mipmaps; j++) { - uint32_t subresource = D3D12CalcSubresource(src_tex->base_mipmap + p_base_mipmap + j, src_tex->base_layer + p_base_layer + i, 0, src_tex->owner_mipmaps, src_tex->owner_layers); - _resource_transition_batch(src_tex, subresource, src_tex->planes, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, src_tex->owner_resource); - } - } - _resource_transitions_flush(command_list); - - device->CreateUnorderedAccessView( - src_tex->owner_resource, - nullptr, - &src_tex->owner_uav_desc, - frames[frame].desc_heap_walkers.aux.get_curr_cpu_handle()); - - device->CopyDescriptorsSimple( - 1, - frames[frame].desc_heap_walkers.resources.get_curr_cpu_handle(), - frames[frame].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(), - }; - command_list->ClearUnorderedAccessViewUint( - frames[frame].desc_heap_walkers.resources.get_curr_gpu_handle(), - frames[frame].desc_heap_walkers.aux.get_curr_cpu_handle(), - src_tex->owner_resource, - values, - 0, - nullptr); - - frames[frame].desc_heap_walkers.resources.advance(); - frames[frame].desc_heap_walkers.aux.advance(); - } - - return OK; -} - -bool RenderingDeviceD3D12::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_ - - D3D12_FEATURE_DATA_FORMAT_SUPPORT srv_rtv_support = {}; - srv_rtv_support.Format = d3d12_formats[p_format].general_format; - HRESULT res = device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &srv_rtv_support, sizeof(srv_rtv_support)); - ERR_FAIL_COND_V_MSG(res, false, "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); - - D3D12_FEATURE_DATA_FORMAT_SUPPORT &uav_support = srv_rtv_support; // Fine for now. - - D3D12_FEATURE_DATA_FORMAT_SUPPORT dsv_support = {}; - dsv_support.Format = d3d12_formats[p_format].dsv_format; - res = device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &dsv_support, sizeof(dsv_support)); - ERR_FAIL_COND_V_MSG(res, false, "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); - - if ((p_usage & TEXTURE_USAGE_SAMPLING_BIT) && !(srv_rtv_support.Support1 & (D3D12_FORMAT_SUPPORT1_SHADER_LOAD | D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE)) && d3d12_formats[p_format].general_format != DXGI_FORMAT_UNKNOWN) { - return false; - } - - if ((p_usage & TEXTURE_USAGE_SAMPLING_BIT) && d3d12_formats[p_format].general_format == DXGI_FORMAT_UNKNOWN) { - return false; - } - - if ((p_usage & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) && !(srv_rtv_support.Support1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET)) { - return false; - } - - if ((p_usage & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !(dsv_support.Support1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL)) { - printf("dxgiformat: %x\n", d3d12_formats[p_format].dsv_format); - return false; - } - - if ((p_usage & TEXTURE_USAGE_STORAGE_BIT) && !(uav_support.Support1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW)) { // Maybe check LOAD/STORE, too? - return false; - } - - if ((p_usage & TEXTURE_USAGE_STORAGE_ATOMIC_BIT) && !(uav_support.Support2 & D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_ADD)) { // Check a basic atomic at least. - return false; - } - - if ((p_usage & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) && d3d12_formats[p_format].general_format != DXGI_FORMAT_R8_UINT) { - return false; - } - - return true; -} - -/********************/ -/**** ATTACHMENT ****/ -/********************/ - -bool RenderingDeviceD3D12::_framebuffer_format_preprocess(FramebufferFormat *p_fb_format, uint32_t p_view_count) { - const Vector<AttachmentFormat> &attachments = p_fb_format->attachments; - - LocalVector<int32_t> attachment_last_pass; - attachment_last_pass.resize(attachments.size()); - - if (p_view_count > 1) { - const D3D12Context::MultiviewCapabilities &capabilities = context->get_multiview_capabilities(); - - // This only works with multiview! - ERR_FAIL_COND_V_MSG(!capabilities.is_supported, false, "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, false, "Hardware does not support requested number of views for Multiview render pass"); - } - - int attachment_count = 0; - HashSet<DXGI_FORMAT> ms_attachment_formats; - for (int i = 0; i < attachments.size(); i++) { - if (attachments[i].usage_flags == AttachmentFormat::UNUSED_ATTACHMENT) { - continue; - } - - ERR_FAIL_INDEX_V(attachments[i].format, DATA_FORMAT_MAX, false); - ERR_FAIL_INDEX_V(attachments[i].samples, TEXTURE_SAMPLES_MAX, false); - ERR_FAIL_COND_V_MSG(!(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)), - ERR_INVALID_PARAMETER, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth-stencil, input or VRS) bit set."); - - attachment_last_pass[i] = -1; - attachment_count++; - - if (attachments[i].samples != TEXTURE_SAMPLES_1) { - if ((attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - ms_attachment_formats.insert(d3d12_formats[attachments[i].format].general_format); - } else if ((attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - ms_attachment_formats.insert(d3d12_formats[attachments[i].format].dsv_format); - } - } - } - - Vector<FramebufferPass> &passes = p_fb_format->passes; - for (int i = 0; i < passes.size(); i++) { - FramebufferPass *pass = &passes.write[i]; - - TextureSamples texture_samples = TEXTURE_SAMPLES_1; - bool is_multisample_first = true; - - ERR_FAIL_COND_V(pass->color_attachments.size() > D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT, false); - for (int j = 0; j < pass->color_attachments.size(); j++) { - int32_t attachment = pass->color_attachments[j]; - if (attachment != FramebufferPass::ATTACHMENT_UNUSED) { - ERR_FAIL_INDEX_V_MSG(attachment, attachments.size(), false, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), color attachment (" + itos(j) + ")."); - ERR_FAIL_COND_V_MSG(!(attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), false, "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, false, "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 = attachments[attachment].samples; - is_multisample_first = false; - } else { - ERR_FAIL_COND_V_MSG(texture_samples != attachments[attachment].samples, false, "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."); - } - attachment_last_pass[attachment] = i; - } - } - - for (int j = 0; j < pass->input_attachments.size(); j++) { - int32_t attachment = pass->input_attachments[j]; - if (attachment != FramebufferPass::ATTACHMENT_UNUSED) { - ERR_FAIL_INDEX_V_MSG(attachment, attachments.size(), false, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), input attachment (" + itos(j) + ")."); - ERR_FAIL_COND_V_MSG(!(attachments[attachment].usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT), false, "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, false, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - - if ((attachments[attachment].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - ERR_FAIL_V_MSG(false, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), the D3D12 driver doesn't yet support using depth-stencil targets as input attachments."); - } - - attachment_last_pass[attachment] = i; - } - } - - if (pass->resolve_attachments.size() > 0) { - ERR_FAIL_COND_V_MSG(pass->resolve_attachments.size() != pass->color_attachments.size(), false, "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, false, "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]; - if (attachment != FramebufferPass::ATTACHMENT_UNUSED) { - ERR_FAIL_INDEX_V_MSG(attachment, attachments.size(), false, "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, false, "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(!(attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), false, "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, false, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - bool multisample = attachments[attachment].samples > TEXTURE_SAMPLES_1; - ERR_FAIL_COND_V_MSG(multisample, false, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachments can't be multisample."); - attachment_last_pass[attachment] = i; - } - } - - if (pass->depth_attachment != FramebufferPass::ATTACHMENT_UNUSED) { - int32_t attachment = pass->depth_attachment; - ERR_FAIL_INDEX_V_MSG(attachment, attachments.size(), false, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), depth attachment."); - ERR_FAIL_COND_V_MSG(!(attachments[attachment].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), false, "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, false, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - attachment_last_pass[attachment] = i; - - if (is_multisample_first) { - texture_samples = attachments[attachment].samples; - is_multisample_first = false; - } else { - ERR_FAIL_COND_V_MSG(texture_samples != attachments[attachment].samples, false, "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."); - } - } - - if (context->get_vrs_capabilities().ss_image_supported && pass->vrs_attachment != FramebufferPass::ATTACHMENT_UNUSED) { - int32_t attachment = pass->vrs_attachment; - ERR_FAIL_INDEX_V_MSG(attachment, attachments.size(), false, "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), VRS attachment."); - ERR_FAIL_COND_V_MSG(!(attachments[attachment].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT), false, "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, false, "Invalid framebuffer VRS attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - attachment_last_pass[attachment] = 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, false, "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, attachments.size(), false, "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; - } - } - - p_fb_format->pass_samples.push_back(texture_samples); - } - - if (p_fb_format->view_count > 1) { - const D3D12Context::MultiviewCapabilities capabilities = context->get_multiview_capabilities(); - - // For now this only works with multiview! - ERR_FAIL_COND_V_MSG(!capabilities.is_supported, ERR_UNAVAILABLE, "Multiview not supported"); - - // Make sure we limit this to the number of views we support. - ERR_FAIL_COND_V_MSG(p_fb_format->view_count > capabilities.max_view_count, ERR_UNAVAILABLE, "Hardware does not support requested number of views for Multiview render pass"); - } - - if (!ms_attachment_formats.is_empty()) { - LocalVector<DXGI_FORMAT> formats; - for (DXGI_FORMAT f : ms_attachment_formats) { - formats.push_back(f); - } - p_fb_format->max_supported_sample_count = _find_max_common_supported_sample_count(formats.ptr(), formats.size()); - } - - return true; -} - -uint32_t RenderingDeviceD3D12::_find_max_common_supported_sample_count(const DXGI_FORMAT *p_formats, uint32_t p_num_formats) { - uint32_t common = UINT32_MAX; - - for (uint32_t i = 0; i < p_num_formats; 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); - } -} - -RenderingDevice::FramebufferFormatID RenderingDeviceD3D12::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 RenderingDeviceD3D12::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { - _THREAD_SAFE_METHOD_ - - FramebufferFormat fb_format; - fb_format.attachments = p_attachments; - fb_format.passes = p_passes; - fb_format.view_count = p_view_count; - if (!_framebuffer_format_preprocess(&fb_format, p_view_count)) { - return INVALID_ID; - } - - FramebufferFormatID id = FramebufferFormatID(framebuffer_formats.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT)); - framebuffer_formats[id] = fb_format; - return id; -} - -RenderingDevice::FramebufferFormatID RenderingDeviceD3D12::framebuffer_format_create_empty(TextureSamples p_samples) { - _THREAD_SAFE_METHOD_ - - FramebufferFormat fb_format; - fb_format.passes.push_back(FramebufferPass()); - fb_format.pass_samples.push_back(p_samples); - - FramebufferFormatID id = FramebufferFormatID(framebuffer_formats.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT)); - framebuffer_formats[id] = fb_format; - return id; -} - -RenderingDevice::TextureSamples RenderingDeviceD3D12::framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass) { - HashMap<FramebufferFormatID, FramebufferFormat>::Iterator E = framebuffer_formats.find(p_format); - ERR_FAIL_NULL_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 RenderingDeviceD3D12::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; - - return framebuffer_owner.make_rid(framebuffer); -} - -RID RenderingDeviceD3D12::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); -} - -D3D12_RENDER_TARGET_VIEW_DESC RenderingDeviceD3D12::_make_rtv_for_texture(const RenderingDeviceD3D12::Texture *p_texture, uint32_t p_mipmap_offset, uint32_t p_layer_offset, uint32_t p_layers) { - D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = {}; - rtv_desc.Format = p_texture->srv_desc.Format; - - switch (p_texture->srv_desc.ViewDimension) { - case D3D12_SRV_DIMENSION_TEXTURE1D: { - rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D; - rtv_desc.Texture1D.MipSlice = p_texture->srv_desc.Texture1D.MostDetailedMip + p_mipmap_offset; - } break; - case D3D12_SRV_DIMENSION_TEXTURE1DARRAY: { - rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1DARRAY; - rtv_desc.Texture1DArray.MipSlice = p_texture->srv_desc.Texture1DArray.MostDetailedMip + p_mipmap_offset; - rtv_desc.Texture1DArray.FirstArraySlice = p_texture->srv_desc.Texture1DArray.FirstArraySlice + p_layer_offset; - rtv_desc.Texture1DArray.ArraySize = p_layers == UINT32_MAX ? p_texture->srv_desc.Texture1DArray.ArraySize : p_layers; - } break; - case D3D12_SRV_DIMENSION_TEXTURE2D: { - rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - rtv_desc.Texture2D.MipSlice = p_texture->srv_desc.Texture2D.MostDetailedMip + p_mipmap_offset; - rtv_desc.Texture2D.PlaneSlice = p_texture->srv_desc.Texture2D.PlaneSlice; - } break; - case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: { - rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; - rtv_desc.Texture2DArray.MipSlice = p_texture->srv_desc.Texture2DArray.MostDetailedMip + p_mipmap_offset; - rtv_desc.Texture2DArray.FirstArraySlice = p_texture->srv_desc.Texture2DArray.FirstArraySlice + p_layer_offset; - rtv_desc.Texture2DArray.ArraySize = p_layers == UINT32_MAX ? p_texture->srv_desc.Texture2DArray.ArraySize : p_layers; - rtv_desc.Texture2DArray.PlaneSlice = p_texture->srv_desc.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_texture->srv_desc.Texture2DMSArray.FirstArraySlice + p_layer_offset; - rtv_desc.Texture2DMSArray.ArraySize = p_layers == UINT32_MAX ? p_texture->srv_desc.Texture2DMSArray.ArraySize : p_layers; - } break; - case D3D12_SRV_DIMENSION_TEXTURE3D: { - rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D; - rtv_desc.Texture3D.MipSlice = p_texture->srv_desc.Texture3D.MostDetailedMip + p_mipmap_offset; - rtv_desc.Texture3D.FirstWSlice = 0; - rtv_desc.Texture3D.WSize = p_texture->depth; - } break; - default: { - ERR_FAIL_V_MSG(D3D12_RENDER_TARGET_VIEW_DESC(), "Can't create an RTV from an SRV whose view dimension is " + itos(p_texture->srv_desc.ViewDimension) + "."); - } - } - - return rtv_desc; -} - -D3D12_DEPTH_STENCIL_VIEW_DESC RenderingDeviceD3D12::_make_dsv_for_texture(const RenderingDeviceD3D12::Texture *p_texture) { - D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc = {}; - dsv_desc.Format = d3d12_formats[p_texture->format].dsv_format; - dsv_desc.Flags = D3D12_DSV_FLAG_NONE; - - switch (p_texture->srv_desc.ViewDimension) { - case D3D12_SRV_DIMENSION_TEXTURE1D: { - dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1D; - dsv_desc.Texture1D.MipSlice = p_texture->srv_desc.Texture1D.MostDetailedMip; - } break; - case D3D12_SRV_DIMENSION_TEXTURE1DARRAY: { - dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1DARRAY; - dsv_desc.Texture1DArray.MipSlice = p_texture->srv_desc.Texture1DArray.MostDetailedMip; - dsv_desc.Texture1DArray.FirstArraySlice = p_texture->srv_desc.Texture1DArray.FirstArraySlice; - dsv_desc.Texture1DArray.ArraySize = p_texture->srv_desc.Texture1DArray.ArraySize; - } break; - case D3D12_SRV_DIMENSION_TEXTURE2D: { - dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; - dsv_desc.Texture2D.MipSlice = p_texture->srv_desc.Texture2D.MostDetailedMip; - } break; - case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: { - dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY; - dsv_desc.Texture2DArray.MipSlice = p_texture->srv_desc.Texture2DArray.MostDetailedMip; - dsv_desc.Texture2DArray.FirstArraySlice = p_texture->srv_desc.Texture2DArray.FirstArraySlice; - dsv_desc.Texture2DArray.ArraySize = p_texture->srv_desc.Texture2DArray.ArraySize; - } break; - case D3D12_SRV_DIMENSION_TEXTURE2DMS: { - dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; - dsv_desc.Texture2DMS.UnusedField_NothingToDefine = p_texture->srv_desc.Texture2DMS.UnusedField_NothingToDefine; - } break; - case D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY: { - dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY; - dsv_desc.Texture2DMSArray.FirstArraySlice = p_texture->srv_desc.Texture2DMSArray.FirstArraySlice; - dsv_desc.Texture2DMSArray.ArraySize = p_texture->srv_desc.Texture2DMSArray.ArraySize; - } break; - default: { - ERR_FAIL_V_MSG(D3D12_DEPTH_STENCIL_VIEW_DESC(), "Can't create an RTV from an SRV whose view dimension is " + itos(p_texture->srv_desc.ViewDimension) + "."); - } - } - - return dsv_desc; -} - -RID RenderingDeviceD3D12::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()); - Vector<uint32_t> attachments_handle_inds; - attachments_handle_inds.resize(p_texture_attachments.size()); - Size2i size; - bool size_set = false; - int num_color = 0; - int num_depth = 0; - 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; - attachments_handle_inds.write[i] = UINT32_MAX; - } 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; - - bool is_vrs = texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && i == p_passes[0].vrs_attachment; - if (is_vrs) { - } else if ((texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - attachments_handle_inds.write[i] = num_color; - num_color++; - } else if ((texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - attachments_handle_inds.write[i] = num_depth; - num_depth++; - } else { - attachments_handle_inds.write[i] = UINT32_MAX; - } - } - 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.attachments_handle_inds = attachments_handle_inds; - framebuffer.size = size; - framebuffer.view_count = p_view_count; - - { - if (num_color) { - Error err = framebuffer.rtv_heap.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, num_color, false); - ERR_FAIL_COND_V(err, RID()); - } - DescriptorsHeap::Walker rtv_heap_walker = framebuffer.rtv_heap.make_walker(); - - if (num_depth) { - Error err = framebuffer.dsv_heap.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV, num_depth, false); - ERR_FAIL_COND_V(err, RID()); - } - DescriptorsHeap::Walker dsv_heap_walker = framebuffer.dsv_heap.make_walker(); - - for (int i = 0; i < p_texture_attachments.size(); i++) { - Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]); - if (!texture) { - continue; - } - - bool is_vrs = texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && i == p_passes[0].vrs_attachment; - if (is_vrs) { - } else if ((texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = _make_rtv_for_texture(texture); - device->CreateRenderTargetView(texture->resource, &rtv_desc, rtv_heap_walker.get_curr_cpu_handle()); - rtv_heap_walker.advance(); - } else if ((texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc = _make_dsv_for_texture(texture); - device->CreateDepthStencilView(texture->resource, &dsv_desc, dsv_heap_walker.get_curr_cpu_handle()); - dsv_heap_walker.advance(); - } - } - - DEV_ASSERT(rtv_heap_walker.is_at_eof()); - DEV_ASSERT(dsv_heap_walker.is_at_eof()); - } - - RID id = framebuffer_owner.make_rid(framebuffer); - - 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 RenderingDeviceD3D12::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 RenderingDeviceD3D12::framebuffer_is_valid(RID p_framebuffer) const { - _THREAD_SAFE_METHOD_ - - return framebuffer_owner.owns(p_framebuffer); -} - -void RenderingDeviceD3D12::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 RenderingDeviceD3D12::sampler_create(const SamplerState &p_state) { - _THREAD_SAFE_METHOD_ - - D3D12_SAMPLER_DESC sampler_desc = {}; - - 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 d3d12_filter_types[] = { - D3D12_FILTER_TYPE_POINT, // SAMPLER_FILTER_NEAREST. - D3D12_FILTER_TYPE_LINEAR, // SAMPLER_FILTER_LINEAR. - }; - sampler_desc.Filter = D3D12_ENCODE_BASIC_FILTER( - d3d12_filter_types[p_state.min_filter], - d3d12_filter_types[p_state.mag_filter], - d3d12_filter_types[p_state.mip_filter], - p_state.enable_compare ? D3D12_FILTER_REDUCTION_TYPE_COMPARISON : D3D12_FILTER_REDUCTION_TYPE_STANDARD); - } - - ERR_FAIL_INDEX_V(p_state.repeat_u, SAMPLER_REPEAT_MODE_MAX, RID()); - sampler_desc.AddressU = address_modes[p_state.repeat_u]; - ERR_FAIL_INDEX_V(p_state.repeat_v, SAMPLER_REPEAT_MODE_MAX, RID()); - sampler_desc.AddressV = address_modes[p_state.repeat_v]; - ERR_FAIL_INDEX_V(p_state.repeat_w, SAMPLER_REPEAT_MODE_MAX, RID()); - sampler_desc.AddressW = address_modes[p_state.repeat_w]; - - ERR_FAIL_INDEX_V(p_state.border_color, SAMPLER_BORDER_COLOR_MAX, RID()); - for (int i = 0; i < 4; i++) { - sampler_desc.BorderColor[i] = sampler_border_colors[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; - - ERR_FAIL_INDEX_V(p_state.compare_op, COMPARE_OP_MAX, RID()); - sampler_desc.ComparisonFunc = p_state.enable_compare ? compare_operators[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 sampler_owner.make_rid(sampler_desc); -} - -bool RenderingDeviceD3D12::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_ - - D3D12_FEATURE_DATA_FORMAT_SUPPORT srv_rtv_support = {}; - srv_rtv_support.Format = d3d12_formats[p_format].general_format; - HRESULT res = device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &srv_rtv_support, sizeof(srv_rtv_support)); - ERR_FAIL_COND_V_MSG(res, false, "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); - - return (srv_rtv_support.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE); -} - -/**********************/ -/**** VERTEX ARRAY ****/ -/**********************/ - -RID RenderingDeviceD3D12::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()); - - Buffer buffer; - D3D12_RESOURCE_STATES usage = D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; - if (p_use_as_storage) { - usage |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - } - Error err = _buffer_allocate(&buffer, p_size_bytes, usage, D3D12_HEAP_TYPE_DEFAULT); - 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); - } - - 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 RenderingDeviceD3D12::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.elements_desc.resize(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."); - - // SPIRV-Cross maps `layout(location = <N>) in` to `TEXCOORD<N>`. - vdcache.elements_desc.write[i].SemanticName = "TEXCOORD"; // SPIRV-Cross will apply TEXCOORD semantic to vertex attributes. - vdcache.elements_desc.write[i].SemanticIndex = p_vertex_formats[i].location; - vdcache.elements_desc.write[i].Format = d3d12_formats[p_vertex_formats[i].format].general_format; - vdcache.elements_desc.write[i].InputSlot = i; // TODO: Can the same slot be used if data comes from the same buffer (regardless format)? - vdcache.elements_desc.write[i].AlignedByteOffset = p_vertex_formats[i].offset; - if (p_vertex_formats[i].frequency == VERTEX_FREQUENCY_INSTANCE) { - vdcache.elements_desc.write[i].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA; - vdcache.elements_desc.write[i].InstanceDataStepRate = 1; - } else { - vdcache.elements_desc.write[i].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; - vdcache.elements_desc.write[i].InstanceDataStepRate = 0; - } - used_locations.insert(p_vertex_formats[i].location); - } - - 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 RenderingDeviceD3D12::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()) { - ERR_FAIL_COND_V(p_offsets.size() != p_src_buffers.size(), RID()); - } - - 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. - HashSet<Buffer *> unique_buffers; - for (int i = 0; i < p_src_buffers.size(); i++) { - Buffer *buffer = vertex_buffer_owner.get_or_null(p_src_buffers[i]); - - const VertexAttribute &atf = vd.vertex_formats[i]; - - // Validate with buffer. - { - 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); - } - } - - unique_buffers.insert(buffer); - - D3D12_VERTEX_BUFFER_VIEW view = {}; - uint64_t data_offset = p_offsets.is_empty() ? 0 : p_offsets[i]; - view.BufferLocation = buffer->resource->GetGPUVirtualAddress() + data_offset; - view.SizeInBytes = buffer->size; - view.StrideInBytes = atf.stride; - vertex_array.views.push_back(view); - } - - for (Buffer *buffer : unique_buffers) { - vertex_array.unique_buffers.push_back(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 RenderingDeviceD3D12::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_format = (p_format == INDEX_BUFFER_FORMAT_UINT16) ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; - 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 - Error err = _buffer_allocate(&index_buffer, size_bytes, D3D12_RESOURCE_STATE_INDEX_BUFFER, D3D12_HEAP_TYPE_DEFAULT); - 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(&index_buffer, 0, r, data_size); - } - 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 RenderingDeviceD3D12::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.buffer = index_buffer; - index_array.max_index = index_buffer->max_index; - index_array.offset = p_index_offset; - index_array.indices = p_index_count; - index_array.supports_restart_indices = index_buffer->supports_restart_indices; - index_array.view.BufferLocation = index_buffer->resource->GetGPUVirtualAddress(); - index_array.view.SizeInBytes = p_index_count * (index_buffer->index_format == DXGI_FORMAT_R16_UINT ? 2 : 4); - index_array.view.Format = index_buffer->index_format; - - 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 + 1] = { - "Sampler", "CombinedSampler", "Texture", "Image", "TextureBuffer", "SamplerTextureBuffer", "ImageBuffer", "UniformBuffer", "StorageBuffer", "InputAttachment", "N/A" -}; - -static uint32_t shader_stage_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, -}; - -String RenderingDeviceD3D12::_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].uniforms.size(); j++) { - const UniformInfo &ui = shader->sets[i].uniforms[j].info; - 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; -} - -uint32_t RenderingDeviceD3D12::_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_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_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_stage_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 RenderingDeviceD3D12::_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; -} - -// Version 1: Initial. -// Version 2: 64-bit vertex input mask. -#define SHADER_BINARY_VERSION 2 - -String RenderingDeviceD3D12::shader_get_binary_cache_key() const { - return "D3D12-SV" + itos(SHADER_BINARY_VERSION); -} - -enum RootSignatureLocationType { - RS_LOC_TYPE_RESOURCE, - RS_LOC_TYPE_SAMPLER, -}; - -enum ResourceClass { - RES_CLASS_INVALID, - RES_CLASS_CBV, - RES_CLASS_SRV, - RES_CLASS_UAV, -}; - -// 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 RenderingDeviceD3D12ShaderBinaryDataBinding { - // - Phase 1. - 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; - // - Phase 2. - uint32_t res_class; - uint32_t has_sampler; - uint32_t dxil_stages; - 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 RenderingDeviceD3D12ShaderBinaryDataBinding &p_other) const { - return binding < p_other.binding; - } -}; - -struct RenderingDeviceD3D12ShaderBinarySpecializationConstant { - // - Phase 1. - uint32_t type; - uint32_t constant_id; - union { - uint32_t int_value; - float float_value; - bool bool_value; - }; - // - Phase 2. - uint64_t stages_bit_offsets[D3D12_BITCODE_OFFSETS_NUM_STAGES]; -}; - -struct RenderingDeviceD3D12ShaderBinaryData { - uint64_t vertex_input_mask; - uint32_t fragment_output_mask; - uint32_t specialization_constants_count; - uint32_t spirv_specialization_constants_ids_mask; - uint32_t is_compute; - uint32_t compute_local_size[3]; - uint32_t set_count; - uint32_t push_constant_size; - uint32_t dxil_push_constant_stages; // Phase 2. - uint32_t nir_runtime_data_root_param_idx; // Phase 2. - uint32_t stage_count; - uint32_t shader_name_len; - uint32_t root_signature_len; - uint32_t root_signature_crc; -}; - -Vector<uint8_t> RenderingDeviceD3D12::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>(); - } - - // Collect reflection data into binary data. - RenderingDeviceD3D12ShaderBinaryData binary_data = {}; - Vector<Vector<RenderingDeviceD3D12ShaderBinaryDataBinding>> uniform_info; - Vector<RenderingDeviceD3D12ShaderBinarySpecializationConstant> 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; - binary_data.nir_runtime_data_root_param_idx = UINT32_MAX; - binary_data.stage_count = p_spirv.size(); - - for (const Vector<SpirvReflectionData::Uniform> &spirv_set : spirv_data.uniforms) { - Vector<RenderingDeviceD3D12ShaderBinaryDataBinding> set_bindings; - for (const SpirvReflectionData::Uniform &spirv_uniform : spirv_set) { - RenderingDeviceD3D12ShaderBinaryDataBinding 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) { - RenderingDeviceD3D12ShaderBinarySpecializationConstant 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; - 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 (int 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); - - { - 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 *nir_shader = spirv_to_nir( - (const uint32_t *)p_spirv[i].spir_v.ptr(), - p_spirv[i].spir_v.size() / sizeof(uint32_t), - nullptr, - 0, - SPIRV_TO_MESA_STAGES[stage], - entry_point, - dxil_spirv_nir_get_spirv_options(), &nir_options); - if (!nir_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(nir_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(nir_shader); - bool requires_runtime_data = {}; - dxil_spirv_nir_passes(nir_shader, &dxil_runtime_conf, &requires_runtime_data); - - stages_nir_shaders[stage] = nir_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 (int i = 0; i < p_spirv.size(); i++) { - ShaderStage stage = (ShaderStage)p_spirv[i].shader_stage; - - struct ShaderData { - ShaderStage stage; - RenderingDeviceD3D12ShaderBinaryData &binary_data; - Vector<Vector<RenderingDeviceD3D12ShaderBinaryDataBinding>> &uniform_info; - Vector<RenderingDeviceD3D12ShaderBinarySpecializationConstant> &specialization_constants; - } shader_data{ stage, binary_data, uniform_info, 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 = *(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.binary_data.dxil_push_constant_stages |= (1 << shader_data.stage); - } else if (p_register == RUNTIME_DATA_REGISTER && p_space == RUNTIME_DATA_SPACE) { - DEV_ASSERT(res_class == RES_CLASS_CBV); - shader_data.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.uniform_info.size()); - bool found = false; - for (int i = 0; i < shader_data.uniform_info[set].size(); i++) { - if (shader_data.uniform_info[set][i].binding != binding) { - continue; - } - - RenderingDeviceD3D12ShaderBinaryDataBinding &binding_info = shader_data.uniform_info.write[set].write[i]; - - binding_info.dxil_stages |= (1 << shader_data.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 = *(ShaderData *)p_data; - - bool found = false; - for (int i = 0; i < shader_data.specialization_constants.size(); i++) { - if (shader_data.specialization_constants[i].constant_id != p_sc_id) { - continue; - } - - uint32_t offset_idx = shader_stage_bit_offset_indices[shader_data.stage]; - DEV_ASSERT(shader_data.specialization_constants.write[i].stages_bit_offsets[offset_idx] == 0); - shader_data.specialization_constants.write[i].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 = *(ShaderData *)p_data; - uint32_t offset_idx = shader_stage_bit_offset_indices[shader_data.stage]; - for (int i = 0; i < shader_data.specialization_constants.size(); i++) { - if (shader_data.specialization_constants.write[i].stages_bit_offsets[offset_idx] == 0) { - // This SC has been optimized out from this stage. - continue; - } - shader_data.specialization_constants.write[i].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 RenderingDeviceD3D12ShaderBinarySpecializationConstant &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<RenderingDeviceD3D12ShaderBinaryDataBinding::RootSignatureLocation *> root_sig_locations; - }; - Vector<TraceableDescriptorTable> resource_tables_maps; - Vector<TraceableDescriptorTable> sampler_tables_maps; - - for (int set = 0; set < uniform_info.size(); set++) { - bool first_resource_in_set = true; - bool first_sampler_in_set = true; - uniform_info.write[set].sort(); - for (int i = 0; i < uniform_info[set].size(); i++) { - const RenderingDeviceD3D12ShaderBinaryDataBinding &binding = uniform_info[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, - RenderingDeviceD3D12ShaderBinaryDataBinding::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, - uniform_info[set][i].dxil_stages, - uniform_info.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, - uniform_info[set][i].dxil_stages, - uniform_info.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(res, Vector<uint8_t>(), - "Serialization of root signature failed with error " + vformat("0x%08ux", 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 (int 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(RenderingDeviceD3D12ShaderBinaryData); - - 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(RenderingDeviceD3D12ShaderBinaryDataBinding); - } - - total_size += sizeof(RenderingDeviceD3D12ShaderBinarySpecializationConstant) * 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(SHADER_BINARY_VERSION, binptr + offset); - offset += sizeof(uint32_t); - encode_uint32(sizeof(RenderingDeviceD3D12ShaderBinaryData), binptr + offset); - offset += sizeof(uint32_t); - memcpy(binptr + offset, &binary_data, sizeof(RenderingDeviceD3D12ShaderBinaryData)); - offset += sizeof(RenderingDeviceD3D12ShaderBinaryData); - - 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(RenderingDeviceD3D12ShaderBinaryDataBinding) * count); - offset += sizeof(RenderingDeviceD3D12ShaderBinaryDataBinding) * count; - } - } - - if (specialization_constants.size()) { - memcpy(binptr + offset, specialization_constants.ptr(), sizeof(RenderingDeviceD3D12ShaderBinarySpecializationConstant) * specialization_constants.size()); - offset += sizeof(RenderingDeviceD3D12ShaderBinarySpecializationConstant) * 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; -} - -RID RenderingDeviceD3D12::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(RenderingDeviceD3D12ShaderBinaryData), 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 RenderingDeviceD3D12ShaderBinaryData &binary_data = *(reinterpret_cast<const RenderingDeviceD3D12ShaderBinaryData *>(binptr + 12)); - - 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<Shader::Set> set_info; - set_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 RenderingDeviceD3D12ShaderBinaryDataBinding *set_ptr = reinterpret_cast<const RenderingDeviceD3D12ShaderBinaryDataBinding *>(binptr + read_offset); - uint32_t set_size = set_count * sizeof(RenderingDeviceD3D12ShaderBinaryDataBinding); - ERR_FAIL_COND_V(read_offset + set_size >= binsize, RID()); - - for (uint32_t j = 0; j < set_count; j++) { - Shader::ShaderUniformInfo sui; - - sui.info.type = UniformType(set_ptr[j].type); - sui.info.writable = set_ptr[j].writable; - sui.info.length = set_ptr[j].length; - sui.info.binding = set_ptr[j].binding; - - sui.binding.stages = set_ptr[j].dxil_stages; - sui.binding.res_class = (ResourceClass)set_ptr[j].res_class; - static_assert(sizeof(UniformBindingInfo::root_sig_locations) == sizeof(RenderingDeviceD3D12ShaderBinaryDataBinding::root_sig_locations)); - memcpy(&sui.binding.root_sig_locations, &set_ptr[j].root_sig_locations, sizeof(UniformBindingInfo::root_sig_locations)); - - set_info.write[i].uniforms.push_back(sui); - - if (sui.binding.root_sig_locations.resource.root_param_idx != UINT32_MAX) { - set_info.write[i].num_root_params.resources++; - } - if (sui.binding.root_sig_locations.sampler.root_param_idx != UINT32_MAX) { - set_info.write[i].num_root_params.samplers++; - } - } - - read_offset += set_size; - } - - ERR_FAIL_COND_V(read_offset + binary_data.specialization_constants_count * sizeof(RenderingDeviceD3D12ShaderBinarySpecializationConstant) >= binsize, RID()); - - Vector<Shader::SpecializationConstant> specialization_constants; - - for (uint32_t i = 0; i < binary_data.specialization_constants_count; i++) { - const RenderingDeviceD3D12ShaderBinarySpecializationConstant &src_sc = *(reinterpret_cast<const RenderingDeviceD3D12ShaderBinarySpecializationConstant *>(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; - memcpy(sc.stages_bit_offsets, src_sc.stages_bit_offsets, sizeof(sc.stages_bit_offsets)); - specialization_constants.push_back(sc); - - read_offset += sizeof(RenderingDeviceD3D12ShaderBinarySpecializationConstant); - } - - HashMap<ShaderStage, Vector<uint8_t>> stages_bytecode; - - 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 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, RID()); - 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, RID()); - - read_offset += zstd_size; - } - - const uint8_t *root_sig_data_ptr = binptr + read_offset; - - ComPtr<ID3D12RootSignatureDeserializer> root_sig_deserializer; - HRESULT res = D3D12CreateRootSignatureDeserializer(root_sig_data_ptr, binary_data.root_signature_len, IID_PPV_ARGS(root_sig_deserializer.GetAddressOf())); - ERR_FAIL_COND_V_MSG(res, RID(), "D3D12CreateRootSignatureDeserializer failed with error " + vformat("0x%08ux", res) + "."); - read_offset += binary_data.root_signature_len; - - ERR_FAIL_COND_V(read_offset != binsize, RID()); - - // TODO: Need to lock? - _THREAD_SAFE_METHOD_ - - ComPtr<ID3D12RootSignature> root_signature; - res = device->CreateRootSignature(0, root_sig_data_ptr, binary_data.root_signature_len, IID_PPV_ARGS(root_signature.GetAddressOf())); - ERR_FAIL_COND_V_MSG(res, RID(), "CreateRootSignature failed with error " + vformat("0x%08ux", res) + "."); - - 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->spirv_push_constant_size = binary_data.push_constant_size; - shader->dxil_push_constant_size = binary_data.dxil_push_constant_stages ? binary_data.push_constant_size : 0; - shader->nir_runtime_data_root_param_idx = binary_data.nir_runtime_data_root_param_idx; - 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->spirv_specialization_constants_ids_mask = binary_data.spirv_specialization_constants_ids_mask; - shader->name = name; - shader->root_signature = root_signature; - shader->root_signature_deserializer = root_sig_deserializer; - shader->root_signature_desc = root_sig_deserializer->GetRootSignatureDesc(); - shader->root_signature_crc = binary_data.root_signature_crc; - shader->stages_bytecode = stages_bytecode; - - // Proceed to create descriptor sets. - for (uint32_t i = 0; i < binary_data.set_count; i++) { - uint32_t format = 0; // No format, default. - - Shader::Set &set = set_info.write[i]; - if (set.uniforms.size()) { - // Has data, needs an actual format;. - UniformSetFormat usformat; - usformat.uniform_info.resize(set.uniforms.size()); - for (int j = 0; j < set.uniforms.size(); j++) { - usformat.uniform_info.write[j] = set.uniforms[j].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; - E = uniform_set_format_cache.insert(usformat, format); - uniform_set_format_cache_reverse.push_back(E); - DEV_ASSERT(uniform_set_format_cache_reverse.size() == uniform_set_format_cache.size()); - } - } - - shader->sets.push_back(set); - shader->set_formats.push_back(format); - } - -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - return id; -} - -RID RenderingDeviceD3D12::shader_create_placeholder() { - Shader shader; - return shader_owner.make_rid(shader); -} - -uint64_t RenderingDeviceD3D12::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 RenderingDeviceD3D12::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, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, D3D12_HEAP_TYPE_DEFAULT); - 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); - } - RID id = uniform_buffer_owner.make_rid(buffer); -#ifdef DEV_ENABLED - set_resource_name(id, "RID:" + itos(id.get_id())); -#endif - return id; -} - -RID RenderingDeviceD3D12::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; - D3D12_RESOURCE_STATES states = D3D12_RESOURCE_STATE_COPY_SOURCE | D3D12_RESOURCE_STATE_COPY_DEST | D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - if (p_usage.has_flag(STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT)) { - states |= D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT; - } - Error err = _buffer_allocate(&buffer, p_size_bytes, states, D3D12_HEAP_TYPE_DEFAULT); - 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); - } - return storage_buffer_owner.make_rid(buffer); -} - -RID RenderingDeviceD3D12::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, D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE, D3D12_HEAP_TYPE_DEFAULT); - 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); - } - - // 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; -} - -Error RenderingDeviceD3D12::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(res, ERR_CANT_CREATE, "CreateDescriptorHeap failed with error " + vformat("0x%08ux", res) + "."); - - return OK; -} - -RenderingDeviceD3D12::DescriptorsHeap::Walker RenderingDeviceD3D12::DescriptorsHeap::make_walker() const { - Walker walker; - walker.handle_size = handle_size; - walker.handle_count = desc.NumDescriptors; - if (heap) { - walker.first_cpu_handle = heap->GetCPUDescriptorHandleForHeapStart(); - if ((desc.Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)) { - walker.first_gpu_handle = heap->GetGPUDescriptorHandleForHeapStart(); - } - } - return walker; -} - -void RenderingDeviceD3D12::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 RenderingDeviceD3D12::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 RenderingDeviceD3D12::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 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; - } - } -} - -RID RenderingDeviceD3D12::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].uniforms.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.uniforms.size(); - const Shader::ShaderUniformInfo *set_uniforms = set.uniforms.ptr(); - - // Do a first pass to count resources and samplers, and error checking. - uint32_t num_resource_descs = 0; - uint32_t num_sampler_descs = 0; - LocalVector<int> uniform_indices; - uniform_indices.resize(set_uniform_count); - for (uint32_t i = 0; i < set_uniform_count; i++) { - const UniformInfo &set_uniform = set_uniforms[i].info; - 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."); - uniform_indices[i] = uniform_idx; - - 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] + "'."); - - // 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; - _add_descriptor_count_for_uniform(uniform.uniform_type, set_uniform.length, true, num_resource_descs, num_sampler_descs, srv_uav_ambiguity); - } - - struct { - DescriptorsHeap resources; - DescriptorsHeap samplers; - } desc_heaps; -#ifdef DEV_ENABLED - LocalVector<UniformSet::ResourceDescInfo> resources_desc_info; -#endif - - if (num_resource_descs) { - Error err = desc_heaps.resources.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, num_resource_descs, false); - ERR_FAIL_COND_V(err, RID()); - } - if (num_sampler_descs) { - Error err = desc_heaps.samplers.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, num_sampler_descs, false); - ERR_FAIL_COND_V(err, RID()); - } - struct { - DescriptorsHeap::Walker resources; - DescriptorsHeap::Walker samplers; - } desc_heap_walkers; - desc_heap_walkers.resources = desc_heaps.resources.make_walker(); - desc_heap_walkers.samplers = desc_heaps.samplers.make_walker(); - - // Used for verification to make sure a uniform set does not use a framebuffer bound texture. - LocalVector<UniformSet::AttachableTexture> attachable_textures; - struct RIDState { - bool is_buffer = false; - uint64_t shader_uniform_idx_mask = 0; - ResourceState state; - }; - HashMap<Resource *, RIDState> resource_states; - - for (uint32_t i = 0; i < set_uniform_count; i++) { - const Shader::ShaderUniformInfo &set_uniform = set_uniforms[i]; - const Uniform &uniform = uniforms[uniform_indices[i]]; - - // Stages defined in the shader may be missing for a uniform due to the optimizer, - // but the opposite (extraneous stages present in the uniform stages mask) would be an error. - DEV_ASSERT(!(shader->is_compute && (set_uniform.binding.stages & (SHADER_STAGE_VERTEX_BIT | SHADER_STAGE_FRAGMENT_BIT)))); - DEV_ASSERT(!(!shader->is_compute && (set_uniform.binding.stages & SHADER_STAGE_COMPUTE_BIT))); - - switch (uniform.uniform_type) { - case UNIFORM_TYPE_SAMPLER: { - if (uniform.get_id_count() != (uint32_t)set_uniform.info.length) { - if (set_uniform.info.length > 1) { - ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.info.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()) + ")."); - } - } - - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - D3D12_SAMPLER_DESC *sampler_desc = sampler_owner.get_or_null(uniform.get_id(j)); - ERR_FAIL_COND_V_MSG(!sampler_desc, RID(), "Sampler (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid sampler."); - - device->CreateSampler(sampler_desc, desc_heap_walkers.samplers.get_curr_cpu_handle()); - desc_heap_walkers.samplers.advance(); - } - } break; - case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { - if (uniform.get_id_count() != (uint32_t)set_uniform.info.length * 2) { - if (set_uniform.info.length > 1) { - ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.info.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()) + ")."); - } - } - - for (uint32_t j = 0; j < uniform.get_id_count(); j += 2) { - D3D12_SAMPLER_DESC *sampler_desc = sampler_owner.get_or_null(uniform.get_id(j)); - ERR_FAIL_COND_V_MSG(!sampler_desc, RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid sampler."); - - RID rid = uniform.get_id(j + 1); - Texture *texture = texture_owner.get_or_null(rid); - ERR_FAIL_COND_V_MSG(!texture, RID(), "SamplerTexture (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."); - - device->CreateSampler(sampler_desc, desc_heap_walkers.samplers.get_curr_cpu_handle()); - desc_heap_walkers.samplers.advance(); - device->CreateShaderResourceView(texture->resource, &texture->srv_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); -#ifdef DEV_ENABLED - resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_SRV, texture->srv_desc.ViewDimension }); -#endif - desc_heap_walkers.resources.advance(); - - RIDState &rs = resource_states[texture]; - rs.shader_uniform_idx_mask |= ((uint64_t)1 << i); - rs.state.extend(D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE); - - 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.info.binding; - attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.get_id(j + 1); - attachable_textures.push_back(attachable_texture); - } - - DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); - } - } break; - case UNIFORM_TYPE_TEXTURE: { - if (uniform.get_id_count() != (uint32_t)set_uniform.info.length) { - if (set_uniform.info.length > 1) { - ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.info.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()) + ")."); - } - } - - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - RID rid = uniform.get_id(j); - Texture *texture = texture_owner.get_or_null(rid); - ERR_FAIL_COND_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."); - - device->CreateShaderResourceView(texture->resource, &texture->srv_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); -#ifdef DEV_ENABLED - resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_SRV, texture->srv_desc.ViewDimension }); -#endif - desc_heap_walkers.resources.advance(); - - RIDState &rs = resource_states[texture]; - rs.shader_uniform_idx_mask |= ((uint64_t)1 << i); - rs.state.extend(D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE); - - 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.info.binding; - attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.get_id(j); - attachable_textures.push_back(attachable_texture); - } - - DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); - } - } break; - case UNIFORM_TYPE_IMAGE: { - if (uniform.get_id_count() != (uint32_t)set_uniform.info.length) { - if (set_uniform.info.length > 1) { - ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.info.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()) + ")."); - } - } - - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - RID rid = uniform.get_id(j); - Texture *texture = texture_owner.get_or_null(rid); - - ERR_FAIL_COND_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."); - - RIDState &rs = resource_states[texture]; - rs.shader_uniform_idx_mask |= ((uint64_t)1 << i); - rs.state.extend(D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - } - - // SRVs first. [[SRV_UAV_AMBIGUITY]] - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - RID rid = uniform.get_id(j); - Texture *texture = texture_owner.get_or_null(rid); - - ERR_FAIL_COND_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."); - - device->CreateShaderResourceView(texture->resource, &texture->srv_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); -#ifdef DEV_ENABLED - resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_SRV, texture->srv_desc.ViewDimension }); -#endif - desc_heap_walkers.resources.advance(); - - DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); - } - - // UAVs then. [[SRV_UAV_AMBIGUITY]] - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - RID rid = uniform.get_id(j); - Texture *texture = texture_owner.get_or_null(rid); - - device->CreateUnorderedAccessView(texture->resource, nullptr, &texture->uav_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); -#ifdef DEV_ENABLED - resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_UAV, {} }); -#endif - desc_heap_walkers.resources.advance(); - } - } break; - case UNIFORM_TYPE_TEXTURE_BUFFER: { - if (uniform.get_id_count() != (uint32_t)set_uniform.info.length) { - if (set_uniform.info.length > 1) { - ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.info.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()) + ")."); - } - } - - 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_COND_V_MSG(!buffer, RID(), "Texture Buffer (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture buffer."); - - CRASH_NOW_MSG("Unimplemented!"); - } - } break; - case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: { - CRASH_NOW(); - if (uniform.get_id_count() != (uint32_t)set_uniform.info.length * 2) { - if (set_uniform.info.length > 1) { - ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.info.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()) + ")."); - } - } - - for (uint32_t j = 0; j < uniform.get_id_count(); j += 2) { - D3D12_SAMPLER_DESC *sampler_desc = sampler_owner.get_or_null(uniform.get_id(j)); - ERR_FAIL_COND_V_MSG(!sampler_desc, 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)); - ERR_FAIL_COND_V_MSG(!buffer, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid texture buffer."); - - device->CreateSampler(sampler_desc, desc_heap_walkers.samplers.get_curr_cpu_handle()); - desc_heap_walkers.samplers.advance(); - - CRASH_NOW_MSG("Unimplemented!"); - } - } 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)."); - - RID rid = uniform.get_id(0); - Buffer *buffer = uniform_buffer_owner.get_or_null(rid); - ERR_FAIL_COND_V_MSG(!buffer, RID(), "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); - - ERR_FAIL_COND_V_MSG(buffer->size < (uint32_t)set_uniform.info.length, RID(), - "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " is smaller than size of shader uniform: (" + itos(set_uniform.info.length) + ")."); - - D3D12_CONSTANT_BUFFER_VIEW_DESC cbv_desc = {}; - cbv_desc.BufferLocation = buffer->resource->GetGPUVirtualAddress(); - cbv_desc.SizeInBytes = ALIGN(buffer->size, 256); - device->CreateConstantBufferView(&cbv_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); - desc_heap_walkers.resources.advance(); -#ifdef DEV_ENABLED - resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_CBV, {} }); -#endif - - RIDState &rs = resource_states[buffer]; - rs.is_buffer = true; - rs.shader_uniform_idx_mask |= ((uint64_t)1 << i); - rs.state.extend(D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); - } 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)."); - - RID rid = uniform.get_id(0); - Buffer *buffer = nullptr; - - if (storage_buffer_owner.owns(rid)) { - buffer = storage_buffer_owner.get_or_null(rid); - } else if (vertex_buffer_owner.owns(rid)) { - buffer = vertex_buffer_owner.get_or_null(rid); - // Due to [[SRV_UAV_AMBIGUITY]] we can't make this check because it wouldn't make sense in the case of an SRV (r/o storage buffer). - //ERR_FAIL_COND_V_MSG(!(buffer->usage & D3D12_RESOURCE_STATE_UNORDERED_ACCESS), RID(), "Vertex buffer supplied (binding: " + itos(uniform.binding) + ") was not created with storage flag."); - } - ERR_FAIL_COND_V_MSG(!buffer, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); - - // If 0, then it's sized at link time. - ERR_FAIL_COND_V_MSG(set_uniform.info.length > 0 && buffer->size != (uint32_t)set_uniform.info.length, RID(), - "Storage buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " does not match size of shader uniform: (" + itos(set_uniform.info.length) + ")."); - - RIDState &rs = resource_states[buffer]; - rs.shader_uniform_idx_mask |= ((uint64_t)1 << i); - rs.is_buffer = true; - rs.state.extend(D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - - // 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 = (buffer->size + 3) / 4; - srv_desc.Buffer.StructureByteStride = 0; - srv_desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; - device->CreateShaderResourceView(buffer->resource, &srv_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); -#ifdef DEV_ENABLED - 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 ((buffer->usage & D3D12_RESOURCE_STATE_UNORDERED_ACCESS)) { - 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 = (buffer->size + 3) / 4; - uav_desc.Buffer.StructureByteStride = 0; - uav_desc.Buffer.CounterOffsetInBytes = 0; - uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW; - device->CreateUnorderedAccessView(buffer->resource, nullptr, &uav_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); -#ifdef DEV_ENABLED - 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(); - } - } 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.info.length) { - if (set_uniform.info.length > 1) { - ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.info.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()) + ")."); - } - } - - for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - RID rid = uniform.get_id(j); - Texture *texture = texture_owner.get_or_null(rid); - ERR_FAIL_COND_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."); - - device->CreateShaderResourceView(texture->resource, &texture->srv_desc, desc_heap_walkers.resources.get_curr_cpu_handle()); -#ifdef DEV_ENABLED - resources_desc_info.push_back({ D3D12_DESCRIPTOR_RANGE_TYPE_SRV, texture->srv_desc.ViewDimension }); -#endif - desc_heap_walkers.resources.advance(); - - RIDState &rs = resource_states[texture]; - rs.shader_uniform_idx_mask |= ((uint64_t)1 << i); - rs.state.extend(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - - DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); - } - } break; - default: { - } - } - } - - DEV_ASSERT(desc_heap_walkers.resources.is_at_eof()); - DEV_ASSERT(desc_heap_walkers.samplers.is_at_eof()); - - UniformSet uniform_set; - uniform_set.desc_heaps.resources = desc_heaps.resources; - uniform_set.desc_heaps.samplers = desc_heaps.samplers; - uniform_set.format = shader->set_formats[p_shader_set]; - uniform_set.attachable_textures = attachable_textures; - uniform_set.shader_set = p_shader_set; - uniform_set.shader_id = p_shader; -#ifdef DEV_ENABLED - uniform_set._resources_desc_info = resources_desc_info; - uniform_set._shader = shader; -#endif - - { - uniform_set.resource_states.resize(resource_states.size()); - uint32_t i = 0; - for (const KeyValue<Resource *, RIDState> &E : resource_states) { - UniformSet::StateRequirement sr; - sr.resource = E.key; - sr.is_buffer = E.value.is_buffer; - sr.states = E.value.state.get_state_mask(); - sr.shader_uniform_idx_mask = E.value.shader_uniform_idx_mask; - uniform_set.resource_states.write[i] = sr; - i++; - } - } - - RID id = uniform_set_owner.make_rid(uniform_set); - // 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)); - } - } - - return id; -} - -bool RenderingDeviceD3D12::uniform_set_is_valid(RID p_uniform_set) { - return uniform_set_owner.owns(p_uniform_set); -} - -void RenderingDeviceD3D12::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 RenderingDeviceD3D12::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"); - - Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer); - if (!src_buffer) { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Source buffer argument is not a valid buffer of any type."); - } - - Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer); - 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. - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - _resource_transition_batch(src_buffer, 0, 1, D3D12_RESOURCE_STATE_COPY_SOURCE); - _resource_transition_batch(src_buffer, 0, 1, D3D12_RESOURCE_STATE_COPY_DEST); - _resource_transitions_flush(command_list); - - command_list->CopyBufferRegion(dst_buffer->resource, p_dst_offset, src_buffer->resource, p_src_offset, p_size); - - return OK; -} - -Error RenderingDeviceD3D12::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"); - - Buffer *buffer = _get_buffer_from_owner(p_buffer); - 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."); - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - _resource_transition_batch(buffer, 0, 1, D3D12_RESOURCE_STATE_COPY_DEST); - _resource_transitions_flush(command_list); - - Error err = _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, p_post_barrier); - if (err) { - return err; - } - - return OK; -} - -Error RenderingDeviceD3D12::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"); - - Buffer *buffer = _get_buffer_from_owner(p_buffer); - 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."); - - if (frames[frame].desc_heap_walkers.resources.is_at_eof()) { - if (!frames[frame].desc_heaps_exhausted_reported.resources) { - frames[frame].desc_heaps_exhausted_reported.resources = true; - ERR_FAIL_V_MSG(ERR_BUSY, - "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 ERR_BUSY; - } - } - if (frames[frame].desc_heap_walkers.aux.is_at_eof()) { - if (!frames[frame].desc_heaps_exhausted_reported.aux) { - frames[frame].desc_heaps_exhausted_reported.aux = true; - ERR_FAIL_V_MSG(ERR_BUSY, - "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 ERR_BUSY; - } - } - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - _resource_transition_batch(buffer, 0, 1, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - _resource_transitions_flush(command_list); - - 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 = (buffer->size + 3) / 4; - uav_desc.Buffer.StructureByteStride = 0; - uav_desc.Buffer.CounterOffsetInBytes = 0; - uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW; - device->CreateUnorderedAccessView( - buffer->resource, - nullptr, - &uav_desc, - frames[frame].desc_heap_walkers.aux.get_curr_cpu_handle()); - - device->CopyDescriptorsSimple( - 1, - frames[frame].desc_heap_walkers.resources.get_curr_cpu_handle(), - frames[frame].desc_heap_walkers.aux.get_curr_cpu_handle(), - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - - static const UINT values[4] = {}; - command_list->ClearUnorderedAccessViewUint( - frames[frame].desc_heap_walkers.resources.get_curr_gpu_handle(), - frames[frame].desc_heap_walkers.aux.get_curr_cpu_handle(), - buffer->resource, - values, - 0, - nullptr); - - frames[frame].desc_heap_walkers.resources.advance(); - frames[frame].desc_heap_walkers.aux.advance(); - - return OK; -} - -Vector<uint8_t> RenderingDeviceD3D12::buffer_get_data(RID p_buffer, uint32_t p_offset, uint32_t p_size) { - _THREAD_SAFE_METHOD_ - - // Get the vulkan buffer and the potential stage/access possible. - Buffer *buffer = _get_buffer_from_owner(p_buffer); - 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."); - } - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - // 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."); - } - - _resource_transition_batch(buffer, 0, 1, D3D12_RESOURCE_STATE_COPY_SOURCE); - _resource_transitions_flush(command_list); - - Buffer tmp_buffer; - Error err = _buffer_allocate(&tmp_buffer, p_size, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_HEAP_TYPE_READBACK); - ERR_FAIL_COND_V(err != OK, Vector<uint8_t>()); - - command_list->CopyBufferRegion(tmp_buffer.resource, 0, buffer->resource, p_offset, p_size); - - // Flush everything so memory can be safely mapped. - _flush(true); - - void *buffer_mem; - HRESULT res = tmp_buffer.resource->Map(0, &VOID_RANGE, &buffer_mem); - ERR_FAIL_COND_V_MSG(res, Vector<uint8_t>(), "Map failed with error " + vformat("0x%08ux", res) + "."); - - Vector<uint8_t> buffer_data; - { - buffer_data.resize(buffer->size); - uint8_t *w = buffer_data.ptrw(); - memcpy(w, buffer_mem, buffer->size); - } - - tmp_buffer.resource->Unmap(0, &VOID_RANGE); - - _buffer_free(&tmp_buffer); - - return buffer_data; -} - -/*******************/ -/**** PIPELINES ****/ -/*******************/ - -Error RenderingDeviceD3D12::_apply_specialization_constants( - const Shader *p_shader, - const Vector<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->stages_bytecode; - uint32_t stages_re_sign_mask = 0; - for (const PipelineSpecializationConstant &psc : p_specialization_constants) { - if (!(p_shader->spirv_specialization_constants_ids_mask & (1 << psc.constant_id))) { - // This SC wasn't even in the original SPIR-V shader. - continue; - } - for (const Shader::SpecializationConstant &sc : p_shader->specialization_constants) { - if (psc.constant_id == sc.constant.constant_id) { - ERR_FAIL_COND_V_MSG(psc.type != sc.constant.type, ERR_INVALID_PARAMETER, "Specialization constant provided for id (" + itos(sc.constant.constant_id) + ") is of the wrong type."); - if (psc.int_value != sc.constant.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, ERR_QUERY_FAILED); - } - } - - return OK; -} - -#ifdef DEV_ENABLED -String RenderingDeviceD3D12::_build_pipeline_blob_filename( - const Vector<uint8_t> &p_blob, - const Shader *p_shader, - const Vector<PipelineSpecializationConstant> &p_specialization_constants, - const String &p_extra_name_suffix, - const String &p_forced_id) { - String id; - if (p_forced_id == "") { - HashingContext hc; - hc.start(HashingContext::HASH_MD5); - hc.update(p_blob); - Vector<uint8_t> hash_bin = hc.finish(); - String hash_str = String::hex_encode_buffer(hash_bin.ptr(), hash_bin.size()); - } else { - id = p_forced_id; - } - - Vector<String> sc_str_pieces; - for (const Shader::SpecializationConstant &sc : p_shader->specialization_constants) { - uint32_t int_value = sc.constant.int_value; - for (const PipelineSpecializationConstant &psc : p_specialization_constants) { - if (psc.constant_id == sc.constant.constant_id) { - int_value = psc.int_value; - break; - } - } - sc_str_pieces.push_back(itos(sc.constant.constant_id) + "=" + itos(int_value)); - } - - String res = p_shader->name.replace(":", "-"); - res += "." + id; - res += "." + String("_").join(sc_str_pieces); - if (p_extra_name_suffix != "") { - res += "." + p_extra_name_suffix; - } - return res; -} - -void RenderingDeviceD3D12::_save_pso_blob( - ID3D12PipelineState *p_pso, - const Shader *p_shader, - const Vector<PipelineSpecializationConstant> &p_specialization_constants) { - ComPtr<ID3DBlob> pso_blob; - p_pso->GetCachedBlob(pso_blob.GetAddressOf()); - Vector<uint8_t> pso_vector; - pso_vector.resize(pso_blob->GetBufferSize()); - memcpy(pso_vector.ptrw(), pso_blob->GetBufferPointer(), pso_blob->GetBufferSize()); - - String base_filename = _build_pipeline_blob_filename(pso_vector, p_shader, p_specialization_constants); - - Ref<FileAccess> fa = FileAccess::open("pso." + base_filename + ".bin", FileAccess::WRITE); - fa->store_buffer((const uint8_t *)pso_blob->GetBufferPointer(), pso_blob->GetBufferSize()); -} - -void RenderingDeviceD3D12::_save_stages_bytecode( - const HashMap<ShaderStage, Vector<uint8_t>> &p_stages_bytecode, - const Shader *p_shader, - const RID p_shader_rid, - const Vector<PipelineSpecializationConstant> &p_specialization_constants) { - for (const KeyValue<ShaderStage, Vector<uint8_t>> &E : p_stages_bytecode) { - ShaderStage stage = E.key; - const Vector<uint8_t> &bytecode = E.value; - - String base_filename = _build_pipeline_blob_filename(bytecode, p_shader, p_specialization_constants, shader_stage_names[stage], itos(p_shader_rid.get_id())); - - Ref<FileAccess> fa = FileAccess::open("dxil." + base_filename + ".bin", FileAccess::WRITE); - fa->store_buffer(bytecode.ptr(), bytecode.size()); - } -} -#endif - -/*************************/ -/**** RENDER PIPELINE ****/ -/*************************/ - -RID RenderingDeviceD3D12::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) { -#ifdef DEV_ENABLED -//#define DEBUG_CREATE_DEBUG_PSO -//#define DEBUG_SAVE_PSO_BLOBS -//#define DEBUG_SAVE_DXIL_BLOBS -#endif - _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]; - const FramebufferPass &pass = fb_format.passes[p_for_render_pass]; - - { // Validate shader vs framebuffer. - - ERR_FAIL_COND_V_MSG(p_for_render_pass >= uint32_t(fb_format.passes.size()), RID(), "Render pass requested for pipeline creation (" + itos(p_for_render_pass) + ") is out of bounds"); - 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."); - } - - CD3DX12_PIPELINE_STATE_STREAM pipeline_desc; - RenderPipeline::DynamicParams dyn_params; - - // Attachment formats. - { - for (int i = 0; i < pass.color_attachments.size(); i++) { - int32_t attachment = pass.color_attachments[i]; - if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { - (&pipeline_desc.RTVFormats)->RTFormats[i] = DXGI_FORMAT_UNKNOWN; - } else { - (&pipeline_desc.RTVFormats)->RTFormats[i] = d3d12_formats[fb_format.attachments[attachment].format].general_format; - } - } - (&pipeline_desc.RTVFormats)->NumRenderTargets = pass.color_attachments.size(); - - if (pass.depth_attachment == FramebufferPass::ATTACHMENT_UNUSED) { - pipeline_desc.DSVFormat = DXGI_FORMAT_UNKNOWN; - } else { - pipeline_desc.DSVFormat = d3d12_formats[fb_format.attachments[pass.depth_attachment].format].dsv_format; - } - } - - // Vertex. - 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_desc.InputLayout)->pInputElementDescs = vd.elements_desc.ptr(); - (&pipeline_desc.InputLayout)->NumElements = vd.elements_desc.size(); - - // Validate with inputs. - for (uint32_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. - - 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 & tessellation. - - ERR_FAIL_INDEX_V(p_render_primitive, RENDER_PRIMITIVE_MAX, RID()); - - static const D3D12_PRIMITIVE_TOPOLOGY_TYPE topology_types[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 topologies[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, - }; - - pipeline_desc.PrimitiveTopologyType = topology_types[p_render_primitive]; - if (p_render_primitive == RENDER_PRIMITIVE_TESSELATION_PATCH) { - ERR_FAIL_COND_V(p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > 32, RID()); // Is there any way to get the true point count limit? - dyn_params.primitive_topology = (D3D12_PRIMITIVE_TOPOLOGY)((int)D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + p_rasterization_state.patch_control_points); - } else { - dyn_params.primitive_topology = topologies[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, RID()); - (&pipeline_desc.RasterizerState)->FillMode = p_rasterization_state.wireframe ? D3D12_FILL_MODE_WIREFRAME : D3D12_FILL_MODE_SOLID; - static const D3D12_CULL_MODE cull_mode[3] = { - D3D12_CULL_MODE_NONE, - D3D12_CULL_MODE_FRONT, - D3D12_CULL_MODE_BACK, - }; - - ERR_FAIL_INDEX_V(p_rasterization_state.cull_mode, 3, RID()); - (&pipeline_desc.RasterizerState)->CullMode = 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 && fb_format.passes[p_for_render_pass].depth_attachment != FramebufferPass::ATTACHMENT_UNUSED) { - (&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; - } else { - (&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 = rasterization_sample_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), RID()); - - // Multisample. - ERR_FAIL_COND_V(p_multisample_state.enable_sample_shading, RID()); // How one enables this in D3D12? - if ((&pipeline_desc.RTVFormats)->NumRenderTargets || pipeline_desc.DSVFormat != DXGI_FORMAT_UNKNOWN) { - uint32_t sample_count = MIN( - fb_format.max_supported_sample_count, - rasterization_sample_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()) { - // Use sample mask. - ERR_FAIL_COND_V(rasterization_sample_count[p_multisample_state.sample_count] != (uint32_t)p_multisample_state.sample_mask.size(), RID()); - 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], RID()); - } - pipeline_desc.SampleMask = p_multisample_state.sample_mask[0]; - } else { - pipeline_desc.SampleMask = 0xffffffff; - } - - // Depth stencil. - - if (pass.depth_attachment == FramebufferPass::ATTACHMENT_UNUSED) { - (&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; - ERR_FAIL_INDEX_V(p_depth_stencil_state.depth_compare_operator, COMPARE_OP_MAX, RID()); - (&pipeline_desc.DepthStencilState)->DepthFunc = compare_operators[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, RID()); - ERR_FAIL_COND_V(p_depth_stencil_state.front_op.write_mask != p_depth_stencil_state.back_op.write_mask, RID()); - ERR_FAIL_COND_V(p_depth_stencil_state.front_op.reference != p_depth_stencil_state.back_op.reference, RID()); - (&pipeline_desc.DepthStencilState)->StencilReadMask = p_depth_stencil_state.front_op.compare_mask; - (&pipeline_desc.DepthStencilState)->StencilWriteMask = p_depth_stencil_state.front_op.write_mask; - - ERR_FAIL_INDEX_V(p_depth_stencil_state.front_op.fail, STENCIL_OP_MAX, RID()); - (&pipeline_desc.DepthStencilState)->FrontFace.StencilFailOp = stencil_operations[p_depth_stencil_state.front_op.fail]; - ERR_FAIL_INDEX_V(p_depth_stencil_state.front_op.pass, STENCIL_OP_MAX, RID()); - (&pipeline_desc.DepthStencilState)->FrontFace.StencilPassOp = 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()); - (&pipeline_desc.DepthStencilState)->FrontFace.StencilDepthFailOp = 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()); - (&pipeline_desc.DepthStencilState)->FrontFace.StencilFunc = compare_operators[p_depth_stencil_state.front_op.compare]; - - ERR_FAIL_INDEX_V(p_depth_stencil_state.back_op.fail, STENCIL_OP_MAX, RID()); - (&pipeline_desc.DepthStencilState)->BackFace.StencilFailOp = stencil_operations[p_depth_stencil_state.back_op.fail]; - ERR_FAIL_INDEX_V(p_depth_stencil_state.back_op.pass, STENCIL_OP_MAX, RID()); - (&pipeline_desc.DepthStencilState)->BackFace.StencilPassOp = 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()); - (&pipeline_desc.DepthStencilState)->BackFace.StencilDepthFailOp = 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()); - (&pipeline_desc.DepthStencilState)->BackFace.StencilFunc = compare_operators[p_depth_stencil_state.back_op.compare]; - - dyn_params.depth_bounds_min = p_depth_stencil_state.enable_depth_range ? p_depth_stencil_state.depth_range_min : 0.0f; - dyn_params.depth_bounds_max = p_depth_stencil_state.enable_depth_range ? p_depth_stencil_state.depth_range_max : 1.0f; - dyn_params.stencil_reference = p_depth_stencil_state.front_op.reference; - } - - // Blend state. - (&pipeline_desc.BlendState)->AlphaToCoverageEnable = p_multisample_state.enable_alpha_to_coverage; - { - ERR_FAIL_COND_V(p_blend_state.attachments.size() < pass.color_attachments.size(), RID()); - - bool all_attachments_same_blend = true; - for (int i = 0; i < pass.color_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 = logic_operations[p_blend_state.logic_op]; - - ERR_FAIL_INDEX_V(bs.src_color_blend_factor, BLEND_FACTOR_MAX, RID()); - bd.SrcBlend = blend_factors[bs.src_color_blend_factor]; - ERR_FAIL_INDEX_V(bs.dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); - bd.DestBlend = blend_factors[bs.dst_color_blend_factor]; - ERR_FAIL_INDEX_V(bs.color_blend_op, BLEND_OP_MAX, RID()); - bd.BlendOp = blend_operations[bs.color_blend_op]; - - ERR_FAIL_INDEX_V(bs.src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - bd.SrcBlendAlpha = blend_factors[bs.src_alpha_blend_factor]; - ERR_FAIL_INDEX_V(bs.dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - bd.DestBlendAlpha = blend_factors[bs.dst_alpha_blend_factor]; - ERR_FAIL_INDEX_V(bs.alpha_blend_op, BLEND_OP_MAX, RID()); - bd.BlendOpAlpha = blend_operations[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, RID()); - - (&pipeline_desc.BlendState)->IndependentBlendEnable = !all_attachments_same_blend; - } - - dyn_params.blend_constant = p_blend_state.blend_constant; - - // Stages bytecodes + specialization constants. - - pipeline_desc.pRootSignature = shader->root_signature.Get(); - -#ifdef DEBUG_CREATE_DEBUG_PSO - pipeline_desc.Flags = D3D12_PIPELINE_STATE_FLAG_TOOL_DEBUG; -#endif - - HashMap<ShaderStage, Vector<uint8_t>> final_stages_bytecode; - Error err = _apply_specialization_constants(shader, p_specialization_constants, final_stages_bytecode); - ERR_FAIL_COND_V(err, RID()); - -#ifdef DEV_ENABLED - // Ensure signing worked. - for (KeyValue<ShaderStage, Vector<uint8_t>> &E : final_stages_bytecode) { - bool any_non_zero = false; - for (int j = 0; j < 16; j++) { - if (E.value.ptr()[4 + j]) { - any_non_zero = true; - break; - } - } - DEV_ASSERT(any_non_zero); - } -#endif - - if (shader->stages_bytecode.has(SHADER_STAGE_VERTEX)) { - pipeline_desc.VS = D3D12_SHADER_BYTECODE{ - final_stages_bytecode[SHADER_STAGE_VERTEX].ptr(), - (SIZE_T)final_stages_bytecode[SHADER_STAGE_VERTEX].size() - }; - } - if (shader->stages_bytecode.has(SHADER_STAGE_FRAGMENT)) { - pipeline_desc.PS = D3D12_SHADER_BYTECODE{ - final_stages_bytecode[SHADER_STAGE_FRAGMENT].ptr(), - (SIZE_T)final_stages_bytecode[SHADER_STAGE_FRAGMENT].size() - }; - } - - RenderPipeline pipeline; - { - ComPtr<ID3D12Device2> device2; - device.As(&device2); - HRESULT res = {}; - if (device2) { - D3D12_PIPELINE_STATE_STREAM_DESC pssd = {}; - pssd.pPipelineStateSubobjectStream = &pipeline_desc; - pssd.SizeInBytes = sizeof(pipeline_desc); - res = device2->CreatePipelineState(&pssd, IID_PPV_ARGS(pipeline.pso.GetAddressOf())); - } else { - // Some features won't be available (like depth bounds). - // TODO: Check and/or report error then? - D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = pipeline_desc.GraphicsDescV0(); - res = device->CreateGraphicsPipelineState(&desc, IID_PPV_ARGS(pipeline.pso.GetAddressOf())); - } - ERR_FAIL_COND_V_MSG(res, RID(), "CreateGraphicsPipelineState failed with error " + vformat("0x%08ux", res) + " for shader '" + shader->name + "'."); - -#ifdef DEBUG_SAVE_PSO_BLOBS - _save_pso_blob(pipeline.pso.Get(), shader, p_specialization_constants); -#endif -#ifdef DEBUG_SAVE_DXIL_BLOBS - _save_stages_bytecode(final_stages_bytecode, shader, p_shader, p_specialization_constants); -#endif - } - - { - Vector<Vector<UniformBindingInfo>> bindings; - bindings.resize(shader->sets.size()); - for (int i = 0; i < shader->sets.size(); i++) { - bindings.write[i].resize(shader->sets[i].uniforms.size()); - for (int j = 0; j < shader->sets[i].uniforms.size(); j++) { - bindings.write[i].write[j] = shader->sets[i].uniforms[j].binding; - } - } - pipeline_bindings[next_pipeline_binding_id] = bindings; - pipeline.bindings_id = next_pipeline_binding_id; - next_pipeline_binding_id++; - } - - pipeline.root_signature_crc = shader->root_signature_crc; - pipeline.set_formats = shader->set_formats; - pipeline.shader = p_shader; - pipeline.spirv_push_constant_size = shader->spirv_push_constant_size; - pipeline.dxil_push_constant_size = shader->dxil_push_constant_size; - pipeline.nir_runtime_data_root_param_idx = shader->nir_runtime_data_root_param_idx; - pipeline.dyn_params = dyn_params; - -#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 = pipeline_desc.IBStripCutValue != D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED; - - 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 RenderingDeviceD3D12::render_pipeline_is_valid(RID p_pipeline) { - _THREAD_SAFE_METHOD_ - return render_pipeline_owner.owns(p_pipeline); -} - -/**************************/ -/**** COMPUTE PIPELINE ****/ -/**************************/ - -RID RenderingDeviceD3D12::compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { -#ifdef DEV_ENABLED -//#define DEBUG_CREATE_DEBUG_PSO -//#define DEBUG_SAVE_PSO_BLOBS -//#define DEBUG_SAVE_DXIL_BLOBS -#endif - _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"); - - CD3DX12_PIPELINE_STATE_STREAM pipeline_desc = {}; - - // Stages bytecodes + specialization constants. - - pipeline_desc.pRootSignature = shader->root_signature.Get(); - -#ifdef DEBUG_CREATE_DEBUG_PSO - pipeline_desc.Flags = D3D12_PIPELINE_STATE_FLAG_TOOL_DEBUG; -#endif - - HashMap<ShaderStage, Vector<uint8_t>> final_stages_bytecode; - Error err = _apply_specialization_constants(shader, p_specialization_constants, final_stages_bytecode); - ERR_FAIL_COND_V(err, RID()); - - pipeline_desc.CS = D3D12_SHADER_BYTECODE{ - final_stages_bytecode[SHADER_STAGE_COMPUTE].ptr(), - (SIZE_T)final_stages_bytecode[SHADER_STAGE_COMPUTE].size() - }; - - ComputePipeline pipeline; - { - ComPtr<ID3D12Device2> device2; - device.As(&device2); - HRESULT res = {}; - if (device2) { - D3D12_PIPELINE_STATE_STREAM_DESC pssd = {}; - pssd.pPipelineStateSubobjectStream = &pipeline_desc; - pssd.SizeInBytes = sizeof(pipeline_desc); - res = device2->CreatePipelineState(&pssd, IID_PPV_ARGS(pipeline.pso.GetAddressOf())); - } else { - D3D12_COMPUTE_PIPELINE_STATE_DESC desc = pipeline_desc.ComputeDescV0(); - res = device->CreateComputePipelineState(&desc, IID_PPV_ARGS(pipeline.pso.GetAddressOf())); - } - ERR_FAIL_COND_V_MSG(res, RID(), "CreateComputePipelineState failed with error " + vformat("0x%08ux", res) + " for shader '" + shader->name + "'."); - -#ifdef DEBUG_SAVE_PSO_BLOBS - _save_pso_blob(pipeline.pso.Get(), shader, p_specialization_constants); -#endif -#ifdef DEBUG_SAVE_DXIL_BLOBS - _save_stages_bytecode(final_stages_bytecode, shader, p_shader, p_specialization_constants); -#endif - } - - { - Vector<Vector<UniformBindingInfo>> bindings; - bindings.resize(shader->sets.size()); - for (int i = 0; i < shader->sets.size(); i++) { - bindings.write[i].resize(shader->sets[i].uniforms.size()); - for (int j = 0; j < shader->sets[i].uniforms.size(); j++) { - bindings.write[i].write[j] = shader->sets[i].uniforms[j].binding; - } - } - pipeline_bindings[next_pipeline_binding_id] = bindings; - pipeline.bindings_id = next_pipeline_binding_id; - next_pipeline_binding_id++; - } - - pipeline.root_signature_crc = shader->root_signature_crc; - pipeline.set_formats = shader->set_formats; - pipeline.shader = p_shader; - pipeline.spirv_push_constant_size = shader->spirv_push_constant_size; - pipeline.dxil_push_constant_size = shader->dxil_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 RenderingDeviceD3D12::compute_pipeline_is_valid(RID p_pipeline) { - return compute_pipeline_owner.owns(p_pipeline); -} - -/****************/ -/**** SCREEN ****/ -/****************/ - -int RenderingDeviceD3D12::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 RenderingDeviceD3D12::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 RenderingDeviceD3D12::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. - 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 == d3d12_formats[i].general_format) { - 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<RenderingDeviceD3D12 *>(this)->framebuffer_format_create(screen_attachment); -} - -/*******************/ -/**** DRAW LIST ****/ -/*******************/ - -RenderingDevice::DrawListID RenderingDeviceD3D12::draw_list_begin_for_screen(DisplayServer::WindowID p_screen, const Color &p_clear_color) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(local_device.is_valid(), INVALID_ID, "Local devices have no screen"); - - ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); - ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); - - if (!context->window_is_valid_swapchain(p_screen)) { - return INVALID_ID; - } - - Size2i size = Size2i(context->window_get_width(p_screen), context->window_get_height(p_screen)); - - _draw_list_allocate(Rect2i(Vector2i(), size), 0, 0); - - Vector<Color> clear_colors; - clear_colors.push_back(p_clear_color); - - curr_screen_framebuffer = Framebuffer(); - curr_screen_framebuffer.window_id = p_screen; - curr_screen_framebuffer.format_id = screen_get_framebuffer_format(); - curr_screen_framebuffer.size = size; - curr_screen_framebuffer.screen_rtv_handle = context->window_get_framebuffer_rtv_handle(p_screen); - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - Error err = _draw_list_render_pass_begin(&curr_screen_framebuffer, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_DROP, FINAL_ACTION_DISCARD, clear_colors, 0.0f, 0, Rect2i(), Point2i(), size, command_list, Vector<RID>()); - - if (err != OK) { - return INVALID_ID; - } - - return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; -} - -Error RenderingDeviceD3D12::_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, const Rect2 &p_region, Point2i viewport_offset, Point2i viewport_size, ID3D12GraphicsCommandList *command_list, const Vector<RID> &p_storage_textures) { - const FramebufferFormat &fb_format = framebuffer_formats[framebuffer->format_id]; - - bool is_screen = framebuffer->window_id != DisplayServer::INVALID_WINDOW_ID; - if (!is_screen) { - ERR_FAIL_COND_V(fb_format.attachments.size() != framebuffer->texture_ids.size(), ERR_BUG); - } - - CD3DX12_RECT region_rect(0, 0, framebuffer->size.x, framebuffer->size.y); - if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. - Rect2i viewport(viewport_offset, viewport_size); - Rect2i regioni = p_region; - if (!viewport.encloses(regioni)) { - 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; - - region_rect = CD3DX12_RECT( - p_region.position.x, - p_region.position.y, - p_region.position.x + p_region.size.x, - p_region.position.y + p_region.size.y); - } - - if (p_initial_color_action == INITIAL_ACTION_CLEAR) { // Check clear values. - int color_count = 0; - if (is_screen) { - color_count = 1; - - } else { - 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) && !(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_colors.size() != color_count, ERR_INVALID_PARAMETER, - "Clear color values supplied (" + itos(p_clear_colors.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(color_count) + ")."); - } - - struct SetupInfo { - enum { - ACTION_NONE, - ACTION_DISCARD, - ACTION_CLEAR, - } action = ACTION_NONE; - UINT num_rects = 0; - D3D12_RECT *rect_ptr = nullptr; - D3D12_RESOURCE_STATES new_state = {}; - - SetupInfo(InitialAction p_action, D3D12_RECT *p_region_rect, bool p_is_color) { - switch (p_action) { - case INITIAL_ACTION_CLEAR: { - action = ACTION_CLEAR; - } break; - case INITIAL_ACTION_CLEAR_REGION: { - action = ACTION_CLEAR; - num_rects = 1; - rect_ptr = p_region_rect; - } break; - case INITIAL_ACTION_CLEAR_REGION_CONTINUE: { - action = ACTION_CLEAR; - num_rects = 1; - rect_ptr = p_region_rect; - } break; - case INITIAL_ACTION_KEEP: { - } break; - case INITIAL_ACTION_DROP: { - action = ACTION_DISCARD; // TODO: Are we really intended to do a resource Discard() as initial action, when final action can already do? - } break; - case INITIAL_ACTION_CONTINUE: { - } break; - } - } - }; - - SetupInfo setup_color(p_initial_color_action, ®ion_rect, true); - SetupInfo setup_depth(p_initial_depth_action, ®ion_rect, false); - - 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; - - ID3D12Resource **discards = (ID3D12Resource **)alloca(sizeof(ID3D12Resource *) * fb_format.attachments.size()); - uint32_t num_discards = 0; - - struct RTVClear { - D3D12_CPU_DESCRIPTOR_HANDLE handle; - Color color; - }; - RTVClear *rtv_clears = (RTVClear *)alloca(sizeof(RTVClear) * fb_format.attachments.size()); - uint32_t num_rtv_clears = 0; - - bool dsv_clear = false; - - DescriptorsHeap::Walker rtv_heap_walker = framebuffer->rtv_heap.make_walker(); - - int color_index = 0; - for (int i = 0; i < fb_format.attachments.size(); i++) { - RID texture_rid; - Texture *texture = nullptr; - if (!is_screen) { - texture_rid = framebuffer->texture_ids[i]; - if (texture_rid.is_null()) { - color_index++; - continue; - } - - texture = texture_owner.get_or_null(texture_rid); - ERR_FAIL_NULL_V(texture, ERR_BUG); - - texture->bound = true; - draw_list_bound_textures.push_back(texture_rid); - } - - // 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 (in Vulkan). - // [[VRS_EVERY_SUBPASS_OR_NONE]] - bool is_vrs = fb_format.attachments[i].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && i == fb_format.passes[0].vrs_attachment; - if (is_vrs) { - DEV_ASSERT(!is_screen); - - DEV_ASSERT(texture->owner_mipmaps == 1); - DEV_ASSERT(texture->owner_layers == 1); - _resource_transition_batch(texture, 0, texture->planes, D3D12_RESOURCE_STATE_SHADING_RATE_SOURCE); - } else { - if ((fb_format.attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - if (!is_screen) { // Screen backbuffers are transitioned in prepare_buffers(). - for (uint32_t j = 0; j < texture->layers; j++) { - for (uint32_t k = 0; k < texture->mipmaps; k++) { - uint32_t subresource = D3D12CalcSubresource(texture->base_mipmap + k, texture->base_layer + j, 0, texture->owner_mipmaps, texture->owner_layers); - _resource_transition_batch(texture, subresource, texture->planes, D3D12_RESOURCE_STATE_RENDER_TARGET); - } - } - } - - if (setup_color.action == SetupInfo::ACTION_DISCARD) { - ID3D12Resource *resource = is_screen ? context->window_get_framebuffer_texture(framebuffer->window_id) : texture->resource; - discards[num_discards++] = resource; - } else if (setup_color.action == SetupInfo::ACTION_CLEAR) { - D3D12_CPU_DESCRIPTOR_HANDLE handle = is_screen ? framebuffer->screen_rtv_handle : rtv_heap_walker.get_curr_cpu_handle(); - Color clear_color = color_index < p_clear_colors.size() ? p_clear_colors[color_index] : Color(); - rtv_clears[num_rtv_clears++] = RTVClear{ handle, clear_color }; - } - - color_index++; - if (!is_screen) { - rtv_heap_walker.advance(); - } - } else if ((fb_format.attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - DEV_ASSERT(!is_screen); - - for (uint32_t j = 0; j < texture->layers; j++) { - for (uint32_t k = 0; k < texture->mipmaps; k++) { - uint32_t subresource = D3D12CalcSubresource(texture->base_mipmap + k, texture->base_layer + j, 0, texture->owner_mipmaps, texture->owner_layers); - _resource_transition_batch(texture, subresource, texture->planes, D3D12_RESOURCE_STATE_DEPTH_WRITE); - } - } - - if (setup_depth.action == SetupInfo::ACTION_DISCARD) { - discards[num_discards++] = texture->resource; - } else if (setup_depth.action == SetupInfo::ACTION_CLEAR) { - dsv_clear = true; - } - } - } - } - - 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."); - } - - _resource_transitions_flush(frames[frame].draw_command_list.Get()); - - for (uint32_t i = 0; i < num_discards; i++) { - command_list->DiscardResource(discards[i], nullptr); - } - for (uint32_t i = 0; i < num_rtv_clears; i++) { - command_list->ClearRenderTargetView( - rtv_clears[i].handle, - rtv_clears[i].color.components, - setup_color.num_rects, - setup_color.rect_ptr); - } - - if (dsv_clear) { - command_list->ClearDepthStencilView( - framebuffer->dsv_heap.get_heap()->GetCPUDescriptorHandleForHeapStart(), - D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, - p_clear_depth, - p_clear_stencil, - setup_depth.num_rects, - setup_depth.rect_ptr); - } - - { - CD3DX12_VIEWPORT viewport( - viewport_offset.x, - viewport_offset.y, - viewport_size.x, - viewport_size.y, - 0.0f, - 1.0f); - command_list->RSSetViewports(1, &viewport); - - CD3DX12_RECT scissor( - viewport_offset.x, - viewport_offset.y, - viewport_offset.x + viewport_size.x, - viewport_offset.y + viewport_size.y); - command_list->RSSetScissorRects(1, &scissor); - } - - draw_list_subpass_count = fb_format.passes.size(); - draw_list_current_subpass = 0; - draw_list_final_color_action = p_final_color_action; - draw_list_final_depth_action = p_final_depth_action; - draw_list_framebuffer = framebuffer; - draw_list_viewport_size = viewport_size; - - _draw_list_subpass_begin(); - - return OK; -} - -RenderingDevice::DrawListID RenderingDeviceD3D12::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); - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - Error 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, p_region, Point2i(), framebuffer->size, command_list, p_storage_textures); - - if (err != OK) { - return INVALID_ID; - } - - _draw_list_allocate(Rect2i(Point2i(), framebuffer->size), 0, 0); - - return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; -} - -Error RenderingDeviceD3D12::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); - - ID3D12GraphicsCommandList *frame_command_list = frames[frame].draw_command_list.Get(); - Error 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, p_region, Point2i(), framebuffer->size, frame_command_list, p_storage_textures); - - if (err != OK) { - return ERR_CANT_CREATE; - } - - err = _draw_list_allocate(Rect2i(Point2i(), framebuffer->size), p_splits, 0); - if (err != OK) { - return err; - } - - for (uint32_t i = 0; i < p_splits; i++) { - // In Vulkan, we'd be setting viewports and scissors for each split here; - // D3D12 doesn't need it (it's even forbidden, for that matter). - - r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; - } - - return OK; -} - -RenderingDeviceD3D12::DrawList *RenderingDeviceD3D12::_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 RenderingDeviceD3D12::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 - - dl->command_list->OMSetBlendFactor(p_color.components); -} - -void RenderingDeviceD3D12::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_id && 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.pso = pipeline->pso.Get(); - - dl->command_list->IASetPrimitiveTopology(pipeline->dyn_params.primitive_topology); - dl->command_list->OMSetBlendFactor(pipeline->dyn_params.blend_constant.components); - dl->command_list->OMSetStencilRef(pipeline->dyn_params.stencil_reference); - - ID3D12GraphicsCommandList1 *command_list_1 = nullptr; - dl->command_list->QueryInterface<ID3D12GraphicsCommandList1>(&command_list_1); - if (command_list_1) { - command_list_1->OMSetDepthBounds(pipeline->dyn_params.depth_bounds_min, pipeline->dyn_params.depth_bounds_max); - command_list_1->Release(); - } - - Shader *shader = shader_owner.get_or_null(pipeline->shader); - - if (dl->state.pipeline_shader != pipeline->shader) { - if (dl->state.root_signature_crc != pipeline->root_signature_crc) { - dl->command_list->SetGraphicsRootSignature(shader->root_signature.Get()); - dl->state.root_signature_crc = pipeline->root_signature_crc; - - // Root signature changed, so current descriptor set bindings become invalid. - for (uint32_t i = 0; i < dl->state.set_count; i++) { - dl->state.sets[i].bound = false; - } - - if (pipeline->nir_runtime_data_root_param_idx != UINT32_MAX) { - // Set the viewport size part of the DXIL-NIR runtime data, which is the only we know to need currently. - constexpr dxil_spirv_vertex_runtime_data dummy_data = {}; - uint32_t offset = constexpr((char *)&dummy_data.viewport_width - (char *)&dummy_data) / 4; - dl->command_list->SetGraphicsRoot32BitConstants(pipeline->nir_runtime_data_root_param_idx, 2, &draw_list_viewport_size, offset); - } - } - - const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats. - dl->state.set_count = pipeline->set_formats.size(); // Update set count. - for (uint32_t i = 0; i < dl->state.set_count; i++) { - dl->state.sets[i].pipeline_expected_format = pformats[i]; -#ifdef DEV_ENABLED - dl->state.sets[i]._pipeline_expected_format = pformats[i] ? &uniform_set_format_cache_reverse[pformats[i] - 1]->key().uniform_info : nullptr; -#endif - } - - if (pipeline->spirv_push_constant_size) { -#ifdef DEBUG_ENABLED - dl->validation.pipeline_push_constant_supplied = false; -#endif - } - - dl->state.pipeline_shader = pipeline->shader; - dl->state.pipeline_dxil_push_constant_size = pipeline->dxil_push_constant_size; - dl->state.pipeline_bindings_id = pipeline->bindings_id; -#ifdef DEV_ENABLED - dl->state._shader = shader; -#endif - } - -#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_spirv_push_constant_size = pipeline->spirv_push_constant_size; -#endif -} - -void RenderingDeviceD3D12::draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index) { - 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].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; -#ifdef DEV_ENABLED - dl->state.sets[p_index]._uniform_set = uniform_set_owner.get_or_null(p_uniform_set); -#endif - -#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 RenderingDeviceD3D12::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; - - for (Buffer *buffer : vertex_array->unique_buffers) { - _resource_transition_batch(buffer, 0, 1, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); - } - _resource_transitions_flush(dl->command_list); - - dl->command_list->IASetVertexBuffers(0, vertex_array->views.size(), vertex_array->views.ptr()); -} - -void RenderingDeviceD3D12::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; - - _resource_transition_batch(index_array->buffer, 0, 1, D3D12_RESOURCE_STATE_INDEX_BUFFER); - _resource_transitions_flush(dl->command_list); - - dl->command_list->IASetIndexBuffer(&index_array->view); -} - -void RenderingDeviceD3D12::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 - - 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."); - } -} - -void RenderingDeviceD3D12::_bind_uniform_set(UniformSet *p_uniform_set, const Shader::Set &p_shader_set, const Vector<UniformBindingInfo> &p_bindings, ID3D12GraphicsCommandList *p_command_list, bool p_for_compute) { - 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 ? compute_list->state.root_signature_crc : draw_list->state.root_signature_crc; - UniformSet::RecentBind *last_bind = nullptr; - for (int i = 0; i < ARRAY_SIZE(p_uniform_set->recent_binds); i++) { - if (p_uniform_set->recent_binds[i].execution_index == frames[frame].execution_index) { - if (p_uniform_set->recent_binds[i].root_signature_crc == root_sig_crc) { - for (const RootDescriptorTable &table : p_uniform_set->recent_binds[i].root_tables.resources) { - (p_command_list->*set_root_desc_table_fn)(table.root_param_idx, table.start_gpu_handle); - } - for (const RootDescriptorTable &table : p_uniform_set->recent_binds[i].root_tables.samplers) { - (p_command_list->*set_root_desc_table_fn)(table.root_param_idx, table.start_gpu_handle); - } -#ifdef DEV_ENABLED - p_uniform_set->recent_binds[i].uses++; - frames[frame].uniform_set_reused++; -#endif - return; - } else { - if (!last_bind || p_uniform_set->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 = &p_uniform_set->recent_binds[i]; - } - } - } else { - // Prefer this one since it's unused. - last_bind = &p_uniform_set->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].desc_heap_walkers.resources; - frame_heap_walkers.samplers = &frames[frame].desc_heap_walkers.samplers; - - struct { - DescriptorsHeap::Walker resources; - DescriptorsHeap::Walker samplers; - } set_heap_walkers; - set_heap_walkers.resources = p_uniform_set->desc_heaps.resources.make_walker(); - set_heap_walkers.samplers = p_uniform_set->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 (int i = 0; i < p_shader_set.uniforms.size(); i++) { - bool has_rs_locations = false; - if (p_bindings[i].root_sig_locations.resource.root_param_idx != UINT32_MAX || - p_bindings[i].root_sig_locations.sampler.root_param_idx != UINT32_MAX) { - has_rs_locations = true; - break; - } - - bool has_stages = p_bindings[i].stages; - - DEV_ASSERT(has_rs_locations == has_stages); - } -#endif - - last_bind->root_tables.resources.reserve(p_shader_set.num_root_params.resources); - last_bind->root_tables.resources.clear(); - last_bind->root_tables.samplers.reserve(p_shader_set.num_root_params.samplers); - last_bind->root_tables.samplers.clear(); - last_bind->uses++; - - struct { - RootDescriptorTable *resources = nullptr; - RootDescriptorTable *samplers = nullptr; - } tables; - for (int i = 0; i < p_shader_set.uniforms.size(); i++) { - const Shader::ShaderUniformInfo &uniform_info = p_shader_set.uniforms[i]; - - uint32_t num_resource_descs = 0; - uint32_t num_sampler_descs = 0; - bool srv_uav_ambiguity = false; - _add_descriptor_count_for_uniform(uniform_info.info.type, uniform_info.info.length, false, num_resource_descs, num_sampler_descs, srv_uav_ambiguity); - - bool resource_used = false; - if (p_bindings[i].stages) { - { - const UniformBindingInfo::RootSignatureLocation &rs_loc_resource = p_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 && (p_bindings[i].res_class != RES_CLASS_SRV && p_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); - - (p_command_list->*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].desc_heaps_exhausted_reported.resources) { - frames[frame].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 && p_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 && p_bindings[i].res_class != RES_CLASS_UAV) { - set_heap_walkers.resources.advance(num_resource_descs); - } - - resource_used = true; - } - } - - { - const UniformBindingInfo::RootSignatureLocation &rs_loc_sampler = p_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); - - (p_command_list->*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].desc_heaps_exhausted_reported.samplers) { - frames[frame].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) { - (p_command_list->*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) { - (p_command_list->*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->execution_index = frames[frame].execution_index; -} - -void RenderingDeviceD3D12::_apply_uniform_set_resource_states(const UniformSet *p_uniform_set, const Shader::Set &p_shader_set) { - for (const UniformSet::StateRequirement &sr : p_uniform_set->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 Shader::ShaderUniformInfo &info = p_shader_set.uniforms[bit]; - if (unlikely(!info.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 (info.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(info.info.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 |= info.binding.stages; - writable = info.info.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) { - Texture *texture = (Texture *)sr.resource; - CRASH_COND_MSG(texture->resource != texture->owner_resource, "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 state. - - // TODO: If subresources involved already in the needed state, 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 Shader::ShaderUniformInfo &info = p_shader_set.uniforms[bit]; - if (unlikely(!info.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 (info.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 |= info.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 { - Texture *texture = (Texture *)sr.resource; - for (uint32_t i = 0; i < texture->layers; i++) { - for (uint32_t j = 0; j < texture->mipmaps; j++) { - uint32_t subresource = D3D12CalcSubresource(texture->base_mipmap + j, texture->base_layer + i, 0, texture->owner_mipmaps, texture->owner_layers); - _resource_transition_batch(texture, subresource, texture->planes, wanted_state, texture->owner_resource); - } - } - } - } - } - } -} - -void RenderingDeviceD3D12::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_spirv_push_constant_size, - "This render pipeline requires (" + itos(dl->validation.pipeline_spirv_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); -#endif - if (dl->state.pipeline_dxil_push_constant_size) { - dl->command_list->SetGraphicsRoot32BitConstants(0, p_data_size / sizeof(uint32_t), p_data, 0); - } -#ifdef DEBUG_ENABLED - dl->validation.pipeline_push_constant_supplied = true; -#endif -} - -void RenderingDeviceD3D12::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_spirv_push_constant_size) { - // 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. - - Shader *shader = shader_owner.get_or_null(dl->state.pipeline_shader); - struct SetToBind { - uint32_t set; - UniformSet *uniform_set; - const Shader::Set *shader_set; - }; - SetToBind *sets_to_bind = (SetToBind *)alloca(sizeof(SetToBind) * dl->state.set_count); - uint32_t num_sets_to_bind = 0; - 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 - UniformSet *uniform_set = uniform_set_owner.get_or_null(dl->state.sets[i].uniform_set); - const Shader::Set &shader_set = shader->sets[i]; - _apply_uniform_set_resource_states(uniform_set, shader_set); - if (!dl->state.sets[i].bound) { - sets_to_bind[num_sets_to_bind].set = i; - sets_to_bind[num_sets_to_bind].uniform_set = uniform_set; - sets_to_bind[num_sets_to_bind].shader_set = &shader_set; - num_sets_to_bind++; - dl->state.sets[i].bound = true; - } - } - - _resource_transitions_flush(dl->command_list); - - for (uint32_t i = 0; i < num_sets_to_bind; i++) { - _bind_uniform_set(sets_to_bind[i].uniform_set, *sets_to_bind[i].shader_set, pipeline_bindings[dl->state.pipeline_bindings_id][sets_to_bind[i].set], dl->command_list, false); - } - - if (dl->state.bound_pso != dl->state.pso) { - dl->command_list->SetPipelineState(dl->state.pso); - dl->state.bound_pso = dl->state.pso; - } - 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 - - dl->command_list->DrawIndexedInstanced(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 - - dl->command_list->DrawInstanced(to_draw, p_instances, 0, 0); - } -} - -void RenderingDeviceD3D12::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; - } - CD3DX12_RECT scissor( - rect.position.x, - rect.position.y, - rect.position.x + rect.size.width, - rect.position.y + rect.size.height); - - dl->command_list->RSSetScissorRects(1, &scissor); -} - -void RenderingDeviceD3D12::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 - - CD3DX12_RECT scissor( - dl->viewport.position.x, - dl->viewport.position.y, - dl->viewport.position.x + dl->viewport.size.width, - dl->viewport.position.y + dl->viewport.size.height); - dl->command_list->RSSetScissorRects(1, &scissor); -} - -uint32_t RenderingDeviceD3D12::draw_list_get_current_pass() { - return draw_list_current_subpass; -} - -void RenderingDeviceD3D12::_draw_list_subpass_begin() { // [[MANUAL_SUBPASSES]] - const FramebufferFormat &fb_format = framebuffer_formats[draw_list_framebuffer->format_id]; - const FramebufferPass &pass = fb_format.passes[draw_list_current_subpass]; - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - bool is_screen = draw_list_framebuffer->window_id != DisplayServer::INVALID_WINDOW_ID; - - if (is_screen) { - DEV_ASSERT(!draw_list_framebuffer->dsv_heap.get_descriptor_count()); - command_list->OMSetRenderTargets(1, &draw_list_framebuffer->screen_rtv_handle, true, nullptr); - } else { - D3D12_CPU_DESCRIPTOR_HANDLE *rtv_handles = (D3D12_CPU_DESCRIPTOR_HANDLE *)alloca(sizeof(D3D12_CPU_DESCRIPTOR_HANDLE) * pass.color_attachments.size()); - DescriptorsHeap::Walker rtv_heap_walker = draw_list_framebuffer->rtv_heap.make_walker(); - for (int i = 0; i < pass.color_attachments.size(); i++) { - uint32_t attachment = pass.color_attachments[i]; - if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { - if (!frames[frame].null_rtv_handle.ptr) { - // No null descriptor-handle created for this frame yet. - - if (frames[frame].desc_heap_walkers.rtv.is_at_eof()) { - if (!frames[frame].desc_heaps_exhausted_reported.rtv) { - frames[frame].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].null_rtv_handle = frames[frame].desc_heap_walkers.rtv.get_curr_cpu_handle(); - device->CreateRenderTargetView(nullptr, &rtv_desc_null, frames[frame].null_rtv_handle); - frames[frame].desc_heap_walkers.rtv.advance(); - } - rtv_handles[i] = frames[frame].null_rtv_handle; - } else { - uint32_t rt_index = draw_list_framebuffer->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 = draw_list_framebuffer->dsv_heap.make_walker(); - if (pass.depth_attachment != FramebufferPass::ATTACHMENT_UNUSED) { - uint32_t ds_index = draw_list_framebuffer->attachments_handle_inds[pass.depth_attachment]; - dsv_heap_walker.rewind(); - dsv_heap_walker.advance(ds_index); - dsv_handle = dsv_heap_walker.get_curr_cpu_handle(); - } - } - - command_list->OMSetRenderTargets(pass.color_attachments.size(), rtv_handles, false, dsv_handle.ptr ? &dsv_handle : nullptr); - - // [[VRS_EVERY_SUBPASS_OR_NONE]] - if (context->get_vrs_capabilities().ss_image_supported && draw_list_current_subpass == 0) { - if (execution_index != vrs_state_execution_index) { - vrs_state = {}; - } - - Texture *vrs_texture = nullptr; - RID vrs_texture_id; - if (pass.vrs_attachment != FramebufferPass::ATTACHMENT_UNUSED) { - vrs_texture_id = draw_list_framebuffer->texture_ids[pass.vrs_attachment]; - vrs_texture = texture_owner.get_or_null(vrs_texture_id); - if (!vrs_texture) { - vrs_texture_id = RID(); - } - } - - if (vrs_texture_id != vrs_state.texture_bound) { - ID3D12GraphicsCommandList5 *command_list_5 = nullptr; - command_list->QueryInterface<ID3D12GraphicsCommandList5>(&command_list_5); - DEV_ASSERT(command_list_5); - - if (vrs_texture_id.is_valid()) { - if (!vrs_state.configured) { - static const D3D12_SHADING_RATE_COMBINER combiners[D3D12_RS_SET_SHADING_RATE_COMBINER_COUNT] = { - D3D12_SHADING_RATE_COMBINER_PASSTHROUGH, - D3D12_SHADING_RATE_COMBINER_OVERRIDE, - }; - command_list_5->RSSetShadingRate(D3D12_SHADING_RATE_1X1, combiners); - vrs_state.configured = true; - - command_list_5->RSSetShadingRateImage(vrs_texture->resource); - vrs_state.texture_bound = vrs_texture_id; - } - } else { - command_list_5->RSSetShadingRateImage(nullptr); - vrs_state.texture_bound = RID(); - } - - command_list_5->Release(); - } - - vrs_state_execution_index = execution_index; - } - } -} - -void RenderingDeviceD3D12::_draw_list_subpass_end() { // [[MANUAL_SUBPASSES]] - const FramebufferFormat &fb_format = framebuffer_formats[draw_list_framebuffer->format_id]; - const FramebufferPass &pass = fb_format.passes[draw_list_current_subpass]; - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - struct Resolve { - ID3D12Resource *src_res; - uint32_t src_subres; - ID3D12Resource *dst_res; - uint32_t dst_subres; - DXGI_FORMAT format; - }; - Resolve *resolves = (Resolve *)alloca(sizeof(Resolve) * pass.resolve_attachments.size()); - uint32_t num_resolves = 0; - - for (int i = 0; i < pass.resolve_attachments.size(); i++) { - int32_t color_index = pass.color_attachments[i]; - int32_t resolve_index = pass.resolve_attachments[i]; - DEV_ASSERT((color_index == FramebufferPass::ATTACHMENT_UNUSED) == (resolve_index == FramebufferPass::ATTACHMENT_UNUSED)); - if (color_index == FramebufferPass::ATTACHMENT_UNUSED || draw_list_framebuffer->texture_ids[color_index].is_null()) { - continue; - } - - Texture *src_tex = texture_owner.get_or_null(draw_list_framebuffer->texture_ids[color_index]); - uint32_t src_subresource = D3D12CalcSubresource(src_tex->base_mipmap, src_tex->base_layer, 0, src_tex->owner_mipmaps, src_tex->owner_layers); - _resource_transition_batch(src_tex, src_subresource, src_tex->planes, D3D12_RESOURCE_STATE_RESOLVE_SOURCE); - - Texture *dst_tex = texture_owner.get_or_null(draw_list_framebuffer->texture_ids[resolve_index]); - uint32_t dst_subresource = D3D12CalcSubresource(dst_tex->base_mipmap, dst_tex->base_layer, 0, dst_tex->owner_mipmaps, dst_tex->owner_layers); - _resource_transition_batch(dst_tex, dst_subresource, dst_tex->planes, D3D12_RESOURCE_STATE_RESOLVE_DEST); - - resolves[num_resolves].src_res = src_tex->resource; - resolves[num_resolves].src_subres = src_subresource; - resolves[num_resolves].dst_res = dst_tex->resource; - resolves[num_resolves].dst_subres = dst_subresource; - resolves[num_resolves].format = d3d12_formats[src_tex->format].general_format; - num_resolves++; - } - - _resource_transitions_flush(command_list); - - for (uint32_t i = 0; i < num_resolves; i++) { - command_list->ResolveSubresource(resolves[i].dst_res, resolves[i].dst_subres, resolves[i].src_res, resolves[i].src_subres, resolves[i].format); - } -} - -RenderingDevice::DrawListID RenderingDeviceD3D12::draw_list_switch_to_next_pass() { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(draw_list == nullptr, INVALID_ID); - ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID); - - _draw_list_subpass_end(); - draw_list_current_subpass++; - _draw_list_subpass_begin(); - - Rect2i viewport; - _draw_list_free(&viewport); - - _draw_list_allocate(viewport, 0, draw_list_current_subpass); - - return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; -} - -Error RenderingDeviceD3D12::draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(draw_list == nullptr, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, ERR_INVALID_PARAMETER); - - _draw_list_subpass_end(); - draw_list_current_subpass++; - _draw_list_subpass_begin(); - - Rect2i viewport; - _draw_list_free(&viewport); - - _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 RenderingDeviceD3D12::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) { - if (p_splits == 0) { - draw_list = memnew(DrawList); - draw_list->command_list = frames[frame].draw_command_list.Get(); - 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++) { - HRESULT res = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_BUNDLE, IID_PPV_ARGS(&split_draw_list_allocators.write[i].command_allocator)); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "CreateCommandAllocator failed with error " + vformat("0x%08ux", res) + "."); - - for (int j = 0; j < frame_count; j++) { - ID3D12GraphicsCommandList *command_list = nullptr; - - res = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_BUNDLE, split_draw_list_allocators[i].command_allocator, nullptr, IID_PPV_ARGS(&command_list)); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "CreateCommandList failed with error " + vformat("0x%08ux", res) + "."); - - split_draw_list_allocators.write[i].command_lists.push_back(command_list); - } - } - } - 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++) { - ID3D12GraphicsCommandList *command_list = split_draw_list_allocators[i].command_lists[frame]; - - HRESULT res = frames[frame].setup_command_allocator->Reset(); - ERR_FAIL_COND_V_MSG(ERR_CANT_CREATE, ERR_CANT_CREATE, "Command allocator Reset failed with error " + vformat("0x%08ux", res) + "."); - res = command_list->Reset(split_draw_list_allocators[i].command_allocator, nullptr); - if (res) { - memdelete_arr(draw_list); - draw_list = nullptr; - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Command allocator Reset failed with error " + vformat("0x%08ux", res) + "."); - } - - draw_list[i].command_list = command_list; - draw_list[i].viewport = p_viewport; - } - } - - return OK; -} - -void RenderingDeviceD3D12::_draw_list_free(Rect2i *r_last_viewport) { - if (draw_list_split) { - // Send all command buffers. - for (uint32_t i = 0; i < draw_list_count; i++) { - draw_list[i].command_list->Close(); - frames[frame].draw_command_list->ExecuteBundle(draw_list[i].command_list); - if (r_last_viewport) { - if (i == 0 || draw_list[i].viewport_set) { - *r_last_viewport = draw_list[i].viewport; - } - } - } - - 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_count = 0; -} - -void RenderingDeviceD3D12::draw_list_end(BitField<BarrierMask> p_post_barrier) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive."); - - _draw_list_subpass_end(); - - const FramebufferFormat &fb_format = framebuffer_formats[draw_list_framebuffer->format_id]; - bool is_screen = draw_list_framebuffer->window_id != DisplayServer::INVALID_WINDOW_ID; - - ID3D12GraphicsCommandList *command_list = frames[frame].draw_command_list.Get(); - - for (int i = 0; i < fb_format.attachments.size(); i++) { - Texture *texture = nullptr; - if (!is_screen) { - texture = texture_owner.get_or_null(draw_list_framebuffer->texture_ids[i]); - } - if ((fb_format.attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - switch (draw_list_final_color_action) { - case FINAL_ACTION_READ: { - // Nothing to do now. - } break; - case FINAL_ACTION_DISCARD: { - ID3D12Resource *resource = is_screen ? context->window_get_framebuffer_texture(draw_list_framebuffer->window_id) : texture->resource; - command_list->DiscardResource(resource, nullptr); - } break; - case FINAL_ACTION_CONTINUE: { - ERR_FAIL_COND(draw_list_unbind_color_textures); // Bug! - } break; - } - } else if ((fb_format.attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - ERR_FAIL_COND(is_screen); // Bug! - switch (draw_list_final_depth_action) { - case FINAL_ACTION_READ: { - // Nothing to do now. - } break; - case FINAL_ACTION_DISCARD: { - ID3D12Resource *resource = is_screen ? context->window_get_framebuffer_texture(draw_list_framebuffer->window_id) : texture->resource; - command_list->DiscardResource(resource, nullptr); - } break; - case FINAL_ACTION_CONTINUE: { - ERR_FAIL_COND(draw_list_unbind_depth_textures); // Bug! - } break; - } - } - } - - draw_list_subpass_count = 0; - draw_list_current_subpass = 0; - draw_list_framebuffer = nullptr; - - _draw_list_free(); - - 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; - } - } - draw_list_bound_textures.clear(); -} - -/***********************/ -/**** COMPUTE LISTS ****/ -/***********************/ - -RenderingDevice::ComputeListID RenderingDeviceD3D12::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."); - - compute_list = memnew(ComputeList); - compute_list->command_list = frames[frame].draw_command_list.Get(); - compute_list->state.allow_draw_overlap = p_allow_draw_overlap; - - return ID_TYPE_COMPUTE_LIST; -} - -void RenderingDeviceD3D12::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.pso = pipeline->pso.Get(); - - Shader *shader = shader_owner.get_or_null(pipeline->shader); - - if (cl->state.pipeline_shader != pipeline->shader) { - if (cl->state.root_signature_crc != pipeline->root_signature_crc) { - cl->command_list->SetComputeRootSignature(shader->root_signature.Get()); - cl->state.root_signature_crc = pipeline->root_signature_crc; - // Root signature changed, so current descriptor set bindings become invalid. - for (uint32_t i = 0; i < cl->state.set_count; i++) { - cl->state.sets[i].bound = false; - } - } - - const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats. - cl->state.set_count = pipeline->set_formats.size(); // Update set count. - for (uint32_t i = 0; i < cl->state.set_count; i++) { - cl->state.sets[i].pipeline_expected_format = pformats[i]; -#ifdef DEV_ENABLED - cl->state.sets[i]._pipeline_expected_format = pformats[i] ? &uniform_set_format_cache_reverse[pformats[i] - 1]->key().uniform_info : nullptr; -#endif - } - - if (pipeline->spirv_push_constant_size) { -#ifdef DEBUG_ENABLED - cl->validation.pipeline_push_constant_supplied = false; -#endif - } - - cl->state.pipeline_shader = pipeline->shader; - cl->state.pipeline_dxil_push_constant_size = pipeline->dxil_push_constant_size; - cl->state.pipeline_bindings_id = pipeline->bindings_id; - 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 DEV_ENABLED - cl->state._shader = shader; -#endif - } - -#ifdef DEBUG_ENABLED - // Update compute pass pipeline info. - cl->validation.pipeline_active = true; - cl->validation.pipeline_spirv_push_constant_size = pipeline->spirv_push_constant_size; -#endif -} - -void RenderingDeviceD3D12::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(!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].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; -#ifdef DEV_ENABLED - cl->state.sets[p_index]._uniform_set = uniform_set_owner.get_or_null(p_uniform_set); -#endif -} - -void RenderingDeviceD3D12::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_spirv_push_constant_size, - "This render pipeline requires (" + itos(cl->validation.pipeline_spirv_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); -#endif - if (cl->state.pipeline_dxil_push_constant_size) { - cl->command_list->SetComputeRoot32BitConstants(0, p_data_size / sizeof(uint32_t), p_data, 0); - } -#ifdef DEBUG_ENABLED - cl->validation.pipeline_push_constant_supplied = true; -#endif -} - -void RenderingDeviceD3D12::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 > D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, - "Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION) + ")"); - ERR_FAIL_COND_MSG(p_y_groups > D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, - "Dispatch amount of Y compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION) + ")"); - ERR_FAIL_COND_MSG(p_z_groups > D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, - "Dispatch amount of Z compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION) + ")"); - - 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_spirv_push_constant_size) { - // 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. - Shader *shader = shader_owner.get_or_null(cl->state.pipeline_shader); - struct SetToBind { - uint32_t set; - UniformSet *uniform_set; - const Shader::Set *shader_set; - }; - SetToBind *sets_to_bind = (SetToBind *)alloca(sizeof(SetToBind) * cl->state.set_count); - uint32_t num_sets_to_bind = 0; - 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 - UniformSet *uniform_set = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); - const Shader::Set &shader_set = shader->sets[i]; - _apply_uniform_set_resource_states(uniform_set, shader_set); - if (!cl->state.sets[i].bound) { - sets_to_bind[num_sets_to_bind].set = i; - sets_to_bind[num_sets_to_bind].uniform_set = uniform_set; - sets_to_bind[num_sets_to_bind].shader_set = &shader_set; - num_sets_to_bind++; - cl->state.sets[i].bound = true; - } - } - - _resource_transitions_flush(cl->command_list); - - for (uint32_t i = 0; i < num_sets_to_bind; i++) { - _bind_uniform_set(sets_to_bind[i].uniform_set, *sets_to_bind[i].shader_set, pipeline_bindings[cl->state.pipeline_bindings_id][sets_to_bind[i].set], cl->command_list, true); - } - - if (cl->state.bound_pso != cl->state.pso) { - cl->command_list->SetPipelineState(cl->state.pso); - cl->state.bound_pso = cl->state.pso; - } - cl->command_list->Dispatch(p_x_groups, p_y_groups, p_z_groups); -} - -void RenderingDeviceD3D12::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_spirv_push_constant_size) { - // 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 RenderingDeviceD3D12::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 & D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT), "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.pipeline_active, "No compute pipeline was set before attempting to draw."); - - if (cl->validation.pipeline_spirv_push_constant_size) { - // 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. - - Shader *shader = shader_owner.get_or_null(cl->state.pipeline_shader); - struct SetToBind { - uint32_t set; - UniformSet *uniform_set; - const Shader::Set *shader_set; - }; - SetToBind *sets_to_bind = (SetToBind *)alloca(sizeof(SetToBind) * cl->state.set_count); - uint32_t num_sets_to_bind = 0; - 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 - UniformSet *uniform_set = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); - const Shader::Set &shader_set = shader->sets[i]; - _apply_uniform_set_resource_states(uniform_set, shader_set); - if (!cl->state.sets[i].bound) { - sets_to_bind[num_sets_to_bind].set = i; - sets_to_bind[num_sets_to_bind].uniform_set = uniform_set; - sets_to_bind[num_sets_to_bind].shader_set = &shader_set; - num_sets_to_bind++; - cl->state.sets[i].bound = true; - } - } - - _resource_transition_batch(buffer, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); - - _resource_transitions_flush(cl->command_list); - - for (uint32_t i = 0; i < num_sets_to_bind; i++) { - _bind_uniform_set(sets_to_bind[i].uniform_set, *sets_to_bind[i].shader_set, pipeline_bindings[cl->state.pipeline_bindings_id][sets_to_bind[i].set], cl->command_list, true); - } - - if (cl->state.bound_pso != cl->state.pso) { - cl->command_list->SetPipelineState(cl->state.pso); - cl->state.bound_pso = cl->state.pso; - } - cl->command_list->ExecuteIndirect(indirect_dispatch_cmd_sig.Get(), 1, buffer->resource, p_offset, nullptr, 0); -} - -void RenderingDeviceD3D12::compute_list_add_barrier(ComputeListID p_list) { - // Must be called within a compute list, the class mutex is locked during that time - -#ifdef FORCE_FULL_BARRIER - full_barrier(); -#else - // Due to D3D12 resource-wise barriers, this is no op. -#endif -} - -void RenderingDeviceD3D12::compute_list_end(BitField<BarrierMask> p_post_barrier) { - ERR_FAIL_NULL(compute_list); - -#ifdef FORCE_FULL_BARRIER - full_barrier(); -#endif - - memdelete(compute_list); - compute_list = nullptr; -} - -void RenderingDeviceD3D12::barrier(BitField<BarrierMask> p_from, BitField<BarrierMask> p_to) { - // Due to D3D12 resource-wise barriers, this is no op. -} - -void RenderingDeviceD3D12::full_barrier() { -#ifndef DEBUG_ENABLED - ERR_PRINT("Full barrier is debug-only, should not be used in production"); -#endif - - // 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. - frames[frame].draw_command_list->DiscardResource(texture_owner.get_or_null(aux_resource)->resource, nullptr); -} - -void RenderingDeviceD3D12::_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)) { - 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); - frames[frame].buffers_to_dispose_of.push_back(*index_buffer); - 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); - 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); - 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 RenderingDeviceD3D12::free(RID p_id) { - _THREAD_SAFE_METHOD_ - - _free_dependencies(p_id); // Recursively erase dependencies first, to avoid potential API problems. - _free_internal(p_id); -} - -void RenderingDeviceD3D12::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); - // Don't set the source texture's name when calling on a texture view. - if (texture->owner.is_null()) { - context->set_object_name(texture->resource, p_name); - } - } else if (framebuffer_owner.owns(p_id)) { - // No D3D12 object to name. - } else if (sampler_owner.owns(p_id)) { - // No D3D12 object to name. - } else if (shader_owner.owns(p_id)) { - Shader *shader = shader_owner.get_or_null(p_id); - context->set_object_name(shader->root_signature.Get(), p_name + " Root Signature"); - } else if (uniform_set_owner.owns(p_id)) { - // No D3D12 object to name. - } else if (render_pipeline_owner.owns(p_id)) { - RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id); - context->set_object_name(pipeline->pso.Get(), p_name); - } else if (compute_pipeline_owner.owns(p_id)) { - ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_id); - context->set_object_name(pipeline->pso.Get(), p_name); - } else { - Buffer *buffer = _get_buffer_from_owner(p_id); - if (buffer) { - context->set_object_name(buffer->resource, p_name); - } 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 RenderingDeviceD3D12::draw_command_begin_label(String p_label_name, const Color p_color) { - _THREAD_SAFE_METHOD_ - context->command_begin_label(frames[frame].draw_command_list.Get(), p_label_name, p_color); -} - -void RenderingDeviceD3D12::draw_command_insert_label(String p_label_name, const Color p_color) { - _THREAD_SAFE_METHOD_ - context->command_insert_label(frames[frame].draw_command_list.Get(), p_label_name, p_color); -} - -void RenderingDeviceD3D12::draw_command_end_label() { - _THREAD_SAFE_METHOD_ - context->command_end_label(frames[frame].draw_command_list.Get()); -} - -String RenderingDeviceD3D12::get_device_vendor_name() const { - return context->get_device_vendor_name(); -} - -String RenderingDeviceD3D12::get_device_name() const { - return context->get_device_name(); -} - -RenderingDevice::DeviceType RenderingDeviceD3D12::get_device_type() const { - return context->get_device_type(); -} - -String RenderingDeviceD3D12::get_device_api_version() const { - return context->get_device_api_version(); -} - -String RenderingDeviceD3D12::get_device_pipeline_cache_uuid() const { - return context->get_device_pipeline_cache_uuid(); -} - -void RenderingDeviceD3D12::_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). - frames[frame].setup_command_list->Close(); - frames[frame].draw_command_list->Close(); - } -} - -void RenderingDeviceD3D12::_begin_frame() { - // Erase pending resources. - _free_pending_resources(frame); - - HRESULT res = frames[frame].setup_command_allocator->Reset(); - ERR_FAIL_COND_MSG(res, "Command allocator Reset failed with error " + vformat("0x%08ux", res) + "."); - res = frames[frame].setup_command_list->Reset(frames[frame].setup_command_allocator.Get(), nullptr); - ERR_FAIL_COND_MSG(res, "Command list Reset failed with error " + vformat("0x%08ux", res) + "."); - res = frames[frame].draw_command_allocator->Reset(); - ERR_FAIL_COND_MSG(res, "Command allocator Reset failed with error " + vformat("0x%08ux", res) + "."); - res = frames[frame].draw_command_list->Reset(frames[frame].draw_command_allocator.Get(), nullptr); - ERR_FAIL_COND_MSG(res, "Command list Reset failed with error " + vformat("0x%08ux", res) + "."); - - ID3D12DescriptorHeap *heaps[] = { - frames[frame].desc_heaps.resources.get_heap(), - frames[frame].desc_heaps.samplers.get_heap(), - }; - frames[frame].draw_command_list->SetDescriptorHeaps(2, heaps); - - frames[frame].desc_heap_walkers.resources.rewind(); - frames[frame].desc_heap_walkers.samplers.rewind(); - frames[frame].desc_heap_walkers.aux.rewind(); - frames[frame].desc_heap_walkers.rtv.rewind(); - frames[frame].desc_heaps_exhausted_reported = {}; - frames[frame].null_rtv_handle = {}; - -#ifdef DEBUG_COUNT_BARRIERS - print_verbose(vformat("Last frame: %d barriers (%d batches); %.1f ms", frame_barriers_count, frame_barriers_batches_count, frame_barriers_cpu_time * 0.001f)); - frame_barriers_count = 0; - frame_barriers_batches_count = 0; - frame_barriers_cpu_time = 0; -#endif - - if (local_device.is_null()) { - context->append_command_list(frames[frame].draw_command_list.Get()); - context->set_setup_list(frames[frame].setup_command_list.Get()); // 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; - } - - context->get_allocator()->SetCurrentFrameIndex(Engine::get_singleton()->get_frames_drawn()); - if (frames[frame].timestamp_count) { - frames[frame].setup_command_list->ResolveQueryData(frames[frame].timestamp_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 0, frames[frame].timestamp_count, frames[frame].timestamp_result_values_buffer.resource, 0); - uint64_t *gpu_timestamps = nullptr; - res = frames[frame].timestamp_result_values_buffer.resource->Map(0, nullptr, (void **)&gpu_timestamps); - if (SUCCEEDED(res)) { - memcpy(frames[frame].timestamp_result_values.ptr(), gpu_timestamps, sizeof(uint64_t) * frames[frame].timestamp_count); - frames[frame].timestamp_result_values_buffer.resource->Unmap(0, nullptr); - } - 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(); - frames[frame].execution_index = execution_index; -#ifdef DEV_ENABLED - frames[frame].uniform_set_reused = 0; -#endif -} - -void RenderingDeviceD3D12::swap_buffers() { - ERR_FAIL_COND_MSG(local_device.is_valid(), "Local devices can't swap buffers."); - _THREAD_SAFE_METHOD_ - - context->postpare_buffers(frames[frame].draw_command_list.Get()); - screen_prepared = false; - - _finalize_command_bufers(); - - context->swap_buffers(); - execution_index++; - - frame = (frame + 1) % frame_count; - - _begin_frame(); -} - -void RenderingDeviceD3D12::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(); - - ID3D12CommandList *command_lists[2] = { frames[frame].setup_command_list.Get(), frames[frame].draw_command_list.Get() }; - context->local_device_push_command_lists(local_device, command_lists, 2); - execution_index++; - - local_device_processing = true; -} - -void RenderingDeviceD3D12::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; -} - -#ifdef USE_SMALL_ALLOCS_POOL -D3D12MA::Pool *RenderingDeviceD3D12::_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 (context->get_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 = context->get_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(res, nullptr, "CreatePool failed with error " + vformat("0x%08ux", res) + "."); - - return pool.Get(); -} -#endif - -void RenderingDeviceD3D12::_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 *rp = &frames[p_frame].render_pipelines_to_dispose_of.front()->get(); - pipeline_bindings.erase(rp->bindings_id); - frames[p_frame].render_pipelines_to_dispose_of.pop_front(); - } - while (frames[p_frame].compute_pipelines_to_dispose_of.front()) { - ComputePipeline *cp = &frames[p_frame].compute_pipelines_to_dispose_of.front()->get(); - pipeline_bindings.erase(cp->bindings_id); - frames[p_frame].compute_pipelines_to_dispose_of.pop_front(); - } - - // Shaders. - frames[p_frame].shaders_to_dispose_of.clear(); - - // Framebuffers. - frames[p_frame].framebuffers_to_dispose_of.clear(); - - // 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."); - } - if (texture->owner.is_null()) { - // Actually owns the image and the allocation too. - image_memory -= texture->allocation->GetSize(); - for (uint32_t i = 0; i < texture->aliases.size(); i++) { - if (texture->aliases[i]) { - texture->aliases[i]->Release(); - } - } - texture->resource->Release(); - texture->resource = nullptr; - texture->allocation->Release(); - texture->allocation = nullptr; - } - 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 RenderingDeviceD3D12::prepare_screen_for_drawing() { - _THREAD_SAFE_METHOD_ - context->prepare_buffers(frames[frame].draw_command_list.Get()); - screen_prepared = true; -} - -uint32_t RenderingDeviceD3D12::get_frame_delay() const { - return frame_count; -} - -uint64_t RenderingDeviceD3D12::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 { - D3D12MA::TotalStatistics stats; - context->get_allocator()->CalculateStatistics(&stats); - return stats.Total.Stats.BlockBytes; - } -} - -void RenderingDeviceD3D12::_flush(bool p_flush_current_frame) { - if (local_device.is_valid() && !p_flush_current_frame) { - return; // Flushing previous frames has no effect with local device. - } - - if (p_flush_current_frame) { - frames[frame].setup_command_list->Close(); - frames[frame].draw_command_list->Close(); - } - - if (local_device.is_valid()) { - ID3D12CommandList *command_lists[2] = { frames[frame].setup_command_list.Get(), frames[frame].draw_command_list.Get() }; - context->local_device_push_command_lists(local_device, command_lists, 2); - execution_index++; - context->local_device_sync(local_device); - - HRESULT res = frames[frame].setup_command_allocator->Reset(); - ERR_FAIL_COND_MSG(res, "Command allocator Reset failed with error " + vformat("0x%08ux", res) + "."); - res = frames[frame].setup_command_list->Reset(frames[frame].setup_command_allocator.Get(), nullptr); - ERR_FAIL_COND_MSG(res, "Command allocator Reset failed with error " + vformat("0x%08ux", res) + "."); - res = frames[frame].draw_command_allocator->Reset(); - ERR_FAIL_COND_MSG(res, "Command allocator Reset failed with error " + vformat("0x%08ux", res) + "."); - res = frames[frame].draw_command_list->Reset(frames[frame].draw_command_allocator.Get(), nullptr); - ERR_FAIL_COND_MSG(res, "Command allocator Reset failed with error " + vformat("0x%08ux", res) + "."); - - ID3D12DescriptorHeap *heaps[] = { - frames[frame].desc_heaps.resources.get_heap(), - frames[frame].desc_heaps.samplers.get_heap(), - }; - frames[frame].draw_command_list->SetDescriptorHeaps(2, heaps); - frames[frame].desc_heap_walkers.resources.rewind(); - frames[frame].desc_heap_walkers.samplers.rewind(); - frames[frame].desc_heap_walkers.aux.rewind(); - frames[frame].desc_heap_walkers.rtv.rewind(); - frames[frame].desc_heaps_exhausted_reported = {}; - frames[frame].null_rtv_handle = {}; - frames[frame].execution_index = execution_index; - } else { - context->flush(p_flush_current_frame, p_flush_current_frame); - // Re-create the setup command. - if (p_flush_current_frame) { - execution_index++; - - HRESULT res = frames[frame].setup_command_allocator->Reset(); - ERR_FAIL_COND_MSG(res, "Command allocator Reset failed with error " + vformat("0x%08ux", res) + "."); - res = frames[frame].draw_command_allocator->Reset(); - ERR_FAIL_COND_MSG(res, "Command allocator Reset failed with error " + vformat("0x%08ux", res) + "."); - res = frames[frame].setup_command_list->Reset(frames[frame].setup_command_allocator.Get(), nullptr); - ERR_FAIL_COND_MSG(res, "Command list Reset failed with error " + vformat("0x%08ux", res) + "."); - res = frames[frame].draw_command_list->Reset(frames[frame].draw_command_allocator.Get(), nullptr); - ERR_FAIL_COND_MSG(res, "Command list Reset failed with error " + vformat("0x%08ux", res) + "."); - - ID3D12DescriptorHeap *heaps[] = { - frames[frame].desc_heaps.resources.get_heap(), - frames[frame].desc_heaps.samplers.get_heap(), - }; - frames[frame].draw_command_list->SetDescriptorHeaps(2, heaps); - - frames[frame].desc_heap_walkers.resources.rewind(); - frames[frame].desc_heap_walkers.samplers.rewind(); - frames[frame].desc_heap_walkers.aux.rewind(); - frames[frame].desc_heap_walkers.rtv.rewind(); - frames[frame].desc_heaps_exhausted_reported = {}; - frames[frame].null_rtv_handle = {}; - frames[frame].execution_index = execution_index; - - context->set_setup_list(frames[frame].setup_command_list.Get()); // Append now so it's added before everything else. - context->append_command_list(frames[frame].draw_command_list.Get()); - } - } -} - -void RenderingDeviceD3D12::initialize(D3D12Context *p_context, bool p_local_device) { - // Get our device capabilities. - { - device_capabilities.version_major = p_context->get_feat_level_major(); - device_capabilities.version_minor = p_context->get_feat_level_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_d3d12_device(local_device); - } else { - frame_count = p_context->get_swapchain_image_count() + 1; - } - limits = p_context->get_device_limits(); - max_timestamp_query_elements = 256; - - { // Create command signature for indirect dispatch. - D3D12_INDIRECT_ARGUMENT_DESC iarg_desc = {}; - iarg_desc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH; - D3D12_COMMAND_SIGNATURE_DESC cs_desc = {}; - cs_desc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS); - cs_desc.NumArgumentDescs = 1; - cs_desc.pArgumentDescs = &iarg_desc; - cs_desc.NodeMask = 0; - HRESULT res = device->CreateCommandSignature(&cs_desc, nullptr, IID_PPV_ARGS(indirect_dispatch_cmd_sig.GetAddressOf())); - ERR_FAIL_COND_MSG(res, "CreateCommandSignature failed with error " + vformat("0x%08ux", res) + "."); - } - - uint32_t resource_descriptors_per_frame = GLOBAL_DEF("rendering/rendering_device/d3d12/max_resource_descriptors_per_frame", 16384); - uint32_t sampler_descriptors_per_frame = GLOBAL_DEF("rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame", 1024); - uint32_t misc_descriptors_per_frame = GLOBAL_DEF("rendering/rendering_device/d3d12/max_misc_descriptors_per_frame", 512); - - frames.resize(frame_count); - frame = 0; - // Create setup and frame buffers. - for (int i = 0; i < frame_count; i++) { - frames[i].index = 0; - - { // Create descriptor heaps. - Error err = frames[i].desc_heaps.resources.allocate(device.Get(), 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.Get(), 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.Get(), 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.Get(), 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(); - } - - { // Create command allocators. - HRESULT res = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(frames[i].setup_command_allocator.GetAddressOf())); - ERR_CONTINUE_MSG(res, "CreateCommandAllocator failed with error " + vformat("0x%08ux", res) + "."); - - res = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(frames[i].draw_command_allocator.GetAddressOf())); - ERR_CONTINUE_MSG(res, "CreateCommandAllocator failed with error " + vformat("0x%08ux", res) + "."); - } - - { // Create command lists. - HRESULT res = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frames[i].setup_command_allocator.Get(), nullptr, IID_PPV_ARGS(frames[i].setup_command_list.GetAddressOf())); - ERR_CONTINUE_MSG(res, "CreateCommandList failed with error " + vformat("0x%08ux", res) + "."); - - res = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frames[i].draw_command_allocator.Get(), nullptr, IID_PPV_ARGS(frames[i].draw_command_list.GetAddressOf())); - ERR_CONTINUE_MSG(res, "CreateCommandList failed with error " + vformat("0x%08ux", res) + "."); - - if (i > 0) { - frames[i].setup_command_list->Close(); - frames[i].draw_command_list->Close(); - } - } - - if (i == 0) { - ID3D12DescriptorHeap *heaps[] = { - frames[frame].desc_heaps.resources.get_heap(), - frames[frame].desc_heaps.samplers.get_heap(), - }; - frames[frame].draw_command_list->SetDescriptorHeaps(2, heaps); - } - - { - // Create query heap. - D3D12_QUERY_HEAP_DESC qh_desc = {}; - qh_desc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; - qh_desc.Count = max_timestamp_query_elements; - qh_desc.NodeMask = 0; - HRESULT res = device->CreateQueryHeap(&qh_desc, IID_PPV_ARGS(frames[i].timestamp_heap.GetAddressOf())); - ERR_CONTINUE_MSG(res, "CreateQueryHeap failed with error " + vformat("0x%08ux", res) + "."); - - 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); - Error err = _buffer_allocate(&frames[i].timestamp_result_values_buffer, sizeof(uint64_t) * max_timestamp_query_elements, D3D12_RESOURCE_STATE_COMMON, D3D12_HEAP_TYPE_READBACK); - ERR_CONTINUE(err); - frames[i].timestamp_result_count = 0; - } - } - - if (local_device.is_null()) { - context->set_setup_list(frames[0].setup_command_list.Get()); // Append now so it's added before everything else. - context->append_command_list(frames[0].draw_command_list.Get()); - } - - 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 functions. - 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. - execution_index = 1; - - // 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); - } - - { - aux_resource = texture_create(TextureFormat(), TextureView()); - ERR_FAIL_COND(!aux_resource.is_valid()); - } - - draw_list = nullptr; - draw_list_count = 0; - draw_list_split = false; - - vrs_state_execution_index = 0; - vrs_state = {}; - - compute_list = nullptr; - - glsl_type_singleton_init_or_ref(); -} - -dxil_validator *RenderingDeviceD3D12::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; -} - -template <class T> -void RenderingDeviceD3D12::_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 RenderingDeviceD3D12::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. - full_barrier(); - - frames[frame].draw_command_list->EndQuery(frames[frame].timestamp_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 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 RenderingDeviceD3D12::get_driver_resource(DriverResource p_resource, RID p_rid, uint64_t p_index) { - _THREAD_SAFE_METHOD_ - return 0; -} - -uint32_t RenderingDeviceD3D12::get_captured_timestamps_count() const { - return frames[frame].timestamp_result_count; -} - -uint64_t RenderingDeviceD3D12::get_captured_timestamps_frame() const { - return frames[frame].index; -} - -uint64_t RenderingDeviceD3D12::get_captured_timestamp_gpu_time(uint32_t p_index) const { - ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); - - return frames[frame].timestamp_result_values[p_index] / (double)limits.timestamp_frequency * 1000000000.0; -} - -uint64_t RenderingDeviceD3D12::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 RenderingDeviceD3D12::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 RenderingDeviceD3D12::limit_get(Limit p_limit) const { - switch (p_limit) { - case LIMIT_MAX_TEXTURES_PER_SHADER_STAGE: - return 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: { - D3D12Context::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); - return subgroup_capabilities.size; - } - case LIMIT_SUBGROUP_IN_SHADERS: { - D3D12Context::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); - return subgroup_capabilities.supported_stages_flags_rd(); - } - case LIMIT_SUBGROUP_OPERATIONS: { - 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: - // It's important to return a number that at least won't overflow any typical integer type. -#ifdef DEV_ENABLED - WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + "."); -#endif - return (uint64_t)1 << 30; - } -} - -bool RenderingDeviceD3D12::has_feature(const Features p_feature) const { - switch (p_feature) { - case SUPPORTS_MULTIVIEW: { - D3D12Context::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: { - 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; - } - } -} - -void RenderingDeviceD3D12::finalize() { - // Free all resources. - - _flush(false); - - free(aux_resource); - - _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); - frames[i].timestamp_result_values_buffer.allocation->Release(); - frames[i].timestamp_result_values_buffer.resource->Release(); - } - - frames.clear(); - - pipeline_bindings.clear(); - next_pipeline_binding_id = 1; - - for (int i = 0; i < split_draw_list_allocators.size(); i++) { - for (int j = 0; i < split_draw_list_allocators[i].command_lists.size(); j++) { - split_draw_list_allocators[i].command_lists[j]->Release(); - } - split_draw_list_allocators[i].command_allocator->Release(); - } - - res_barriers_requests.clear(); - res_barriers.clear(); - - for (int i = 0; i < staging_buffer_blocks.size(); i++) { - staging_buffer_blocks[i].allocation->Release(); - staging_buffer_blocks[i].resource->Release(); - } -#ifdef USE_SMALL_ALLOCS_POOL - small_allocs_pools.clear(); -#endif - - indirect_dispatch_cmd_sig.Reset(); - - vertex_formats.clear(); - - framebuffer_formats.clear(); - - // All these should be clear at this point. - ERR_FAIL_COND(dependency_map.size()); - ERR_FAIL_COND(reverse_dependency_map.size()); - - { - MutexLock lock(dxil_mutex); - for (const KeyValue<int, dxil_validator *> &E : dxil_validators) { - dxil_destroy_validator(E.value); - } - } - - glsl_type_singleton_decref(); -} - -RenderingDevice *RenderingDeviceD3D12::create_local_device() { - RenderingDeviceD3D12 *rd = memnew(RenderingDeviceD3D12); - rd->initialize(context, true); - return rd; -} - -RenderingDeviceD3D12::RenderingDeviceD3D12() { - device_capabilities.device_family = DEVICE_DIRECTX; -} - -RenderingDeviceD3D12::~RenderingDeviceD3D12() { - if (local_device.is_valid()) { - finalize(); - context->local_device_free(local_device); - } -} diff --git a/drivers/d3d12/rendering_device_d3d12.h b/drivers/d3d12/rendering_device_d3d12.h deleted file mode 100644 index 92ae2f78fb..0000000000 --- a/drivers/d3d12/rendering_device_d3d12.h +++ /dev/null @@ -1,1277 +0,0 @@ -/**************************************************************************/ -/* rendering_device_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_D3D12_H -#define RENDERING_DEVICE_D3D12_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 "drivers/d3d12/d3d12_context.h" -#include "servers/rendering/rendering_device.h" - -#include <wrl/client.h> - -using Microsoft::WRL::ComPtr; - -#define D3D12_BITCODE_OFFSETS_NUM_STAGES 3 - -struct dxil_validator; - -class RenderingDeviceD3D12 : public RenderingDevice { - _THREAD_SAFE_CLASS_ - // Miscellaneous tables that map - // our enums to enums used - // by DXGI/D3D12. - - D3D12Context::DeviceLimits limits = {}; - 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 d3d12_formats[DATA_FORMAT_MAX]; - static const char *named_formats[DATA_FORMAT_MAX]; - static const D3D12_COMPARISON_FUNC compare_operators[COMPARE_OP_MAX]; - static const D3D12_STENCIL_OP stencil_operations[STENCIL_OP_MAX]; - static const UINT rasterization_sample_count[TEXTURE_SAMPLES_MAX]; - static const D3D12_LOGIC_OP logic_operations[RenderingDevice::LOGIC_OP_MAX]; - static const D3D12_BLEND blend_factors[RenderingDevice::BLEND_FACTOR_MAX]; - static const D3D12_BLEND_OP blend_operations[RenderingDevice::BLEND_OP_MAX]; - static const D3D12_TEXTURE_ADDRESS_MODE address_modes[SAMPLER_REPEAT_MODE_MAX]; - static const FLOAT sampler_border_colors[SAMPLER_BORDER_COLOR_MAX][4]; - static const D3D12_RESOURCE_DIMENSION d3d12_texture_dimension[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_plane_count(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); - - 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(); - - 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; - }; - - /***************************/ - /**** 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. - }; - - ComPtr<ID3D12Device> device; - - 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); - - /******************/ - /**** RESOURCE ****/ - /******************/ - - class ResourceState { - D3D12_RESOURCE_STATES states = D3D12_RESOURCE_STATE_COMMON; - - public: - void extend(D3D12_RESOURCE_STATES p_states_to_add); - D3D12_RESOURCE_STATES get_state_mask() const { return states; } - - ResourceState() {} - ResourceState(D3D12_RESOURCE_STATES p_states) : - states(p_states) {} - }; - - struct Resource { - struct States { - // As many subresources as mipmaps * layers; planes (for depth-stencil) are tracked together. - LocalVector<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; - D3D12MA::Allocation *allocation = nullptr; - - States own_states; // Used only if not a view. - States *states = nullptr; // Non-null only if a view. - - States *get_states_ptr() { return states ? states : &own_states; } - }; - - 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; // Must be multiple of 64. - ID3D12Resource *dx_resource; - uint8_t subres_mask_qwords; - uint8_t planes; - struct Group { - ResourceState state; - uint64_t subres_mask[MAX_SUBRESOURCES / 64]; - } groups[MAX_GROUPS]; - uint8_t groups_count; - static const D3D12_RESOURCE_STATES DELETED_GROUP = D3D12_RESOURCE_STATE_COMMON; - }; - HashMap<Resource::States *, BarrierRequest> 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(Resource *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_command_list); - - /*****************/ - /**** TEXTURE ****/ - /*****************/ - - struct Texture : Resource { - D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {}; - D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = {}; - D3D12_UNORDERED_ACCESS_VIEW_DESC owner_uav_desc = {}; // [[CROSS_FAMILY_ALIASING]]. - - TextureType type; - DataFormat format; - uint32_t planes = 1; - TextureSamples samples; - uint32_t width = 0; - uint32_t height = 0; - uint32_t depth = 0; - uint32_t layers = 0; - uint32_t mipmaps = 0; - uint32_t owner_layers = 0; - uint32_t owner_mipmaps = 0; - uint32_t usage_flags = 0; - uint32_t base_mipmap = 0; - uint32_t base_layer = 0; - - Vector<DataFormat> allowed_shared_formats; - TightLocalVector<ID3D12Resource *> aliases; // [[CROSS_FAMILY_ALIASING]]. - ID3D12Resource *owner_resource = nullptr; // Always the one of the main format passed to creation. [[CROSS_FAMILY_ALIASING]]. - - bool is_resolve_buffer = false; - - 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, uint32_t p_layer, bool p_2d = false); - Error _texture_update(Texture *p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier, ID3D12GraphicsCommandList *p_command_list); - - /*****************/ - /**** SAMPLER ****/ - /*****************/ - - RID_Owner<D3D12_SAMPLER_DESC> 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 { - ID3D12Resource *resource = nullptr; // Owned, but ComPtr would have too much overhead in a Vector. - D3D12MA::Allocation *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 : Resource { - uint32_t size = 0; - D3D12_RESOURCE_STATES usage = {}; - uint32_t last_execution = 0; - }; - - Error _buffer_allocate(Buffer *p_buffer, uint32_t p_size, D3D12_RESOURCE_STATES p_usage, D3D12_HEAP_TYPE p_heap_type); - 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_list = false, uint32_t p_required_align = 32); - - /*********************/ - /**** FRAMEBUFFER ****/ - /*********************/ - - static D3D12_RENDER_TARGET_VIEW_DESC _make_rtv_for_texture(const RenderingDeviceD3D12::Texture *p_texture, uint32_t p_mipmap_offset = 0, uint32_t p_layer_offset = 0, uint32_t p_layers = UINT32_MAX); - static D3D12_DEPTH_STENCIL_VIEW_DESC _make_dsv_for_texture(const RenderingDeviceD3D12::Texture *p_texture); - - // In Vulkan we'd create some structures the driver uses for render pass based rendering. - // (Dynamic rendering is supported on Vulkan 1.3+, though, but Godot is not using it.) - // In contrast, in D3D12 we'll go the dynamic rendering way, since it's more convenient - // and render pass based render setup is not available on every version. - // Therefore, we just need to keep the data at hand and use it where appropriate. - - struct FramebufferFormat { - Vector<AttachmentFormat> attachments; - Vector<FramebufferPass> passes; - Vector<TextureSamples> pass_samples; - uint32_t view_count = 1; - uint32_t max_supported_sample_count = 1; - }; - - bool _framebuffer_format_preprocess(FramebufferFormat *p_fb_format, uint32_t p_view_count); - - HashMap<FramebufferFormatID, FramebufferFormat> framebuffer_formats; - - struct Framebuffer { - DisplayServer::WindowID window_id = DisplayServer::INVALID_WINDOW_ID; - FramebufferFormatID format_id = 0; - Vector<RID> texture_ids; // Empty if for screen. - InvalidationCallback invalidated_callback = nullptr; - void *invalidated_callback_userdata = nullptr; - Vector<uint32_t> attachments_handle_inds; // RTV heap index for color; DSV heap index for DSV. - Size2 size; - uint32_t view_count = 1; - DescriptorsHeap rtv_heap; // Used only if not for screen and some color attachments. - D3D12_CPU_DESCRIPTOR_HANDLE screen_rtv_handle = {}; // Used only if for screen. - DescriptorsHeap dsv_heap; // Used only if not for screen and some depth-stencil attachments. - }; - - RID_Owner<Framebuffer, true> framebuffer_owner; - - /***********************/ - /**** VERTEX BUFFER ****/ - /***********************/ - - 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; - Vector<D3D12_INPUT_ELEMENT_DESC> elements_desc; - }; - - HashMap<VertexFormatID, VertexDescriptionCache> vertex_formats; - - struct VertexArray { - Vector<Buffer *> unique_buffers; - VertexFormatID description = 0; - int vertex_count = 0; - uint32_t max_instances_allowed = 0; - Vector<D3D12_VERTEX_BUFFER_VIEW> views; - }; - - RID_Owner<VertexArray, true> vertex_array_owner; - - struct IndexBuffer : public Buffer { - uint32_t max_index = 0; // Used for validation. - uint32_t index_count = 0; - DXGI_FORMAT index_format = {}; - bool supports_restart_indices = false; - }; - - RID_Owner<IndexBuffer, true> index_buffer_owner; - - struct IndexArray { - IndexBuffer *buffer = nullptr; - uint32_t max_index = 0; // Remember the maximum index here too, for validation. - uint32_t offset = 0; - uint32_t indices = 0; - bool supports_restart_indices = false; - D3D12_INDEX_BUFFER_VIEW view = {}; - }; - - RID_Owner<IndexArray, true> index_array_owner; - - /****************/ - /**** SHADER ****/ - /****************/ - - 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 ResourceClass { - RES_CLASS_INVALID, - RES_CLASS_CBV, - RES_CLASS_SRV, - RES_CLASS_UAV, - }; - - struct UniformBindingInfo { - uint32_t stages = 0; // Actual shader stages using the uniform (0 if totally optimized out). - ResourceClass res_class = RES_CLASS_INVALID; - struct RootSignatureLocation { - uint32_t root_param_idx = UINT32_MAX; - uint32_t range_idx = UINT32_MAX; - }; - struct { - RootSignatureLocation resource; - RootSignatureLocation sampler; - } root_sig_locations; - }; - - struct UniformInfo { - UniformType type = UniformType::UNIFORM_TYPE_MAX; - bool writable = false; - int binding = 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 || 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; - } - 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; - Vector<RBMap<UniformSetFormat, uint32_t>::Element *> uniform_set_format_cache_reverse; - - struct Shader { - struct ShaderUniformInfo { - UniformInfo info; - UniformBindingInfo binding; - - bool operator<(const ShaderUniformInfo &p_info) const { - return *((UniformInfo *)this) < (const UniformInfo &)p_info; - } - }; - struct Set { - Vector<ShaderUniformInfo> uniforms; - struct { - uint32_t resources = 0; - uint32_t samplers = 0; - } num_root_params; - }; - - uint64_t vertex_input_mask = 0; // Inputs used, this is mostly for validation. - uint32_t fragment_output_mask = 0; - - uint32_t spirv_push_constant_size = 0; - uint32_t dxil_push_constant_size = 0; - uint32_t nir_runtime_data_root_param_idx = UINT32_MAX; - - uint32_t compute_local_size[3] = { 0, 0, 0 }; - - struct SpecializationConstant { - PipelineSpecializationConstant constant; - uint64_t stages_bit_offsets[D3D12_BITCODE_OFFSETS_NUM_STAGES]; - }; - - bool is_compute = false; - Vector<Set> sets; - Vector<uint32_t> set_formats; - Vector<SpecializationConstant> specialization_constants; - uint32_t spirv_specialization_constants_ids_mask = 0; - HashMap<ShaderStage, Vector<uint8_t>> stages_bytecode; - String name; // Used for debug. - - 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; - }; - - String _shader_uniform_debug(RID p_shader, int p_set = -1); - - RID_Owner<Shader, true> shader_owner; - - 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_sign_dxil_bytecode(ShaderStage p_stage, Vector<uint8_t> &r_dxil_blob); - - /******************/ - /**** UNIFORMS ****/ - /******************/ - - RID_Owner<Buffer, true> uniform_buffer_owner; - RID_Owner<Buffer, true> storage_buffer_owner; - - // Texture buffer needs a view. - struct TextureBuffer { - Buffer buffer; - }; - - RID_Owner<TextureBuffer, true> texture_buffer_owner; - - struct RootDescriptorTable { - uint32_t root_param_idx = UINT32_MAX; - D3D12_GPU_DESCRIPTOR_HANDLE start_gpu_handle = {}; - }; - - // 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; - struct { - DescriptorsHeap resources; - DescriptorsHeap samplers; - } desc_heaps; - struct StateRequirement { - Resource *resource; - bool is_buffer; - D3D12_RESOURCE_STATES states; - uint64_t shader_uniform_idx_mask; - }; - struct AttachableTexture { - uint32_t bind; - RID texture; - }; - - struct RecentBind { - uint64_t execution_index = 0; - uint32_t root_signature_crc = 0; - struct { - LocalVector<RootDescriptorTable> resources; - LocalVector<RootDescriptorTable> samplers; - } root_tables; - int uses = 0; - } recent_binds[4]; // A better amount may be empirically found. - - LocalVector<AttachableTexture> attachable_textures; // Used for validation. - Vector<StateRequirement> resource_states; - InvalidationCallback invalidated_callback = nullptr; - void *invalidated_callback_userdata = nullptr; - -#ifdef DEV_ENABLED - // Filthy, but useful for dev. - struct ResourceDescInfo { - D3D12_DESCRIPTOR_RANGE_TYPE type; - D3D12_SRV_DIMENSION srv_dimension; - }; - LocalVector<ResourceDescInfo> _resources_desc_info; - const Shader *_shader = nullptr; -#endif - }; - - RID_Owner<UniformSet, true> uniform_set_owner; - - void _bind_uniform_set(UniformSet *p_uniform_set, const Shader::Set &p_shader_set, const Vector<UniformBindingInfo> &p_bindings, ID3D12GraphicsCommandList *p_command_list, bool p_for_compute); - void _apply_uniform_set_resource_states(const UniformSet *p_uniform_set, const Shader::Set &p_shader_set); - - /*******************/ - /**** PIPELINES ****/ - /*******************/ - - Error _apply_specialization_constants( - const Shader *p_shader, - const Vector<PipelineSpecializationConstant> &p_specialization_constants, - HashMap<ShaderStage, Vector<uint8_t>> &r_final_stages_bytecode); -#ifdef DEV_ENABLED - String _build_pipeline_blob_filename( - const Vector<uint8_t> &p_blob, - const Shader *p_shader, - const Vector<PipelineSpecializationConstant> &p_specialization_constants, - const String &p_extra_name_suffix = "", - const String &p_forced_id = ""); - void _save_pso_blob( - ID3D12PipelineState *p_pso, - const Shader *p_shader, - const Vector<PipelineSpecializationConstant> &p_specialization_constants); - void _save_stages_bytecode( - const HashMap<ShaderStage, Vector<uint8_t>> &p_stages_bytecode, - const Shader *p_shader, - const RID p_shader_rid, - const Vector<PipelineSpecializationConstant> &p_specialization_constants); -#endif - - // Render pipeline contains ALL the - // information required for drawing. - // This includes all the rasterizer state - // as well as shader used, framebuffer format, - // etc. - // Some parameters aren't fixed in D3D12, - // so they are stored in an ancillary - // dynamic parameters structure to be set - // on pipeline activation via several calls. - - 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 - RID shader; - Vector<uint32_t> set_formats; - uint32_t bindings_id = 0; - ComPtr<ID3D12PipelineState> pso; - uint32_t root_signature_crc = 0; - uint32_t spirv_push_constant_size = 0; - uint32_t dxil_push_constant_size = 0; - uint32_t nir_runtime_data_root_param_idx = UINT32_MAX; - struct DynamicParams { - 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; - }; - - HashMap<uint32_t, Vector<Vector<UniformBindingInfo>>> pipeline_bindings; - uint32_t next_pipeline_binding_id = 1; - - RID_Owner<RenderPipeline, true> render_pipeline_owner; - - struct ComputePipeline { - RID shader; - Vector<uint32_t> set_formats; - uint32_t bindings_id = 0; - ComPtr<ID3D12PipelineState> pso; - uint32_t root_signature_crc = 0; - uint32_t spirv_push_constant_size = 0; - uint32_t dxil_push_constant_size = 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 bundles. - // As they can be created in threads, - // each needs its own command allocator. - - struct SplitDrawListAllocator { - // All pointers are owned, but not using ComPtr to avoid overhead in the vector. - ID3D12CommandAllocator *command_allocator = nullptr; - Vector<ID3D12GraphicsCommandList *> command_lists; // One for each frame. - }; - - Vector<SplitDrawListAllocator> split_draw_list_allocators; - - struct DrawList { - ID3D12GraphicsCommandList *command_list = nullptr; // 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; - RID uniform_set; - bool bound = false; -#ifdef DEV_ENABLED - // Filthy, but useful for dev. - const Vector<UniformInfo> *_pipeline_expected_format = nullptr; - const UniformSet *_uniform_set = nullptr; -#endif - }; - - struct State { - SetState sets[MAX_UNIFORM_SETS]; - uint32_t set_count = 0; - RID pipeline; - ID3D12PipelineState *pso = nullptr; - ID3D12PipelineState *bound_pso = nullptr; - RID pipeline_shader; - uint32_t pipeline_dxil_push_constant_size = 0; - uint32_t pipeline_bindings_id = 0; - uint32_t root_signature_crc = 0; - RID vertex_array; - RID index_array; -#ifdef DEV_ENABLED - // Filthy, but useful for dev. - Shader *_shader = nullptr; -#endif - } 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_spirv_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; - Framebuffer curr_screen_framebuffer; // Only valid while a screen draw list is open. - Framebuffer *draw_list_framebuffer = nullptr; - FinalAction draw_list_final_color_action = FINAL_ACTION_DISCARD; - FinalAction draw_list_final_depth_action = FINAL_ACTION_DISCARD; - Vector2 draw_list_viewport_size = {}; - uint32_t draw_list_current_subpass = 0; - - bool draw_list_split = false; - Vector<RID> draw_list_bound_textures; - bool draw_list_unbind_color_textures = false; - bool draw_list_unbind_depth_textures = false; - - struct { - RID texture_bound; - bool configured = false; - } vrs_state; - uint32_t vrs_state_execution_index = 0; - - 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, const Rect2 &p_region, Point2i viewport_offset, Point2i viewport_size, ID3D12GraphicsCommandList *command_list, const Vector<RID> &p_storage_textures); - _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); - Buffer *_get_buffer_from_owner(RID p_buffer); - 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); - void _draw_list_subpass_begin(); - void _draw_list_subpass_end(); - - /**********************/ - /**** COMPUTE LIST ****/ - /**********************/ - - struct ComputeList { - ID3D12GraphicsCommandList *command_list = nullptr; // 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; - RID uniform_set; - bool bound = false; -#ifdef DEV_ENABLED - // Filthy, but useful for dev. - const Vector<UniformInfo> *_pipeline_expected_format = nullptr; - const UniformSet *_uniform_set = nullptr; -#endif - }; - - struct State { - HashSet<Texture *> textures_to_sampled_layout; - SetState sets[MAX_UNIFORM_SETS]; - uint32_t set_count = 0; - RID pipeline; - ID3D12PipelineState *pso = nullptr; - ID3D12PipelineState *bound_pso = nullptr; - RID pipeline_shader; - uint32_t pipeline_dxil_push_constant_size = 0; - uint32_t pipeline_bindings_id = 0; - uint32_t root_signature_crc = 0; - uint32_t local_group_size[3] = { 0, 0, 0 }; - bool allow_draw_overlap; -#ifdef DEV_ENABLED - // Filthy, but useful for dev. - Shader *_shader = nullptr; -#endif - } 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 pipeline_spirv_push_constant_size = 0; - bool pipeline_push_constant_supplied = false; - } validation; -#endif - }; - - ComputeList *compute_list = nullptr; - - /**************************/ - /**** 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<Shader> shaders_to_dispose_of; - List<RenderPipeline> render_pipelines_to_dispose_of; - List<ComputePipeline> compute_pipelines_to_dispose_of; - 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; - bool samplers; - bool aux; - bool rtv; - } desc_heaps_exhausted_reported; - CD3DX12_CPU_DESCRIPTOR_HANDLE null_rtv_handle = {}; // For [[MANUAL_SUBPASSES]]. - - ComPtr<ID3D12CommandAllocator> setup_command_allocator; - ComPtr<ID3D12CommandAllocator> draw_command_allocator; - ComPtr<ID3D12GraphicsCommandList> setup_command_list; // Used at the beginning of every frame for set-up. - ComPtr<ID3D12GraphicsCommandList> draw_command_list; - - struct Timestamp { - String description; - uint64_t value = 0; - }; - - ComPtr<ID3D12QueryHeap> timestamp_heap; - - 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; - Buffer timestamp_result_values_buffer; - TightLocalVector<uint64_t> timestamp_result_values; - uint32_t timestamp_result_count = 0; - uint64_t index = 0; - uint64_t execution_index = 0; -#ifdef DEV_ENABLED - uint32_t uniform_set_reused = 0; -#endif - }; - - 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; - uint32_t execution_index = 0; // Gets incremented on every call to ExecuteCommandLists (each frame and each flush). - RID local_device; - bool local_device_processing = false; - - void _free_pending_resources(int p_frame); - -//#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; - }; - 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 - - ComPtr<ID3D12CommandSignature> indirect_dispatch_cmd_sig; - RID aux_resource; // Used for causing full barriers. - - D3D12Context *context = nullptr; - - uint64_t image_memory = 0; - uint64_t buffer_memory = 0; - - void _free_internal(RID p_id); - void _flush(bool p_flush_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 - - HashMap<DXGI_FORMAT, uint32_t> format_sample_counts_mask_cache; - uint32_t _find_max_common_supported_sample_count(const DXGI_FORMAT *p_formats, uint32_t p_num_formats); - -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(D3D12Context *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; - - RenderingDeviceD3D12(); - ~RenderingDeviceD3D12(); -}; - -#endif // RENDERING_DEVICE_D3D12_H diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp new file mode 100644 index 0000000000..6a2a3c32b0 --- /dev/null +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -0,0 +1,5491 @@ +/**************************************************************************/ +/* 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 + +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, tqp_info->query_count, results_buffer, p_index * sizeof(uint64_t)); +} + +/****************/ +/**** 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..bd19572878 --- /dev/null +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -0,0 +1,858 @@ +/**************************************************************************/ +/* 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; + + /****************/ + /**** 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/vulkan/SCsub b/drivers/vulkan/SCsub index a076c0ac54..5e4bc986b8 100644 --- a/drivers/vulkan/SCsub +++ b/drivers/vulkan/SCsub @@ -2,6 +2,8 @@ Import("env") +env.Append(CPPDEFINES=["RD_ENABLED"]) + thirdparty_obj = [] thirdparty_dir = "#thirdparty/vulkan" thirdparty_volk_dir = "#thirdparty/volk" diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp new file mode 100644 index 0000000000..911efa7755 --- /dev/null +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -0,0 +1,3350 @@ +/**************************************************************************/ +/* 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; + } + 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); + } + r_layout->size = get_image_format_required_size(tex_info->rd_format, w, h, d, 1); + r_layout->row_pitch = r_layout->size / (h * 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_SIMULTANEOUS_USE_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); + } + + struct Stage { + ShaderStage type = SHADER_STAGE_MAX; + Vector<uint8_t> spirv; + }; + Vector<Stage> stages; + + 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; + 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])); + } + + Stage stage_entry; + stage_entry.type = ShaderStage(stage); + stage_entry.spirv = spirv; + stages.push_back(stage_entry); + + 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 < 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[i].spirv.size(); + shader_module_create_info.pCode = (const uint32_t *)stages[i].spirv.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[stages[i].type]); + 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[stages[i].type]; + 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(), "vkCreateComputePipelines 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); +} + +/****************/ +/**** 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..6d8f6fd0e0 --- /dev/null +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -0,0 +1,482 @@ +/**************************************************************************/ +/* 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; + + /****************/ + /**** 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 04aeac2bfc..0000000000 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ /dev/null @@ -1,9765 +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_mem_usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST) { - allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; - } - 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 = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; - 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, true); - 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, bool p_constrained_to_region) { - 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; - - if (p_constrained_to_region) { - 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; - } else { - 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 constrained_to_region = false; - 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 clearing regions both in color and depth, we can switch to a fast path where we let Vulkan to the clears - // and we constrain the render area to the region. - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION && p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - constrained_to_region = true; - p_initial_color_action = INITIAL_ACTION_CLEAR; - p_initial_depth_action = INITIAL_ACTION_CLEAR; - } else { - 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, constrained_to_region); - - 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) { - DEV_ASSERT(!constrained_to_region); - _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 constrained_to_region = false; - 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 clearing regions both in color and depth, we can switch to a fast path where we let Vulkan to the clears - // and we constrain the render area to the region. - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION && p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - constrained_to_region = true; - p_initial_color_action = INITIAL_ACTION_CLEAR; - p_initial_depth_action = INITIAL_ACTION_CLEAR; - } else { - 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]); - - 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, constrained_to_region); - - 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) { - DEV_ASSERT(!constrained_to_region); - _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 e8ad0e4f45..0000000000 --- a/drivers/vulkan/rendering_device_vulkan.h +++ /dev/null @@ -1,1293 +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 "thirdparty/vulkan/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> 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> 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> 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> 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> 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> 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> 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> uniform_buffer_owner; - RID_Owner<Buffer> storage_buffer_owner; - - // Texture buffer needs a view. - struct TextureBuffer { - Buffer buffer; - VkBufferView view = VK_NULL_HANDLE; - }; - - RID_Owner<TextureBuffer> 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> 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> 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> 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, bool p_constrained_to_region); - _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; - // Used for filling up newly created buffers with data provided on creation. - // Primarily intended to be accessed by worker threads. - // Ideally this cmd buffer should use an async transfer queue. - VkCommandBuffer setup_command_buffer = VK_NULL_HANDLE; - // The main cmd buffer for drawing and compute. - // Primarily intended to be used by the main thread to do most stuff. - VkCommandBuffer draw_command_buffer = VK_NULL_HANDLE; - - 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..1b1d4fa50f 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,19 +2366,32 @@ 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++; } @@ -2389,7 +2417,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); } @@ -2415,7 +2443,7 @@ void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending) { vkDeviceWaitIdle(device); } -Error VulkanContext::prepare_buffers() { +Error VulkanContext::prepare_buffers(RDD::CommandBufferID p_command_buffer) { if (!queues_initialized) { return OK; } @@ -2468,6 +2496,9 @@ Error VulkanContext::prepare_buffers() { return OK; } +void VulkanContext::postpare_buffers(RDD::CommandBufferID p_command_buffer) { +} + Error VulkanContext::swap_buffers() { if (!queues_initialized) { return OK; @@ -2535,7 +2566,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 +2721,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 +2753,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 +2784,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,11 +2813,12 @@ 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) { +void VulkanContext::command_begin_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) { if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { return; } @@ -2826,10 +2832,10 @@ void VulkanContext::command_begin_label(VkCommandBuffer p_command_buffer, String label.color[1] = p_color[1]; label.color[2] = p_color[2]; label.color[3] = p_color[3]; - CmdBeginDebugUtilsLabelEXT(p_command_buffer, &label); + CmdBeginDebugUtilsLabelEXT((VkCommandBuffer)p_command_buffer.id, &label); } -void VulkanContext::command_insert_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color) { +void VulkanContext::command_insert_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) { if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { return; } @@ -2842,14 +2848,14 @@ void VulkanContext::command_insert_label(VkCommandBuffer p_command_buffer, Strin label.color[1] = p_color[1]; label.color[2] = p_color[2]; label.color[3] = p_color[3]; - CmdInsertDebugUtilsLabelEXT(p_command_buffer, &label); + CmdInsertDebugUtilsLabelEXT((VkCommandBuffer)p_command_buffer.id, &label); } -void VulkanContext::command_end_label(VkCommandBuffer p_command_buffer) { +void VulkanContext::command_end_label(RDD::CommandBufferID p_command_buffer) { if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { return; } - CmdEndDebugUtilsLabelEXT(p_command_buffer); + CmdEndDebugUtilsLabelEXT((VkCommandBuffer)p_command_buffer.id); } void VulkanContext::set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name) { @@ -2897,12 +2903,25 @@ 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; + } +} + 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..ce1299a559 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,46 @@ 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) 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; + + virtual void command_begin_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) override final; + virtual void command_insert_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) override final; + virtual void command_end_label(RDD::CommandBufferID p_command_buffer) 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; VulkanContext(); virtual ~VulkanContext(); |
