From 4e0219924dd55ec40e21e1bf55a597811db70fe4 Mon Sep 17 00:00:00 2001 From: chris bell Date: Sat, 11 Jul 2026 17:01:42 -0500 Subject: [PATCH] Inital commit --- .envrc | 1 + .gitignore | 6 + CMakeLists.txt | 48 + README.md | 22 + assets/main.rml | 11 + assets/styles/main.rcss | 71 + compile_commands.json | 1 + flake.lock | 61 + flake.nix | 106 + src/App.hpp | 74 + src/ButtonClickListener.hpp | 18 + src/main.cpp | 106 + vendor/rmlui_backend/CMakeLists.txt | 253 + vendor/rmlui_backend/RmlUi_Backend.h | 41 + .../rmlui_backend/RmlUi_Backend_GLFW_DX12.cpp | 280 + .../rmlui_backend/RmlUi_Backend_GLFW_GL2.cpp | 227 + .../rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp | 235 + .../rmlui_backend/RmlUi_Backend_GLFW_VK.cpp | 268 + .../rmlui_backend/RmlUi_Backend_SDL_DX12.cpp | 394 + .../rmlui_backend/RmlUi_Backend_SDL_GL2.cpp | 353 + .../rmlui_backend/RmlUi_Backend_SDL_GL3.cpp | 388 + .../rmlui_backend/RmlUi_Backend_SDL_GPU.cpp | 246 + .../RmlUi_Backend_SDL_SDLrenderer.cpp | 242 + vendor/rmlui_backend/RmlUi_Backend_SDL_VK.cpp | 319 + .../rmlui_backend/RmlUi_Backend_SFML_GL2.cpp | 296 + .../RmlUi_Backend_Win32_DX12.cpp | 393 + .../rmlui_backend/RmlUi_Backend_Win32_GL2.cpp | 444 + .../rmlui_backend/RmlUi_Backend_Win32_VK.cpp | 395 + .../rmlui_backend/RmlUi_Backend_X11_GL2.cpp | 292 + ...Ui_Backend_BackwardCompatible_GLFW_GL2.cpp | 226 + ...Ui_Backend_BackwardCompatible_GLFW_GL3.cpp | 240 + .../RmlUi_Renderer_BackwardCompatible_GL2.cpp | 300 + .../RmlUi_Renderer_BackwardCompatible_GL2.h | 46 + .../RmlUi_Renderer_BackwardCompatible_GL3.cpp | 765 + .../RmlUi_Renderer_BackwardCompatible_GL3.h | 117 + .../rmlui_backend/RmlUi_DirectX/.clang-format | 2 + .../RmlUi_DirectX/D3D12MemAlloc.cpp | 10247 ++++++++ .../RmlUi_DirectX/D3D12MemAlloc.h | 2533 ++ .../rmlui_backend/RmlUi_DirectX/LICENSE.txt | 74 + .../RmlUi_DirectX/offsetAllocator.cpp | 513 + .../RmlUi_DirectX/offsetAllocator.hpp | 97 + .../rmlui_backend/RmlUi_Include_DirectX_12.h | 284 + vendor/rmlui_backend/RmlUi_Include_GL3.h | 3582 +++ vendor/rmlui_backend/RmlUi_Include_Vulkan.h | 32 + vendor/rmlui_backend/RmlUi_Include_Windows.h | 20 + vendor/rmlui_backend/RmlUi_Include_Xlib.h | 18 + vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp | 330 + vendor/rmlui_backend/RmlUi_Platform_GLFW.h | 56 + vendor/rmlui_backend/RmlUi_Platform_SDL.cpp | 569 + vendor/rmlui_backend/RmlUi_Platform_SDL.h | 78 + vendor/rmlui_backend/RmlUi_Platform_SFML.cpp | 287 + vendor/rmlui_backend/RmlUi_Platform_SFML.h | 60 + vendor/rmlui_backend/RmlUi_Platform_Win32.cpp | 730 + vendor/rmlui_backend/RmlUi_Platform_Win32.h | 119 + vendor/rmlui_backend/RmlUi_Platform_X11.cpp | 610 + vendor/rmlui_backend/RmlUi_Platform_X11.h | 75 + vendor/rmlui_backend/RmlUi_Renderer_DX12.cpp | 9995 ++++++++ vendor/rmlui_backend/RmlUi_Renderer_DX12.h | 696 + vendor/rmlui_backend/RmlUi_Renderer_GL2.cpp | 325 + vendor/rmlui_backend/RmlUi_Renderer_GL2.h | 49 + vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp | 2171 ++ vendor/rmlui_backend/RmlUi_Renderer_GL3.h | 210 + vendor/rmlui_backend/RmlUi_Renderer_SDL.cpp | 212 + vendor/rmlui_backend/RmlUi_Renderer_SDL.h | 46 + .../rmlui_backend/RmlUi_Renderer_SDL_GPU.cpp | 630 + vendor/rmlui_backend/RmlUi_Renderer_SDL_GPU.h | 122 + vendor/rmlui_backend/RmlUi_Renderer_VK.cpp | 3017 +++ vendor/rmlui_backend/RmlUi_Renderer_VK.h | 563 + .../RmlUi_SDL_GPU/SDL_shadercross/LICENSE.txt | 28 + .../RmlUi_SDL_GPU/ShadersCompiledSPV.h | 817 + .../RmlUi_SDL_GPU/compile_shaders.py | 58 + .../RmlUi_SDL_GPU/shader_frag_color.frag | 3 + .../RmlUi_SDL_GPU/shader_frag_texture.frag | 8 + .../RmlUi_SDL_GPU/shader_vert.vert | 28 + .../rmlui_backend/RmlUi_Vulkan/.clang-format | 2 + vendor/rmlui_backend/RmlUi_Vulkan/LICENSE.txt | 29 + .../RmlUi_Vulkan/ShadersCompiledSPV.h | 180 + .../RmlUi_Vulkan/compile_shaders.py | 38 + .../RmlUi_Vulkan/shader_frag_color.frag | 12 + .../RmlUi_Vulkan/shader_frag_texture.frag | 15 + .../RmlUi_Vulkan/shader_vert.vert | 25 + .../rmlui_backend/RmlUi_Vulkan/vk_mem_alloc.h | 19558 ++++++++++++++++ vendor/rmlui_backend/RmlUi_Vulkan/vulkan.h | 4486 ++++ 83 files changed, 70924 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 assets/main.rml create mode 100644 assets/styles/main.rcss create mode 120000 compile_commands.json create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/App.hpp create mode 100644 src/ButtonClickListener.hpp create mode 100644 src/main.cpp create mode 100644 vendor/rmlui_backend/CMakeLists.txt create mode 100644 vendor/rmlui_backend/RmlUi_Backend.h create mode 100644 vendor/rmlui_backend/RmlUi_Backend_GLFW_DX12.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_GLFW_GL2.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_GLFW_VK.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_SDL_DX12.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_SDL_GL2.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_SDL_GL3.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_SDL_GPU.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_SDL_SDLrenderer.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_SDL_VK.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_SFML_GL2.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_Win32_DX12.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_Win32_GL2.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_Win32_VK.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Backend_X11_GL2.cpp create mode 100644 vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Backend_BackwardCompatible_GLFW_GL2.cpp create mode 100644 vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Backend_BackwardCompatible_GLFW_GL3.cpp create mode 100644 vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL2.cpp create mode 100644 vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL2.h create mode 100644 vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL3.cpp create mode 100644 vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL3.h create mode 100644 vendor/rmlui_backend/RmlUi_DirectX/.clang-format create mode 100644 vendor/rmlui_backend/RmlUi_DirectX/D3D12MemAlloc.cpp create mode 100644 vendor/rmlui_backend/RmlUi_DirectX/D3D12MemAlloc.h create mode 100644 vendor/rmlui_backend/RmlUi_DirectX/LICENSE.txt create mode 100644 vendor/rmlui_backend/RmlUi_DirectX/offsetAllocator.cpp create mode 100644 vendor/rmlui_backend/RmlUi_DirectX/offsetAllocator.hpp create mode 100644 vendor/rmlui_backend/RmlUi_Include_DirectX_12.h create mode 100644 vendor/rmlui_backend/RmlUi_Include_GL3.h create mode 100644 vendor/rmlui_backend/RmlUi_Include_Vulkan.h create mode 100644 vendor/rmlui_backend/RmlUi_Include_Windows.h create mode 100644 vendor/rmlui_backend/RmlUi_Include_Xlib.h create mode 100644 vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Platform_GLFW.h create mode 100644 vendor/rmlui_backend/RmlUi_Platform_SDL.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Platform_SDL.h create mode 100644 vendor/rmlui_backend/RmlUi_Platform_SFML.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Platform_SFML.h create mode 100644 vendor/rmlui_backend/RmlUi_Platform_Win32.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Platform_Win32.h create mode 100644 vendor/rmlui_backend/RmlUi_Platform_X11.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Platform_X11.h create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_DX12.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_DX12.h create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_GL2.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_GL2.h create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_GL3.h create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_SDL.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_SDL.h create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_SDL_GPU.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_SDL_GPU.h create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_VK.cpp create mode 100644 vendor/rmlui_backend/RmlUi_Renderer_VK.h create mode 100644 vendor/rmlui_backend/RmlUi_SDL_GPU/SDL_shadercross/LICENSE.txt create mode 100644 vendor/rmlui_backend/RmlUi_SDL_GPU/ShadersCompiledSPV.h create mode 100644 vendor/rmlui_backend/RmlUi_SDL_GPU/compile_shaders.py create mode 100644 vendor/rmlui_backend/RmlUi_SDL_GPU/shader_frag_color.frag create mode 100644 vendor/rmlui_backend/RmlUi_SDL_GPU/shader_frag_texture.frag create mode 100644 vendor/rmlui_backend/RmlUi_SDL_GPU/shader_vert.vert create mode 100644 vendor/rmlui_backend/RmlUi_Vulkan/.clang-format create mode 100644 vendor/rmlui_backend/RmlUi_Vulkan/LICENSE.txt create mode 100644 vendor/rmlui_backend/RmlUi_Vulkan/ShadersCompiledSPV.h create mode 100644 vendor/rmlui_backend/RmlUi_Vulkan/compile_shaders.py create mode 100644 vendor/rmlui_backend/RmlUi_Vulkan/shader_frag_color.frag create mode 100644 vendor/rmlui_backend/RmlUi_Vulkan/shader_frag_texture.frag create mode 100644 vendor/rmlui_backend/RmlUi_Vulkan/shader_vert.vert create mode 100644 vendor/rmlui_backend/RmlUi_Vulkan/vk_mem_alloc.h create mode 100644 vendor/rmlui_backend/RmlUi_Vulkan/vulkan.h diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..765c983 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.direnv/ +.cache/ +.vscode/ +build/ +result + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e4c2326 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.16) +project(rmlui-template LANGUAGES CXX) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(PkgConfig REQUIRED) +find_package(Freetype REQUIRED) +find_package(glfw3 CONFIG REQUIRED) +find_package(Lua REQUIRED) + +pkg_check_modules(LUNASVG REQUIRED IMPORTED_TARGET lunasvg) +add_library(lunasvg::lunasvg ALIAS PkgConfig::LUNASVG) + +find_package(RmlUi CONFIG REQUIRED) + +add_executable(${PROJECT_NAME} + src/main.cpp + vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp + vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp + vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp +) + +target_include_directories(${PROJECT_NAME} PRIVATE + src + vendor/rmlui_backend + ${FREETYPE_INCLUDE_DIRS} +) + +target_link_libraries(${PROJECT_NAME} PRIVATE + RmlUi::RmlUi + RmlUi::Debugger + RmlUi::Lua + ${LUA_LIBRARIES} + Freetype::Freetype + lunasvg::lunasvg + glfw +) + +install(TARGETS ${PROJECT_NAME} DESTINATION bin) +install(DIRECTORY assets DESTINATION bin) + +target_compile_definitions(${PROJECT_NAME} PRIVATE + ASSETS_DIR="${CMAKE_SOURCE_DIR}/bin/assets" +) + diff --git a/README.md b/README.md new file mode 100644 index 0000000..08f46e5 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# RmlUi Application Project Template (Nix Flake) + +This is a template project for an RmlUi application, with Nix support. + +## Template features: +- Enabled plugins: SVG, Lua +- Hot reload RML/RCSS/Lua with F5 key +- Customizable "shell" rml with inner page loading and page state tracking +- application state manager +- Basic stylesheet for common html/rml controls, with a user-agent styleheet that takes priority +- Easily defined custom rml controls +- GLFW/GL3 backend, but all premade backends are included in the vendor folder +- Nix Flake included with a dev shell env and `nix build` capabilities +0 wrapped binary + +Other libraries included: +- Raylib + +Nix Flake features: +- Dev shell + - LSP for C++ and Lua +- Building & running +- RmlUi pinned to v6.2 \ No newline at end of file diff --git a/assets/main.rml b/assets/main.rml new file mode 100644 index 0000000..de9d38a --- /dev/null +++ b/assets/main.rml @@ -0,0 +1,11 @@ + + + RmlUi Temaplate + + + +
+

Test!..

+
+ +
\ No newline at end of file diff --git a/assets/styles/main.rcss b/assets/styles/main.rcss new file mode 100644 index 0000000..a7ae0e8 --- /dev/null +++ b/assets/styles/main.rcss @@ -0,0 +1,71 @@ +* { + font-family: "rmlui-debugger-font"; +} + +div +{ + display: block; +} + +p +{ + display: block; +} + +h1 +{ + display: block; +} + +em +{ + font-style: italic; +} + +strong +{ + font-weight: bold; +} + +select +{ + text-align: left; +} + +tabset tabs +{ + display: block; +} + +table { + box-sizing: border-box; + display: table; +} +tr { + box-sizing: border-box; + display: table-row; +} +td { + box-sizing: border-box; + display: table-cell; +} +col { + box-sizing: border-box; + display: table-column; +} +colgroup { + display: table-column-group; +} +thead, tbody, tfoot { + display: table-row-group; +} + +body { + --main-bg-color: #ffffff; + --text-color: #000000; + + width: 100vw; + height: 100vh; + background-color: var(--main-bg-color); + color: var(--text-color); +} \ No newline at end of file diff --git a/compile_commands.json b/compile_commands.json new file mode 120000 index 0000000..25eb4b2 --- /dev/null +++ b/compile_commands.json @@ -0,0 +1 @@ +build/compile_commands.json \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..3adcc44 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1783224372, + "narHash": "sha256-8i/87eeoqiGE4yOTjwSA3Eh/ziJRQEmd/unYU+K27sk=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "d407951447dcd00442e97087bf374aad70c04cea", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "utils": "utils" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..42d0e26 --- /dev/null +++ b/flake.nix @@ -0,0 +1,106 @@ +{ + description = "C++ RmlUI + Raylib development environemt"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, utils }: + utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + + rmlui = pkgs.stdenv.mkDerivation rec { + pname = "rmlui"; + version = "master"; + src = pkgs.fetchFromGitHub { + owner = "mikke89"; + repo = "RmlUi"; + rev = "9c74c4fadc2ad7f1496239e560bab19ca2fe5072"; + hash = "sha256-YZz41ViE3+a0VPypnbJ1Orlf5Hyxd65se5yzyHIY7ZI="; + }; + + nativeBuildInputs = [ + pkgs.cmake + pkgs.pkg-config + ]; + + buildInputs = [ + pkgs.freetype + pkgs.lunasvg + pkgs.lua + ]; + + cmakeFlags = [ + "-DRMLUI_LUA_BINDINGS=ON" + "-DRMLUI_LUA_BINDINGS_LIBRARY=lua" + "-DRMLUI_SVG_PLUGIN=ON" + "-DBUILD_SHARED_LIBS=ON" + "-DBUILD_SAMPLES=OFF" + "-DBUILD_TESTING=OFF" + "-DNO_FONT_INTERFACE_DEFAULT=OFF" + ]; + }; + + buildDeps = with pkgs; [ + cmake + pkg-config + gcc + clang-tools + lua-language-server + ]; + + runtimeDeps = with pkgs; [ + raylib + glfw + libGL + clang-tools + libX11 + libXcursor + libXrandr + libXinerama + libXi + wayland + libxkbcommon + rmlui + freetype + lunasvg + lua + lua-language-server + ]; + in + { + devShells.default = pkgs.mkShell { + nativeBuildInputs = buildDeps; + buildInputs = runtimeDeps; + + shellHook = '' + export CP_PATH="${pkgs.raylib}/include:${pkgs.glfw}/include:${rmlui}/include" + export LIBRARY_PATH="${pkgs.raylib}/lib:${pkgs.glfw}/lib:${pkgs.libGL}/lib:${rmlui}/lib" + export LD_LIBRARY_PATH="$LIBRARY_PATH:$LD_LIBRARY_PATH" + + echo "Env ready" + ''; + }; + + packages.default = pkgs.stdenv.mkDerivation { + pname = "rmlui-template"; + version = "0.1.0"; + src = ./.; + + nativeBuildInputs = buildDeps ++ [ pkgs.makeWrapper ]; + buildInputs = runtimeDeps; + + cmakeFlags = [ + "-DCMAKE_BUILD_TYPE=Release" + ]; + + postInstall = '' + wrapProgram $out/bin/''$pname \ + --prefix LD_LIBRARY_PATH : "${pkgs.lib.makeLibraryPath runtimeDeps}" + ''; + }; + } + ); +} diff --git a/src/App.hpp b/src/App.hpp new file mode 100644 index 0000000..d619f1f --- /dev/null +++ b/src/App.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct ApplicationData { + +}; + +class App { +public: + + App(Rml::Context *ctx, ApplicationData *appData) : ctx(ctx), appData(appData), doc(nullptr) {} + + void SetupListeners(std::function bindCallback) { + onBindListeners = bindCallback; + } + + void RegisterListener(Rml::Element* element, std::unique_ptr listener) { + if (element && listener) { + element->AddEventListener(Rml::EventId::Click, listener.get()); + listeners.push_back(std::move(listener)); + } + } + + bool LoadUi() { + if (!ctx) return false; + + Rml::ElementDocument *doc = ctx->LoadDocument(GetAssetsPath() + "/main.rml"); + if (!doc) return false; + + doc->Show(); + + listeners.clear(); + if (onBindListeners) { + onBindListeners(doc); + } + + return true; + } + + void Reload() { + Rml::Factory::ClearStyleSheetCache(); + Rml::Factory::ClearTemplateCache(); + + if (doc) { + doc->Close(); + doc = nullptr; + } + LoadUi(); + } + + static inline std::string GetAssetsPath() { + std::string install_path = std::string(ASSETS_DIR) + "/main.rml"; + if (std::filesystem::exists(install_path)) { + return std::string(ASSETS_DIR); + } + if (std::filesystem::exists("./assets/main.rml")) { + return "./assets"; + } + return "."; + } + +private: + ApplicationData *appData; + Rml::Context* ctx; + Rml::ElementDocument* doc; + std::vector> listeners; + std::function onBindListeners; +}; + diff --git a/src/ButtonClickListener.hpp b/src/ButtonClickListener.hpp new file mode 100644 index 0000000..c5d742f --- /dev/null +++ b/src/ButtonClickListener.hpp @@ -0,0 +1,18 @@ +#include +#include + + +class ButtonClickListener : public Rml::EventListener { +public: + using ClickCallback = std::function; + ButtonClickListener(ClickCallback callback) : callback(callback) {} + + void ProcessEvent(Rml::Event &event) override { + if (event.GetId() == Rml::EventId::Click) { + if (callback) callback(event); + } + } + +private: + ClickCallback callback; +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..c55a8e4 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,106 @@ +#include "App.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool is_initalized = false; +static bool is_running = false; +static Rml::Context *ctx; +static App *app; + +bool InitalizeApplication() { + if (!Backend::Initialize("RmlUi App Template", 800, 600, true)) { + std::cout << "[ERROR] backend was not initalized successfully.\n"; + return false; + } + + Rml::SetSystemInterface(Backend::GetSystemInterface()); + Rml::SetRenderInterface(Backend::GetRenderInterface()); + Rml::Initialise(); + Rml::Lua::Initialise(); + + ctx = Rml::CreateContext("main", Rml::Vector2i(800,600)); + if (!ctx) { + std::cout << "[ERROR] context was null.\n"; + Rml::Shutdown(); + Backend::Shutdown(); + return false; + } + + Rml::Debugger::Initialise(ctx); + + app = new App(ctx, nullptr); + if (!app) { + std::cout << "[ERROR] app was null.\n"; + Rml::Shutdown(); + Backend::Shutdown(); + return false; + } + + std::cout << "Application initalized...\n"; + + return true; +} + +bool f5_was_pressed = false; +bool f8_was_pressed = false; + +void handle_input() { + GLFWwindow *window = glfwGetCurrentContext(); + if(window) { + // Reload main page + bool f5_state = glfwGetKey(window, GLFW_KEY_F5); + if (f5_state == GLFW_PRESS && !f5_was_pressed) { + f5_was_pressed = true; + app->Reload(); + } else if (f5_state == GLFW_RELEASE) { + f5_was_pressed = false; + } + + // Toggle debugger window + bool f8_state = glfwGetKey(window, GLFW_KEY_F8); + if (f8_state == GLFW_PRESS && !f8_was_pressed) { + f8_was_pressed = true; + bool is_visible = Rml::Debugger::IsVisible(); + Rml::Debugger::SetVisible(!is_visible); + } else if (f8_state == GLFW_RELEASE) { + f8_was_pressed = false; + } + } +} + +int main() { + std::cout << "Application starting...\n"; + is_initalized = InitalizeApplication(); + if (!is_initalized) return 1; + + is_running = true; + + std::cout << "Loading UI...\n"; + app->LoadUi(); + std::cout << "UI Loaded\n"; + + while (is_running) { + is_running = Backend::ProcessEvents(ctx); + + handle_input(); + + ctx->Update(); + Backend::BeginFrame(); + ctx->Render(); + Backend::PresentFrame(); + } + + delete app; + Rml::Shutdown(); + Backend::Shutdown(); + + return 0; +} diff --git a/vendor/rmlui_backend/CMakeLists.txt b/vendor/rmlui_backend/CMakeLists.txt new file mode 100644 index 0000000..9e7e112 --- /dev/null +++ b/vendor/rmlui_backend/CMakeLists.txt @@ -0,0 +1,253 @@ +#[[ + Interface libraries for each backend, including their source files and linking requirements. + + Every time a new backend gets added or its target name is modified, please update + the list of available backends found in OptionsLists.cmake +]] + +add_library(rmlui_backend_common_headers INTERFACE) +target_include_directories(rmlui_backend_common_headers INTERFACE "${CMAKE_CURRENT_LIST_DIR}") +target_sources(rmlui_backend_common_headers INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend.h" +) + +if(RMLUI_BACKEND_SIMULATE_TOUCH) + set(RMLUI_BACKEND_SIMULATE_TOUCH_SUPPORTED + "SDL_GL2" + "SDL_GL3" + "SDL_VK" + "SDL_SDLrenderer" + "SDL_GPU" + ) + if(NOT RMLUI_BACKEND IN_LIST RMLUI_BACKEND_SIMULATE_TOUCH_SUPPORTED) + message(WARNING "RMLUI_BACKEND_SIMULATE_TOUCH enabled but not supported on selected backend ${RMLUI_BACKEND}") + else() + target_compile_definitions(rmlui_backend_common_headers INTERFACE "RMLUI_BACKEND_SIMULATE_TOUCH") + endif() +endif() + +add_library(rmlui_backend_Win32_GL2 INTERFACE) +target_sources(rmlui_backend_Win32_GL2 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_Win32.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_Win32_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_Win32.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL2.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_Windows.h" +) +target_link_libraries(rmlui_backend_Win32_GL2 INTERFACE rmlui_backend_common_headers OpenGL::GL) + +add_library(rmlui_backend_Win32_VK INTERFACE) +target_sources(rmlui_backend_Win32_VK INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_Win32.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_VK.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_Win32_VK.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_Win32.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_VK.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_Vulkan.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Vulkan/ShadersCompiledSPV.h" +) +target_link_libraries(rmlui_backend_Win32_VK INTERFACE rmlui_backend_common_headers) + +add_library(rmlui_backend_Win32_DX12 INTERFACE) +target_sources(rmlui_backend_Win32_DX12 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_Win32.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_Win32_DX12.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_Win32.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_DirectX_12.h" +) +target_link_libraries(rmlui_backend_Win32_DX12 INTERFACE rmlui_backend_common_headers) + +add_library(rmlui_backend_X11_GL2 INTERFACE) +target_sources(rmlui_backend_X11_GL2 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_X11.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_X11_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_X11.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL2.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_Xlib.h" +) + +# RMLUI_CMAKE_MINIMUM_VERSION_RAISE_NOTICE: +# Once the minimum CMake version is CMake >= 3.14, "${X11_LIBRARIES}" should +# be substituted by "X11:X11" in addition to any of the other imported that might +# be required. More info: +# https://cmake.org/cmake/help/latest/module/FindX11.html +target_link_libraries(rmlui_backend_X11_GL2 INTERFACE rmlui_backend_common_headers OpenGL::GL ${X11_LIBRARIES}) + +add_library(rmlui_backend_SDL_GL2 INTERFACE) +target_sources(rmlui_backend_SDL_GL2 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_SDL_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL2.h" +) +target_link_libraries(rmlui_backend_SDL_GL2 INTERFACE rmlui_backend_common_headers OpenGL::GL SDL::SDL SDL_image::SDL_image) + +add_library(rmlui_backend_SDL_GL3 INTERFACE) +target_sources(rmlui_backend_SDL_GL3 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL3.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_SDL_GL3.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL3.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_GL3.h" +) +target_link_libraries(rmlui_backend_SDL_GL3 INTERFACE rmlui_backend_common_headers SDL::SDL SDL_image::SDL_image) +if(UNIX) + # The OpenGL 3 renderer implementation uses dlopen/dlclose + # This is required in some UNIX and UNIX-like operating systems to load shared object files at runtime + target_link_libraries(rmlui_backend_SDL_GL3 INTERFACE ${CMAKE_DL_LIBS}) +endif() +if(EMSCRIPTEN) + # Only Emscripten requires linking to OpenGL::GL, for other platforms we use 'glad' as an OpenGL loader. + target_link_libraries(rmlui_backend_SDL_GL3 INTERFACE OpenGL::GL) +endif() + +add_library(rmlui_backend_SDL_VK INTERFACE) +target_sources(rmlui_backend_SDL_VK INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_VK.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_SDL_VK.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_VK.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_Vulkan.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Vulkan/ShadersCompiledSPV.h" +) +target_link_libraries(rmlui_backend_SDL_VK INTERFACE rmlui_backend_common_headers SDL::SDL) +if(UNIX) + # The Vulkan renderer implementation uses dlopen/dlclose + # This is required in some UNIX and UNIX-like operating systems to load shared object files at runtime + target_link_libraries(rmlui_backend_SDL_VK INTERFACE ${CMAKE_DL_LIBS}) +endif() + +add_library(rmlui_backend_SDL_DX12 INTERFACE) +target_sources(rmlui_backend_SDL_DX12 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_SDL_DX12.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_DirectX_12.h" +) +target_link_libraries(rmlui_backend_SDL_DX12 INTERFACE rmlui_backend_common_headers SDL::SDL SDL_image::SDL_image) + +add_library(rmlui_backend_SDL_SDLrenderer INTERFACE) +target_sources(rmlui_backend_SDL_SDLrenderer INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_SDL.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_SDL_SDLrenderer.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_SDL.h" +) +target_link_libraries(rmlui_backend_SDL_SDLrenderer INTERFACE rmlui_backend_common_headers SDL::SDL SDL_image::SDL_image) + +add_library(rmlui_backend_SDL_GPU INTERFACE) +target_sources(rmlui_backend_SDL_GPU INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_SDL_GPU.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_SDL_GPU.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SDL.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_SDL_GPU.h" +) +target_link_libraries(rmlui_backend_SDL_GPU INTERFACE rmlui_backend_common_headers SDL::SDL SDL_image::SDL_image) + +add_library(rmlui_backend_SFML_GL2 INTERFACE) +target_sources(rmlui_backend_SFML_GL2 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SFML.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_SFML_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_SFML.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL2.h" +) +target_link_libraries(rmlui_backend_SFML_GL2 INTERFACE + rmlui_backend_common_headers OpenGL::GL SFML::Graphics SFML::Window SFML::System +) + +add_library(rmlui_backend_GLFW_GL2 INTERFACE) +target_sources(rmlui_backend_GLFW_GL2 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_GLFW_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL2.h" +) +target_link_libraries(rmlui_backend_GLFW_GL2 INTERFACE rmlui_backend_common_headers OpenGL::GL glfw) + +add_library(rmlui_backend_GLFW_GL3 INTERFACE) +target_sources(rmlui_backend_GLFW_GL3 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL3.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_GLFW_GL3.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_GL3.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_GL3.h" +) +target_link_libraries(rmlui_backend_GLFW_GL3 INTERFACE rmlui_backend_common_headers glfw) +if(UNIX) + # The OpenGL 3 renderer implementation uses dlopen/dlclose + # This is required in some UNIX and UNIX-like operating systems to load shared object files at runtime + target_link_libraries(rmlui_backend_GLFW_GL3 INTERFACE ${CMAKE_DL_LIBS}) +endif() + +add_library(rmlui_backend_GLFW_VK INTERFACE) +target_sources(rmlui_backend_GLFW_VK INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_VK.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_GLFW_VK.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_VK.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_Vulkan.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Vulkan/ShadersCompiledSPV.h" +) +target_link_libraries(rmlui_backend_GLFW_VK INTERFACE rmlui_backend_common_headers glfw) +if(UNIX) + # The Vulkan renderer implementation uses dlopen/dlclose + # This is required in some UNIX and UNIX-like operating systems to load shared object files at runtime + target_link_libraries(rmlui_backend_GLFW_VK INTERFACE ${CMAKE_DL_LIBS}) +endif() + +add_library(rmlui_backend_GLFW_DX12 INTERFACE) +target_sources(rmlui_backend_GLFW_DX12 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Backend_GLFW_DX12.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Renderer_DX12.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Include_DirectX_12.h" +) +target_link_libraries(rmlui_backend_GLFW_DX12 INTERFACE rmlui_backend_common_headers glfw) + +add_library(rmlui_backend_BackwardCompatible_GLFW_GL2 INTERFACE) +target_sources(rmlui_backend_BackwardCompatible_GLFW_GL2 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL2.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL2.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_BackwardCompatible/RmlUi_Backend_BackwardCompatible_GLFW_GL2.cpp" +) +target_link_libraries(rmlui_backend_BackwardCompatible_GLFW_GL2 INTERFACE rmlui_backend_common_headers OpenGL::GL glfw) + +add_library(rmlui_backend_BackwardCompatible_GLFW_GL3 INTERFACE) +target_sources(rmlui_backend_BackwardCompatible_GLFW_GL3 INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_Platform_GLFW.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL3.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL3.h" + "${CMAKE_CURRENT_LIST_DIR}/RmlUi_BackwardCompatible/RmlUi_Backend_BackwardCompatible_GLFW_GL3.cpp" +) +target_link_libraries(rmlui_backend_BackwardCompatible_GLFW_GL3 INTERFACE rmlui_backend_common_headers glfw) +if(UNIX) + # The OpenGL 3 renderer implementation uses dlopen/dlclose + # This is required in some UNIX and UNIX-like operating systems to load shared object files at runtime + target_link_libraries(rmlui_backend_BackwardCompatible_GLFW_GL3 INTERFACE ${CMAKE_DL_LIBS}) +endif() + +if(RMLUI_IS_ROOT_PROJECT) + install(DIRECTORY "./" + DESTINATION "${CMAKE_INSTALL_DATADIR}/Backends" + ) +endif() diff --git a/vendor/rmlui_backend/RmlUi_Backend.h b/vendor/rmlui_backend/RmlUi_Backend.h new file mode 100644 index 0000000..1be8722 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + +using KeyDownCallback = bool (*)(Rml::Context* context, Rml::Input::KeyIdentifier key, int key_modifier, float native_dp_ratio, bool priority); + +/** + This interface serves as a basic abstraction over the various backends included with RmlUi. It is mainly intended as an example to get something + simple up and running, and provides just enough functionality for the included samples. + + This interface may be used directly for simple applications and testing. However, for anything more advanced we recommend to use the backend as a + starting point and copy relevant parts into the main loop of your application. On the other hand, the underlying platform and renderer used by the + backend are intended to be re-usable as is. + */ +namespace Backend { + +// Initializes the backend, including the custom system and render interfaces, and opens a window for rendering the RmlUi context. +bool Initialize(const char* window_name, int width, int height, bool allow_resize); +// Closes the window and release all resources owned by the backend, including the system and render interfaces. +void Shutdown(); + +// Returns a pointer to the custom system interface which should be provided to RmlUi. +Rml::SystemInterface* GetSystemInterface(); +// Returns a pointer to the custom render interface which should be provided to RmlUi. +Rml::RenderInterface* GetRenderInterface(); + +// Polls and processes events from the current platform, and applies any relevant events to the provided RmlUi context and the key down callback. +// @return False to indicate that the application should be closed. +bool ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback = nullptr, bool power_save = false); +// Request application closure during the next event processing call. +void RequestExit(); + +// Prepares the render state to accept rendering commands from RmlUi, call before rendering the RmlUi context. +void BeginFrame(); +// Presents the rendered frame to the screen, call after rendering the RmlUi context. +void PresentFrame(); + +} // namespace Backend diff --git a/vendor/rmlui_backend/RmlUi_Backend_GLFW_DX12.cpp b/vendor/rmlui_backend/RmlUi_Backend_GLFW_DX12.cpp new file mode 100644 index 0000000..0b63bd3 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_GLFW_DX12.cpp @@ -0,0 +1,280 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Platform_GLFW.h" +#include "RmlUi_Renderer_DX12.h" +#include +#include +#include +#include +#include +#include + +#define GLFW_EXPOSE_NATIVE_WIN32 +#include + +static void SetupCallbacks(GLFWwindow* window); + +static void LogErrorFromGLFW(int error, const char* description) +{ + Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW error (0x%x): %s", error, description); +} + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(GLFWwindow* window) : system_interface(window), window(window) {} + + SystemInterface_GLFW system_interface; + Rml::UniquePtr render_interface; + + GLFWwindow* window; + Rml::Context* context = nullptr; + KeyDownCallback key_down_callback = nullptr; + + int glfw_active_modifiers = 0; + bool running = true; + bool context_dimensions_dirty = true; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + glfwSetErrorCallback(LogErrorFromGLFW); + + if (!glfwInit()) + { + glfwTerminate(); + return false; + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, allow_resize ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); + + GLFWwindow* window = glfwCreateWindow(width, height, window_name, nullptr, nullptr); + + if (!window) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW failed to create window"); + return false; + } + + HWND window_handle = glfwGetWin32Window(window); + if (!window_handle) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Could not retrieve native window handle from GLFW"); + return false; + } + + data = Rml::MakeUnique(window); + + RmlRendererSettings settings = {}; + settings.vsync = true; + settings.msaa_sample_count = RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT; + + data->render_interface = Rml::MakeUnique(window_handle, settings); + + if (!data->render_interface || !(*data->render_interface)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to initialize DirectX 12 render interface"); + data.reset(); + return false; + } + + // The window size may have been scaled by DPI settings, get the actual pixel size. + glfwGetFramebufferSize(window, &width, &height); + + data->render_interface->SetViewport(width, height); + + // Receive num lock and caps lock modifiers for proper handling of numpad inputs in text fields. + glfwSetInputMode(window, GLFW_LOCK_KEY_MODS, GLFW_TRUE); + + SetupCallbacks(window); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + data->render_interface.reset(); + + glfwDestroyWindow(data->window); + + data.reset(); + + glfwTerminate(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return data->render_interface.get(); +} + +static bool WaitForValidSwapchain() +{ + bool result = true; + + // In some situations the swapchain may become invalid, such as when the window is minimized. In this state the renderer cannot accept any render + // calls. Since we don't have full control over the main loop here we may risk calls to Context::Render if we were to return. Instead, we keep the + // application inside this loop until we are able to recreate the swapchain and render again. + while (!data->render_interface->IsSwapchainValid()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + if (glfwWindowShouldClose(data->window)) + { + glfwRestoreWindow(data->window); + result = false; + } + + glfwPollEvents(); + data->render_interface->RecreateSwapchain(); + } + + return result; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + bool result = data->running; + + // The initial window size may have been affected by system DPI settings, apply the actual pixel size and dp-ratio to the context. + if (data->context_dimensions_dirty) + { + data->context_dimensions_dirty = false; + + Rml::Vector2i window_size; + float dp_ratio = 1.f; + glfwGetFramebufferSize(data->window, &window_size.x, &window_size.y); + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + context->SetDimensions(window_size); + context->SetDensityIndependentPixelRatio(dp_ratio); + } + + data->context = context; + data->key_down_callback = key_down_callback; + + if (power_save) + glfwWaitEventsTimeout(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0)); + else + glfwPollEvents(); + + if (!WaitForValidSwapchain()) + result = false; + + data->context = nullptr; + data->key_down_callback = nullptr; + + result = !glfwWindowShouldClose(data->window); + glfwSetWindowShouldClose(data->window, GLFW_FALSE); + + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; + glfwSetWindowShouldClose(data->window, GLFW_TRUE); +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface->BeginFrame(); + data->render_interface->Clear(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface->EndFrame(); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} + +static void SetupCallbacks(GLFWwindow* window) +{ + RMLUI_ASSERT(data); + + // Key input + glfwSetKeyCallback(window, [](GLFWwindow* /*window*/, int glfw_key, int /*scancode*/, int glfw_action, int glfw_mods) { + if (!data->context) + return; + + // Store the active modifiers for later because GLFW doesn't provide them in the callbacks to the mouse input events. + data->glfw_active_modifiers = glfw_mods; + + // Override the default key event callback to add global shortcuts for the samples. + Rml::Context* context = data->context; + KeyDownCallback key_down_callback = data->key_down_callback; + + switch (glfw_action) + { + case GLFW_PRESS: + case GLFW_REPEAT: + { + const Rml::Input::KeyIdentifier key = RmlGLFW::ConvertKey(glfw_key); + const int key_modifier = RmlGLFW::ConvertKeyModifiers(glfw_mods); + float dp_ratio = 1.f; + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, false)) + break; + } + break; + case GLFW_RELEASE: RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods); break; + } + }); + + glfwSetCharCallback(window, [](GLFWwindow* /*window*/, unsigned int codepoint) { RmlGLFW::ProcessCharCallback(data->context, codepoint); }); + + glfwSetCursorEnterCallback(window, [](GLFWwindow* /*window*/, int entered) { RmlGLFW::ProcessCursorEnterCallback(data->context, entered); }); + + // Mouse input + glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) { + RmlGLFW::ProcessCursorPosCallback(data->context, window, xpos, ypos, data->glfw_active_modifiers); + }); + + glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) { + data->glfw_active_modifiers = mods; + RmlGLFW::ProcessMouseButtonCallback(data->context, button, action, mods); + }); + + glfwSetScrollCallback(window, [](GLFWwindow* /*window*/, double /*xoffset*/, double yoffset) { + RmlGLFW::ProcessScrollCallback(data->context, yoffset, data->glfw_active_modifiers); + }); + + // Window events + glfwSetFramebufferSizeCallback(window, [](GLFWwindow* /*window*/, int width, int height) { + data->render_interface->SetViewport(width, height); + RmlGLFW::ProcessFramebufferSizeCallback(data->context, width, height); + }); + + glfwSetWindowContentScaleCallback(window, + [](GLFWwindow* /*window*/, float xscale, float /*yscale*/) { RmlGLFW::ProcessContentScaleCallback(data->context, xscale); }); +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_GLFW_GL2.cpp b/vendor/rmlui_backend/RmlUi_Backend_GLFW_GL2.cpp new file mode 100644 index 0000000..0a69e33 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_GLFW_GL2.cpp @@ -0,0 +1,227 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Platform_GLFW.h" +#include "RmlUi_Renderer_GL2.h" +#include +#include +#include +#include + +static void SetupCallbacks(GLFWwindow* window); + +static void LogErrorFromGLFW(int error, const char* description) +{ + Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW error (0x%x): %s", error, description); +} + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(GLFWwindow* window) : system_interface(window) {} + + SystemInterface_GLFW system_interface; + RenderInterface_GL2 render_interface; + GLFWwindow* window = nullptr; + int glfw_active_modifiers = 0; + bool context_dimensions_dirty = true; + + // Arguments set during event processing and nulled otherwise. + Rml::Context* context = nullptr; + KeyDownCallback key_down_callback = nullptr; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + glfwSetErrorCallback(LogErrorFromGLFW); + + if (!glfwInit()) + return false; + + // Set window hints for OpenGL 2 context creation. + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); + + // Request stencil buffer of at least 8-bit size to supporting clipping on transformed elements. + glfwWindowHint(GLFW_STENCIL_BITS, 8); + + // Enable MSAA for better-looking visuals, especially when transforms are applied. + glfwWindowHint(GLFW_SAMPLES, 2); + + // Apply window properties and create it. + glfwWindowHint(GLFW_RESIZABLE, allow_resize ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); + + GLFWwindow* window = glfwCreateWindow(width, height, name, nullptr, nullptr); + if (!window) + return false; + + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + + data = Rml::MakeUnique(window); + data->window = window; + + // The window size may have been scaled by DPI settings, get the actual pixel size. + glfwGetFramebufferSize(window, &width, &height); + data->render_interface.SetViewport(width, height); + + // Receive num lock and caps lock modifiers for proper handling of numpad inputs in text fields. + glfwSetInputMode(window, GLFW_LOCK_KEY_MODS, GLFW_TRUE); + + // Setup the input and window event callback functions. + SetupCallbacks(window); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + glfwDestroyWindow(data->window); + data.reset(); + glfwTerminate(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + // The initial window size may have been affected by system DPI settings, apply the actual pixel size and dp-ratio to the context. + if (data->context_dimensions_dirty) + { + data->context_dimensions_dirty = false; + + Rml::Vector2i window_size; + float dp_ratio = 1.f; + glfwGetFramebufferSize(data->window, &window_size.x, &window_size.y); + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + context->SetDimensions(window_size); + context->SetDensityIndependentPixelRatio(dp_ratio); + } + + data->context = context; + data->key_down_callback = key_down_callback; + + if (power_save) + glfwWaitEventsTimeout(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0)); + else + glfwPollEvents(); + + data->context = nullptr; + data->key_down_callback = nullptr; + + const bool result = !glfwWindowShouldClose(data->window); + glfwSetWindowShouldClose(data->window, GLFW_FALSE); + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + glfwSetWindowShouldClose(data->window, GLFW_TRUE); +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.BeginFrame(); + data->render_interface.Clear(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); + glfwSwapBuffers(data->window); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} + +static void SetupCallbacks(GLFWwindow* window) +{ + RMLUI_ASSERT(data); + + // Key input + glfwSetKeyCallback(window, [](GLFWwindow* /*window*/, int glfw_key, int /*scancode*/, int glfw_action, int glfw_mods) { + if (!data->context) + return; + + // Store the active modifiers for later because GLFW doesn't provide them in the callbacks to the mouse input events. + data->glfw_active_modifiers = glfw_mods; + + // Override the default key event callback to add global shortcuts for the samples. + Rml::Context* context = data->context; + KeyDownCallback key_down_callback = data->key_down_callback; + + switch (glfw_action) + { + case GLFW_PRESS: + case GLFW_REPEAT: + { + const Rml::Input::KeyIdentifier key = RmlGLFW::ConvertKey(glfw_key); + const int key_modifier = RmlGLFW::ConvertKeyModifiers(glfw_mods); + float dp_ratio = 1.f; + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, false)) + break; + } + break; + case GLFW_RELEASE: RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods); break; + } + }); + + glfwSetCharCallback(window, [](GLFWwindow* /*window*/, unsigned int codepoint) { RmlGLFW::ProcessCharCallback(data->context, codepoint); }); + + glfwSetCursorEnterCallback(window, [](GLFWwindow* /*window*/, int entered) { RmlGLFW::ProcessCursorEnterCallback(data->context, entered); }); + + // Mouse input + glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) { + RmlGLFW::ProcessCursorPosCallback(data->context, window, xpos, ypos, data->glfw_active_modifiers); + }); + + glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) { + data->glfw_active_modifiers = mods; + RmlGLFW::ProcessMouseButtonCallback(data->context, button, action, mods); + }); + + glfwSetScrollCallback(window, [](GLFWwindow* /*window*/, double /*xoffset*/, double yoffset) { + RmlGLFW::ProcessScrollCallback(data->context, yoffset, data->glfw_active_modifiers); + }); + + // Window events + glfwSetFramebufferSizeCallback(window, [](GLFWwindow* /*window*/, int width, int height) { + data->render_interface.SetViewport(width, height); + RmlGLFW::ProcessFramebufferSizeCallback(data->context, width, height); + }); + + glfwSetWindowContentScaleCallback(window, + [](GLFWwindow* /*window*/, float xscale, float /*yscale*/) { RmlGLFW::ProcessContentScaleCallback(data->context, xscale); }); +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp b/vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp new file mode 100644 index 0000000..22acad6 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp @@ -0,0 +1,235 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Platform_GLFW.h" +#include "RmlUi_Renderer_GL3.h" +#include +#include +#include +#include + +static void SetupCallbacks(GLFWwindow* window); + +static void LogErrorFromGLFW(int error, const char* description) +{ + Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW error (0x%x): %s", error, description); +} + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(GLFWwindow* window) : system_interface(window) {} + + SystemInterface_GLFW system_interface; + RenderInterface_GL3 render_interface; + GLFWwindow* window = nullptr; + int glfw_active_modifiers = 0; + bool context_dimensions_dirty = true; + + // Arguments set during event processing and nulled otherwise. + Rml::Context* context = nullptr; + KeyDownCallback key_down_callback = nullptr; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + glfwSetErrorCallback(LogErrorFromGLFW); + + if (!glfwInit()) + return false; + + // Set window hints for OpenGL 3.3 Core context creation. + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); + + // Apply window properties and create it. + glfwWindowHint(GLFW_RESIZABLE, allow_resize ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); + + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + + GLFWwindow* window = glfwCreateWindow(width, height, name, nullptr, nullptr); + if (!window) + return false; + + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + + // Load the OpenGL functions. + Rml::String renderer_message; + if (!RmlGL3::Initialize(&renderer_message)) + return false; + + // Construct the system and render interface, this includes compiling all the shaders. If this fails, it is likely an error in the shader code. + data = Rml::MakeUnique(window); + if (!data || !data->render_interface) + return false; + + data->window = window; + data->system_interface.LogMessage(Rml::Log::LT_INFO, renderer_message); + + // The window size may have been scaled by DPI settings, get the actual pixel size. + glfwGetFramebufferSize(window, &width, &height); + data->render_interface.SetViewport(width, height); + + // Receive num lock and caps lock modifiers for proper handling of numpad inputs in text fields. + glfwSetInputMode(window, GLFW_LOCK_KEY_MODS, GLFW_TRUE); + + // Setup the input and window event callback functions. + SetupCallbacks(window); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + glfwDestroyWindow(data->window); + data.reset(); + RmlGL3::Shutdown(); + glfwTerminate(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + // The initial window size may have been affected by system DPI settings, apply the actual pixel size and dp-ratio to the context. + if (data->context_dimensions_dirty) + { + data->context_dimensions_dirty = false; + + Rml::Vector2i window_size; + float dp_ratio = 1.f; + glfwGetFramebufferSize(data->window, &window_size.x, &window_size.y); + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + context->SetDimensions(window_size); + context->SetDensityIndependentPixelRatio(dp_ratio); + } + + data->context = context; + data->key_down_callback = key_down_callback; + + if (power_save) + glfwWaitEventsTimeout(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0)); + else + glfwPollEvents(); + + data->context = nullptr; + data->key_down_callback = nullptr; + + const bool result = !glfwWindowShouldClose(data->window); + glfwSetWindowShouldClose(data->window, GLFW_FALSE); + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + glfwSetWindowShouldClose(data->window, GLFW_TRUE); +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.Clear(); + data->render_interface.BeginFrame(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); + glfwSwapBuffers(data->window); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} + +static void SetupCallbacks(GLFWwindow* window) +{ + RMLUI_ASSERT(data); + + // Key input + glfwSetKeyCallback(window, [](GLFWwindow* /*window*/, int glfw_key, int /*scancode*/, int glfw_action, int glfw_mods) { + if (!data->context) + return; + + // Store the active modifiers for later because GLFW doesn't provide them in the callbacks to the mouse input events. + data->glfw_active_modifiers = glfw_mods; + + // Override the default key event callback to add global shortcuts for the samples. + Rml::Context* context = data->context; + KeyDownCallback key_down_callback = data->key_down_callback; + + switch (glfw_action) + { + case GLFW_PRESS: + case GLFW_REPEAT: + { + const Rml::Input::KeyIdentifier key = RmlGLFW::ConvertKey(glfw_key); + const int key_modifier = RmlGLFW::ConvertKeyModifiers(glfw_mods); + float dp_ratio = 1.f; + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, false)) + break; + } + break; + case GLFW_RELEASE: RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods); break; + } + }); + + glfwSetCharCallback(window, [](GLFWwindow* /*window*/, unsigned int codepoint) { RmlGLFW::ProcessCharCallback(data->context, codepoint); }); + + glfwSetCursorEnterCallback(window, [](GLFWwindow* /*window*/, int entered) { RmlGLFW::ProcessCursorEnterCallback(data->context, entered); }); + + // Mouse input + glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) { + RmlGLFW::ProcessCursorPosCallback(data->context, window, xpos, ypos, data->glfw_active_modifiers); + }); + + glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) { + data->glfw_active_modifiers = mods; + RmlGLFW::ProcessMouseButtonCallback(data->context, button, action, mods); + }); + + glfwSetScrollCallback(window, [](GLFWwindow* /*window*/, double /*xoffset*/, double yoffset) { + RmlGLFW::ProcessScrollCallback(data->context, yoffset, data->glfw_active_modifiers); + }); + + // Window events + glfwSetFramebufferSizeCallback(window, [](GLFWwindow* /*window*/, int width, int height) { + data->render_interface.SetViewport(width, height); + RmlGLFW::ProcessFramebufferSizeCallback(data->context, width, height); + }); + + glfwSetWindowContentScaleCallback(window, + [](GLFWwindow* /*window*/, float xscale, float /*yscale*/) { RmlGLFW::ProcessContentScaleCallback(data->context, xscale); }); +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_GLFW_VK.cpp b/vendor/rmlui_backend/RmlUi_Backend_GLFW_VK.cpp new file mode 100644 index 0000000..fa80a9c --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_GLFW_VK.cpp @@ -0,0 +1,268 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Renderer_VK.h" +// This space is intentional to prevent autoformat from reordering RmlUi_Renderer_VK behind RmlUi_Platform_GLFW +#include "RmlUi_Platform_GLFW.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static void SetupCallbacks(GLFWwindow* window); + +static void LogErrorFromGLFW(int error, const char* description) +{ + Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW error (0x%x): %s", error, description); +} + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(GLFWwindow* window) : system_interface(window) {} + + SystemInterface_GLFW system_interface; + RenderInterface_VK render_interface; + + GLFWwindow* window = nullptr; + Rml::Context* context = nullptr; + KeyDownCallback key_down_callback = nullptr; + + int glfw_active_modifiers = 0; + bool running = true; + bool context_dimensions_dirty = true; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + glfwSetErrorCallback(LogErrorFromGLFW); + + if (!glfwInit()) + { + glfwTerminate(); + return false; + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, allow_resize ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); + + GLFWwindow* window = glfwCreateWindow(width, height, window_name, nullptr, nullptr); + + if (!window) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW failed to create window"); + return false; + } + + data = Rml::MakeUnique(window); + data->window = window; + + uint32_t count; + const char** extensions = glfwGetRequiredInstanceExtensions(&count); + RMLUI_VK_ASSERTMSG(extensions != nullptr, "Failed to query GLFW Vulkan extensions"); + if (!data->render_interface.Initialize(Rml::Vector(extensions, extensions + count), + [](VkInstance instance, VkSurfaceKHR* out_surface) { + return glfwCreateWindowSurface(instance, data->window, nullptr, out_surface) == VkResult::VK_SUCCESS; + ; + })) + { + data.reset(); + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to initialize Vulkan render interface"); + return false; + } + + data->render_interface.SetViewport(width, height); + + // Receive num lock and caps lock modifiers for proper handling of numpad inputs in text fields. + glfwSetInputMode(window, GLFW_LOCK_KEY_MODS, GLFW_TRUE); + + SetupCallbacks(window); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + data->render_interface.Shutdown(); + + glfwDestroyWindow(data->window); + + data.reset(); + + glfwTerminate(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +static bool WaitForValidSwapchain() +{ + bool result = true; + + // In some situations the swapchain may become invalid, such as when the window is minimized. In this state the renderer cannot accept any render + // calls. Since we don't have full control over the main loop here we may risk calls to Context::Render if we were to return. Instead, we keep the + // application inside this loop until we are able to recreate the swapchain and render again. + + while (!data->render_interface.IsSwapchainValid()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + if (glfwWindowShouldClose(data->window)) + { + glfwRestoreWindow(data->window); + result = false; + } + + glfwPollEvents(); + data->render_interface.RecreateSwapchain(); + } + + return result; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + bool result = data->running; + + if (data->context_dimensions_dirty) + { + data->context_dimensions_dirty = false; + + Rml::Vector2i window_size; + float dp_ratio = 1.f; + glfwGetFramebufferSize(data->window, &window_size.x, &window_size.y); + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + context->SetDimensions(window_size); + context->SetDensityIndependentPixelRatio(dp_ratio); + } + + data->context = context; + data->key_down_callback = key_down_callback; + + if (power_save) + glfwWaitEventsTimeout(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0)); + else + glfwPollEvents(); + + if (!WaitForValidSwapchain()) + result = false; + + data->context = nullptr; + data->key_down_callback = nullptr; + + result = !glfwWindowShouldClose(data->window); + glfwSetWindowShouldClose(data->window, GLFW_FALSE); + + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; + glfwSetWindowShouldClose(data->window, GLFW_TRUE); +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.BeginFrame(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); +} + +static void SetupCallbacks(GLFWwindow* window) +{ + RMLUI_ASSERT(data); + + // Key input + glfwSetKeyCallback(window, [](GLFWwindow* /*window*/, int glfw_key, int /*scancode*/, int glfw_action, int glfw_mods) { + if (!data->context) + return; + + // Store the active modifiers for later because GLFW doesn't provide them in the callbacks to the mouse input events. + data->glfw_active_modifiers = glfw_mods; + + // Override the default key event callback to add global shortcuts for the samples. + Rml::Context* context = data->context; + KeyDownCallback key_down_callback = data->key_down_callback; + + switch (glfw_action) + { + case GLFW_PRESS: + case GLFW_REPEAT: + { + const Rml::Input::KeyIdentifier key = RmlGLFW::ConvertKey(glfw_key); + const int key_modifier = RmlGLFW::ConvertKeyModifiers(glfw_mods); + float dp_ratio = 1.f; + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, false)) + break; + } + break; + case GLFW_RELEASE: RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods); break; + } + }); + + glfwSetCharCallback(window, [](GLFWwindow* /*window*/, unsigned int codepoint) { RmlGLFW::ProcessCharCallback(data->context, codepoint); }); + + glfwSetCursorEnterCallback(window, [](GLFWwindow* /*window*/, int entered) { RmlGLFW::ProcessCursorEnterCallback(data->context, entered); }); + + // Mouse input + glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) { + RmlGLFW::ProcessCursorPosCallback(data->context, window, xpos, ypos, data->glfw_active_modifiers); + }); + + glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) { + data->glfw_active_modifiers = mods; + RmlGLFW::ProcessMouseButtonCallback(data->context, button, action, mods); + }); + + glfwSetScrollCallback(window, [](GLFWwindow* /*window*/, double /*xoffset*/, double yoffset) { + RmlGLFW::ProcessScrollCallback(data->context, yoffset, data->glfw_active_modifiers); + }); + + // Window events + glfwSetFramebufferSizeCallback(window, [](GLFWwindow* /*window*/, int width, int height) { + data->render_interface.SetViewport(width, height); + RmlGLFW::ProcessFramebufferSizeCallback(data->context, width, height); + }); + + glfwSetWindowContentScaleCallback(window, + [](GLFWwindow* /*window*/, float xscale, float /*yscale*/) { RmlGLFW::ProcessContentScaleCallback(data->context, xscale); }); +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_SDL_DX12.cpp b/vendor/rmlui_backend/RmlUi_Backend_SDL_DX12.cpp new file mode 100644 index 0000000..325ad86 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_SDL_DX12.cpp @@ -0,0 +1,394 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Platform_SDL.h" +#include "RmlUi_Renderer_DX12.h" +#include +#include +#include +#include +#include + +#if SDL_MAJOR_VERSION >= 3 + #include +#else + #include + #include +#endif + +/** + Custom render interface example for the SDL/DX12 backend. + + Overloads the DirectX 12 render interface to load textures through SDL_image's built-in texture loading functionality. + */ +class RenderInterface_DX12_SDL : public RenderInterface_DX12 { +public: + RenderInterface_DX12_SDL(void* p_window_handle, const Backend::RmlRendererSettings& settings) : RenderInterface_DX12(p_window_handle, settings) {} + + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override + { + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + return {}; + + file_interface->Seek(file_handle, 0, SEEK_END); + const size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + using Rml::byte; + Rml::UniquePtr buffer(new byte[buffer_size]); + file_interface->Read(buffer.get(), buffer_size, file_handle); + file_interface->Close(file_handle); + + const size_t i_ext = source.rfind('.'); + Rml::String extension = (i_ext == Rml::String::npos ? Rml::String() : source.substr(i_ext + 1)); + +#if SDL_MAJOR_VERSION >= 3 + auto CreateSurface = [&]() { return IMG_LoadTyped_IO(SDL_IOFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str()); }; + auto GetSurfaceFormat = [](SDL_Surface* surface) { return surface->format; }; + auto ConvertSurface = [](SDL_Surface* surface, SDL_PixelFormat format) { return SDL_ConvertSurface(surface, format); }; + auto DestroySurface = [](SDL_Surface* surface) { SDL_DestroySurface(surface); }; +#else + auto CreateSurface = [&]() { return IMG_LoadTyped_RW(SDL_RWFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str()); }; + auto GetSurfaceFormat = [](SDL_Surface* surface) { return surface->format->format; }; + auto ConvertSurface = [](SDL_Surface* surface, Uint32 format) { return SDL_ConvertSurfaceFormat(surface, format, 0); }; + auto DestroySurface = [](SDL_Surface* surface) { SDL_FreeSurface(surface); }; +#endif + + SDL_Surface* surface = CreateSurface(); + if (!surface) + return {}; + + texture_dimensions = {surface->w, surface->h}; + + if (GetSurfaceFormat(surface) != SDL_PIXELFORMAT_RGBA32) + { + // Ensure correct format for premultiplied alpha conversion and GenerateTexture below. + SDL_Surface* converted_surface = ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); + DestroySurface(surface); + if (!converted_surface) + return {}; + + surface = converted_surface; + } + + // Convert colors to premultiplied alpha, which is necessary for correct alpha compositing. + const size_t pixels_byte_size = surface->w * surface->h * 4; + byte* pixels = static_cast(surface->pixels); + for (size_t i = 0; i < pixels_byte_size; i += 4) + { + const byte alpha = pixels[i + 3]; + for (size_t j = 0; j < 3; ++j) + pixels[i + j] = byte(int(pixels[i + j]) * int(alpha) / 255); + } + + Rml::TextureHandle texture_handle = RenderInterface_DX12::GenerateTexture({pixels, pixels_byte_size}, texture_dimensions); + + DestroySurface(surface); + + return texture_handle; + } +}; + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(SDL_Window* window) : system_interface(window), window(window) {} + + SystemInterface_SDL system_interface; + Rml::UniquePtr render_interface; + TextInputMethodEditor_SDL text_input_method_editor; + + SDL_Window* window; + + bool running = true; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + +#if SDL_MAJOR_VERSION >= 3 + SDL_SetHint(SDL_HINT_IME_IMPLEMENTED_UI, "composition"); + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) + return false; +#else + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0) + return false; +#endif + + // Submit click events when focusing the window. + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); + // Touch events are handled natively, no need to generate synthetic mouse events for touch devices. + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + +#if defined RMLUI_BACKEND_SIMULATE_TOUCH + // Simulate touch events from mouse events for testing touch behavior on a desktop machine. + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1"); +#endif + +#if SDL_MAJOR_VERSION >= 3 + const float window_size_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, window_name); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, int(width * window_size_scale)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, int(height * window_size_scale)); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, allow_resize); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, true); + SDL_Window* window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); +#else + const Uint32 window_flags = (allow_resize ? SDL_WINDOW_RESIZABLE : 0); + SDL_Window* window = SDL_CreateWindow(window_name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, window_flags); + // SDL2 implicitly activates text input on window creation. Turn it off for now, it will be activated again e.g. when focusing a text input field. + SDL_StopTextInput(); +#endif + + if (!window) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "SDL error on create window: %s", SDL_GetError()); + return false; + } + +#if SDL_MAJOR_VERSION >= 3 + HWND window_handle = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); +#else + SDL_SysWMinfo wm_info = {}; + SDL_VERSION(&wm_info.version); + if (!SDL_GetWindowWMInfo(window, &wm_info)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "SDL error getting window info: %s", SDL_GetError()); + SDL_DestroyWindow(window); + SDL_Quit(); + return false; + } + HWND window_handle = wm_info.info.win.window; +#endif + + if (!window_handle) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Could not retrieve native window handle from SDL"); + SDL_DestroyWindow(window); + SDL_Quit(); + return false; + } + + // Get the actual window size (may differ due to DPI scaling). +#if SDL_MAJOR_VERSION >= 3 + SDL_GetWindowSizeInPixels(window, &width, &height); +#else + SDL_GetWindowSize(window, &width, &height); +#endif + + data = Rml::MakeUnique(window); + + RmlRendererSettings settings = {}; + settings.vsync = true; + settings.msaa_sample_count = RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT; + + data->render_interface = Rml::MakeUnique(window_handle, settings); + + if (!data->render_interface || !(*data->render_interface)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to initialize DirectX 12 render interface"); + SDL_DestroyWindow(window); + data.reset(); + SDL_Quit(); + return false; + } + + data->render_interface->SetViewport(width, height); + + Rml::SetTextInputHandler(&data->text_input_method_editor); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + data->render_interface.reset(); + + SDL_DestroyWindow(data->window); + + data.reset(); + + SDL_Quit(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return data->render_interface.get(); +} + +static bool WaitForValidSwapchain() +{ +#if SDL_MAJOR_VERSION >= 3 + constexpr auto event_quit = SDL_EVENT_QUIT; +#else + constexpr auto event_quit = SDL_QUIT; +#endif + + bool result = true; + + // In some situations the swapchain may become invalid, such as when the window is minimized. In this state the renderer cannot accept any render + // calls. Since we don't have full control over the main loop here we may risk calls to Context::Render if we were to return. Instead, we keep the + // application inside this loop until we are able to recreate the swapchain and render again. + while (!data->render_interface->IsSwapchainValid()) + { + SDL_Event ev; + while (SDL_PollEvent(&ev)) + { + if (ev.type == event_quit) + { + // Restore the window so that we can recreate the swapchain, and then properly release all resource and shutdown cleanly. + SDL_RestoreWindow(data->window); + result = false; + } + } + SDL_Delay(10); + data->render_interface->RecreateSwapchain(); + } + + return result; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + +#if SDL_MAJOR_VERSION >= 3 + #define RMLSDL_WINDOW_EVENTS_BEGIN + #define RMLSDL_WINDOW_EVENTS_END + auto GetKey = [](const SDL_Event& event) { return event.key.key; }; + auto GetDisplayScale = []() { return SDL_GetWindowDisplayScale(data->window); }; + constexpr auto event_quit = SDL_EVENT_QUIT; + constexpr auto event_key_down = SDL_EVENT_KEY_DOWN; + constexpr auto event_text_editing = SDL_EVENT_TEXT_EDITING; + constexpr auto event_window_size_changed = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED; + bool has_event = false; +#else + #define RMLSDL_WINDOW_EVENTS_BEGIN \ + case SDL_WINDOWEVENT: \ + { \ + switch (ev.window.event) \ + { + #define RMLSDL_WINDOW_EVENTS_END \ + } \ + } \ + break; + auto GetKey = [](const SDL_Event& event) { return event.key.keysym.sym; }; + auto GetDisplayScale = []() { return 1.f; }; + constexpr auto event_quit = SDL_QUIT; + constexpr auto event_key_down = SDL_KEYDOWN; + constexpr auto event_text_editing = SDL_TEXTEDITING; + constexpr auto event_window_size_changed = SDL_WINDOWEVENT_SIZE_CHANGED; + int has_event = 0; +#endif + + bool result = data->running; + data->running = true; + + SDL_Event ev; + if (power_save) + has_event = SDL_WaitEventTimeout(&ev, static_cast(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0) * 1000)); + else + has_event = SDL_PollEvent(&ev); + + while (has_event) + { + bool propagate_event = true; + switch (ev.type) + { + case event_quit: + { + propagate_event = false; + result = false; + } + break; + case event_key_down: + { + propagate_event = false; + const Rml::Input::KeyIdentifier key = RmlSDL::ConvertKey(GetKey(ev)); + const int key_modifier = RmlSDL::GetKeyModifierState(); + const float native_dp_ratio = GetDisplayScale(); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlSDL::InputEventHandler(context, data->window, ev)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, false)) + break; + } + break; + case event_text_editing: + { + propagate_event = false; + data->text_input_method_editor.HandleEdit(ev.edit); + } + break; + + RMLSDL_WINDOW_EVENTS_BEGIN + + case event_window_size_changed: + { + Rml::Vector2i dimensions = {ev.window.data1, ev.window.data2}; + data->render_interface->SetViewport(dimensions.x, dimensions.y); + } + break; + + RMLSDL_WINDOW_EVENTS_END + + default: break; + } + + if (propagate_event) + RmlSDL::InputEventHandler(context, data->window, ev); + + has_event = SDL_PollEvent(&ev); + } + + if (!WaitForValidSwapchain()) + result = false; + + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface->BeginFrame(); + data->render_interface->Clear(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface->EndFrame(); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_SDL_GL2.cpp b/vendor/rmlui_backend/RmlUi_Backend_SDL_GL2.cpp new file mode 100644 index 0000000..b822680 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_SDL_GL2.cpp @@ -0,0 +1,353 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Platform_SDL.h" +#include "RmlUi_Renderer_GL2.h" +#include +#include +#include +#include + +#if SDL_MAJOR_VERSION >= 3 + #include +#else + #include +#endif + +#if SDL_MAJOR_VERSION == 2 && !(SDL_VIDEO_RENDER_OGL) + #error "Only the OpenGL SDL backend is supported." +#endif + +/** + Custom render interface example for the SDL/GL2 backend. + + Overloads the OpenGL2 render interface to load textures through SDL_image's built-in texture loading functionality. + */ +class RenderInterface_GL2_SDL : public RenderInterface_GL2 { +public: + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override + { + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + return {}; + + file_interface->Seek(file_handle, 0, SEEK_END); + const size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + using Rml::byte; + Rml::UniquePtr buffer(new byte[buffer_size]); + file_interface->Read(buffer.get(), buffer_size, file_handle); + file_interface->Close(file_handle); + + const size_t i_ext = source.rfind('.'); + Rml::String extension = (i_ext == Rml::String::npos ? Rml::String() : source.substr(i_ext + 1)); + +#if SDL_MAJOR_VERSION >= 3 + auto CreateSurface = [&]() { return IMG_LoadTyped_IO(SDL_IOFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str()); }; + auto GetSurfaceFormat = [](SDL_Surface* surface) { return surface->format; }; + auto ConvertSurface = [](SDL_Surface* surface, SDL_PixelFormat format) { return SDL_ConvertSurface(surface, format); }; + auto DestroySurface = [](SDL_Surface* surface) { SDL_DestroySurface(surface); }; +#else + auto CreateSurface = [&]() { return IMG_LoadTyped_RW(SDL_RWFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str()); }; + auto GetSurfaceFormat = [](SDL_Surface* surface) { return surface->format->format; }; + auto ConvertSurface = [](SDL_Surface* surface, Uint32 format) { return SDL_ConvertSurfaceFormat(surface, format, 0); }; + auto DestroySurface = [](SDL_Surface* surface) { SDL_FreeSurface(surface); }; +#endif + + SDL_Surface* surface = CreateSurface(); + if (!surface) + return {}; + + texture_dimensions = {surface->w, surface->h}; + + if (GetSurfaceFormat(surface) != SDL_PIXELFORMAT_RGBA32) + { + // Ensure correct format for premultiplied alpha conversion and GenerateTexture below. + SDL_Surface* converted_surface = ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); + DestroySurface(surface); + if (!converted_surface) + return {}; + + surface = converted_surface; + } + + // Convert colors to premultiplied alpha, which is necessary for correct alpha compositing. + const size_t pixels_byte_size = surface->w * surface->h * 4; + byte* pixels = static_cast(surface->pixels); + for (size_t i = 0; i < pixels_byte_size; i += 4) + { + const byte alpha = pixels[i + 3]; + for (size_t j = 0; j < 3; ++j) + pixels[i + j] = byte(int(pixels[i + j]) * int(alpha) / 255); + } + + Rml::TextureHandle texture_handle = RenderInterface_GL2::GenerateTexture({pixels, pixels_byte_size}, texture_dimensions); + + DestroySurface(surface); + + return texture_handle; + } +}; + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(SDL_Window* window) : system_interface(window) {} + + SystemInterface_SDL system_interface; + RenderInterface_GL2_SDL render_interface; + TextInputMethodEditor_SDL text_input_method_editor; + + SDL_Window* window = nullptr; + SDL_GLContext glcontext = nullptr; + + bool running = true; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + +#if SDL_MAJOR_VERSION >= 3 + SDL_SetHint(SDL_HINT_IME_IMPLEMENTED_UI, "composition"); + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) + return false; +#else + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0) + return false; +#endif + + // Submit click events when focusing the window. + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); + // Touch events are handled natively, no need to generate synthetic mouse events for touch devices. + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + +#if defined RMLUI_BACKEND_SIMULATE_TOUCH + // Simulate touch events from mouse events for testing touch behavior on a desktop machine. + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1"); +#endif + + // Request stencil buffer of at least 8-bit size to supporting clipping on transformed elements. + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + // Enable MSAA for better-looking visuals, especially when transforms are applied. + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 2); + +#if SDL_MAJOR_VERSION >= 3 + auto CreateWindow = [&]() { + const float window_size_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, window_name); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, int(width * window_size_scale)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, int(height * window_size_scale)); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, allow_resize); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, true); + SDL_Window* window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); + return window; + }; +#else + auto CreateWindow = [&]() { + const Uint32 window_flags = (SDL_WINDOW_OPENGL | (allow_resize ? SDL_WINDOW_RESIZABLE : 0)); + SDL_Window* window = SDL_CreateWindow(window_name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, window_flags); + // SDL2 implicitly activates text input on window creation. Turn it off for now, it will be activated again e.g. when focusing a text input + // field. + SDL_StopTextInput(); + return window; + }; + // Enable linear filtering, SDL 3 already defaults to it. + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); +#endif + + SDL_Window* window = CreateWindow(); + if (!window) + { + // Try again on low-quality settings. + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); + window = CreateWindow(); + if (!window) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "SDL error on create window: %s", SDL_GetError()); + return false; + } + } + + SDL_GLContext glcontext = SDL_GL_CreateContext(window); + SDL_GL_MakeCurrent(window, glcontext); + SDL_GL_SetSwapInterval(1); + + data = Rml::MakeUnique(window); + + data->window = window; + data->glcontext = glcontext; + + data->render_interface.SetViewport(width, height); + + Rml::SetTextInputHandler(&data->text_input_method_editor); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + +#if SDL_MAJOR_VERSION >= 3 + SDL_GL_DestroyContext(data->glcontext); +#else + SDL_GL_DeleteContext(data->glcontext); +#endif + + SDL_DestroyWindow(data->window); + + data.reset(); + + SDL_Quit(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + +#if SDL_MAJOR_VERSION >= 3 + #define RMLSDL_WINDOW_EVENTS_BEGIN + #define RMLSDL_WINDOW_EVENTS_END + auto GetKey = [](const SDL_Event& event) { return event.key.key; }; + auto GetDisplayScale = []() { return SDL_GetWindowDisplayScale(data->window); }; + constexpr auto event_quit = SDL_EVENT_QUIT; + constexpr auto event_key_down = SDL_EVENT_KEY_DOWN; + constexpr auto event_text_editing = SDL_EVENT_TEXT_EDITING; + constexpr auto event_window_size_changed = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED; + bool has_event = false; +#else + #define RMLSDL_WINDOW_EVENTS_BEGIN \ + case SDL_WINDOWEVENT: \ + { \ + switch (ev.window.event) \ + { + #define RMLSDL_WINDOW_EVENTS_END \ + } \ + } \ + break; + auto GetKey = [](const SDL_Event& event) { return event.key.keysym.sym; }; + auto GetDisplayScale = []() { return 1.f; }; + constexpr auto event_quit = SDL_QUIT; + constexpr auto event_key_down = SDL_KEYDOWN; + constexpr auto event_text_editing = SDL_TEXTEDITING; + constexpr auto event_window_size_changed = SDL_WINDOWEVENT_SIZE_CHANGED; + int has_event = 0; +#endif + + bool result = data->running; + data->running = true; + + SDL_Event ev; + if (power_save) + has_event = SDL_WaitEventTimeout(&ev, static_cast(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0) * 1000)); + else + has_event = SDL_PollEvent(&ev); + + while (has_event) + { + bool propagate_event = true; + switch (ev.type) + { + case event_quit: + { + propagate_event = false; + result = false; + } + break; + case event_key_down: + { + propagate_event = false; + const Rml::Input::KeyIdentifier key = RmlSDL::ConvertKey(GetKey(ev)); + const int key_modifier = RmlSDL::GetKeyModifierState(); + const float native_dp_ratio = GetDisplayScale(); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlSDL::InputEventHandler(context, data->window, ev)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, false)) + break; + } + break; + case event_text_editing: + { + propagate_event = false; + data->text_input_method_editor.HandleEdit(ev.edit); + } + break; + + RMLSDL_WINDOW_EVENTS_BEGIN + + case event_window_size_changed: + { + Rml::Vector2i dimensions = {ev.window.data1, ev.window.data2}; + data->render_interface.SetViewport(dimensions.x, dimensions.y); + } + break; + + RMLSDL_WINDOW_EVENTS_END + + default: break; + } + + if (propagate_event) + RmlSDL::InputEventHandler(context, data->window, ev); + + has_event = SDL_PollEvent(&ev); + } + + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + + data->render_interface.Clear(); + data->render_interface.BeginFrame(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + + data->render_interface.EndFrame(); + SDL_GL_SwapWindow(data->window); +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_SDL_GL3.cpp b/vendor/rmlui_backend/RmlUi_Backend_SDL_GL3.cpp new file mode 100644 index 0000000..090a2a7 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_SDL_GL3.cpp @@ -0,0 +1,388 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Platform_SDL.h" +#include "RmlUi_Renderer_GL3.h" +#include +#include +#include +#include +#include + +#if SDL_MAJOR_VERSION >= 3 + #include +#else + #include +#endif + +#if defined RMLUI_PLATFORM_EMSCRIPTEN + #include +#elif SDL_MAJOR_VERSION == 2 && !(SDL_VIDEO_RENDER_OGL) + #error "Only the OpenGL SDL backend is supported." +#endif + +/** + Custom render interface example for the SDL/GL3 backend. + + Overloads the OpenGL3 render interface to load textures through SDL_image's built-in texture loading functionality. + */ +class RenderInterface_GL3_SDL : public RenderInterface_GL3 { +public: + RenderInterface_GL3_SDL() {} + + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override + { + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + return {}; + + file_interface->Seek(file_handle, 0, SEEK_END); + const size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + using Rml::byte; + Rml::UniquePtr buffer(new byte[buffer_size]); + file_interface->Read(buffer.get(), buffer_size, file_handle); + file_interface->Close(file_handle); + + const size_t i_ext = source.rfind('.'); + Rml::String extension = (i_ext == Rml::String::npos ? Rml::String() : source.substr(i_ext + 1)); + +#if SDL_MAJOR_VERSION >= 3 + auto CreateSurface = [&]() { return IMG_LoadTyped_IO(SDL_IOFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str()); }; + auto GetSurfaceFormat = [](SDL_Surface* surface) { return surface->format; }; + auto ConvertSurface = [](SDL_Surface* surface, SDL_PixelFormat format) { return SDL_ConvertSurface(surface, format); }; + auto DestroySurface = [](SDL_Surface* surface) { SDL_DestroySurface(surface); }; +#else + auto CreateSurface = [&]() { return IMG_LoadTyped_RW(SDL_RWFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str()); }; + auto GetSurfaceFormat = [](SDL_Surface* surface) { return surface->format->format; }; + auto ConvertSurface = [](SDL_Surface* surface, Uint32 format) { return SDL_ConvertSurfaceFormat(surface, format, 0); }; + auto DestroySurface = [](SDL_Surface* surface) { SDL_FreeSurface(surface); }; +#endif + + SDL_Surface* surface = CreateSurface(); + if (!surface) + return {}; + + texture_dimensions = {surface->w, surface->h}; + + if (GetSurfaceFormat(surface) != SDL_PIXELFORMAT_RGBA32) + { + // Ensure correct format for premultiplied alpha conversion and GenerateTexture below. + SDL_Surface* converted_surface = ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); + DestroySurface(surface); + if (!converted_surface) + return {}; + + surface = converted_surface; + } + + // Convert colors to premultiplied alpha, which is necessary for correct alpha compositing. + const size_t pixels_byte_size = surface->w * surface->h * 4; + byte* pixels = static_cast(surface->pixels); + for (size_t i = 0; i < pixels_byte_size; i += 4) + { + const byte alpha = pixels[i + 3]; + for (size_t j = 0; j < 3; ++j) + pixels[i + j] = byte(int(pixels[i + j]) * int(alpha) / 255); + } + + Rml::TextureHandle texture_handle = RenderInterface_GL3::GenerateTexture({pixels, pixels_byte_size}, texture_dimensions); + + DestroySurface(surface); + + return texture_handle; + } +}; + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(SDL_Window* window) : system_interface(window) {} + + SystemInterface_SDL system_interface; + RenderInterface_GL3_SDL render_interface; + TextInputMethodEditor_SDL text_input_method_editor; + + SDL_Window* window = nullptr; + SDL_GLContext glcontext = nullptr; + + bool running = true; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + +#if SDL_MAJOR_VERSION >= 3 + SDL_SetHint(SDL_HINT_IME_IMPLEMENTED_UI, "composition"); + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) + return false; +#else + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0) + return false; +#endif + + // Submit click events when focusing the window. + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); + // Touch events are handled natively, no need to generate synthetic mouse events for touch devices. + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + +#if defined RMLUI_BACKEND_SIMULATE_TOUCH + // Simulate touch events from mouse events for testing touch behavior on a desktop machine. + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1"); +#endif + +#if defined(RMLUI_PLATFORM_EMSCRIPTEN) + // GLES 3.0 (WebGL 2.0) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#elif defined(__ANDROID__) + // GLES 3.2 on Android + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); +#else + // GL 3.3 Core + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); +#endif + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + +#if SDL_MAJOR_VERSION >= 3 + const float window_size_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, window_name); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, int(width * window_size_scale)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, int(height * window_size_scale)); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, allow_resize); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, true); + SDL_Window* window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); +#else + const Uint32 window_flags = (SDL_WINDOW_OPENGL | (allow_resize ? SDL_WINDOW_RESIZABLE : 0)); + SDL_Window* window = SDL_CreateWindow(window_name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, window_flags); + // SDL2 implicitly activates text input on window creation. Turn it off for now, it will be activated again e.g. when focusing a text input field. + SDL_StopTextInput(); +#endif + + if (!window) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "SDL error on create window: %s", SDL_GetError()); + return false; + } + + SDL_GLContext glcontext = SDL_GL_CreateContext(window); + SDL_GL_MakeCurrent(window, glcontext); + SDL_GL_SetSwapInterval(1); + + if (!RmlGL3::Initialize()) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to initialize OpenGL renderer"); + return false; + } + + data = Rml::MakeUnique(window); + + if (!data->render_interface) + { + data.reset(); + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to initialize OpenGL3 render interface"); + return false; + } + + data->window = window; + data->glcontext = glcontext; + + data->render_interface.SetViewport(width, height); + + Rml::SetTextInputHandler(&data->text_input_method_editor); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + SDL_Window* window = data->window; + SDL_GLContext glcontext = data->glcontext; + + data.reset(); + +#if SDL_MAJOR_VERSION >= 3 + SDL_GL_DestroyContext(glcontext); +#else + SDL_GL_DeleteContext(glcontext); +#endif + + SDL_DestroyWindow(window); + + SDL_Quit(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + +#if defined RMLUI_PLATFORM_EMSCRIPTEN + + // Ideally we would hand over control of the main loop to emscripten: + // + // // Hand over control of the main loop to the WebAssembly runtime. + // emscripten_set_main_loop_arg(EventLoopIteration, (void*)user_data_handle, 0, true); + // + // The above is the recommended approach. However, as we don't control the main loop here we have to make due with another approach. Instead, use + // Asyncify to yield by sleeping. + // Important: Must be linked with option -sASYNCIFY + emscripten_sleep(1); + +#endif + +#if SDL_MAJOR_VERSION >= 3 + #define RMLSDL_WINDOW_EVENTS_BEGIN + #define RMLSDL_WINDOW_EVENTS_END + auto GetKey = [](const SDL_Event& event) { return event.key.key; }; + auto GetDisplayScale = []() { return SDL_GetWindowDisplayScale(data->window); }; + constexpr auto event_quit = SDL_EVENT_QUIT; + constexpr auto event_key_down = SDL_EVENT_KEY_DOWN; + constexpr auto event_text_editing = SDL_EVENT_TEXT_EDITING; + constexpr auto event_window_size_changed = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED; + bool has_event = false; +#else + #define RMLSDL_WINDOW_EVENTS_BEGIN \ + case SDL_WINDOWEVENT: \ + { \ + switch (ev.window.event) \ + { + #define RMLSDL_WINDOW_EVENTS_END \ + } \ + } \ + break; + auto GetKey = [](const SDL_Event& event) { return event.key.keysym.sym; }; + auto GetDisplayScale = []() { return 1.f; }; + constexpr auto event_quit = SDL_QUIT; + constexpr auto event_key_down = SDL_KEYDOWN; + constexpr auto event_text_editing = SDL_TEXTEDITING; + constexpr auto event_window_size_changed = SDL_WINDOWEVENT_SIZE_CHANGED; + int has_event = 0; +#endif + + bool result = data->running; + data->running = true; + + SDL_Event ev; + if (power_save) + has_event = SDL_WaitEventTimeout(&ev, static_cast(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0) * 1000)); + else + has_event = SDL_PollEvent(&ev); + + while (has_event) + { + bool propagate_event = true; + switch (ev.type) + { + case event_quit: + { + propagate_event = false; + result = false; + } + break; + case event_key_down: + { + propagate_event = false; + const Rml::Input::KeyIdentifier key = RmlSDL::ConvertKey(GetKey(ev)); + const int key_modifier = RmlSDL::GetKeyModifierState(); + const float native_dp_ratio = GetDisplayScale(); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlSDL::InputEventHandler(context, data->window, ev)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, false)) + break; + } + break; + case event_text_editing: + { + propagate_event = false; + data->text_input_method_editor.HandleEdit(ev.edit); + } + break; + + RMLSDL_WINDOW_EVENTS_BEGIN + + case event_window_size_changed: + { + Rml::Vector2i dimensions = {ev.window.data1, ev.window.data2}; + data->render_interface.SetViewport(dimensions.x, dimensions.y); + } + break; + + RMLSDL_WINDOW_EVENTS_END + + default: break; + } + + if (propagate_event) + RmlSDL::InputEventHandler(context, data->window, ev); + + has_event = SDL_PollEvent(&ev); + } + + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + + data->render_interface.Clear(); + data->render_interface.BeginFrame(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + + data->render_interface.EndFrame(); + SDL_GL_SwapWindow(data->window); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_SDL_GPU.cpp b/vendor/rmlui_backend/RmlUi_Backend_SDL_GPU.cpp new file mode 100644 index 0000000..f2da0a5 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_SDL_GPU.cpp @@ -0,0 +1,246 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Platform_SDL.h" +#include "RmlUi_Renderer_SDL_GPU.h" +#include +#include +#include + +#ifndef RMLUI_BACKEND_SDL_GPU_DEBUG + #define RMLUI_BACKEND_SDL_GPU_DEBUG false +#endif + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(SDL_Window* window, SDL_GPUDevice* device) : system_interface(window), render_interface(device, window) {} + + SystemInterface_SDL system_interface; + RenderInterface_SDL_GPU render_interface; + TextInputMethodEditor_SDL text_input_method_editor; + + SDL_Window* window = nullptr; + SDL_GPUDevice* device = nullptr; + SDL_GPUCommandBuffer* command_buffer = nullptr; + + bool running = true; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + SDL_SetHint(SDL_HINT_IME_IMPLEMENTED_UI, "composition"); + + if (!SDL_Init(SDL_INIT_VIDEO)) + return false; + + // Submit click events when focusing the window. + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); + // Touch events are handled natively, no need to generate synthetic mouse events for touch devices. + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + +#if defined RMLUI_BACKEND_SIMULATE_TOUCH + // Simulate touch events from mouse events for testing touch behavior on a desktop machine. + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1"); +#endif + + const float window_size_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, window_name); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, int(width * window_size_scale)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, int(height * window_size_scale)); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, allow_resize); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, true); + SDL_Window* window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); + + if (!window) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "SDL error on create window: %s", SDL_GetError()); + return false; + } + + props = SDL_CreateProperties(); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, RMLUI_BACKEND_SDL_GPU_DEBUG); + SDL_GPUDevice* device = SDL_CreateGPUDeviceWithProperties(props); + SDL_DestroyProperties(props); + + if (!device) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "SDL error on create GPU device: %s", SDL_GetError()); + return false; + } + + if (!SDL_ClaimWindowForGPUDevice(device, window)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "SDL error on claiming window for GPU device: %s", SDL_GetError()); + return false; + } + + data = Rml::MakeUnique(window, device); + data->window = window; + data->device = device; + + const char* renderer_name = SDL_GetGPUDeviceDriver(device); + data->system_interface.LogMessage(Rml::Log::LT_INFO, Rml::CreateString("Using SDL device driver: %s", renderer_name)); + + Rml::SetTextInputHandler(&data->text_input_method_editor); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + data->render_interface.Shutdown(); + + SDL_ReleaseWindowFromGPUDevice(data->device, data->window); + SDL_DestroyGPUDevice(data->device); + SDL_DestroyWindow(data->window); + + data.reset(); + + SDL_Quit(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + auto GetKey = [](const SDL_Event& event) { return event.key.key; }; + auto GetDisplayScale = []() { return SDL_GetWindowDisplayScale(data->window); }; + constexpr auto event_quit = SDL_EVENT_QUIT; + constexpr auto event_key_down = SDL_EVENT_KEY_DOWN; + constexpr auto event_text_editing = SDL_EVENT_TEXT_EDITING; + bool has_event = false; + + bool result = data->running; + data->running = true; + + SDL_Event ev; + if (power_save) + has_event = SDL_WaitEventTimeout(&ev, static_cast(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0) * 1000)); + else + has_event = SDL_PollEvent(&ev); + + while (has_event) + { + bool propagate_event = true; + switch (ev.type) + { + case event_quit: + { + propagate_event = false; + result = false; + } + break; + case event_key_down: + { + propagate_event = false; + const Rml::Input::KeyIdentifier key = RmlSDL::ConvertKey(GetKey(ev)); + const int key_modifier = RmlSDL::GetKeyModifierState(); + const float native_dp_ratio = GetDisplayScale(); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlSDL::InputEventHandler(context, data->window, ev)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, false)) + break; + } + break; + case event_text_editing: + { + propagate_event = false; + data->text_input_method_editor.HandleEdit(ev.edit); + } + break; + default: break; + } + + if (propagate_event) + RmlSDL::InputEventHandler(context, data->window, ev); + + has_event = SDL_PollEvent(&ev); + } + + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + + data->command_buffer = SDL_AcquireGPUCommandBuffer(data->device); + if (!data->command_buffer) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to acquire command buffer: %s", SDL_GetError()); + return; + } + + SDL_GPUTexture* swapchain_texture; + uint32_t width; + uint32_t height; + if (!SDL_WaitAndAcquireGPUSwapchainTexture(data->command_buffer, data->window, &swapchain_texture, &width, &height)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to acquire swapchain texture: %s", SDL_GetError()); + return; + } + + if (!swapchain_texture || !width || !height) + { + // Not an error. Happens on minimize + SDL_CancelGPUCommandBuffer(data->command_buffer); + return; + } + + // Do your normal draw operations (make sure you clear the swapchain texture) + SDL_GPUColorTargetInfo color_info{}; + color_info.texture = swapchain_texture; + color_info.load_op = SDL_GPU_LOADOP_CLEAR; + color_info.store_op = SDL_GPU_STOREOP_STORE; + SDL_GPURenderPass* render_pass = SDL_BeginGPURenderPass(data->command_buffer, &color_info, 1, nullptr); + SDL_EndGPURenderPass(render_pass); + + data->render_interface.BeginFrame(data->command_buffer, swapchain_texture, width, height); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); + + SDL_SubmitGPUCommandBuffer(data->command_buffer); + data->command_buffer = nullptr; +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_SDL_SDLrenderer.cpp b/vendor/rmlui_backend/RmlUi_Backend_SDL_SDLrenderer.cpp new file mode 100644 index 0000000..ed16617 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_SDL_SDLrenderer.cpp @@ -0,0 +1,242 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Platform_SDL.h" +#include "RmlUi_Renderer_SDL.h" +#include +#include +#include + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(SDL_Window* window, SDL_Renderer* renderer) : system_interface(window), render_interface(renderer) {} + + SystemInterface_SDL system_interface; + RenderInterface_SDL render_interface; + TextInputMethodEditor_SDL text_input_method_editor; + + SDL_Window* window = nullptr; + SDL_Renderer* renderer = nullptr; + + bool running = true; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + +#if SDL_MAJOR_VERSION >= 3 + SDL_SetHint(SDL_HINT_IME_IMPLEMENTED_UI, "composition"); + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) + return false; +#else + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0) + return false; +#endif + + // Submit click events when focusing the window. + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); + // Touch events are handled natively, no need to generate synthetic mouse events for touch devices. + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + +#if defined RMLUI_BACKEND_SIMULATE_TOUCH + // Simulate touch events from mouse events for testing touch behavior on a desktop machine. + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1"); +#endif + +#if SDL_MAJOR_VERSION >= 3 + const float window_size_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, window_name); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, int(width * window_size_scale)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, int(height * window_size_scale)); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, allow_resize); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, true); + SDL_Window* window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); +#else + const Uint32 window_flags = (allow_resize ? SDL_WINDOW_RESIZABLE : 0); + SDL_Window* window = SDL_CreateWindow(window_name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, window_flags); + // SDL2 implicitly activates text input on window creation. Turn it off for now, it will be activated again e.g. when focusing a text input field. + SDL_StopTextInput(); +#endif + + if (!window) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "SDL error on create window: %s\n", SDL_GetError()); + return false; + } + + // Force a specific SDL renderer + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2"); + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "direct3d"); + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "direct3d12"); + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "vulkan"); + +#if SDL_MAJOR_VERSION >= 3 + SDL_Renderer* renderer = SDL_CreateRenderer(window, nullptr); +#else + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); +#endif + + if (!renderer) + return false; + + data = Rml::MakeUnique(window, renderer); + data->window = window; + data->renderer = renderer; + + const char* renderer_name = nullptr; + Rml::String available_renderers; + +#if SDL_MAJOR_VERSION >= 3 + SDL_SetRenderVSync(renderer, 1); + renderer_name = SDL_GetRendererName(renderer); + for (int i = 0; i < SDL_GetNumRenderDrivers(); i++) + { + if (i > 0) + available_renderers += ", "; + available_renderers += SDL_GetRenderDriver(i); + } +#else + SDL_RendererInfo renderer_info; + if (SDL_GetRendererInfo(renderer, &renderer_info) == 0) + renderer_name = renderer_info.name; +#endif + + if (!available_renderers.empty()) + data->system_interface.LogMessage(Rml::Log::LT_INFO, Rml::CreateString("Available SDL renderers: %s", available_renderers.c_str())); + if (renderer_name) + data->system_interface.LogMessage(Rml::Log::LT_INFO, Rml::CreateString("Using SDL renderer: %s", renderer_name)); + + Rml::SetTextInputHandler(&data->text_input_method_editor); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + SDL_DestroyRenderer(data->renderer); + SDL_DestroyWindow(data->window); + + data.reset(); + + SDL_Quit(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + +#if SDL_MAJOR_VERSION >= 3 + auto GetKey = [](const SDL_Event& event) { return event.key.key; }; + auto GetDisplayScale = []() { return SDL_GetWindowDisplayScale(data->window); }; + constexpr auto event_quit = SDL_EVENT_QUIT; + constexpr auto event_key_down = SDL_EVENT_KEY_DOWN; + constexpr auto event_text_editing = SDL_EVENT_TEXT_EDITING; + bool has_event = false; +#else + auto GetKey = [](const SDL_Event& event) { return event.key.keysym.sym; }; + auto GetDisplayScale = []() { return 1.f; }; + constexpr auto event_quit = SDL_QUIT; + constexpr auto event_key_down = SDL_KEYDOWN; + constexpr auto event_text_editing = SDL_TEXTEDITING; + int has_event = 0; +#endif + + bool result = data->running; + data->running = true; + + SDL_Event ev; + if (power_save) + has_event = SDL_WaitEventTimeout(&ev, static_cast(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0) * 1000)); + else + has_event = SDL_PollEvent(&ev); + + while (has_event) + { + bool propagate_event = true; + switch (ev.type) + { + case event_quit: + { + propagate_event = false; + result = false; + } + break; + case event_key_down: + { + propagate_event = false; + const Rml::Input::KeyIdentifier key = RmlSDL::ConvertKey(GetKey(ev)); + const int key_modifier = RmlSDL::GetKeyModifierState(); + const float native_dp_ratio = GetDisplayScale(); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlSDL::InputEventHandler(context, data->window, ev)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, false)) + break; + } + break; + case event_text_editing: + { + propagate_event = false; + data->text_input_method_editor.HandleEdit(ev.edit); + } + break; + default: break; + } + + if (propagate_event) + RmlSDL::InputEventHandler(context, data->window, ev); + + has_event = SDL_PollEvent(&ev); + } + + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.BeginFrame(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); + SDL_RenderPresent(data->renderer); +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_SDL_VK.cpp b/vendor/rmlui_backend/RmlUi_Backend_SDL_VK.cpp new file mode 100644 index 0000000..0d6cc97 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_SDL_VK.cpp @@ -0,0 +1,319 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Platform_SDL.h" +#include "RmlUi_Renderer_VK.h" +#include +#include +#include +#include + +#if SDL_MAJOR_VERSION == 2 && !(SDL_VIDEO_VULKAN) + #error "Only the Vulkan SDL backend is supported." +#endif + +#if SDL_MAJOR_VERSION >= 3 + #include +#else + #include +#endif + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(SDL_Window* window) : system_interface(window) {} + + SystemInterface_SDL system_interface; + RenderInterface_VK render_interface; + TextInputMethodEditor_SDL text_input_method_editor; + + SDL_Window* window = nullptr; + + bool running = true; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + +#if SDL_MAJOR_VERSION >= 3 + SDL_SetHint(SDL_HINT_IME_IMPLEMENTED_UI, "composition"); + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) + return false; +#else + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0) + return false; +#endif + + // Submit click events when focusing the window. + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); + // Touch events are handled natively, no need to generate synthetic mouse events for touch devices. + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + +#if defined RMLUI_BACKEND_SIMULATE_TOUCH + // Simulate touch events from mouse events for testing touch behavior on a desktop machine. + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1"); +#endif + +#if SDL_MAJOR_VERSION >= 3 + const float window_size_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, window_name); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, int(width * window_size_scale)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, int(height * window_size_scale)); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_VULKAN_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, allow_resize); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, true); + SDL_Window* window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); + auto CreateSurface = [](VkInstance instance, VkSurfaceKHR* out_surface) { + return SDL_Vulkan_CreateSurface(data->window, instance, nullptr, out_surface); + }; +#else + const Uint32 window_flags = (SDL_WINDOW_VULKAN | (allow_resize ? SDL_WINDOW_RESIZABLE : 0)); + SDL_Window* window = SDL_CreateWindow(window_name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, window_flags); + // SDL2 implicitly activates text input on window creation. Turn it off for now, it will be activated again e.g. when focusing a text input field. + SDL_StopTextInput(); + auto CreateSurface = [](VkInstance instance, VkSurfaceKHR* out_surface) { + return (bool)SDL_Vulkan_CreateSurface(data->window, instance, out_surface); + }; +#endif + + if (!window) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "SDL error on create window: %s", SDL_GetError()); + return false; + } + + data = Rml::MakeUnique(window); + data->window = window; + + Rml::Vector extensions; + { + unsigned int count; +#if SDL_MAJOR_VERSION >= 3 + const char* const* extensions_list = SDL_Vulkan_GetInstanceExtensions(&count); + if (!extensions_list) + { + data.reset(); + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to get required vulkan extensions"); + return false; + } + extensions.resize(count); + for (unsigned int i = 0; i < count; i++) + extensions[i] = extensions_list[i]; +#else + + if (!SDL_Vulkan_GetInstanceExtensions(window, &count, nullptr)) + { + data.reset(); + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to get required vulkan extensions"); + return false; + } + extensions.resize(count); + if (!SDL_Vulkan_GetInstanceExtensions(window, &count, extensions.data())) + { + data.reset(); + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to get required vulkan extensions"); + return false; + } +#endif + } + + if (!data->render_interface.Initialize(std::move(extensions), CreateSurface)) + { + data.reset(); + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to initialize Vulkan render interface"); + return false; + } + + data->render_interface.SetViewport(width, height); + + Rml::SetTextInputHandler(&data->text_input_method_editor); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + data->render_interface.Shutdown(); + + SDL_DestroyWindow(data->window); + + data.reset(); + + SDL_Quit(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +static bool WaitForValidSwapchain() +{ +#if SDL_MAJOR_VERSION >= 3 + constexpr auto event_quit = SDL_EVENT_QUIT; +#else + constexpr auto event_quit = SDL_QUIT; +#endif + + bool result = true; + + // In some situations the swapchain may become invalid, such as when the window is minimized. In this state the renderer cannot accept any render + // calls. Since we don't have full control over the main loop here we may risk calls to Context::Render if we were to return. Instead, we keep the + // application inside this loop until we are able to recreate the swapchain and render again. + while (!data->render_interface.IsSwapchainValid()) + { + SDL_Event ev; + while (SDL_PollEvent(&ev)) + { + if (ev.type == event_quit) + { + // Restore the window so that we can recreate the swapchain, and then properly release all resource and shutdown cleanly. + SDL_RestoreWindow(data->window); + result = false; + } + } + SDL_Delay(10); + data->render_interface.RecreateSwapchain(); + } + + return result; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + +#if SDL_MAJOR_VERSION >= 3 + #define RMLSDL_WINDOW_EVENTS_BEGIN + #define RMLSDL_WINDOW_EVENTS_END + auto GetKey = [](const SDL_Event& event) { return event.key.key; }; + auto GetDisplayScale = []() { return SDL_GetWindowDisplayScale(data->window); }; + constexpr auto event_quit = SDL_EVENT_QUIT; + constexpr auto event_key_down = SDL_EVENT_KEY_DOWN; + constexpr auto event_text_editing = SDL_EVENT_TEXT_EDITING; + constexpr auto event_window_size_changed = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED; + bool has_event = false; +#else + #define RMLSDL_WINDOW_EVENTS_BEGIN \ + case SDL_WINDOWEVENT: \ + { \ + switch (ev.window.event) \ + { + #define RMLSDL_WINDOW_EVENTS_END \ + } \ + } \ + break; + auto GetKey = [](const SDL_Event& event) { return event.key.keysym.sym; }; + auto GetDisplayScale = []() { return 1.f; }; + constexpr auto event_quit = SDL_QUIT; + constexpr auto event_key_down = SDL_KEYDOWN; + constexpr auto event_text_editing = SDL_TEXTEDITING; + constexpr auto event_window_size_changed = SDL_WINDOWEVENT_SIZE_CHANGED; + int has_event = 0; +#endif + + bool result = data->running; + data->running = true; + + SDL_Event ev; + if (power_save) + has_event = SDL_WaitEventTimeout(&ev, static_cast(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0) * 1000)); + else + has_event = SDL_PollEvent(&ev); + + while (has_event) + { + bool propagate_event = true; + switch (ev.type) + { + case event_quit: + { + propagate_event = false; + result = false; + } + break; + case event_key_down: + { + propagate_event = false; + const Rml::Input::KeyIdentifier key = RmlSDL::ConvertKey(GetKey(ev)); + const int key_modifier = RmlSDL::GetKeyModifierState(); + const float native_dp_ratio = GetDisplayScale(); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlSDL::InputEventHandler(context, data->window, ev)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, false)) + break; + } + break; + case event_text_editing: + { + propagate_event = false; + data->text_input_method_editor.HandleEdit(ev.edit); + } + break; + + RMLSDL_WINDOW_EVENTS_BEGIN + + case event_window_size_changed: + { + Rml::Vector2i dimensions = {ev.window.data1, ev.window.data2}; + data->render_interface.SetViewport(dimensions.x, dimensions.y); + } + break; + + RMLSDL_WINDOW_EVENTS_END + + default: break; + } + + if (propagate_event) + RmlSDL::InputEventHandler(context, data->window, ev); + + has_event = SDL_PollEvent(&ev); + } + + if (!WaitForValidSwapchain()) + result = false; + + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.BeginFrame(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_SFML_GL2.cpp b/vendor/rmlui_backend/RmlUi_Backend_SFML_GL2.cpp new file mode 100644 index 0000000..1c957b0 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_SFML_GL2.cpp @@ -0,0 +1,296 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Platform_SFML.h" +#include "RmlUi_Renderer_GL2.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if SFML_VERSION_MAJOR >= 3 + #define SFML_COORDINATE(x, y) {x, y} +#else + #define SFML_COORDINATE(x, y) x, y +#endif + +/** + Custom render interface example for the SFML/GL2 backend. + + Overloads the OpenGL2 render interface to load textures through SFML's built-in texture loading functionality. + */ +class RenderInterface_GL2_SFML : public RenderInterface_GL2 { +public: + // -- Inherited from Rml::RenderInterface -- + + void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override + { + if (texture) + { + sf::Texture::bind((sf::Texture*)texture); + texture = RenderInterface_GL2::TextureEnableWithoutBinding; + } + + RenderInterface_GL2::RenderGeometry(handle, translation, texture); + } + + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override + { + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + return false; + + file_interface->Seek(file_handle, 0, SEEK_END); + size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + using Rml::byte; + Rml::UniquePtr buffer(new byte[buffer_size]); + file_interface->Read(buffer.get(), buffer_size, file_handle); + file_interface->Close(file_handle); + + sf::Image image; + if (!image.loadFromMemory(buffer.get(), buffer_size)) + return false; + + // Convert colors to premultiplied alpha, which is necessary for correct alpha compositing. + for (unsigned int x = 0; x < image.getSize().x; x++) + { + for (unsigned int y = 0; y < image.getSize().y; y++) + { + sf::Color color = image.getPixel(SFML_COORDINATE(x, y)); + color.r = static_cast((color.r * color.a) / 255); + color.g = static_cast((color.g * color.a) / 255); + color.b = static_cast((color.b * color.a) / 255); + image.setPixel(SFML_COORDINATE(x, y), color); + } + } + + sf::Texture* texture = new sf::Texture(); + texture->setSmooth(true); + + if (!texture->loadFromImage(image)) + { + delete texture; + return false; + } + + texture_dimensions = Rml::Vector2i(texture->getSize().x, texture->getSize().y); + return (Rml::TextureHandle)texture; + } + + Rml::TextureHandle GenerateTexture(Rml::Span source, Rml::Vector2i source_dimensions_i) override + { + const auto source_dimensions = Rml::Vector2(source_dimensions_i); + +#if SFML_VERSION_MAJOR >= 3 + sf::Texture* texture = new sf::Texture(sf::Vector2u{source_dimensions.x, source_dimensions.y}); +#else + sf::Texture* texture = new sf::Texture(); + if (!texture->create(source_dimensions.x, source_dimensions.y)) + { + delete texture; + return false; + } +#endif + texture->setSmooth(true); + texture->update(source.data(), SFML_COORDINATE(source_dimensions.x, source_dimensions.y), SFML_COORDINATE(0, 0)); + return (Rml::TextureHandle)texture; + } + + void ReleaseTexture(Rml::TextureHandle texture_handle) override { delete (sf::Texture*)texture_handle; } +}; + +// Updates the viewport and context dimensions, should be called whenever the window size changes. +static void UpdateWindowDimensions(sf::RenderWindow& window, RenderInterface_GL2_SFML& render_interface, Rml::Context* context) +{ + const int width = (int)window.getSize().x; + const int height = (int)window.getSize().y; + + if (context) + context->SetDimensions(Rml::Vector2i(width, height)); + + sf::View view(sf::FloatRect(SFML_COORDINATE(0.f, 0.f), SFML_COORDINATE((float)width, (float)height))); + window.setView(view); + + render_interface.SetViewport(width, height); +} + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + SystemInterface_SFML system_interface; + RenderInterface_GL2_SFML render_interface; + sf::RenderWindow window; + bool running = true; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + data = Rml::MakeUnique(); + + const std::uint32_t style = (allow_resize ? sf::Style::Default : (sf::Style::Titlebar | sf::Style::Close)); + constexpr unsigned int anti_aliasing_level = 2; + + // Create the window. + sf::RenderWindow out_window; + sf::ContextSettings context_settings; + context_settings.stencilBits = 8; + +#if SFML_VERSION_MAJOR >= 3 + context_settings.antiAliasingLevel = anti_aliasing_level; + data->window.create(sf::VideoMode({(unsigned int)width, (unsigned int)height}), window_name, style, sf::State::Windowed, context_settings); +#else + context_settings.antialiasingLevel = anti_aliasing_level; + data->window.create(sf::VideoMode(width, height), window_name, style, context_settings); +#endif + + data->window.setVerticalSyncEnabled(true); + if (!data->window.isOpen()) + { + data.reset(); + return false; + } + + // Optionally apply the SFML window to the system interface so that it can change its mouse cursor. + data->system_interface.SetWindow(&data->window); + + UpdateWindowDimensions(data->window, data->render_interface, nullptr); + + return true; +} + +void Backend::Shutdown() +{ + data.reset(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + // SFML does not seem to provide a way to wait for events with a timeout. + (void)power_save; + + // The contents of this function is intended to be copied directly into your main loop. + bool result = data->running; + data->running = true; + + auto handle_key_pressed = [&](const sf::Event& ev, sf::Keyboard::Key key_pressed_code) { + const Rml::Input::KeyIdentifier key = RmlSFML::ConvertKey(key_pressed_code); + const int key_modifier = RmlSFML::GetKeyModifierState(); + const float native_dp_ratio = 1.f; + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, true)) + return; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlSFML::InputHandler(context, ev)) + return; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, false)) + return; + }; + +#if SFML_VERSION_MAJOR >= 3 + while (const std::optional ev = data->window.pollEvent()) + { + if (ev->is()) + { + UpdateWindowDimensions(data->window, data->render_interface, context); + } + else if (auto key_pressed = ev->getIf()) + { + handle_key_pressed(*ev, key_pressed->code); + } + else if (ev->is()) + { + result = false; + } + else + { + RmlSFML::InputHandler(context, *ev); + } + } + +#else + + sf::Event ev; + while (data->window.pollEvent(ev)) + { + switch (ev.type) + { + case sf::Event::Resized: UpdateWindowDimensions(data->window, data->render_interface, context); break; + case sf::Event::KeyPressed: handle_key_pressed(ev, ev.key.code); break; + case sf::Event::Closed: result = false; break; + default: RmlSFML::InputHandler(context, ev); break; + } + } +#endif + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + sf::RenderWindow& window = data->window; + + window.resetGLStates(); + window.clear(); + + data->render_interface.BeginFrame(); + +#if 0 + // Draw a simple shape with SFML for demonstration purposes. Make sure to push and pop GL states as appropriate. + sf::Vector2f circle_position(100.f, 100.f); + + window.pushGLStates(); + + sf::CircleShape circle(50.f); + circle.setPosition(circle_position); + circle.setFillColor(sf::Color::Blue); + circle.setOutlineColor(sf::Color::Red); + circle.setOutlineThickness(10.f); + window.draw(circle); + + window.popGLStates(); +#endif +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + + data->render_interface.EndFrame(); + data->window.display(); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_Win32_DX12.cpp b/vendor/rmlui_backend/RmlUi_Backend_Win32_DX12.cpp new file mode 100644 index 0000000..41ecbfd --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_Win32_DX12.cpp @@ -0,0 +1,393 @@ +#include "RmlUi/Config/Config.h" +#include "RmlUi_Backend.h" +#include "RmlUi_Include_Windows.h" +#include "RmlUi_Platform_Win32.h" +#include "RmlUi_Renderer_DX12.h" +#include +#include +#include +#include + +/** + High DPI support using Windows Per Monitor V2 DPI awareness. + + Requires Windows 10, version 1703. + */ +#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 + #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE) - 4) +#endif +#ifndef WM_DPICHANGED + #define WM_DPICHANGED 0x02E0 +#endif + +#define FIX_WARNING_UNUSED(code) (void)(code); + +// Declare pointers to the DPI aware Windows API functions. +using ProcSetProcessDpiAwarenessContext = BOOL(WINAPI*)(HANDLE value); +using ProcGetDpiForWindow = UINT(WINAPI*)(HWND hwnd); +using ProcAdjustWindowRectExForDpi = BOOL(WINAPI*)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi); + +static bool has_dpi_support = false; +static ProcSetProcessDpiAwarenessContext procSetProcessDpiAwarenessContext = NULL; +static ProcGetDpiForWindow procGetDpiForWindow = NULL; +static ProcAdjustWindowRectExForDpi procAdjustWindowRectExForDpi = NULL; + +// Make ourselves DPI aware on supported Windows versions. +static void InitializeDpiSupport() +{ + // Cast function pointers to void* first for MinGW not to emit errors. + procSetProcessDpiAwarenessContext = + (ProcSetProcessDpiAwarenessContext)(void*)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "SetProcessDpiAwarenessContext"); + procGetDpiForWindow = (ProcGetDpiForWindow)(void*)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "GetDpiForWindow"); + procAdjustWindowRectExForDpi = + (ProcAdjustWindowRectExForDpi)(void*)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "AdjustWindowRectExForDpi"); + + if (!has_dpi_support && procSetProcessDpiAwarenessContext != NULL && procGetDpiForWindow != NULL && procAdjustWindowRectExForDpi != NULL) + { + // Activate Per Monitor V2. + if (procSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + has_dpi_support = true; + } +} + +static UINT GetWindowDpi(HWND window_handle) +{ + if (has_dpi_support) + { + UINT dpi = procGetDpiForWindow(window_handle); + if (dpi != 0) + return dpi; + } + return USER_DEFAULT_SCREEN_DPI; +} + +static float GetDensityIndependentPixelRatio(HWND window_handle) +{ + return float(GetWindowDpi(window_handle)) / float(USER_DEFAULT_SCREEN_DPI); +} + +// Create the window but don't show it yet. Returns the pixel size of the window, which may be different from the passed size due to DPI settings. +static HWND InitializeWindow(HINSTANCE instance_handle, const std::wstring& name, int& inout_width, int& inout_height, bool allow_resize); + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + SystemInterface_Win32 system_interface; + Rml::UniquePtr render_interface; + TextInputMethodEditor_Win32 text_input_method_editor; + + HINSTANCE instance_handle = nullptr; + std::wstring instance_name; + HWND window_handle = nullptr; + + bool context_dimensions_dirty = true; + Rml::Vector2i window_dimensions; + bool running = true; + + // Arguments set during event processing and nulled otherwise. + Rml::Context* context = nullptr; + KeyDownCallback key_down_callback = nullptr; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + const std::wstring name = RmlWin32::ConvertToUTF16(Rml::String(window_name)); + + data = Rml::MakeUnique(); + + data->instance_handle = GetModuleHandle(nullptr); + data->instance_name = name; + + InitializeDpiSupport(); + + // Initialize the window but don't show it yet. + HWND window_handle = InitializeWindow(data->instance_handle, name, width, height, allow_resize); + if (!window_handle) + return false; + + data->window_handle = window_handle; + + RmlRendererSettings settings = {}; + settings.vsync = true; + settings.msaa_sample_count = RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT; + + Rml::String initialization_message; + data->render_interface = Rml::MakeUnique(window_handle, settings); + + if (!data->render_interface) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Could not initialize DirectX 12 render interface: %s", initialization_message.c_str()); + ::CloseWindow(window_handle); + data.reset(); + return false; + } + + data->system_interface.SetWindow(window_handle); + data->render_interface->SetViewport(width, height); + + // Now we are ready to show the window. + ::ShowWindow(window_handle, SW_SHOW); + ::SetForegroundWindow(window_handle); + ::SetFocus(window_handle); + + // Provide a backend-specific text input handler to manage the IME. + Rml::SetTextInputHandler(&data->text_input_method_editor); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + // As we forcefully override the global text input handler, we must reset it before the data is destroyed to avoid any potential use-after-free. + if (Rml::GetTextInputHandler() == &data->text_input_method_editor) + Rml::SetTextInputHandler(nullptr); + + if (data->render_interface) + { + data->render_interface.reset(); + ::DestroyWindow(data->window_handle); + ::UnregisterClassW((LPCWSTR)data->instance_name.data(), data->instance_handle); + } + + data.reset(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return data->render_interface.get(); +} + +static bool NextEvent(MSG& message, UINT timeout) +{ + if (timeout != 0) + { + UINT_PTR timer_id = SetTimer(NULL, NULL, timeout, NULL); + BOOL res = GetMessage(&message, NULL, 0, 0); + KillTimer(NULL, timer_id); + if (message.message != WM_TIMER || message.hwnd != nullptr || message.wParam != timer_id) + return res; + } + return PeekMessage(&message, nullptr, 0, 0, PM_REMOVE); +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + // The initial window size may have been affected by system DPI settings, apply the actual pixel size and dp-ratio to the context. + if (data->context_dimensions_dirty) + { + data->context_dimensions_dirty = false; + const float dp_ratio = GetDensityIndependentPixelRatio(data->window_handle); + context->SetDimensions(data->window_dimensions); + context->SetDensityIndependentPixelRatio(dp_ratio); + } + + data->context = context; + data->key_down_callback = key_down_callback; + + MSG message; + // Process events. + bool has_message = NextEvent(message, power_save ? static_cast(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0) * 1000.0) : 0); + while (has_message || !data->render_interface->IsSwapchainValid()) + { + if (has_message) + { + // Dispatch the message to our local event handler below. + TranslateMessage(&message); + DispatchMessage(&message); + } + + // In some situations the swapchain may become invalid, such as when the window is minimized. In this state the renderer cannot accept any + // render calls. Since we don't have full control over the main loop here we may risk calls to Context::Render if we were to return. Instead, + // we trap the application inside this loop until we are able to recreate the swapchain and render again. + if (!data->render_interface->IsSwapchainValid()) + data->render_interface->RecreateSwapchain(); + + has_message = NextEvent(message, 0); + } + + data->context = nullptr; + data->key_down_callback = nullptr; + + return data->running; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + RMLUI_ASSERT(data->render_interface); + + if (data->render_interface) + { + data->render_interface->BeginFrame(); + data->render_interface->Clear(); + } +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + RMLUI_ASSERT(data->render_interface); + + data->render_interface->EndFrame(); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} + +// Local event handler for window and input events. +static LRESULT CALLBACK WindowProcedureHandler(HWND window_handle, UINT message, WPARAM w_param, LPARAM l_param) +{ + RMLUI_ASSERT(data); + + switch (message) + { + case WM_CLOSE: + { + data->running = false; + return 0; + } + break; + case WM_SIZE: + { + const int width = LOWORD(l_param); + const int height = HIWORD(l_param); + data->window_dimensions.x = width; + data->window_dimensions.y = height; + if (data->context) + { + data->render_interface->SetViewport(width, height); + data->context->SetDimensions(data->window_dimensions); + } + return 0; + } + break; + case WM_DPICHANGED: + { + RECT* new_pos = (RECT*)l_param; + SetWindowPos(window_handle, NULL, new_pos->left, new_pos->top, new_pos->right - new_pos->left, new_pos->bottom - new_pos->top, + SWP_NOZORDER | SWP_NOACTIVATE); + if (data->context && has_dpi_support) + data->context->SetDensityIndependentPixelRatio(GetDensityIndependentPixelRatio(window_handle)); + return 0; + } + break; + case WM_KEYDOWN: + { + // Override the default key event callback to add global shortcuts for the samples. + Rml::Context* context = data->context; + KeyDownCallback key_down_callback = data->key_down_callback; + + const Rml::Input::KeyIdentifier rml_key = RmlWin32::ConvertKey((int)w_param); + const int rml_modifier = RmlWin32::GetKeyModifierState(); + const float native_dp_ratio = GetDensityIndependentPixelRatio(window_handle); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, rml_key, rml_modifier, native_dp_ratio, true)) + return 0; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlWin32::WindowProcedure(context, data->text_input_method_editor, window_handle, message, w_param, l_param)) + return 0; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, rml_key, rml_modifier, native_dp_ratio, false)) + return 0; + return 0; + } + break; + default: + { + // Submit it to the platform handler for default input handling. + if (!RmlWin32::WindowProcedure(data->context, data->text_input_method_editor, window_handle, message, w_param, l_param)) + return 0; + } + break; + } + + // All unhandled messages go to DefWindowProc. + return DefWindowProc(window_handle, message, w_param, l_param); +} + +static HWND InitializeWindow(HINSTANCE instance_handle, const std::wstring& name, int& inout_width, int& inout_height, bool allow_resize) +{ + WNDCLASSW window_class; + window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + window_class.lpfnWndProc = &WindowProcedureHandler; // Attach our local event handler. + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = instance_handle; + window_class.hIcon = LoadIcon(nullptr, IDI_WINLOGO); + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.hbrBackground = nullptr; + window_class.lpszMenuName = nullptr; + window_class.lpszClassName = name.data(); + + if (!RegisterClassW(&window_class)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to register window class"); + return nullptr; + } + + HWND window_handle = CreateWindowExW(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, + name.data(), // Window class name. + name.data(), WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW, 0, 0, // Window position. + 0, 0, // Window size. + nullptr, nullptr, instance_handle, nullptr); + + if (!window_handle) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to create window"); + return nullptr; + } + + UINT window_dpi = GetWindowDpi(window_handle); + inout_width = (inout_width * (int)window_dpi) / USER_DEFAULT_SCREEN_DPI; + inout_height = (inout_height * (int)window_dpi) / USER_DEFAULT_SCREEN_DPI; + + DWORD style = (allow_resize ? WS_OVERLAPPEDWINDOW : (WS_OVERLAPPEDWINDOW & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX)); + DWORD extended_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + + // Adjust the window size to take the edges into account. + RECT window_rect; + window_rect.top = 0; + window_rect.left = 0; + window_rect.right = inout_width; + window_rect.bottom = inout_height; + if (has_dpi_support) + procAdjustWindowRectExForDpi(&window_rect, style, FALSE, extended_style, window_dpi); + else + AdjustWindowRectEx(&window_rect, style, FALSE, extended_style); + + SetWindowLong(window_handle, GWL_EXSTYLE, extended_style); + SetWindowLong(window_handle, GWL_STYLE, style); + + // Resize the window and center it on the screen. + Rml::Vector2i screen_size = {GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)}; + Rml::Vector2i window_size = {int(window_rect.right - window_rect.left), int(window_rect.bottom - window_rect.top)}; + Rml::Vector2i window_pos = Rml::Math::Max((screen_size - window_size) / 2, Rml::Vector2i(0)); + + SetWindowPos(window_handle, HWND_TOP, window_pos.x, window_pos.y, window_size.x, window_size.y, SWP_NOACTIVATE); + + return window_handle; +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_Win32_GL2.cpp b/vendor/rmlui_backend/RmlUi_Backend_Win32_GL2.cpp new file mode 100644 index 0000000..b337c8a --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_Win32_GL2.cpp @@ -0,0 +1,444 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Include_Windows.h" +#include "RmlUi_Platform_Win32.h" +#include "RmlUi_Renderer_GL2.h" +#include +#include +#include +#include +#include + +/** + High DPI support using Windows Per Monitor V2 DPI awareness. + + Requires Windows 10, version 1703. + */ +#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 + #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE)-4) +#endif +#ifndef WM_DPICHANGED + #define WM_DPICHANGED 0x02E0 +#endif + +// Declare pointers to the DPI aware Windows API functions. +using ProcSetProcessDpiAwarenessContext = BOOL(WINAPI*)(HANDLE value); +using ProcGetDpiForWindow = UINT(WINAPI*)(HWND hwnd); +using ProcAdjustWindowRectExForDpi = BOOL(WINAPI*)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi); + +static bool has_dpi_support = false; +static ProcSetProcessDpiAwarenessContext procSetProcessDpiAwarenessContext = NULL; +static ProcGetDpiForWindow procGetDpiForWindow = NULL; +static ProcAdjustWindowRectExForDpi procAdjustWindowRectExForDpi = NULL; + +// Make ourselves DPI aware on supported Windows versions. +static void InitializeDpiSupport() +{ + // Cast function pointers to void* first for MinGW not to emit errors. + procSetProcessDpiAwarenessContext = + (ProcSetProcessDpiAwarenessContext)(void*)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "SetProcessDpiAwarenessContext"); + procGetDpiForWindow = (ProcGetDpiForWindow)(void*)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "GetDpiForWindow"); + procAdjustWindowRectExForDpi = + (ProcAdjustWindowRectExForDpi)(void*)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "AdjustWindowRectExForDpi"); + + if (!has_dpi_support && procSetProcessDpiAwarenessContext != NULL && procGetDpiForWindow != NULL && procAdjustWindowRectExForDpi != NULL) + { + // Activate Per Monitor V2. + if (procSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + has_dpi_support = true; + } +} + +static UINT GetWindowDpi(HWND window_handle) +{ + if (has_dpi_support) + { + UINT dpi = procGetDpiForWindow(window_handle); + if (dpi != 0) + return dpi; + } + return USER_DEFAULT_SCREEN_DPI; +} + +static float GetDensityIndependentPixelRatio(HWND window_handle) +{ + return float(GetWindowDpi(window_handle)) / float(USER_DEFAULT_SCREEN_DPI); +} + +// Create the window but don't show it yet. Returns the pixel size of the window, which may be different than the passed size due to DPI settings. +static HWND InitializeWindow(HINSTANCE instance_handle, const std::wstring& name, int& inout_width, int& inout_height, bool allow_resize); +// Attach the OpenGL context. +static bool AttachToNative(HWND window_handle, HDC& out_device_context, HGLRC& out_render_context); +// Detach the OpenGL context. +static void DetachFromNative(HWND window_handle, HDC device_context, HGLRC render_context); + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + SystemInterface_Win32 system_interface; + RenderInterface_GL2 render_interface; + TextInputMethodEditor_Win32 text_input_method_editor; + + HINSTANCE instance_handle = nullptr; + std::wstring instance_name; + HWND window_handle = nullptr; + + HDC device_context = nullptr; + HGLRC render_context = nullptr; + + bool context_dimensions_dirty = true; + Rml::Vector2i window_dimensions; + bool running = true; + + // Arguments set during event processing and nulled otherwise. + Rml::Context* context = nullptr; + KeyDownCallback key_down_callback = nullptr; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + const std::wstring name = RmlWin32::ConvertToUTF16(Rml::String(window_name)); + + data = Rml::MakeUnique(); + + data->instance_handle = GetModuleHandle(nullptr); + data->instance_name = name; + + InitializeDpiSupport(); + + // Initialize the window but don't show it yet. + HWND window_handle = InitializeWindow(data->instance_handle, name, width, height, allow_resize); + if (!window_handle) + return false; + + // Attach the OpenGL context. + if (!AttachToNative(window_handle, data->device_context, data->render_context)) + { + ::CloseWindow(window_handle); + return false; + } + + data->window_handle = window_handle; + data->system_interface.SetWindow(window_handle); + + // Now we are ready to show the window. + ::ShowWindow(window_handle, SW_SHOW); + ::SetForegroundWindow(window_handle); + ::SetFocus(window_handle); + + // Provide a backend-specific text input handler to manage the IME. + Rml::SetTextInputHandler(&data->text_input_method_editor); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + // As we forcefully override the global text input handler, we must reset it before the data is destroyed to avoid any potential use-after-free. + if (Rml::GetTextInputHandler() == &data->text_input_method_editor) + Rml::SetTextInputHandler(nullptr); + + DetachFromNative(data->window_handle, data->device_context, data->render_context); + + ::DestroyWindow(data->window_handle); + ::UnregisterClassW((LPCWSTR)data->instance_name.data(), data->instance_handle); + + data.reset(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +static bool NextEvent(MSG& message, UINT timeout) +{ + if (timeout != 0) + { + UINT_PTR timer_id = SetTimer(NULL, 0, timeout, NULL); + BOOL res = GetMessage(&message, NULL, 0, 0); + KillTimer(NULL, timer_id); + if (message.message != WM_TIMER || message.hwnd != nullptr || message.wParam != timer_id) + return res; + } + return PeekMessage(&message, nullptr, 0, 0, PM_REMOVE); +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + // The initial window size may have been affected by system DPI settings, apply the actual pixel size and dp-ratio to the context. + if (data->context_dimensions_dirty) + { + data->context_dimensions_dirty = false; + const float dp_ratio = GetDensityIndependentPixelRatio(data->window_handle); + context->SetDimensions(data->window_dimensions); + context->SetDensityIndependentPixelRatio(dp_ratio); + } + + data->context = context; + data->key_down_callback = key_down_callback; + + MSG message; + bool has_message = NextEvent(message, power_save ? static_cast(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0) * 1000.0) : 0); + while (has_message) + { + // Dispatch the message to our local event handler below. + TranslateMessage(&message); + DispatchMessage(&message); + + has_message = NextEvent(message, 0); + } + + data->context = nullptr; + data->key_down_callback = nullptr; + + const bool result = data->running; + data->running = true; + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.BeginFrame(); + data->render_interface.Clear(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); + + // Flips the OpenGL buffers. + SwapBuffers(data->device_context); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} + +// Local event handler for window and input events. +static LRESULT CALLBACK WindowProcedureHandler(HWND window_handle, UINT message, WPARAM w_param, LPARAM l_param) +{ + RMLUI_ASSERT(data); + + switch (message) + { + case WM_CLOSE: + { + data->running = false; + return 0; + } + break; + case WM_SIZE: + { + const int width = LOWORD(l_param); + const int height = HIWORD(l_param); + data->window_dimensions.x = width; + data->window_dimensions.y = height; + data->render_interface.SetViewport(width, height); + if (data->context) + data->context->SetDimensions(data->window_dimensions); + return 0; + } + break; + case WM_DPICHANGED: + { + RECT* new_pos = (RECT*)l_param; + SetWindowPos(window_handle, NULL, new_pos->left, new_pos->top, new_pos->right - new_pos->left, new_pos->bottom - new_pos->top, + SWP_NOZORDER | SWP_NOACTIVATE); + if (data->context && has_dpi_support) + data->context->SetDensityIndependentPixelRatio(GetDensityIndependentPixelRatio(window_handle)); + return 0; + } + break; + case WM_KEYDOWN: + { + // Override the default key event callback to add global shortcuts for the samples. + Rml::Context* context = data->context; + KeyDownCallback key_down_callback = data->key_down_callback; + + const Rml::Input::KeyIdentifier rml_key = RmlWin32::ConvertKey((int)w_param); + const int rml_modifier = RmlWin32::GetKeyModifierState(); + const float native_dp_ratio = GetDensityIndependentPixelRatio(window_handle); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, rml_key, rml_modifier, native_dp_ratio, true)) + return 0; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlWin32::WindowProcedure(context, data->text_input_method_editor, window_handle, message, w_param, l_param)) + return 0; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, rml_key, rml_modifier, native_dp_ratio, false)) + return 0; + return 0; + } + break; + default: + { + // Submit it to the platform handler for default input handling. + if (!RmlWin32::WindowProcedure(data->context, data->text_input_method_editor, window_handle, message, w_param, l_param)) + return 0; + } + break; + } + + // All unhandled messages go to DefWindowProc. + return DefWindowProc(window_handle, message, w_param, l_param); +} + +static HWND InitializeWindow(HINSTANCE instance_handle, const std::wstring& name, int& inout_width, int& inout_height, bool allow_resize) +{ + // Fill out the window class struct. + WNDCLASSW window_class; + window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + window_class.lpfnWndProc = &WindowProcedureHandler; // Attach our local event handler. + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = instance_handle; + window_class.hIcon = LoadIcon(nullptr, IDI_WINLOGO); + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.hbrBackground = nullptr; + window_class.lpszMenuName = nullptr; + window_class.lpszClassName = name.data(); + + if (!RegisterClassW(&window_class)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to register window class"); + return nullptr; + } + + HWND window_handle = CreateWindowExW(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, + name.data(), // Window class name. + name.data(), WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW, 0, 0, // Window position. + 0, 0, // Window size. + nullptr, nullptr, instance_handle, nullptr); + + if (!window_handle) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to create window"); + return nullptr; + } + + UINT window_dpi = GetWindowDpi(window_handle); + inout_width = (inout_width * (int)window_dpi) / USER_DEFAULT_SCREEN_DPI; + inout_height = (inout_height * (int)window_dpi) / USER_DEFAULT_SCREEN_DPI; + + DWORD style = (allow_resize ? WS_OVERLAPPEDWINDOW : (WS_OVERLAPPEDWINDOW & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX)); + DWORD extended_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + + // Adjust the window size to take the edges into account. + RECT window_rect; + window_rect.top = 0; + window_rect.left = 0; + window_rect.right = inout_width; + window_rect.bottom = inout_height; + if (has_dpi_support) + procAdjustWindowRectExForDpi(&window_rect, style, FALSE, extended_style, window_dpi); + else + AdjustWindowRectEx(&window_rect, style, FALSE, extended_style); + + SetWindowLong(window_handle, GWL_EXSTYLE, extended_style); + SetWindowLong(window_handle, GWL_STYLE, style); + + // Resize the window and center it on the screen. + Rml::Vector2i screen_size = {GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)}; + Rml::Vector2i window_size = {int(window_rect.right - window_rect.left), int(window_rect.bottom - window_rect.top)}; + Rml::Vector2i window_pos = Rml::Math::Max((screen_size - window_size) / 2, Rml::Vector2i(0)); + + SetWindowPos(window_handle, HWND_TOP, window_pos.x, window_pos.y, window_size.x, window_size.y, SWP_NOACTIVATE); + + return window_handle; +} + +static bool AttachToNative(HWND window_handle, HDC& out_device_context, HGLRC& out_render_context) +{ + HDC device_context = GetDC(window_handle); + + if (!device_context) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to get device context"); + return false; + } + + PIXELFORMATDESCRIPTOR pixel_format_descriptor = {}; + pixel_format_descriptor.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pixel_format_descriptor.nVersion = 1; + pixel_format_descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pixel_format_descriptor.iPixelType = PFD_TYPE_RGBA; + pixel_format_descriptor.cColorBits = 32; + pixel_format_descriptor.cRedBits = 8; + pixel_format_descriptor.cGreenBits = 8; + pixel_format_descriptor.cBlueBits = 8; + pixel_format_descriptor.cAlphaBits = 8; + pixel_format_descriptor.cDepthBits = 24; + pixel_format_descriptor.cStencilBits = 8; + + int pixel_format = ChoosePixelFormat(device_context, &pixel_format_descriptor); + if (!pixel_format) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to choose 32-bit pixel format"); + return false; + } + + if (!SetPixelFormat(device_context, pixel_format, &pixel_format_descriptor)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to set pixel format"); + return false; + } + + HGLRC render_context = wglCreateContext(device_context); + if (!render_context) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to create OpenGL rendering context"); + return false; + } + + // Activate the rendering context. + if (!wglMakeCurrent(device_context, render_context)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to make rendering context current"); + return false; + } + + out_device_context = device_context; + out_render_context = render_context; + + return true; +} + +static void DetachFromNative(HWND window_handle, HDC device_context, HGLRC render_context) +{ + // Shutdown OpenGL + if (render_context) + { + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(render_context); + } + + if (device_context) + { + ReleaseDC(window_handle, device_context); + } +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_Win32_VK.cpp b/vendor/rmlui_backend/RmlUi_Backend_Win32_VK.cpp new file mode 100644 index 0000000..7f85193 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_Win32_VK.cpp @@ -0,0 +1,395 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Include_Windows.h" +#include "RmlUi_Platform_Win32.h" +#include "RmlUi_Renderer_VK.h" +#include +#include +#include +#include +#include +#include + +/** + High DPI support using Windows Per Monitor V2 DPI awareness. + + Requires Windows 10, version 1703. + */ +#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 + #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE)-4) +#endif +#ifndef WM_DPICHANGED + #define WM_DPICHANGED 0x02E0 +#endif + +// Declare pointers to the DPI aware Windows API functions. +using ProcSetProcessDpiAwarenessContext = BOOL(WINAPI*)(HANDLE value); +using ProcGetDpiForWindow = UINT(WINAPI*)(HWND hwnd); +using ProcAdjustWindowRectExForDpi = BOOL(WINAPI*)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi); + +static bool has_dpi_support = false; +static ProcSetProcessDpiAwarenessContext procSetProcessDpiAwarenessContext = NULL; +static ProcGetDpiForWindow procGetDpiForWindow = NULL; +static ProcAdjustWindowRectExForDpi procAdjustWindowRectExForDpi = NULL; + +// Make ourselves DPI aware on supported Windows versions. +static void InitializeDpiSupport() +{ + // Cast function pointers to void* first for MinGW not to emit errors. + procSetProcessDpiAwarenessContext = + (ProcSetProcessDpiAwarenessContext)(void*)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "SetProcessDpiAwarenessContext"); + procGetDpiForWindow = (ProcGetDpiForWindow)(void*)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "GetDpiForWindow"); + procAdjustWindowRectExForDpi = + (ProcAdjustWindowRectExForDpi)(void*)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "AdjustWindowRectExForDpi"); + + if (!has_dpi_support && procSetProcessDpiAwarenessContext != NULL && procGetDpiForWindow != NULL && procAdjustWindowRectExForDpi != NULL) + { + // Activate Per Monitor V2. + if (procSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + has_dpi_support = true; + } +} + +static UINT GetWindowDpi(HWND window_handle) +{ + if (has_dpi_support) + { + UINT dpi = procGetDpiForWindow(window_handle); + if (dpi != 0) + return dpi; + } + return USER_DEFAULT_SCREEN_DPI; +} + +static float GetDensityIndependentPixelRatio(HWND window_handle) +{ + return float(GetWindowDpi(window_handle)) / float(USER_DEFAULT_SCREEN_DPI); +} + +// Create the window but don't show it yet. Returns the pixel size of the window, which may be different than the passed size due to DPI settings. +static HWND InitializeWindow(HINSTANCE instance_handle, const std::wstring& name, int& inout_width, int& inout_height, bool allow_resize); +// Create the Win32 Vulkan surface. +static bool CreateVulkanSurface(VkInstance instance, VkSurfaceKHR* out_surface); + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + SystemInterface_Win32 system_interface; + RenderInterface_VK render_interface; + TextInputMethodEditor_Win32 text_input_method_editor; + + HINSTANCE instance_handle = nullptr; + std::wstring instance_name; + HWND window_handle = nullptr; + + bool context_dimensions_dirty = true; + Rml::Vector2i window_dimensions; + bool running = true; + + // Arguments set during event processing and nulled otherwise. + Rml::Context* context = nullptr; + KeyDownCallback key_down_callback = nullptr; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + const std::wstring name = RmlWin32::ConvertToUTF16(Rml::String(window_name)); + + data = Rml::MakeUnique(); + + data->instance_handle = GetModuleHandle(nullptr); + data->instance_name = name; + + InitializeDpiSupport(); + + // Initialize the window but don't show it yet. + HWND window_handle = InitializeWindow(data->instance_handle, name, width, height, allow_resize); + if (!window_handle) + return false; + + data->window_handle = window_handle; + + Rml::Vector extensions; + extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); + extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); + if (!data->render_interface.Initialize(std::move(extensions), CreateVulkanSurface)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to initialize Vulkan render interface"); + ::CloseWindow(window_handle); + data.reset(); + return false; + } + + data->system_interface.SetWindow(window_handle); + data->render_interface.SetViewport(width, height); + + // Now we are ready to show the window. + ::ShowWindow(window_handle, SW_SHOW); + ::SetForegroundWindow(window_handle); + ::SetFocus(window_handle); + + // Provide a backend-specific text input handler to manage the IME. + Rml::SetTextInputHandler(&data->text_input_method_editor); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + // As we forcefully override the global text input handler, we must reset it before the data is destroyed to avoid any potential use-after-free. + if (Rml::GetTextInputHandler() == &data->text_input_method_editor) + Rml::SetTextInputHandler(nullptr); + + data->render_interface.Shutdown(); + + ::DestroyWindow(data->window_handle); + ::UnregisterClassW((LPCWSTR)data->instance_name.data(), data->instance_handle); + + data.reset(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +static bool NextEvent(MSG& message, UINT timeout) +{ + if (timeout != 0) + { + UINT_PTR timer_id = SetTimer(NULL, 0, timeout, NULL); + BOOL res = GetMessage(&message, NULL, 0, 0); + KillTimer(NULL, timer_id); + if (message.message != WM_TIMER || message.hwnd != nullptr || message.wParam != timer_id) + return res; + } + return PeekMessage(&message, nullptr, 0, 0, PM_REMOVE); +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + // The initial window size may have been affected by system DPI settings, apply the actual pixel size and dp-ratio to the context. + if (data->context_dimensions_dirty) + { + data->context_dimensions_dirty = false; + const float dp_ratio = GetDensityIndependentPixelRatio(data->window_handle); + context->SetDimensions(data->window_dimensions); + context->SetDensityIndependentPixelRatio(dp_ratio); + } + + data->context = context; + data->key_down_callback = key_down_callback; + + MSG message; + // Process events. + bool has_message = NextEvent(message, power_save ? static_cast(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0) * 1000.0) : 0); + while (has_message || !data->render_interface.IsSwapchainValid()) + { + if (has_message) + { + // Dispatch the message to our local event handler below. + TranslateMessage(&message); + DispatchMessage(&message); + } + + // In some situations the swapchain may become invalid, such as when the window is minimized. In this state the renderer cannot accept any + // render calls. Since we don't have full control over the main loop here we may risk calls to Context::Render if we were to return. Instead, + // we trap the application inside this loop until we are able to recreate the swapchain and render again. + if (!data->render_interface.IsSwapchainValid()) + data->render_interface.RecreateSwapchain(); + + has_message = NextEvent(message, 0); + } + + data->context = nullptr; + data->key_down_callback = nullptr; + + return data->running; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.BeginFrame(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} + +// Local event handler for window and input events. +static LRESULT CALLBACK WindowProcedureHandler(HWND window_handle, UINT message, WPARAM w_param, LPARAM l_param) +{ + RMLUI_ASSERT(data); + + switch (message) + { + case WM_CLOSE: + { + data->running = false; + return 0; + } + break; + case WM_SIZE: + { + const int width = LOWORD(l_param); + const int height = HIWORD(l_param); + data->window_dimensions.x = width; + data->window_dimensions.y = height; + if (data->context) + { + data->render_interface.SetViewport(width, height); + data->context->SetDimensions(data->window_dimensions); + } + return 0; + } + break; + case WM_DPICHANGED: + { + RECT* new_pos = (RECT*)l_param; + SetWindowPos(window_handle, NULL, new_pos->left, new_pos->top, new_pos->right - new_pos->left, new_pos->bottom - new_pos->top, + SWP_NOZORDER | SWP_NOACTIVATE); + if (data->context && has_dpi_support) + data->context->SetDensityIndependentPixelRatio(GetDensityIndependentPixelRatio(window_handle)); + return 0; + } + break; + case WM_KEYDOWN: + { + // Override the default key event callback to add global shortcuts for the samples. + Rml::Context* context = data->context; + KeyDownCallback key_down_callback = data->key_down_callback; + + const Rml::Input::KeyIdentifier rml_key = RmlWin32::ConvertKey((int)w_param); + const int rml_modifier = RmlWin32::GetKeyModifierState(); + const float native_dp_ratio = GetDensityIndependentPixelRatio(window_handle); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, rml_key, rml_modifier, native_dp_ratio, true)) + return 0; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlWin32::WindowProcedure(context, data->text_input_method_editor, window_handle, message, w_param, l_param)) + return 0; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, rml_key, rml_modifier, native_dp_ratio, false)) + return 0; + return 0; + } + break; + default: + { + // Submit it to the platform handler for default input handling. + if (!RmlWin32::WindowProcedure(data->context, data->text_input_method_editor, window_handle, message, w_param, l_param)) + return 0; + } + break; + } + + // All unhandled messages go to DefWindowProc. + return DefWindowProc(window_handle, message, w_param, l_param); +} + +static HWND InitializeWindow(HINSTANCE instance_handle, const std::wstring& name, int& inout_width, int& inout_height, bool allow_resize) +{ + // Fill out the window class struct. + WNDCLASSW window_class; + window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + window_class.lpfnWndProc = &WindowProcedureHandler; // Attach our local event handler. + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = instance_handle; + window_class.hIcon = LoadIcon(nullptr, IDI_WINLOGO); + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.hbrBackground = nullptr; + window_class.lpszMenuName = nullptr; + window_class.lpszClassName = name.data(); + + if (!RegisterClassW(&window_class)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to register window class"); + return nullptr; + } + + HWND window_handle = CreateWindowExW(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, + name.data(), // Window class name. + name.data(), WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW, 0, 0, // Window position. + 0, 0, // Window size. + nullptr, nullptr, instance_handle, nullptr); + + if (!window_handle) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to create window"); + return nullptr; + } + + UINT window_dpi = GetWindowDpi(window_handle); + inout_width = (inout_width * (int)window_dpi) / USER_DEFAULT_SCREEN_DPI; + inout_height = (inout_height * (int)window_dpi) / USER_DEFAULT_SCREEN_DPI; + + DWORD style = (allow_resize ? WS_OVERLAPPEDWINDOW : (WS_OVERLAPPEDWINDOW & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX)); + DWORD extended_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + + // Adjust the window size to take the edges into account. + RECT window_rect; + window_rect.top = 0; + window_rect.left = 0; + window_rect.right = inout_width; + window_rect.bottom = inout_height; + if (has_dpi_support) + procAdjustWindowRectExForDpi(&window_rect, style, FALSE, extended_style, window_dpi); + else + AdjustWindowRectEx(&window_rect, style, FALSE, extended_style); + + SetWindowLong(window_handle, GWL_EXSTYLE, extended_style); + SetWindowLong(window_handle, GWL_STYLE, style); + + // Resize the window and center it on the screen. + Rml::Vector2i screen_size = {GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)}; + Rml::Vector2i window_size = {int(window_rect.right - window_rect.left), int(window_rect.bottom - window_rect.top)}; + Rml::Vector2i window_pos = Rml::Math::Max((screen_size - window_size) / 2, Rml::Vector2i(0)); + + SetWindowPos(window_handle, HWND_TOP, window_pos.x, window_pos.y, window_size.x, window_size.y, SWP_NOACTIVATE); + + return window_handle; +} + +bool CreateVulkanSurface(VkInstance instance, VkSurfaceKHR* out_surface) +{ + VkWin32SurfaceCreateInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + info.hinstance = GetModuleHandle(NULL); + info.hwnd = data->window_handle; + + VkResult status = vkCreateWin32SurfaceKHR(instance, &info, nullptr, out_surface); + + bool result = (status == VK_SUCCESS); + RMLUI_VK_ASSERTMSG(result, "Failed to create Win32 Vulkan surface"); + return result; +} diff --git a/vendor/rmlui_backend/RmlUi_Backend_X11_GL2.cpp b/vendor/rmlui_backend/RmlUi_Backend_X11_GL2.cpp new file mode 100644 index 0000000..563aaee --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Backend_X11_GL2.cpp @@ -0,0 +1,292 @@ +#include "RmlUi_Backend.h" +#include "RmlUi_Include_Xlib.h" +#include "RmlUi_Platform_X11.h" +#include "RmlUi_Renderer_GL2.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Attach the OpenGL context to the window. +static bool AttachToNative(GLXContext& out_gl_context, Display* display, Window window, XVisualInfo* visual_info) +{ + GLXContext gl_context = glXCreateContext(display, visual_info, nullptr, GL_TRUE); + if (!gl_context) + return false; + + if (!glXMakeCurrent(display, window, gl_context)) + return false; + + if (!glXIsDirect(display, gl_context)) + Rml::Log::Message(Rml::Log::LT_INFO, "OpenGL context does not support direct rendering; performance is likely to be poor."); + + out_gl_context = gl_context; + + Window root_window; + int x, y; + unsigned int width, height; + unsigned int border_width, depth; + XGetGeometry(display, window, &root_window, &x, &y, &width, &height, &border_width, &depth); + + return true; +} + +// Shutdown the OpenGL context. +static void DetachFromNative(GLXContext gl_context, Display* display) +{ + glXMakeCurrent(display, 0L, nullptr); + glXDestroyContext(display, gl_context); +} + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + BackendData(Display* display) : system_interface(display) {} + + SystemInterface_X11 system_interface; + RenderInterface_GL2 render_interface; + + Display* display = nullptr; + Window window = 0; + GLXContext gl_context = nullptr; + + bool running = true; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* window_name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + Display* display = XOpenDisplay(0); + if (!display) + return false; + + int screen = XDefaultScreen(display); + + // Fetch an appropriate 32-bit visual interface. + int attribute_list[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, 8, + 0L}; + + XVisualInfo* visual_info = glXChooseVisual(display, screen, attribute_list); + if (!visual_info) + return false; + + // Build up our window attributes. + XSetWindowAttributes window_attributes; + window_attributes.colormap = XCreateColormap(display, RootWindow(display, visual_info->screen), visual_info->visual, AllocNone); + window_attributes.border_pixel = 0; + window_attributes.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask; + + // Create the window. + Window window = XCreateWindow(display, RootWindow(display, visual_info->screen), 0, 0, width, height, 0, visual_info->depth, InputOutput, + visual_info->visual, CWBorderPixel | CWColormap | CWEventMask, &window_attributes); + + // Handle delete events in windowed mode. + Atom delete_atom = XInternAtom(display, "WM_DELETE_WINDOW", True); + XSetWMProtocols(display, window, &delete_atom, 1); + + // Capture the events we're interested in. + XSelectInput(display, window, + KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | LeaveWindowMask | PointerMotionMask | StructureNotifyMask); + + if (!allow_resize) + { + // Force the window to remain at the fixed size by asking the window manager nicely, it may choose to ignore us + XSizeHints* win_size_hints = XAllocSizeHints(); // Allocate a size hint structure + if (!win_size_hints) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "XAllocSizeHints - out of memory"); + } + else + { + // Initialize the structure and specify which hints will be providing + win_size_hints->flags = PSize | PMinSize | PMaxSize; + + // Set the sizes we want the window manager to use + win_size_hints->base_width = width; + win_size_hints->base_height = height; + win_size_hints->min_width = width; + win_size_hints->min_height = height; + win_size_hints->max_width = width; + win_size_hints->max_height = height; + + // Pass the size hints to the window manager. + XSetWMNormalHints(display, window, win_size_hints); + + // Free the size buffer + XFree(win_size_hints); + } + } + + // Set the window title and show the window. + XSetStandardProperties(display, window, window_name, "", 0L, nullptr, 0, nullptr); + XMapRaised(display, window); + + GLXContext gl_context = {}; + if (!AttachToNative(gl_context, display, window, visual_info)) + return false; + + data = Rml::MakeUnique(display); + + data->display = display; + data->window = window; + data->gl_context = gl_context; + + data->system_interface.SetWindow(window); + data->render_interface.SetViewport(width, height); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + + DetachFromNative(data->gl_context, data->display); + XCloseDisplay(data->display); + + data.reset(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return &data->render_interface; +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + Display* display = data->display; + bool result = data->running; + data->running = true; + + if (power_save && XPending(display) == 0) + { + int display_fd = ConnectionNumber(display); + fd_set fds{}; + FD_ZERO(&fds); + FD_SET(display_fd, &fds); + + double timeout = Rml::Math::Min(context->GetNextUpdateDelay(), 10.0); + struct timeval tv {}; + double seconds; + tv.tv_usec = std::modf(timeout, &seconds) * 1000000.0; + tv.tv_sec = seconds; + + int ready_fd_count; + do + { + ready_fd_count = select(display_fd + 1, &fds, NULL, NULL, &tv); + // We don't care about the return value as long as select didn't error out + RMLUI_ASSERT(ready_fd_count >= 0); + } while (XPending(display) == 0 && ready_fd_count != 0); + } + + while (XPending(display) > 0) + { + XEvent ev; + XNextEvent(display, &ev); + + switch (ev.type) + { + case ClientMessage: + { + // The only message we register for is WM_DELETE_WINDOW, so if we receive a client message then the window has been closed. + char* event_type = XGetAtomName(display, ev.xclient.message_type); + if (strcmp(event_type, "WM_PROTOCOLS") == 0) + data->running = false; + XFree(event_type); + event_type = nullptr; + } + break; + case ConfigureNotify: + { + int x = ev.xconfigure.width; + int y = ev.xconfigure.height; + + context->SetDimensions({x, y}); + data->render_interface.SetViewport(x, y); + } + break; + case KeyPress: + { + Rml::Input::KeyIdentifier key = RmlX11::ConvertKey(display, ev.xkey.keycode); + const int key_modifier = RmlX11::GetKeyModifierState(ev.xkey.state); + const float native_dp_ratio = 1.f; + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlX11::HandleInputEvent(context, display, ev)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, false)) + break; + } + break; + case SelectionRequest: + { + data->system_interface.HandleSelectionRequest(ev); + } + break; + default: + { + // Pass unhandled events to the platform layer's input handler. + RmlX11::HandleInputEvent(context, display, ev); + } + break; + } + } + + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + data->running = false; +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.BeginFrame(); + data->render_interface.Clear(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); + + // Flips the OpenGL buffers. + glXSwapBuffers(data->display, data->window); +} diff --git a/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Backend_BackwardCompatible_GLFW_GL2.cpp b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Backend_BackwardCompatible_GLFW_GL2.cpp new file mode 100644 index 0000000..2e10fe2 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Backend_BackwardCompatible_GLFW_GL2.cpp @@ -0,0 +1,226 @@ +#include "../RmlUi_Backend.h" +#include "../RmlUi_Platform_GLFW.h" +#include "RmlUi_Renderer_BackwardCompatible_GL2.h" +#include +#include +#include +#include + +static void SetupCallbacks(GLFWwindow* window); + +static void LogErrorFromGLFW(int error, const char* description) +{ + Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW error (0x%x): %s", error, description); +} + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + SystemInterface_GLFW system_interface; + RenderInterface_BackwardCompatible_GL2 render_interface; + GLFWwindow* window = nullptr; + int glfw_active_modifiers = 0; + bool context_dimensions_dirty = true; + + // Arguments set during event processing and nulled otherwise. + Rml::Context* context = nullptr; + KeyDownCallback key_down_callback = nullptr; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + glfwSetErrorCallback(LogErrorFromGLFW); + + if (!glfwInit()) + return false; + + // Set window hints for OpenGL 2 context creation. + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); + + // Request stencil buffer of at least 8-bit size to supporting clipping on transformed elements. + glfwWindowHint(GLFW_STENCIL_BITS, 8); + + // Enable MSAA for better-looking visuals, especially when transforms are applied. + glfwWindowHint(GLFW_SAMPLES, 2); + + // Apply window properties and create it. + glfwWindowHint(GLFW_RESIZABLE, allow_resize ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); + + GLFWwindow* window = glfwCreateWindow(width, height, name, nullptr, nullptr); + if (!window) + return false; + + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + + data = Rml::MakeUnique(); + data->window = window; + data->system_interface.SetWindow(window); + + // The window size may have been scaled by DPI settings, get the actual pixel size. + glfwGetFramebufferSize(window, &width, &height); + data->render_interface.SetViewport(width, height); + + // Receive num lock and caps lock modifiers for proper handling of numpad inputs in text fields. + glfwSetInputMode(window, GLFW_LOCK_KEY_MODS, GLFW_TRUE); + + // Setup the input and window event callback functions. + SetupCallbacks(window); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + glfwDestroyWindow(data->window); + data.reset(); + glfwTerminate(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return data->render_interface.GetAdaptedInterface(); +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + // The initial window size may have been affected by system DPI settings, apply the actual pixel size and dp-ratio to the context. + if (data->context_dimensions_dirty) + { + data->context_dimensions_dirty = false; + + Rml::Vector2i window_size; + float dp_ratio = 1.f; + glfwGetFramebufferSize(data->window, &window_size.x, &window_size.y); + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + context->SetDimensions(window_size); + context->SetDensityIndependentPixelRatio(dp_ratio); + } + + data->context = context; + data->key_down_callback = key_down_callback; + + if (power_save) + glfwWaitEventsTimeout(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0)); + else + glfwPollEvents(); + + data->context = nullptr; + data->key_down_callback = nullptr; + + const bool result = !glfwWindowShouldClose(data->window); + glfwSetWindowShouldClose(data->window, GLFW_FALSE); + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + glfwSetWindowShouldClose(data->window, GLFW_TRUE); +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.BeginFrame(); + data->render_interface.Clear(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); + glfwSwapBuffers(data->window); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} + +static void SetupCallbacks(GLFWwindow* window) +{ + RMLUI_ASSERT(data); + + // Key input + glfwSetKeyCallback(window, [](GLFWwindow* /*window*/, int glfw_key, int /*scancode*/, int glfw_action, int glfw_mods) { + if (!data->context) + return; + + // Store the active modifiers for later because GLFW doesn't provide them in the callbacks to the mouse input events. + data->glfw_active_modifiers = glfw_mods; + + // Override the default key event callback to add global shortcuts for the samples. + Rml::Context* context = data->context; + KeyDownCallback key_down_callback = data->key_down_callback; + + switch (glfw_action) + { + case GLFW_PRESS: + case GLFW_REPEAT: + { + const Rml::Input::KeyIdentifier key = RmlGLFW::ConvertKey(glfw_key); + const int key_modifier = RmlGLFW::ConvertKeyModifiers(glfw_mods); + float dp_ratio = 1.f; + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, false)) + break; + } + break; + case GLFW_RELEASE: RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods); break; + } + }); + + glfwSetCharCallback(window, [](GLFWwindow* /*window*/, unsigned int codepoint) { RmlGLFW::ProcessCharCallback(data->context, codepoint); }); + + glfwSetCursorEnterCallback(window, [](GLFWwindow* /*window*/, int entered) { RmlGLFW::ProcessCursorEnterCallback(data->context, entered); }); + + // Mouse input + glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) { + RmlGLFW::ProcessCursorPosCallback(data->context, window, xpos, ypos, data->glfw_active_modifiers); + }); + + glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) { + data->glfw_active_modifiers = mods; + RmlGLFW::ProcessMouseButtonCallback(data->context, button, action, mods); + }); + + glfwSetScrollCallback(window, [](GLFWwindow* /*window*/, double /*xoffset*/, double yoffset) { + RmlGLFW::ProcessScrollCallback(data->context, yoffset, data->glfw_active_modifiers); + }); + + // Window events + glfwSetFramebufferSizeCallback(window, [](GLFWwindow* /*window*/, int width, int height) { + data->render_interface.SetViewport(width, height); + RmlGLFW::ProcessFramebufferSizeCallback(data->context, width, height); + }); + + glfwSetWindowContentScaleCallback(window, + [](GLFWwindow* /*window*/, float xscale, float /*yscale*/) { RmlGLFW::ProcessContentScaleCallback(data->context, xscale); }); +} diff --git a/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Backend_BackwardCompatible_GLFW_GL3.cpp b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Backend_BackwardCompatible_GLFW_GL3.cpp new file mode 100644 index 0000000..cfbd38b --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Backend_BackwardCompatible_GLFW_GL3.cpp @@ -0,0 +1,240 @@ +#include "../RmlUi_Backend.h" +#include "../RmlUi_Platform_GLFW.h" +#include "RmlUi_Renderer_BackwardCompatible_GL3.h" +#include +#include +#include +#include + +static void SetupCallbacks(GLFWwindow* window); + +static void LogErrorFromGLFW(int error, const char* description) +{ + Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW error (0x%x): %s", error, description); +} + +/** + Global data used by this backend. + + Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown(). + */ +struct BackendData { + SystemInterface_GLFW system_interface; + RenderInterface_BackwardCompatible_GL3 render_interface; + GLFWwindow* window = nullptr; + int glfw_active_modifiers = 0; + bool context_dimensions_dirty = true; + + // Arguments set during event processing and nulled otherwise. + Rml::Context* context = nullptr; + KeyDownCallback key_down_callback = nullptr; +}; +static Rml::UniquePtr data; + +bool Backend::Initialize(const char* name, int width, int height, bool allow_resize) +{ + RMLUI_ASSERT(!data); + + glfwSetErrorCallback(LogErrorFromGLFW); + + if (!glfwInit()) + return false; + + // Set window hints for OpenGL 3.3 Core context creation. + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); + + // Request stencil buffer of at least 8-bit size to supporting clipping on transformed elements. + glfwWindowHint(GLFW_STENCIL_BITS, 8); + + // Enable MSAA for better-looking visuals, especially when transforms are applied. + glfwWindowHint(GLFW_SAMPLES, 2); + + // Apply window properties and create it. + glfwWindowHint(GLFW_RESIZABLE, allow_resize ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); + + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + + GLFWwindow* window = glfwCreateWindow(width, height, name, nullptr, nullptr); + if (!window) + return false; + + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + + // Load the OpenGL functions. + Rml::String renderer_message; + if (!RmlGL3::Initialize(&renderer_message)) + return false; + + // Construct the system and render interface, this includes compiling all the shaders. If this fails, it is likely an error in the shader code. + data = Rml::MakeUnique(); + if (!data || !data->render_interface) + return false; + + data->window = window; + data->system_interface.SetWindow(window); + data->system_interface.LogMessage(Rml::Log::LT_INFO, renderer_message); + + // The window size may have been scaled by DPI settings, get the actual pixel size. + glfwGetFramebufferSize(window, &width, &height); + data->render_interface.SetViewport(width, height); + + // Receive num lock and caps lock modifiers for proper handling of numpad inputs in text fields. + glfwSetInputMode(window, GLFW_LOCK_KEY_MODS, GLFW_TRUE); + + // Setup the input and window event callback functions. + SetupCallbacks(window); + + return true; +} + +void Backend::Shutdown() +{ + RMLUI_ASSERT(data); + glfwDestroyWindow(data->window); + data.reset(); + RmlGL3::Shutdown(); + glfwTerminate(); +} + +Rml::SystemInterface* Backend::GetSystemInterface() +{ + RMLUI_ASSERT(data); + return &data->system_interface; +} + +Rml::RenderInterface* Backend::GetRenderInterface() +{ + RMLUI_ASSERT(data); + return data->render_interface.GetAdaptedInterface(); +} + +bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback, bool power_save) +{ + RMLUI_ASSERT(data && context); + + // The initial window size may have been affected by system DPI settings, apply the actual pixel size and dp-ratio to the context. + if (data->context_dimensions_dirty) + { + data->context_dimensions_dirty = false; + + Rml::Vector2i window_size; + float dp_ratio = 1.f; + glfwGetFramebufferSize(data->window, &window_size.x, &window_size.y); + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + context->SetDimensions(window_size); + context->SetDensityIndependentPixelRatio(dp_ratio); + } + + data->context = context; + data->key_down_callback = key_down_callback; + + if (power_save) + glfwWaitEventsTimeout(Rml::Math::Min(context->GetNextUpdateDelay(), 10.0)); + else + glfwPollEvents(); + + data->context = nullptr; + data->key_down_callback = nullptr; + + const bool result = !glfwWindowShouldClose(data->window); + glfwSetWindowShouldClose(data->window, GLFW_FALSE); + return result; +} + +void Backend::RequestExit() +{ + RMLUI_ASSERT(data); + glfwSetWindowShouldClose(data->window, GLFW_TRUE); +} + +void Backend::BeginFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.BeginFrame(); + data->render_interface.Clear(); +} + +void Backend::PresentFrame() +{ + RMLUI_ASSERT(data); + data->render_interface.EndFrame(); + glfwSwapBuffers(data->window); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; +} + +static void SetupCallbacks(GLFWwindow* window) +{ + RMLUI_ASSERT(data); + + // Key input + glfwSetKeyCallback(window, [](GLFWwindow* /*window*/, int glfw_key, int /*scancode*/, int glfw_action, int glfw_mods) { + if (!data->context) + return; + + // Store the active modifiers for later because GLFW doesn't provide them in the callbacks to the mouse input events. + data->glfw_active_modifiers = glfw_mods; + + // Override the default key event callback to add global shortcuts for the samples. + Rml::Context* context = data->context; + KeyDownCallback key_down_callback = data->key_down_callback; + + switch (glfw_action) + { + case GLFW_PRESS: + case GLFW_REPEAT: + { + const Rml::Input::KeyIdentifier key = RmlGLFW::ConvertKey(glfw_key); + const int key_modifier = RmlGLFW::ConvertKeyModifiers(glfw_mods); + float dp_ratio = 1.f; + glfwGetWindowContentScale(data->window, &dp_ratio, nullptr); + + // See if we have any global shortcuts that take priority over the context. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, true)) + break; + // Otherwise, hand the event over to the context by calling the input handler as normal. + if (!RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods)) + break; + // The key was not consumed by the context either, try keyboard shortcuts of lower priority. + if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, false)) + break; + } + break; + case GLFW_RELEASE: RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods); break; + } + }); + + glfwSetCharCallback(window, [](GLFWwindow* /*window*/, unsigned int codepoint) { RmlGLFW::ProcessCharCallback(data->context, codepoint); }); + + glfwSetCursorEnterCallback(window, [](GLFWwindow* /*window*/, int entered) { RmlGLFW::ProcessCursorEnterCallback(data->context, entered); }); + + // Mouse input + glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) { + RmlGLFW::ProcessCursorPosCallback(data->context, window, xpos, ypos, data->glfw_active_modifiers); + }); + + glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) { + data->glfw_active_modifiers = mods; + RmlGLFW::ProcessMouseButtonCallback(data->context, button, action, mods); + }); + + glfwSetScrollCallback(window, [](GLFWwindow* /*window*/, double /*xoffset*/, double yoffset) { + RmlGLFW::ProcessScrollCallback(data->context, yoffset, data->glfw_active_modifiers); + }); + + // Window events + glfwSetFramebufferSizeCallback(window, [](GLFWwindow* /*window*/, int width, int height) { + data->render_interface.SetViewport(width, height); + RmlGLFW::ProcessFramebufferSizeCallback(data->context, width, height); + }); + + glfwSetWindowContentScaleCallback(window, + [](GLFWwindow* /*window*/, float xscale, float /*yscale*/) { RmlGLFW::ProcessContentScaleCallback(data->context, xscale); }); +} diff --git a/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL2.cpp b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL2.cpp new file mode 100644 index 0000000..9a86df1 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL2.cpp @@ -0,0 +1,300 @@ +#include "RmlUi_Renderer_BackwardCompatible_GL2.h" +#include +#include +#include +#include +#include + +#if defined RMLUI_PLATFORM_WIN32 + #include "RmlUi_Include_Windows.h" + #include + #include +#elif defined RMLUI_PLATFORM_MACOSX + #include + #include + #include + #include +#elif defined RMLUI_PLATFORM_UNIX + #include "RmlUi_Include_Xlib.h" + #include + #include + #include + #include +#endif + +#define GL_CLAMP_TO_EDGE 0x812F + +RenderInterface_BackwardCompatible_GL2::RenderInterface_BackwardCompatible_GL2() {} + +void RenderInterface_BackwardCompatible_GL2::SetViewport(int in_viewport_width, int in_viewport_height) +{ + viewport_width = in_viewport_width; + viewport_height = in_viewport_height; +} + +void RenderInterface_BackwardCompatible_GL2::BeginFrame() +{ + RMLUI_ASSERT(viewport_width >= 0 && viewport_height >= 0); + glViewport(0, 0, viewport_width, viewport_height); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + Rml::Matrix4f projection = Rml::Matrix4f::ProjectOrtho(0, (float)viewport_width, (float)viewport_height, 0, -10000, 10000); + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(projection.data()); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + transform_enabled = false; +} + +void RenderInterface_BackwardCompatible_GL2::EndFrame() {} + +void RenderInterface_BackwardCompatible_GL2::Clear() +{ + glClearStencil(0); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +void RenderInterface_BackwardCompatible_GL2::RenderGeometry(Rml::Vertex* vertices, int /*num_vertices*/, int* indices, int num_indices, + const Rml::TextureHandle texture, const Rml::Vector2f& translation) +{ + glPushMatrix(); + glTranslatef(translation.x, translation.y, 0); + + glVertexPointer(2, GL_FLOAT, sizeof(Rml::Vertex), &vertices[0].position); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Rml::Vertex), &vertices[0].colour); + + if (!texture) + { + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + else + { + glEnable(GL_TEXTURE_2D); + + if (texture != TextureEnableWithoutBinding) + glBindTexture(GL_TEXTURE_2D, (GLuint)texture); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, sizeof(Rml::Vertex), &vertices[0].tex_coord); + } + + glDrawElements(GL_TRIANGLES, num_indices, GL_UNSIGNED_INT, indices); + + glPopMatrix(); +} + +void RenderInterface_BackwardCompatible_GL2::EnableScissorRegion(bool enable) +{ + if (enable) + { + if (!transform_enabled) + { + glEnable(GL_SCISSOR_TEST); + glDisable(GL_STENCIL_TEST); + } + else + { + glDisable(GL_SCISSOR_TEST); + glEnable(GL_STENCIL_TEST); + } + } + else + { + glDisable(GL_SCISSOR_TEST); + glDisable(GL_STENCIL_TEST); + } +} + +void RenderInterface_BackwardCompatible_GL2::SetScissorRegion(int x, int y, int width, int height) +{ + if (!transform_enabled) + { + glScissor(x, viewport_height - (y + height), width, height); + } + else + { + // clear the stencil buffer + glStencilMask(GLuint(-1)); + glClear(GL_STENCIL_BUFFER_BIT); + + // fill the stencil buffer + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glDepthMask(GL_FALSE); + glStencilFunc(GL_NEVER, 1, GLuint(-1)); + glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); + + float fx = (float)x; + float fy = (float)y; + float fwidth = (float)width; + float fheight = (float)height; + + // draw transformed quad + GLfloat vertices[] = {fx, fy, 0, fx, fy + fheight, 0, fx + fwidth, fy + fheight, 0, fx + fwidth, fy, 0}; + glDisableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vertices); + GLushort indices[] = {1, 2, 0, 3}; + glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices); + glEnableClientState(GL_COLOR_ARRAY); + + // prepare for drawing the real thing + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + glStencilMask(0); + glStencilFunc(GL_EQUAL, 1, GLuint(-1)); + } +} + +// Set to byte packing, or the compiler will expand our struct, which means it won't read correctly from file +#pragma pack(1) +struct TGAHeader { + char idLength; + char colourMapType; + char dataType; + short int colourMapOrigin; + short int colourMapLength; + char colourMapDepth; + short int xOrigin; + short int yOrigin; + short int width; + short int height; + char bitsPerPixel; + char imageDescriptor; +}; +// Restore packing +#pragma pack() + +bool RenderInterface_BackwardCompatible_GL2::LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, + const Rml::String& source) +{ + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + { + return false; + } + + file_interface->Seek(file_handle, 0, SEEK_END); + size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + if (buffer_size <= sizeof(TGAHeader)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Texture file size is smaller than TGAHeader, file is not a valid TGA image."); + file_interface->Close(file_handle); + return false; + } + + char* buffer = new char[buffer_size]; + file_interface->Read(buffer, buffer_size, file_handle); + file_interface->Close(file_handle); + + TGAHeader header; + memcpy(&header, buffer, sizeof(TGAHeader)); + + int color_mode = header.bitsPerPixel / 8; + int image_size = header.width * header.height * 4; // We always make 32bit textures + + if (header.dataType != 2) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24/32bit uncompressed TGAs are supported."); + delete[] buffer; + return false; + } + + // Ensure we have at least 3 colors + if (color_mode < 3) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24 and 32bit textures are supported."); + delete[] buffer; + return false; + } + + const char* image_src = buffer + sizeof(TGAHeader); + unsigned char* image_dest = new unsigned char[image_size]; + + // Targa is BGR, swap to RGB and flip Y axis + for (long y = 0; y < header.height; y++) + { + long read_index = y * header.width * color_mode; + long write_index = ((header.imageDescriptor & 32) != 0) ? read_index : (header.height - y - 1) * header.width * color_mode; + for (long x = 0; x < header.width; x++) + { + image_dest[write_index] = image_src[read_index + 2]; + image_dest[write_index + 1] = image_src[read_index + 1]; + image_dest[write_index + 2] = image_src[read_index]; + if (color_mode == 4) + image_dest[write_index + 3] = image_src[read_index + 3]; + else + image_dest[write_index + 3] = 255; + + write_index += 4; + read_index += color_mode; + } + } + + texture_dimensions.x = header.width; + texture_dimensions.y = header.height; + + bool success = GenerateTexture(texture_handle, image_dest, texture_dimensions); + + delete[] image_dest; + delete[] buffer; + + return success; +} + +bool RenderInterface_BackwardCompatible_GL2::GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, + const Rml::Vector2i& source_dimensions) +{ + GLuint texture_id = 0; + glGenTextures(1, &texture_id); + if (texture_id == 0) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to generate texture."); + return false; + } + + glBindTexture(GL_TEXTURE_2D, texture_id); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, source_dimensions.x, source_dimensions.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, source); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + texture_handle = (Rml::TextureHandle)texture_id; + + return true; +} + +void RenderInterface_BackwardCompatible_GL2::ReleaseTexture(Rml::TextureHandle texture_handle) +{ + glDeleteTextures(1, (GLuint*)&texture_handle); +} + +void RenderInterface_BackwardCompatible_GL2::SetTransform(const Rml::Matrix4f* transform) +{ + transform_enabled = (transform != nullptr); + + if (transform) + { + if (std::is_same::value) + glLoadMatrixf(transform->data()); + else if (std::is_same::value) + glLoadMatrixf(transform->Transpose().data()); + } + else + glLoadIdentity(); +} diff --git a/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL2.h b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL2.h new file mode 100644 index 0000000..ce6b4a0 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL2.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +/* + The GL2 renderer from RmlUi 5, only modified to derive from the compatibility interface. + + Implemented for testing and demonstration purposes, not recommended for production use. +*/ + +class RenderInterface_BackwardCompatible_GL2 : public Rml::RenderInterfaceCompatibility { +public: + RenderInterface_BackwardCompatible_GL2(); + + // The viewport should be updated whenever the window size changes. + void SetViewport(int viewport_width, int viewport_height); + + // Sets up OpenGL states for taking rendering commands from RmlUi. + void BeginFrame(); + void EndFrame(); + + // Optional, can be used to clear the framebuffer. + void Clear(); + + // -- Inherited from Rml::RenderInterface -- + + void RenderGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices, Rml::TextureHandle texture, + const Rml::Vector2f& translation) override; + + void EnableScissorRegion(bool enable) override; + void SetScissorRegion(int x, int y, int width, int height) override; + + bool LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, const Rml::String& source) override; + bool GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions) override; + void ReleaseTexture(Rml::TextureHandle texture_handle) override; + + void SetTransform(const Rml::Matrix4f* transform) override; + + // Can be passed to RenderGeometry() to enable texture rendering without changing the bound texture. + static const Rml::TextureHandle TextureEnableWithoutBinding = Rml::TextureHandle(-1); + +private: + int viewport_width = 0; + int viewport_height = 0; + bool transform_enabled = false; +}; diff --git a/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL3.cpp b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL3.cpp new file mode 100644 index 0000000..7e91683 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL3.cpp @@ -0,0 +1,765 @@ +#include "RmlUi_Renderer_BackwardCompatible_GL3.h" +#include +#include +#include +#include +#include + +#if defined RMLUI_PLATFORM_WIN32_NATIVE + // function call missing argument list + #pragma warning(disable : 4551) + // unreferenced local function has been removed + #pragma warning(disable : 4505) +#endif + +#if defined RMLUI_PLATFORM_EMSCRIPTEN + #define RMLUI_SHADER_HEADER "#version 300 es\nprecision highp float;\n" + #include +#elif defined RMLUI_GL3_CUSTOM_LOADER + #define RMLUI_SHADER_HEADER "#version 330\n" + #include RMLUI_GL3_CUSTOM_LOADER +#else + #define RMLUI_SHADER_HEADER "#version 330\n" + #define GLAD_GL_IMPLEMENTATION + #include "../RmlUi_Include_GL3.h" +#endif + +static const char* shader_main_vertex = RMLUI_SHADER_HEADER R"( +uniform vec2 _translate; +uniform mat4 _transform; + +in vec2 inPosition; +in vec4 inColor0; +in vec2 inTexCoord0; + +out vec2 fragTexCoord; +out vec4 fragColor; + +void main() { + fragTexCoord = inTexCoord0; + fragColor = inColor0; + + vec2 translatedPos = inPosition + _translate.xy; + vec4 outPos = _transform * vec4(translatedPos, 0, 1); + + gl_Position = outPos; +} +)"; + +static const char* shader_main_fragment_texture = RMLUI_SHADER_HEADER R"( +uniform sampler2D _tex; +in vec2 fragTexCoord; +in vec4 fragColor; + +out vec4 finalColor; + +void main() { + vec4 texColor = texture(_tex, fragTexCoord); + finalColor = fragColor * texColor; +} +)"; +static const char* shader_main_fragment_color = RMLUI_SHADER_HEADER R"( +in vec2 fragTexCoord; +in vec4 fragColor; + +out vec4 finalColor; + +void main() { + finalColor = fragColor; +} +)"; + +namespace Gfx { + +enum class ProgramUniform { Translate, Transform, Tex, Count }; +static const char* const program_uniform_names[(size_t)ProgramUniform::Count] = {"_translate", "_transform", "_tex"}; + +enum class VertexAttribute { Position, Color0, TexCoord0, Count }; +static const char* const vertex_attribute_names[(size_t)VertexAttribute::Count] = {"inPosition", "inColor0", "inTexCoord0"}; + +struct CompiledGeometryData { + Rml::TextureHandle texture; + GLuint vao; + GLuint vbo; + GLuint ibo; + GLsizei draw_count; +}; + +struct ProgramData { + GLuint id; + GLint uniform_locations[(size_t)ProgramUniform::Count]; +}; + +struct ShadersData { + ProgramData program_color; + ProgramData program_texture; + GLuint shader_main_vertex; + GLuint shader_main_fragment_color; + GLuint shader_main_fragment_texture; +}; + +static void CheckGLError(const char* operation_name) +{ +#ifdef RMLUI_DEBUG + GLenum error_code = glGetError(); + if (error_code != GL_NO_ERROR) + { + static const Rml::Pair error_names[] = {{GL_INVALID_ENUM, "GL_INVALID_ENUM"}, {GL_INVALID_VALUE, "GL_INVALID_VALUE"}, + {GL_INVALID_OPERATION, "GL_INVALID_OPERATION"}, {GL_OUT_OF_MEMORY, "GL_OUT_OF_MEMORY"}}; + const char* error_str = "''"; + for (auto& err : error_names) + { + if (err.first == error_code) + { + error_str = err.second; + break; + } + } + Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL error during %s. Error code 0x%x (%s).", operation_name, error_code, error_str); + } +#endif + (void)operation_name; +} + +// Create the shader, 'shader_type' is either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. +static GLuint CreateShader(GLenum shader_type, const char* code_string) +{ + GLuint id = glCreateShader(shader_type); + + glShaderSource(id, 1, (const GLchar**)&code_string, NULL); + glCompileShader(id); + + GLint status = 0; + glGetShaderiv(id, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) + { + GLint info_log_length = 0; + glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_log_length); + char* info_log_string = new char[info_log_length + 1]; + glGetShaderInfoLog(id, info_log_length, NULL, info_log_string); + + Rml::Log::Message(Rml::Log::LT_ERROR, "Compile failure in OpenGL shader: %s", info_log_string); + delete[] info_log_string; + glDeleteShader(id); + return 0; + } + + CheckGLError("CreateShader"); + + return id; +} + +static void BindAttribLocations(GLuint program) +{ + for (GLuint i = 0; i < (GLuint)VertexAttribute::Count; i++) + { + glBindAttribLocation(program, i, vertex_attribute_names[i]); + } + CheckGLError("BindAttribLocations"); +} + +static bool CreateProgram(GLuint vertex_shader, GLuint fragment_shader, ProgramData& out_program) +{ + GLuint id = glCreateProgram(); + RMLUI_ASSERT(id); + + BindAttribLocations(id); + + glAttachShader(id, vertex_shader); + glAttachShader(id, fragment_shader); + + glLinkProgram(id); + + glDetachShader(id, vertex_shader); + glDetachShader(id, fragment_shader); + + GLint status = 0; + glGetProgramiv(id, GL_LINK_STATUS, &status); + if (status == GL_FALSE) + { + GLint info_log_length = 0; + glGetProgramiv(id, GL_INFO_LOG_LENGTH, &info_log_length); + char* info_log_string = new char[info_log_length + 1]; + glGetProgramInfoLog(id, info_log_length, NULL, info_log_string); + + Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL program linking failure: %s", info_log_string); + delete[] info_log_string; + glDeleteProgram(id); + return false; + } + + out_program = {}; + out_program.id = id; + + // Make a lookup table for the uniform locations. + GLint num_active_uniforms = 0; + glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &num_active_uniforms); + + constexpr size_t name_size = 64; + GLchar name_buf[name_size] = ""; + for (int unif = 0; unif < num_active_uniforms; ++unif) + { + GLint array_size = 0; + GLenum type = 0; + GLsizei actual_length = 0; + glGetActiveUniform(id, unif, name_size, &actual_length, &array_size, &type, name_buf); + GLint location = glGetUniformLocation(id, name_buf); + + // See if we have the name in our pre-defined name list. + ProgramUniform program_uniform = ProgramUniform::Count; + for (int i = 0; i < (int)ProgramUniform::Count; i++) + { + const char* uniform_name = program_uniform_names[i]; + if (strcmp(name_buf, uniform_name) == 0) + { + program_uniform = (ProgramUniform)i; + break; + } + } + + if ((size_t)program_uniform < (size_t)ProgramUniform::Count) + { + out_program.uniform_locations[(size_t)program_uniform] = location; + } + else + { + Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL program uses unknown uniform '%s'.", name_buf); + return false; + } + } + + CheckGLError("CreateProgram"); + + return true; +} + +static bool CreateShaders(ShadersData& out_shaders) +{ + out_shaders = {}; + GLuint& main_vertex = out_shaders.shader_main_vertex; + GLuint& main_fragment_color = out_shaders.shader_main_fragment_color; + GLuint& main_fragment_texture = out_shaders.shader_main_fragment_texture; + + main_vertex = CreateShader(GL_VERTEX_SHADER, shader_main_vertex); + if (!main_vertex) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Could not create OpenGL shader: 'shader_main_vertex'."); + return false; + } + main_fragment_color = CreateShader(GL_FRAGMENT_SHADER, shader_main_fragment_color); + if (!main_fragment_color) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Could not create OpenGL shader: 'shader_main_fragment_color'."); + return false; + } + main_fragment_texture = CreateShader(GL_FRAGMENT_SHADER, shader_main_fragment_texture); + if (!main_fragment_texture) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Could not create OpenGL shader: 'shader_main_fragment_texture'."); + return false; + } + + if (!CreateProgram(main_vertex, main_fragment_color, out_shaders.program_color)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Could not create OpenGL program: 'program_color'."); + return false; + } + if (!CreateProgram(main_vertex, main_fragment_texture, out_shaders.program_texture)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Could not create OpenGL program: 'program_texture'."); + return false; + } + + return true; +} + +static void DestroyShaders(ShadersData& shaders) +{ + glDeleteProgram(shaders.program_color.id); + glDeleteProgram(shaders.program_texture.id); + + glDeleteShader(shaders.shader_main_vertex); + glDeleteShader(shaders.shader_main_fragment_color); + glDeleteShader(shaders.shader_main_fragment_texture); + + shaders = {}; +} + +} // namespace Gfx + +RenderInterface_BackwardCompatible_GL3::RenderInterface_BackwardCompatible_GL3() +{ + shaders = Rml::MakeUnique(); + + if (!Gfx::CreateShaders(*shaders)) + shaders.reset(); +} + +RenderInterface_BackwardCompatible_GL3::~RenderInterface_BackwardCompatible_GL3() +{ + if (shaders) + Gfx::DestroyShaders(*shaders); +} + +void RenderInterface_BackwardCompatible_GL3::SetViewport(int width, int height) +{ + viewport_width = width; + viewport_height = height; +} + +void RenderInterface_BackwardCompatible_GL3::BeginFrame() +{ + RMLUI_ASSERT(viewport_width >= 0 && viewport_height >= 0); + + // Backup GL state. + glstate_backup.enable_cull_face = glIsEnabled(GL_CULL_FACE); + glstate_backup.enable_blend = glIsEnabled(GL_BLEND); + glstate_backup.enable_stencil_test = glIsEnabled(GL_STENCIL_TEST); + glstate_backup.enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + + glGetIntegerv(GL_VIEWPORT, glstate_backup.viewport); + glGetIntegerv(GL_SCISSOR_BOX, glstate_backup.scissor); + + glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &glstate_backup.stencil_clear_value); + glGetFloatv(GL_COLOR_CLEAR_VALUE, glstate_backup.color_clear_value); + + glGetIntegerv(GL_BLEND_EQUATION_RGB, &glstate_backup.blend_equation_rgb); + glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &glstate_backup.blend_equation_alpha); + glGetIntegerv(GL_BLEND_SRC_RGB, &glstate_backup.blend_src_rgb); + glGetIntegerv(GL_BLEND_DST_RGB, &glstate_backup.blend_dst_rgb); + glGetIntegerv(GL_BLEND_SRC_ALPHA, &glstate_backup.blend_src_alpha); + glGetIntegerv(GL_BLEND_DST_ALPHA, &glstate_backup.blend_dst_alpha); + + glGetIntegerv(GL_STENCIL_FUNC, &glstate_backup.stencil_front.func); + glGetIntegerv(GL_STENCIL_REF, &glstate_backup.stencil_front.ref); + glGetIntegerv(GL_STENCIL_VALUE_MASK, &glstate_backup.stencil_front.value_mask); + glGetIntegerv(GL_STENCIL_WRITEMASK, &glstate_backup.stencil_front.writemask); + glGetIntegerv(GL_STENCIL_FAIL, &glstate_backup.stencil_front.fail); + glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &glstate_backup.stencil_front.pass_depth_fail); + glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &glstate_backup.stencil_front.pass_depth_pass); + + glGetIntegerv(GL_STENCIL_BACK_FUNC, &glstate_backup.stencil_back.func); + glGetIntegerv(GL_STENCIL_BACK_REF, &glstate_backup.stencil_back.ref); + glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &glstate_backup.stencil_back.value_mask); + glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &glstate_backup.stencil_back.writemask); + glGetIntegerv(GL_STENCIL_BACK_FAIL, &glstate_backup.stencil_back.fail); + glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &glstate_backup.stencil_back.pass_depth_fail); + glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &glstate_backup.stencil_back.pass_depth_pass); + + // Setup expected GL state. + glViewport(0, 0, viewport_width, viewport_height); + + glClearStencil(0); + glClearColor(0, 0, 0, 1); + + glDisable(GL_CULL_FACE); + + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 1, GLuint(-1)); + glStencilMask(GLuint(-1)); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + projection = Rml::Matrix4f::ProjectOrtho(0, (float)viewport_width, (float)viewport_height, 0, -10000, 10000); + SetTransform(nullptr); +} + +void RenderInterface_BackwardCompatible_GL3::EndFrame() +{ + // Restore GL state. + if (glstate_backup.enable_cull_face) + glEnable(GL_CULL_FACE); + else + glDisable(GL_CULL_FACE); + + if (glstate_backup.enable_blend) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + + if (glstate_backup.enable_stencil_test) + glEnable(GL_STENCIL_TEST); + else + glDisable(GL_STENCIL_TEST); + + if (glstate_backup.enable_scissor_test) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); + + glViewport(glstate_backup.viewport[0], glstate_backup.viewport[1], glstate_backup.viewport[2], glstate_backup.viewport[3]); + glScissor(glstate_backup.scissor[0], glstate_backup.scissor[1], glstate_backup.scissor[2], glstate_backup.scissor[3]); + + glClearStencil(glstate_backup.stencil_clear_value); + glClearColor(glstate_backup.color_clear_value[0], glstate_backup.color_clear_value[1], glstate_backup.color_clear_value[2], + glstate_backup.color_clear_value[3]); + + glBlendEquationSeparate(glstate_backup.blend_equation_rgb, glstate_backup.blend_equation_alpha); + glBlendFuncSeparate(glstate_backup.blend_src_rgb, glstate_backup.blend_dst_rgb, glstate_backup.blend_src_alpha, glstate_backup.blend_dst_alpha); + + glStencilFuncSeparate(GL_FRONT, glstate_backup.stencil_front.func, glstate_backup.stencil_front.ref, glstate_backup.stencil_front.value_mask); + glStencilMaskSeparate(GL_FRONT, glstate_backup.stencil_front.writemask); + glStencilOpSeparate(GL_FRONT, glstate_backup.stencil_front.fail, glstate_backup.stencil_front.pass_depth_fail, + glstate_backup.stencil_front.pass_depth_pass); + + glStencilFuncSeparate(GL_BACK, glstate_backup.stencil_back.func, glstate_backup.stencil_back.ref, glstate_backup.stencil_back.value_mask); + glStencilMaskSeparate(GL_BACK, glstate_backup.stencil_back.writemask); + glStencilOpSeparate(GL_BACK, glstate_backup.stencil_back.fail, glstate_backup.stencil_back.pass_depth_fail, + glstate_backup.stencil_back.pass_depth_pass); +} + +void RenderInterface_BackwardCompatible_GL3::Clear() +{ + glClearStencil(0); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +void RenderInterface_BackwardCompatible_GL3::RenderGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices, + const Rml::TextureHandle texture, const Rml::Vector2f& translation) +{ + Rml::CompiledGeometryHandle geometry = CompileGeometry(vertices, num_vertices, indices, num_indices, texture); + + if (geometry) + { + RenderCompiledGeometry(geometry, translation); + ReleaseCompiledGeometry(geometry); + } +} + +Rml::CompiledGeometryHandle RenderInterface_BackwardCompatible_GL3::CompileGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, + int num_indices, Rml::TextureHandle texture) +{ + constexpr GLenum draw_usage = GL_STATIC_DRAW; + + GLuint vao = 0; + GLuint vbo = 0; + GLuint ibo = 0; + + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + glGenBuffers(1, &ibo); + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(Rml::Vertex) * num_vertices, (const void*)vertices, draw_usage); + + glEnableVertexAttribArray((GLuint)Gfx::VertexAttribute::Position); + glVertexAttribPointer((GLuint)Gfx::VertexAttribute::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Rml::Vertex), + (const GLvoid*)(offsetof(Rml::Vertex, position))); + + glEnableVertexAttribArray((GLuint)Gfx::VertexAttribute::Color0); + glVertexAttribPointer((GLuint)Gfx::VertexAttribute::Color0, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Rml::Vertex), + (const GLvoid*)(offsetof(Rml::Vertex, colour))); + + glEnableVertexAttribArray((GLuint)Gfx::VertexAttribute::TexCoord0); + glVertexAttribPointer((GLuint)Gfx::VertexAttribute::TexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Rml::Vertex), + (const GLvoid*)(offsetof(Rml::Vertex, tex_coord))); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * num_indices, (const void*)indices, draw_usage); + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + Gfx::CheckGLError("CompileGeometry"); + + Gfx::CompiledGeometryData* geometry = new Gfx::CompiledGeometryData; + geometry->texture = texture; + geometry->vao = vao; + geometry->vbo = vbo; + geometry->ibo = ibo; + geometry->draw_count = num_indices; + + return (Rml::CompiledGeometryHandle)geometry; +} + +void RenderInterface_BackwardCompatible_GL3::RenderCompiledGeometry(Rml::CompiledGeometryHandle handle, const Rml::Vector2f& translation) +{ + Gfx::CompiledGeometryData* geometry = (Gfx::CompiledGeometryData*)handle; + + if (geometry->texture) + { + glUseProgram(shaders->program_texture.id); + if (geometry->texture != TextureEnableWithoutBinding) + glBindTexture(GL_TEXTURE_2D, (GLuint)geometry->texture); + SubmitTransformUniform(ProgramId::Texture, shaders->program_texture.uniform_locations[(size_t)Gfx::ProgramUniform::Transform]); + glUniform2fv(shaders->program_texture.uniform_locations[(size_t)Gfx::ProgramUniform::Translate], 1, &translation.x); + } + else + { + glUseProgram(shaders->program_color.id); + glBindTexture(GL_TEXTURE_2D, 0); + SubmitTransformUniform(ProgramId::Color, shaders->program_color.uniform_locations[(size_t)Gfx::ProgramUniform::Transform]); + glUniform2fv(shaders->program_color.uniform_locations[(size_t)Gfx::ProgramUniform::Translate], 1, &translation.x); + } + + glBindVertexArray(geometry->vao); + glDrawElements(GL_TRIANGLES, geometry->draw_count, GL_UNSIGNED_INT, (const GLvoid*)0); + + glBindVertexArray(0); + glUseProgram(0); + glBindTexture(GL_TEXTURE_2D, 0); + + Gfx::CheckGLError("RenderCompiledGeometry"); +} + +void RenderInterface_BackwardCompatible_GL3::ReleaseCompiledGeometry(Rml::CompiledGeometryHandle handle) +{ + Gfx::CompiledGeometryData* geometry = (Gfx::CompiledGeometryData*)handle; + + glDeleteVertexArrays(1, &geometry->vao); + glDeleteBuffers(1, &geometry->vbo); + glDeleteBuffers(1, &geometry->ibo); + + delete geometry; +} + +void RenderInterface_BackwardCompatible_GL3::EnableScissorRegion(bool enable) +{ + ScissoringState new_state = ScissoringState::Disable; + + if (enable) + new_state = (transform_active ? ScissoringState::Stencil : ScissoringState::Scissor); + + if (new_state != scissoring_state) + { + // Disable old + if (scissoring_state == ScissoringState::Scissor) + glDisable(GL_SCISSOR_TEST); + else if (scissoring_state == ScissoringState::Stencil) + glStencilFunc(GL_ALWAYS, 1, GLuint(-1)); + + // Enable new + if (new_state == ScissoringState::Scissor) + glEnable(GL_SCISSOR_TEST); + else if (new_state == ScissoringState::Stencil) + glStencilFunc(GL_EQUAL, 1, GLuint(-1)); + + scissoring_state = new_state; + } +} + +void RenderInterface_BackwardCompatible_GL3::SetScissorRegion(int x, int y, int width, int height) +{ + if (transform_active) + { + const float left = float(x); + const float right = float(x + width); + const float top = float(y); + const float bottom = float(y + height); + + Rml::Vertex vertices[4]; + vertices[0].position = {left, top}; + vertices[1].position = {right, top}; + vertices[2].position = {right, bottom}; + vertices[3].position = {left, bottom}; + + int indices[6] = {0, 2, 1, 0, 3, 2}; + + glClear(GL_STENCIL_BUFFER_BIT); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glStencilFunc(GL_ALWAYS, 1, GLuint(-1)); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + RenderGeometry(vertices, 4, indices, 6, 0, Rml::Vector2f(0, 0)); + + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 1, GLuint(-1)); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + else + { + glScissor(x, viewport_height - (y + height), width, height); + } +} + +// Set to byte packing, or the compiler will expand our struct, which means it won't read correctly from file +#pragma pack(1) +struct TGAHeader { + char idLength; + char colourMapType; + char dataType; + short int colourMapOrigin; + short int colourMapLength; + char colourMapDepth; + short int xOrigin; + short int yOrigin; + short int width; + short int height; + char bitsPerPixel; + char imageDescriptor; +}; +// Restore packing +#pragma pack() + +bool RenderInterface_BackwardCompatible_GL3::LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, + const Rml::String& source) +{ + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + { + return false; + } + + file_interface->Seek(file_handle, 0, SEEK_END); + size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + if (buffer_size <= sizeof(TGAHeader)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Texture file size is smaller than TGAHeader, file is not a valid TGA image."); + file_interface->Close(file_handle); + return false; + } + + using Rml::byte; + byte* buffer = new byte[buffer_size]; + file_interface->Read(buffer, buffer_size, file_handle); + file_interface->Close(file_handle); + + TGAHeader header; + memcpy(&header, buffer, sizeof(TGAHeader)); + + int color_mode = header.bitsPerPixel / 8; + int image_size = header.width * header.height * 4; // We always make 32bit textures + + if (header.dataType != 2) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24/32bit uncompressed TGAs are supported."); + delete[] buffer; + return false; + } + + // Ensure we have at least 3 colors + if (color_mode < 3) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24 and 32bit textures are supported."); + delete[] buffer; + return false; + } + + const byte* image_src = buffer + sizeof(TGAHeader); + byte* image_dest = new byte[image_size]; + + // Targa is BGR, swap to RGB and flip Y axis + for (long y = 0; y < header.height; y++) + { + long read_index = y * header.width * color_mode; + long write_index = ((header.imageDescriptor & 32) != 0) ? read_index : (header.height - y - 1) * header.width * 4; + for (long x = 0; x < header.width; x++) + { + image_dest[write_index] = image_src[read_index + 2]; + image_dest[write_index + 1] = image_src[read_index + 1]; + image_dest[write_index + 2] = image_src[read_index]; + if (color_mode == 4) + { + const int alpha = image_src[read_index + 3]; +#ifdef RMLUI_SRGB_PREMULTIPLIED_ALPHA + image_dest[write_index + 0] = (image_dest[write_index + 0] * alpha) / 255; + image_dest[write_index + 1] = (image_dest[write_index + 1] * alpha) / 255; + image_dest[write_index + 2] = (image_dest[write_index + 2] * alpha) / 255; +#endif + image_dest[write_index + 3] = (byte)alpha; + } + else + { + image_dest[write_index + 3] = 255; + } + + write_index += 4; + read_index += color_mode; + } + } + + texture_dimensions.x = header.width; + texture_dimensions.y = header.height; + + bool success = GenerateTexture(texture_handle, image_dest, texture_dimensions); + + delete[] image_dest; + delete[] buffer; + + return success; +} + +bool RenderInterface_BackwardCompatible_GL3::GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, + const Rml::Vector2i& source_dimensions) +{ + GLuint texture_id = 0; + glGenTextures(1, &texture_id); + if (texture_id == 0) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to generate texture."); + return false; + } + + glBindTexture(GL_TEXTURE_2D, texture_id); + + GLint internal_format = GL_RGBA8; + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, source_dimensions.x, source_dimensions.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, source); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + texture_handle = (Rml::TextureHandle)texture_id; + + glBindTexture(GL_TEXTURE_2D, 0); + + return true; +} + +void RenderInterface_BackwardCompatible_GL3::ReleaseTexture(Rml::TextureHandle texture_handle) +{ + glDeleteTextures(1, (GLuint*)&texture_handle); +} + +void RenderInterface_BackwardCompatible_GL3::SetTransform(const Rml::Matrix4f* new_transform) +{ + transform_active = (new_transform != nullptr); + transform = projection * (new_transform ? *new_transform : Rml::Matrix4f::Identity()); + transform_dirty_state = ProgramId::All; +} + +void RenderInterface_BackwardCompatible_GL3::SubmitTransformUniform(ProgramId program_id, int uniform_location) +{ + if ((int)program_id & (int)transform_dirty_state) + { + glUniformMatrix4fv(uniform_location, 1, false, transform.data()); + transform_dirty_state = ProgramId((int)transform_dirty_state & ~(int)program_id); + } +} + +bool RmlGL3::Initialize(Rml::String* out_message) +{ +#if defined RMLUI_PLATFORM_EMSCRIPTEN + if (out_message) + *out_message = "Started Emscripten WebGL renderer."; +#elif !defined RMLUI_GL3_CUSTOM_LOADER + const int gl_version = gladLoaderLoadGL(); + if (gl_version == 0) + { + if (out_message) + *out_message = "Failed to initialize OpenGL context."; + return false; + } + + if (out_message) + *out_message = Rml::CreateString("Loaded OpenGL %d.%d.", GLAD_VERSION_MAJOR(gl_version), GLAD_VERSION_MINOR(gl_version)); +#endif + + return true; +} + +void RmlGL3::Shutdown() +{ +#if !defined RMLUI_PLATFORM_EMSCRIPTEN && !defined RMLUI_GL3_CUSTOM_LOADER + gladLoaderUnloadGL(); +#endif +} diff --git a/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL3.h b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL3.h new file mode 100644 index 0000000..52dbcd2 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_BackwardCompatible/RmlUi_Renderer_BackwardCompatible_GL3.h @@ -0,0 +1,117 @@ +#pragma once + +#include +#include + +namespace Gfx { +struct ShadersData; +} + +/* + The GL3 renderer from RmlUi 5, only modified to derive from the compatibility interface. + + Implemented for testing and demonstration purposes, not recommended for production use. +*/ + +class RenderInterface_BackwardCompatible_GL3 : public Rml::RenderInterfaceCompatibility { +public: + RenderInterface_BackwardCompatible_GL3(); + ~RenderInterface_BackwardCompatible_GL3(); + + // Returns true if the renderer was successfully constructed. + explicit operator bool() const { return static_cast(shaders); } + + // The viewport should be updated whenever the window size changes. + void SetViewport(int viewport_width, int viewport_height); + + // Sets up OpenGL states for taking rendering commands from RmlUi. + void BeginFrame(); + void EndFrame(); + + // Optional, can be used to clear the framebuffer. + void Clear(); + + // -- Inherited from Rml::RenderInterface -- + + void RenderGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices, Rml::TextureHandle texture, + const Rml::Vector2f& translation) override; + + Rml::CompiledGeometryHandle CompileGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices, + Rml::TextureHandle texture) override; + void RenderCompiledGeometry(Rml::CompiledGeometryHandle geometry, const Rml::Vector2f& translation) override; + void ReleaseCompiledGeometry(Rml::CompiledGeometryHandle geometry) override; + + void EnableScissorRegion(bool enable) override; + void SetScissorRegion(int x, int y, int width, int height) override; + + bool LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, const Rml::String& source) override; + bool GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions) override; + void ReleaseTexture(Rml::TextureHandle texture_handle) override; + + void SetTransform(const Rml::Matrix4f* transform) override; + + // Can be passed to RenderGeometry() to enable texture rendering without changing the bound texture. + static const Rml::TextureHandle TextureEnableWithoutBinding = Rml::TextureHandle(-1); + +private: + enum class ProgramId { None, Texture = 1, Color = 2, All = (Texture | Color) }; + void SubmitTransformUniform(ProgramId program_id, int uniform_location); + + Rml::Matrix4f transform, projection; + ProgramId transform_dirty_state = ProgramId::All; + bool transform_active = false; + + enum class ScissoringState { Disable, Scissor, Stencil }; + ScissoringState scissoring_state = ScissoringState::Disable; + + int viewport_width = 0; + int viewport_height = 0; + + Rml::UniquePtr shaders; + + struct GLStateBackup { + bool enable_cull_face; + bool enable_blend; + bool enable_stencil_test; + bool enable_scissor_test; + + int viewport[4]; + int scissor[4]; + + int stencil_clear_value; + float color_clear_value[4]; + + int blend_equation_rgb; + int blend_equation_alpha; + int blend_src_rgb; + int blend_dst_rgb; + int blend_src_alpha; + int blend_dst_alpha; + + struct Stencil { + int func; + int ref; + int value_mask; + int writemask; + int fail; + int pass_depth_fail; + int pass_depth_pass; + }; + Stencil stencil_front; + Stencil stencil_back; + }; + GLStateBackup glstate_backup = {}; +}; + +/** + Helper functions for the OpenGL 3 renderer. + */ +namespace RmlGL3 { + +// Loads OpenGL functions. Optionally, the out message describes the loaded GL version or an error message on failure. +bool Initialize(Rml::String* out_message = nullptr); + +// Unloads OpenGL functions. +void Shutdown(); + +} // namespace RmlGL3 diff --git a/vendor/rmlui_backend/RmlUi_DirectX/.clang-format b/vendor/rmlui_backend/RmlUi_DirectX/.clang-format new file mode 100644 index 0000000..47a38a9 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_DirectX/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: Never diff --git a/vendor/rmlui_backend/RmlUi_DirectX/D3D12MemAlloc.cpp b/vendor/rmlui_backend/RmlUi_DirectX/D3D12MemAlloc.cpp new file mode 100644 index 0000000..68e92ad --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_DirectX/D3D12MemAlloc.cpp @@ -0,0 +1,10247 @@ +// +// Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. +// +// 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 "D3D12MemAlloc.h" + +#include +#include +#include +#include +#include +#include +#include // for _aligned_malloc, _aligned_free +#ifndef _WIN32 + #include +#endif + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Configuration Begin +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +#ifndef _D3D12MA_CONFIGURATION + +#ifdef _WIN32 + #if !defined(WINVER) || WINVER < 0x0600 + #error Required at least WinAPI version supporting: client = Windows Vista, server = Windows Server 2008. + #endif +#endif + +#ifndef D3D12MA_SORT + #define D3D12MA_SORT(beg, end, cmp) std::sort(beg, end, cmp) +#endif + +#ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED + #include + #if D3D12MA_DXGI_1_4 + #include + #endif +#endif + +#ifndef D3D12MA_ASSERT + #include + #define D3D12MA_ASSERT(cond) assert(cond) +#endif + +// Assert that will be called very often, like inside data structures e.g. operator[]. +// Making it non-empty can make program slow. +#ifndef D3D12MA_HEAVY_ASSERT + #ifdef _DEBUG + #define D3D12MA_HEAVY_ASSERT(expr) //D3D12MA_ASSERT(expr) + #else + #define D3D12MA_HEAVY_ASSERT(expr) + #endif +#endif + +#ifndef D3D12MA_DEBUG_ALIGNMENT + /* + Minimum alignment of all allocations, in bytes. + Set to more than 1 for debugging purposes only. Must be power of two. + */ + #define D3D12MA_DEBUG_ALIGNMENT (1) +#endif + +#ifndef D3D12MA_DEBUG_MARGIN + // Minimum margin before and after every allocation, in bytes. + // Set nonzero for debugging purposes only. + #define D3D12MA_DEBUG_MARGIN (0) +#endif + +#ifndef D3D12MA_DEBUG_GLOBAL_MUTEX + /* + Set this to 1 for debugging purposes only, to enable single mutex protecting all + entry calls to the library. Can be useful for debugging multithreading issues. + */ + #define D3D12MA_DEBUG_GLOBAL_MUTEX (0) +#endif + +/* +Define this macro for debugging purposes only to force specific D3D12_RESOURCE_HEAP_TIER, +especially to test compatibility with D3D12_RESOURCE_HEAP_TIER_1 on modern GPUs. +*/ +//#define D3D12MA_FORCE_RESOURCE_HEAP_TIER D3D12_RESOURCE_HEAP_TIER_1 + +#ifndef D3D12MA_DEFAULT_BLOCK_SIZE + /// Default size of a block allocated as single ID3D12Heap. + #define D3D12MA_DEFAULT_BLOCK_SIZE (64ull * 1024 * 1024) +#endif + +#endif // _D3D12MA_CONFIGURATION +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Configuration End +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#define D3D12MA_IID_PPV_ARGS(ppType) __uuidof(**(ppType)), reinterpret_cast(ppType) + +namespace D3D12MA +{ +static constexpr UINT HEAP_TYPE_COUNT = 4; +static constexpr UINT STANDARD_HEAP_TYPE_COUNT = 3; // Only DEFAULT, UPLOAD, READBACK. +static constexpr UINT DEFAULT_POOL_MAX_COUNT = 9; +static const UINT NEW_BLOCK_SIZE_SHIFT_MAX = 3; +// Minimum size of a free suballocation to register it in the free suballocation collection. +static const UINT64 MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16; + +static const WCHAR* const HeapTypeNames[] = +{ + L"DEFAULT", + L"UPLOAD", + L"READBACK", + L"CUSTOM", +}; + +static const D3D12_HEAP_FLAGS RESOURCE_CLASS_HEAP_FLAGS = + D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES; + +#ifndef _D3D12MA_ENUM_DECLARATIONS + +// Local copy of this enum, as it is provided only by , so it may not be available. +enum DXGI_MEMORY_SEGMENT_GROUP_COPY +{ + DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY = 0, + DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY = 1, + DXGI_MEMORY_SEGMENT_GROUP_COUNT +}; + +enum class ResourceClass +{ + Unknown, Buffer, Non_RT_DS_Texture, RT_DS_Texture +}; + +enum SuballocationType +{ + SUBALLOCATION_TYPE_FREE = 0, + SUBALLOCATION_TYPE_ALLOCATION = 1, +}; + +#endif // _D3D12MA_ENUM_DECLARATIONS + + +#ifndef _D3D12MA_FUNCTIONS + +static void* DefaultAllocate(size_t Size, size_t Alignment, void* /*pPrivateData*/) +{ +#ifdef _WIN32 + return _aligned_malloc(Size, Alignment); +#else + return aligned_alloc(Alignment, Size); +#endif +} +static void DefaultFree(void* pMemory, void* /*pPrivateData*/) +{ +#ifdef _WIN32 + return _aligned_free(pMemory); +#else + return free(pMemory); +#endif +} + +static void* Malloc(const ALLOCATION_CALLBACKS& allocs, size_t size, size_t alignment) +{ + void* const result = (*allocs.pAllocate)(size, alignment, allocs.pPrivateData); + D3D12MA_ASSERT(result); + return result; +} +static void Free(const ALLOCATION_CALLBACKS& allocs, void* memory) +{ + (*allocs.pFree)(memory, allocs.pPrivateData); +} + +template +static T* Allocate(const ALLOCATION_CALLBACKS& allocs) +{ + return (T*)Malloc(allocs, sizeof(T), __alignof(T)); +} +template +static T* AllocateArray(const ALLOCATION_CALLBACKS& allocs, size_t count) +{ + return (T*)Malloc(allocs, sizeof(T) * count, __alignof(T)); +} + +#define D3D12MA_NEW(allocs, type) new(D3D12MA::Allocate(allocs))(type) +#define D3D12MA_NEW_ARRAY(allocs, type, count) new(D3D12MA::AllocateArray((allocs), (count)))(type) + +template +void D3D12MA_DELETE(const ALLOCATION_CALLBACKS& allocs, T* memory) +{ + if (memory) + { + memory->~T(); + Free(allocs, memory); + } +} +template +void D3D12MA_DELETE_ARRAY(const ALLOCATION_CALLBACKS& allocs, T* memory, size_t count) +{ + if (memory) + { + for (size_t i = count; i--; ) + { + memory[i].~T(); + } + Free(allocs, memory); + } +} + +static void SetupAllocationCallbacks(ALLOCATION_CALLBACKS& outAllocs, const ALLOCATION_CALLBACKS* allocationCallbacks) +{ + if (allocationCallbacks) + { + outAllocs = *allocationCallbacks; + D3D12MA_ASSERT(outAllocs.pAllocate != NULL && outAllocs.pFree != NULL); + } + else + { + outAllocs.pAllocate = &DefaultAllocate; + outAllocs.pFree = &DefaultFree; + outAllocs.pPrivateData = NULL; + } +} + +#define SAFE_RELEASE(ptr) do { if(ptr) { (ptr)->Release(); (ptr) = NULL; } } while(false) + +#define D3D12MA_VALIDATE(cond) do { if(!(cond)) { \ + D3D12MA_ASSERT(0 && "Validation failed: " #cond); \ + return false; \ +} } while(false) + +template +static T D3D12MA_MIN(const T& a, const T& b) { return a <= b ? a : b; } +template +static T D3D12MA_MAX(const T& a, const T& b) { return a <= b ? b : a; } + +template +static void D3D12MA_SWAP(T& a, T& b) { T tmp = a; a = b; b = tmp; } + +// Scans integer for index of first nonzero bit from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX +static UINT8 BitScanLSB(UINT64 mask) +{ +#if defined(_MSC_VER) && defined(_WIN64) + unsigned long pos; + if (_BitScanForward64(&pos, mask)) + return static_cast(pos); + return UINT8_MAX; +#elif defined __GNUC__ || defined __clang__ + return static_cast(__builtin_ffsll(mask)) - 1U; +#else + UINT8 pos = 0; + UINT64 bit = 1; + do + { + if (mask & bit) + return pos; + bit <<= 1; + } while (pos++ < 63); + return UINT8_MAX; +#endif +} +// Scans integer for index of first nonzero bit from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX +static UINT8 BitScanLSB(UINT32 mask) +{ +#ifdef _MSC_VER + unsigned long pos; + if (_BitScanForward(&pos, mask)) + return static_cast(pos); + return UINT8_MAX; +#elif defined __GNUC__ || defined __clang__ + return static_cast(__builtin_ffs(mask)) - 1U; +#else + UINT8 pos = 0; + UINT32 bit = 1; + do + { + if (mask & bit) + return pos; + bit <<= 1; + } while (pos++ < 31); + return UINT8_MAX; +#endif +} + +// Scans integer for index of first nonzero bit from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX +static UINT8 BitScanMSB(UINT64 mask) +{ +#if defined(_MSC_VER) && defined(_WIN64) + unsigned long pos; + if (_BitScanReverse64(&pos, mask)) + return static_cast(pos); +#elif defined __GNUC__ || defined __clang__ + if (mask) + return 63 - static_cast(__builtin_clzll(mask)); +#else + UINT8 pos = 63; + UINT64 bit = 1ULL << 63; + do + { + if (mask & bit) + return pos; + bit >>= 1; + } while (pos-- > 0); +#endif + return UINT8_MAX; +} +// Scans integer for index of first nonzero bit from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX +static UINT8 BitScanMSB(UINT32 mask) +{ +#ifdef _MSC_VER + unsigned long pos; + if (_BitScanReverse(&pos, mask)) + return static_cast(pos); +#elif defined __GNUC__ || defined __clang__ + if (mask) + return 31 - static_cast(__builtin_clz(mask)); +#else + UINT8 pos = 31; + UINT32 bit = 1UL << 31; + do + { + if (mask & bit) + return pos; + bit >>= 1; + } while (pos-- > 0); +#endif + return UINT8_MAX; +} + +/* +Returns true if given number is a power of two. +T must be unsigned integer number or signed integer but always nonnegative. +For 0 returns true. +*/ +template +static bool IsPow2(T x) { return (x & (x - 1)) == 0; } + +// Aligns given value up to nearest multiply of align value. For example: AlignUp(11, 8) = 16. +// Use types like UINT, uint64_t as T. +template +static T AlignUp(T val, T alignment) +{ + D3D12MA_HEAVY_ASSERT(IsPow2(alignment)); + return (val + alignment - 1) & ~(alignment - 1); +} +// Aligns given value down to nearest multiply of align value. For example: AlignUp(11, 8) = 8. +// Use types like UINT, uint64_t as T. +template +static T AlignDown(T val, T alignment) +{ + D3D12MA_HEAVY_ASSERT(IsPow2(alignment)); + return val & ~(alignment - 1); +} + +// Division with mathematical rounding to nearest number. +template +static T RoundDiv(T x, T y) { return (x + (y / (T)2)) / y; } +template +static T DivideRoundingUp(T x, T y) { return (x + y - 1) / y; } + +static WCHAR HexDigitToChar(UINT8 digit) +{ + if(digit < 10) + return L'0' + digit; + else + return L'A' + (digit - 10); +} + +/* +Performs binary search and returns iterator to first element that is greater or +equal to `key`, according to comparison `cmp`. + +Cmp should return true if first argument is less than second argument. + +Returned value is the found element, if present in the collection or place where +new element with value (key) should be inserted. +*/ +template +static IterT BinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp) +{ + size_t down = 0, up = (end - beg); + while (down < up) + { + const size_t mid = (down + up) / 2; + if (cmp(*(beg + mid), key)) + { + down = mid + 1; + } + else + { + up = mid; + } + } + return beg + down; +} + +/* +Performs binary search and returns iterator to an element that is equal to `key`, +according to comparison `cmp`. + +Cmp should return true if first argument is less than second argument. + +Returned value is the found element, if present in the collection or end if not +found. +*/ +template +static IterT BinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp) +{ + IterT it = BinaryFindFirstNotLess(beg, end, value, cmp); + if (it == end || + (!cmp(*it, value) && !cmp(value, *it))) + { + return it; + } + return end; +} + +static UINT HeapTypeToIndex(D3D12_HEAP_TYPE type) +{ + switch (type) + { + case D3D12_HEAP_TYPE_DEFAULT: return 0; + case D3D12_HEAP_TYPE_UPLOAD: return 1; + case D3D12_HEAP_TYPE_READBACK: return 2; + case D3D12_HEAP_TYPE_CUSTOM: return 3; + default: D3D12MA_ASSERT(0); return UINT_MAX; + } +} + +static D3D12_HEAP_TYPE IndexToHeapType(UINT heapTypeIndex) +{ + D3D12MA_ASSERT(heapTypeIndex < 4); + // D3D12_HEAP_TYPE_DEFAULT starts at 1. + return (D3D12_HEAP_TYPE)(heapTypeIndex + 1); +} + +static UINT64 HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags, bool denyMsaaTextures) +{ + /* + Documentation of D3D12_HEAP_DESC structure says: + + - D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT defined as 64KB. + - D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT defined as 4MB. An + application must decide whether the heap will contain multi-sample + anti-aliasing (MSAA), in which case, the application must choose [this flag]. + + https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_heap_desc + */ + + if (denyMsaaTextures) + return D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; + + const D3D12_HEAP_FLAGS denyAllTexturesFlags = + D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES; + const bool canContainAnyTextures = + (flags & denyAllTexturesFlags) != denyAllTexturesFlags; + return canContainAnyTextures ? + D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; +} + +static ResourceClass HeapFlagsToResourceClass(D3D12_HEAP_FLAGS heapFlags) +{ + const bool allowBuffers = (heapFlags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0; + const bool allowRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0; + const bool allowNonRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0; + + const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0); + if (allowedGroupCount != 1) + return ResourceClass::Unknown; + + if (allowRtDsTextures) + return ResourceClass::RT_DS_Texture; + if (allowNonRtDsTextures) + return ResourceClass::Non_RT_DS_Texture; + return ResourceClass::Buffer; +} + +static bool IsHeapTypeStandard(D3D12_HEAP_TYPE type) +{ + return type == D3D12_HEAP_TYPE_DEFAULT || + type == D3D12_HEAP_TYPE_UPLOAD || + type == D3D12_HEAP_TYPE_READBACK; +} + +static D3D12_HEAP_PROPERTIES StandardHeapTypeToHeapProperties(D3D12_HEAP_TYPE type) +{ + D3D12MA_ASSERT(IsHeapTypeStandard(type)); + D3D12_HEAP_PROPERTIES result = {}; + result.Type = type; + return result; +} + +static bool IsFormatCompressed(DXGI_FORMAT format) +{ + switch (format) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return true; + default: + return false; + } +} + +// Only some formats are supported. For others it returns 0. +static UINT GetBitsPerPixel(DXGI_FORMAT format) +{ + switch (format) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return 128; + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + return 96; + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + return 64; + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + return 64; + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + return 64; + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + return 32; + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + return 32; + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + return 32; + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + return 32; + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + return 32; + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + return 16; + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + return 16; + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + return 8; + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + return 4; + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + return 8; + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + return 8; + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + return 4; + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + return 8; + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + return 8; + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return 8; + default: + return 0; + } +} + +template +static ResourceClass ResourceDescToResourceClass(const D3D12_RESOURCE_DESC_T& resDesc) +{ + if (resDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) + return ResourceClass::Buffer; + // Else: it's surely a texture. + const bool isRenderTargetOrDepthStencil = + (resDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0; + return isRenderTargetOrDepthStencil ? ResourceClass::RT_DS_Texture : ResourceClass::Non_RT_DS_Texture; +} + +// This algorithm is overly conservative. +template +static bool CanUseSmallAlignment(const D3D12_RESOURCE_DESC_T& resourceDesc) +{ + if (resourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) + return false; + if ((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0) + return false; + if (resourceDesc.SampleDesc.Count > 1) + return false; + if (resourceDesc.DepthOrArraySize != 1) + return false; + + UINT sizeX = (UINT)resourceDesc.Width; + UINT sizeY = resourceDesc.Height; + UINT bitsPerPixel = GetBitsPerPixel(resourceDesc.Format); + if (bitsPerPixel == 0) + return false; + + if (IsFormatCompressed(resourceDesc.Format)) + { + sizeX = DivideRoundingUp(sizeX, 4u); + sizeY = DivideRoundingUp(sizeY, 4u); + bitsPerPixel *= 16; + } + + UINT tileSizeX = 0, tileSizeY = 0; + switch (bitsPerPixel) + { + case 8: tileSizeX = 64; tileSizeY = 64; break; + case 16: tileSizeX = 64; tileSizeY = 32; break; + case 32: tileSizeX = 32; tileSizeY = 32; break; + case 64: tileSizeX = 32; tileSizeY = 16; break; + case 128: tileSizeX = 16; tileSizeY = 16; break; + default: return false; + } + + const UINT tileCount = DivideRoundingUp(sizeX, tileSizeX) * DivideRoundingUp(sizeY, tileSizeY); + return tileCount <= 16; +} + +static bool ValidateAllocateMemoryParameters( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo, + Allocation** ppAllocation) +{ + return pAllocDesc && + pAllocInfo && + ppAllocation && + (pAllocInfo->Alignment == 0 || + pAllocInfo->Alignment == D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT || + pAllocInfo->Alignment == D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT) && + pAllocInfo->SizeInBytes != 0 && + pAllocInfo->SizeInBytes % (64ull * 1024) == 0; +} + +#endif // _D3D12MA_FUNCTIONS + +#ifndef _D3D12MA_STATISTICS_FUNCTIONS + +static void ClearStatistics(Statistics& outStats) +{ + outStats.BlockCount = 0; + outStats.AllocationCount = 0; + outStats.BlockBytes = 0; + outStats.AllocationBytes = 0; +} + +static void ClearDetailedStatistics(DetailedStatistics& outStats) +{ + ClearStatistics(outStats.Stats); + outStats.UnusedRangeCount = 0; + outStats.AllocationSizeMin = UINT64_MAX; + outStats.AllocationSizeMax = 0; + outStats.UnusedRangeSizeMin = UINT64_MAX; + outStats.UnusedRangeSizeMax = 0; +} + +static void AddStatistics(Statistics& inoutStats, const Statistics& src) +{ + inoutStats.BlockCount += src.BlockCount; + inoutStats.AllocationCount += src.AllocationCount; + inoutStats.BlockBytes += src.BlockBytes; + inoutStats.AllocationBytes += src.AllocationBytes; +} + +static void AddDetailedStatistics(DetailedStatistics& inoutStats, const DetailedStatistics& src) +{ + AddStatistics(inoutStats.Stats, src.Stats); + inoutStats.UnusedRangeCount += src.UnusedRangeCount; + inoutStats.AllocationSizeMin = D3D12MA_MIN(inoutStats.AllocationSizeMin, src.AllocationSizeMin); + inoutStats.AllocationSizeMax = D3D12MA_MAX(inoutStats.AllocationSizeMax, src.AllocationSizeMax); + inoutStats.UnusedRangeSizeMin = D3D12MA_MIN(inoutStats.UnusedRangeSizeMin, src.UnusedRangeSizeMin); + inoutStats.UnusedRangeSizeMax = D3D12MA_MAX(inoutStats.UnusedRangeSizeMax, src.UnusedRangeSizeMax); +} + +static void AddDetailedStatisticsAllocation(DetailedStatistics& inoutStats, UINT64 size) +{ + inoutStats.Stats.AllocationCount++; + inoutStats.Stats.AllocationBytes += size; + inoutStats.AllocationSizeMin = D3D12MA_MIN(inoutStats.AllocationSizeMin, size); + inoutStats.AllocationSizeMax = D3D12MA_MAX(inoutStats.AllocationSizeMax, size); +} + +static void AddDetailedStatisticsUnusedRange(DetailedStatistics& inoutStats, UINT64 size) +{ + inoutStats.UnusedRangeCount++; + inoutStats.UnusedRangeSizeMin = D3D12MA_MIN(inoutStats.UnusedRangeSizeMin, size); + inoutStats.UnusedRangeSizeMax = D3D12MA_MAX(inoutStats.UnusedRangeSizeMax, size); +} + +#endif // _D3D12MA_STATISTICS_FUNCTIONS + + +#ifndef _D3D12MA_MUTEX + +#ifndef D3D12MA_MUTEX + class Mutex + { + public: + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + + private: + std::mutex m_Mutex; + }; + #define D3D12MA_MUTEX Mutex +#endif + +#ifndef D3D12MA_RW_MUTEX +#ifdef _WIN32 + class RWMutex + { + public: + RWMutex() { InitializeSRWLock(&m_Lock); } + void LockRead() { AcquireSRWLockShared(&m_Lock); } + void UnlockRead() { ReleaseSRWLockShared(&m_Lock); } + void LockWrite() { AcquireSRWLockExclusive(&m_Lock); } + void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); } + + private: + SRWLOCK m_Lock; + }; +#else // #ifdef _WIN32 + class RWMutex + { + public: + RWMutex() {} + void LockRead() { m_Mutex.lock_shared(); } + void UnlockRead() { m_Mutex.unlock_shared(); } + void LockWrite() { m_Mutex.lock(); } + void UnlockWrite() { m_Mutex.unlock(); } + + private: + std::shared_timed_mutex m_Mutex; + }; +#endif // #ifdef _WIN32 + #define D3D12MA_RW_MUTEX RWMutex +#endif // #ifndef D3D12MA_RW_MUTEX + +// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope). +struct MutexLock +{ + D3D12MA_CLASS_NO_COPY(MutexLock); +public: + MutexLock(D3D12MA_MUTEX& mutex, bool useMutex = true) : + m_pMutex(useMutex ? &mutex : NULL) + { + if (m_pMutex) m_pMutex->Lock(); + } + ~MutexLock() { if (m_pMutex) m_pMutex->Unlock(); } + +private: + D3D12MA_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading. +struct MutexLockRead +{ + D3D12MA_CLASS_NO_COPY(MutexLockRead); +public: + MutexLockRead(D3D12MA_RW_MUTEX& mutex, bool useMutex) + : m_pMutex(useMutex ? &mutex : NULL) + { + if(m_pMutex) + { + m_pMutex->LockRead(); + } + } + ~MutexLockRead() { if (m_pMutex) m_pMutex->UnlockRead(); } + +private: + D3D12MA_RW_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing. +struct MutexLockWrite +{ + D3D12MA_CLASS_NO_COPY(MutexLockWrite); +public: + MutexLockWrite(D3D12MA_RW_MUTEX& mutex, bool useMutex) + : m_pMutex(useMutex ? &mutex : NULL) + { + if (m_pMutex) m_pMutex->LockWrite(); + } + ~MutexLockWrite() { if (m_pMutex) m_pMutex->UnlockWrite(); } + +private: + D3D12MA_RW_MUTEX* m_pMutex; +}; + +#if D3D12MA_DEBUG_GLOBAL_MUTEX + static D3D12MA_MUTEX g_DebugGlobalMutex; + #define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK MutexLock debugGlobalMutexLock(g_DebugGlobalMutex, true); +#else + #define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK +#endif +#endif // _D3D12MA_MUTEX + +#ifndef _D3D12MA_VECTOR +/* +Dynamically resizing continuous array. Class with interface similar to std::vector. +T must be POD because constructors and destructors are not called and memcpy is +used for these objects. +*/ +template +class Vector +{ +public: + using value_type = T; + using iterator = T*; + + // allocationCallbacks externally owned, must outlive this object. + Vector(const ALLOCATION_CALLBACKS& allocationCallbacks); + Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks); + Vector(const Vector& src); + ~Vector(); + + const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; } + bool empty() const { return m_Count == 0; } + size_t size() const { return m_Count; } + T* data() { return m_pArray; } + const T* data() const { return m_pArray; } + void clear(bool freeMemory = false) { resize(0, freeMemory); } + + iterator begin() { return m_pArray; } + iterator end() { return m_pArray + m_Count; } + iterator rend() { return begin() - 1; } + iterator rbegin() { return end() - 1; } + + const iterator cbegin() const { return m_pArray; } + const iterator cend() const { return m_pArray + m_Count; } + const iterator crbegin() const { return cend() - 1; } + const iterator crend() const { return cbegin() - 1; } + + void push_front(const T& src) { insert(0, src); } + void push_back(const T& src); + void pop_front(); + void pop_back(); + + T& front(); + T& back(); + const T& front() const; + const T& back() const; + + void reserve(size_t newCapacity, bool freeMemory = false); + void resize(size_t newCount, bool freeMemory = false); + void insert(size_t index, const T& src); + void remove(size_t index); + + template + size_t InsertSorted(const T& value, const CmpLess& cmp); + template + bool RemoveSorted(const T& value, const CmpLess& cmp); + + Vector& operator=(const Vector& rhs); + T& operator[](size_t index); + const T& operator[](size_t index) const; + +private: + const ALLOCATION_CALLBACKS& m_AllocationCallbacks; + T* m_pArray; + size_t m_Count; + size_t m_Capacity; +}; + +#ifndef _D3D12MA_VECTOR_FUNCTIONS +template +Vector::Vector(const ALLOCATION_CALLBACKS& allocationCallbacks) + : m_AllocationCallbacks(allocationCallbacks), + m_pArray(NULL), + m_Count(0), + m_Capacity(0) {} + +template +Vector::Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks) + : m_AllocationCallbacks(allocationCallbacks), + m_pArray(count ? AllocateArray(allocationCallbacks, count) : NULL), + m_Count(count), + m_Capacity(count) {} + +template +Vector::Vector(const Vector& src) + : m_AllocationCallbacks(src.m_AllocationCallbacks), + m_pArray(src.m_Count ? AllocateArray(src.m_AllocationCallbacks, src.m_Count) : NULL), + m_Count(src.m_Count), + m_Capacity(src.m_Count) +{ + if (m_Count > 0) + { + memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T)); + } +} + +template +Vector::~Vector() +{ + Free(m_AllocationCallbacks, m_pArray); +} + +template +void Vector::push_back(const T& src) +{ + const size_t newIndex = size(); + resize(newIndex + 1); + m_pArray[newIndex] = src; +} + +template +void Vector::pop_front() +{ + D3D12MA_HEAVY_ASSERT(m_Count > 0); + remove(0); +} + +template +void Vector::pop_back() +{ + D3D12MA_HEAVY_ASSERT(m_Count > 0); + resize(size() - 1); +} + +template +T& Vector::front() +{ + D3D12MA_HEAVY_ASSERT(m_Count > 0); + return m_pArray[0]; +} + +template +T& Vector::back() +{ + D3D12MA_HEAVY_ASSERT(m_Count > 0); + return m_pArray[m_Count - 1]; +} + +template +const T& Vector::front() const +{ + D3D12MA_HEAVY_ASSERT(m_Count > 0); + return m_pArray[0]; +} + +template +const T& Vector::back() const +{ + D3D12MA_HEAVY_ASSERT(m_Count > 0); + return m_pArray[m_Count - 1]; +} + +template +void Vector::reserve(size_t newCapacity, bool freeMemory) +{ + newCapacity = D3D12MA_MAX(newCapacity, m_Count); + + if ((newCapacity < m_Capacity) && !freeMemory) + { + newCapacity = m_Capacity; + } + + if (newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? AllocateArray(m_AllocationCallbacks, newCapacity) : NULL; + if (m_Count != 0) + { + memcpy(newArray, m_pArray, m_Count * sizeof(T)); + } + Free(m_AllocationCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } +} + +template +void Vector::resize(size_t newCount, bool freeMemory) +{ + size_t newCapacity = m_Capacity; + if (newCount > m_Capacity) + { + newCapacity = D3D12MA_MAX(newCount, D3D12MA_MAX(m_Capacity * 3 / 2, (size_t)8)); + } + else if (freeMemory) + { + newCapacity = newCount; + } + + if (newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? AllocateArray(m_AllocationCallbacks, newCapacity) : NULL; + const size_t elementsToCopy = D3D12MA_MIN(m_Count, newCount); + if (elementsToCopy != 0) + { + memcpy(newArray, m_pArray, elementsToCopy * sizeof(T)); + } + Free(m_AllocationCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } + + m_Count = newCount; +} + +template +void Vector::insert(size_t index, const T& src) +{ + D3D12MA_HEAVY_ASSERT(index <= m_Count); + const size_t oldCount = size(); + resize(oldCount + 1); + if (index < oldCount) + { + memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T)); + } + m_pArray[index] = src; +} + +template +void Vector::remove(size_t index) +{ + D3D12MA_HEAVY_ASSERT(index < m_Count); + const size_t oldCount = size(); + if (index < oldCount - 1) + { + memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T)); + } + resize(oldCount - 1); +} + +template template +size_t Vector::InsertSorted(const T& value, const CmpLess& cmp) +{ + const size_t indexToInsert = BinaryFindFirstNotLess( + m_pArray, + m_pArray + m_Count, + value, + cmp) - m_pArray; + insert(indexToInsert, value); + return indexToInsert; +} + +template template +bool Vector::RemoveSorted(const T& value, const CmpLess& cmp) +{ + const iterator it = BinaryFindFirstNotLess( + m_pArray, + m_pArray + m_Count, + value, + cmp); + if ((it != end()) && !cmp(*it, value) && !cmp(value, *it)) + { + size_t indexToRemove = it - begin(); + remove(indexToRemove); + return true; + } + return false; +} + +template +Vector& Vector::operator=(const Vector& rhs) +{ + if (&rhs != this) + { + resize(rhs.m_Count); + if (m_Count != 0) + { + memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T)); + } + } + return *this; +} + +template +T& Vector::operator[](size_t index) +{ + D3D12MA_HEAVY_ASSERT(index < m_Count); + return m_pArray[index]; +} + +template +const T& Vector::operator[](size_t index) const +{ + D3D12MA_HEAVY_ASSERT(index < m_Count); + return m_pArray[index]; +} +#endif // _D3D12MA_VECTOR_FUNCTIONS +#endif // _D3D12MA_VECTOR + +#ifndef _D3D12MA_STRING_BUILDER +class StringBuilder +{ +public: + StringBuilder(const ALLOCATION_CALLBACKS& allocationCallbacks) : m_Data(allocationCallbacks) {} + + size_t GetLength() const { return m_Data.size(); } + LPCWSTR GetData() const { return m_Data.data(); } + + void Add(WCHAR ch) { m_Data.push_back(ch); } + void Add(LPCWSTR str); + void AddNewLine() { Add(L'\n'); } + void AddNumber(UINT num); + void AddNumber(UINT64 num); + void AddPointer(const void* ptr); + +private: + Vector m_Data; +}; + +#ifndef _D3D12MA_STRING_BUILDER_FUNCTIONS +void StringBuilder::Add(LPCWSTR str) +{ + const size_t len = wcslen(str); + if (len > 0) + { + const size_t oldCount = m_Data.size(); + m_Data.resize(oldCount + len); + memcpy(m_Data.data() + oldCount, str, len * sizeof(WCHAR)); + } +} + +void StringBuilder::AddNumber(UINT num) +{ + WCHAR buf[11]; + buf[10] = L'\0'; + WCHAR *p = &buf[10]; + do + { + *--p = L'0' + (num % 10); + num /= 10; + } + while (num); + Add(p); +} + +void StringBuilder::AddNumber(UINT64 num) +{ + WCHAR buf[21]; + buf[20] = L'\0'; + WCHAR *p = &buf[20]; + do + { + *--p = L'0' + (num % 10); + num /= 10; + } + while (num); + Add(p); +} + +void StringBuilder::AddPointer(const void* ptr) +{ + WCHAR buf[21]; + uintptr_t num = (uintptr_t)ptr; + buf[20] = L'\0'; + WCHAR *p = &buf[20]; + do + { + *--p = HexDigitToChar((UINT8)(num & 0xF)); + num >>= 4; + } + while (num); + Add(p); +} + +#endif // _D3D12MA_STRING_BUILDER_FUNCTIONS +#endif // _D3D12MA_STRING_BUILDER + +#ifndef _D3D12MA_JSON_WRITER +/* +Allows to conveniently build a correct JSON document to be written to the +StringBuilder passed to the constructor. +*/ +class JsonWriter +{ +public: + // stringBuilder - string builder to write the document to. Must remain alive for the whole lifetime of this object. + JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder); + ~JsonWriter(); + + // Begins object by writing "{". + // Inside an object, you must call pairs of WriteString and a value, e.g.: + // j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject(); + // Will write: { "A": 1, "B": 2 } + void BeginObject(bool singleLine = false); + // Ends object by writing "}". + void EndObject(); + + // Begins array by writing "[". + // Inside an array, you can write a sequence of any values. + void BeginArray(bool singleLine = false); + // Ends array by writing "[". + void EndArray(); + + // Writes a string value inside "". + // pStr can contain any UTF-16 characters, including '"', new line etc. - they will be properly escaped. + void WriteString(LPCWSTR pStr); + + // Begins writing a string value. + // Call BeginString, ContinueString, ContinueString, ..., EndString instead of + // WriteString to conveniently build the string content incrementally, made of + // parts including numbers. + void BeginString(LPCWSTR pStr = NULL); + // Posts next part of an open string. + void ContinueString(LPCWSTR pStr); + // Posts next part of an open string. The number is converted to decimal characters. + void ContinueString(UINT num); + void ContinueString(UINT64 num); + void ContinueString_Pointer(const void* ptr); + // Posts next part of an open string. Pointer value is converted to characters + // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00 + // void ContinueString_Pointer(const void* ptr); + // Ends writing a string value by writing '"'. + void EndString(LPCWSTR pStr = NULL); + + // Writes a number value. + void WriteNumber(UINT num); + void WriteNumber(UINT64 num); + // Writes a boolean value - false or true. + void WriteBool(bool b); + // Writes a null value. + void WriteNull(); + + void AddAllocationToObject(const Allocation& alloc); + void AddDetailedStatisticsInfoObject(const DetailedStatistics& stats); + +private: + static const WCHAR* const INDENT; + + enum CollectionType + { + COLLECTION_TYPE_OBJECT, + COLLECTION_TYPE_ARRAY, + }; + struct StackItem + { + CollectionType type; + UINT valueCount; + bool singleLineMode; + }; + + StringBuilder& m_SB; + Vector m_Stack; + bool m_InsideString; + + void BeginValue(bool isString); + void WriteIndent(bool oneLess = false); +}; + +#ifndef _D3D12MA_JSON_WRITER_FUNCTIONS +const WCHAR* const JsonWriter::INDENT = L" "; + +JsonWriter::JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder) + : m_SB(stringBuilder), + m_Stack(allocationCallbacks), + m_InsideString(false) {} + +JsonWriter::~JsonWriter() +{ + D3D12MA_ASSERT(!m_InsideString); + D3D12MA_ASSERT(m_Stack.empty()); +} + +void JsonWriter::BeginObject(bool singleLine) +{ + D3D12MA_ASSERT(!m_InsideString); + + BeginValue(false); + m_SB.Add(L'{'); + + StackItem stackItem; + stackItem.type = COLLECTION_TYPE_OBJECT; + stackItem.valueCount = 0; + stackItem.singleLineMode = singleLine; + m_Stack.push_back(stackItem); +} + +void JsonWriter::EndObject() +{ + D3D12MA_ASSERT(!m_InsideString); + D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT); + D3D12MA_ASSERT(m_Stack.back().valueCount % 2 == 0); + + WriteIndent(true); + m_SB.Add(L'}'); + + m_Stack.pop_back(); +} + +void JsonWriter::BeginArray(bool singleLine) +{ + D3D12MA_ASSERT(!m_InsideString); + + BeginValue(false); + m_SB.Add(L'['); + + StackItem stackItem; + stackItem.type = COLLECTION_TYPE_ARRAY; + stackItem.valueCount = 0; + stackItem.singleLineMode = singleLine; + m_Stack.push_back(stackItem); +} + +void JsonWriter::EndArray() +{ + D3D12MA_ASSERT(!m_InsideString); + D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY); + + WriteIndent(true); + m_SB.Add(L']'); + + m_Stack.pop_back(); +} + +void JsonWriter::WriteString(LPCWSTR pStr) +{ + BeginString(pStr); + EndString(); +} + +void JsonWriter::BeginString(LPCWSTR pStr) +{ + D3D12MA_ASSERT(!m_InsideString); + + BeginValue(true); + m_InsideString = true; + m_SB.Add(L'"'); + if (pStr != NULL) + { + ContinueString(pStr); + } +} + +void JsonWriter::ContinueString(LPCWSTR pStr) +{ + D3D12MA_ASSERT(m_InsideString); + D3D12MA_ASSERT(pStr); + + for (const WCHAR *p = pStr; *p; ++p) + { + // the strings we encode are assumed to be in UTF-16LE format, the native + // windows wide character Unicode format. In this encoding Unicode code + // points U+0000 to U+D7FF and U+E000 to U+FFFF are encoded in two bytes, + // and everything else takes more than two bytes. We will reject any + // multi wchar character encodings for simplicity. + UINT val = (UINT)*p; + D3D12MA_ASSERT(((val <= 0xD7FF) || (0xE000 <= val && val <= 0xFFFF)) && + "Character not currently supported."); + switch (*p) + { + case L'"': m_SB.Add(L'\\'); m_SB.Add(L'"'); break; + case L'\\': m_SB.Add(L'\\'); m_SB.Add(L'\\'); break; + case L'/': m_SB.Add(L'\\'); m_SB.Add(L'/'); break; + case L'\b': m_SB.Add(L'\\'); m_SB.Add(L'b'); break; + case L'\f': m_SB.Add(L'\\'); m_SB.Add(L'f'); break; + case L'\n': m_SB.Add(L'\\'); m_SB.Add(L'n'); break; + case L'\r': m_SB.Add(L'\\'); m_SB.Add(L'r'); break; + case L'\t': m_SB.Add(L'\\'); m_SB.Add(L't'); break; + default: + // conservatively use encoding \uXXXX for any Unicode character + // requiring more than one byte. + if (32 <= val && val < 256) + m_SB.Add(*p); + else + { + m_SB.Add(L'\\'); + m_SB.Add(L'u'); + for (UINT i = 0; i < 4; ++i) + { + UINT hexDigit = (val & 0xF000) >> 12; + val <<= 4; + if (hexDigit < 10) + m_SB.Add(L'0' + (WCHAR)hexDigit); + else + m_SB.Add(L'A' + (WCHAR)hexDigit); + } + } + break; + } + } +} + +void JsonWriter::ContinueString(UINT num) +{ + D3D12MA_ASSERT(m_InsideString); + m_SB.AddNumber(num); +} + +void JsonWriter::ContinueString(UINT64 num) +{ + D3D12MA_ASSERT(m_InsideString); + m_SB.AddNumber(num); +} + +void JsonWriter::ContinueString_Pointer(const void* ptr) +{ + D3D12MA_ASSERT(m_InsideString); + m_SB.AddPointer(ptr); +} + +void JsonWriter::EndString(LPCWSTR pStr) +{ + D3D12MA_ASSERT(m_InsideString); + + if (pStr) + ContinueString(pStr); + m_SB.Add(L'"'); + m_InsideString = false; +} + +void JsonWriter::WriteNumber(UINT num) +{ + D3D12MA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.AddNumber(num); +} + +void JsonWriter::WriteNumber(UINT64 num) +{ + D3D12MA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.AddNumber(num); +} + +void JsonWriter::WriteBool(bool b) +{ + D3D12MA_ASSERT(!m_InsideString); + BeginValue(false); + if (b) + m_SB.Add(L"true"); + else + m_SB.Add(L"false"); +} + +void JsonWriter::WriteNull() +{ + D3D12MA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.Add(L"null"); +} + +void JsonWriter::AddAllocationToObject(const Allocation& alloc) +{ + WriteString(L"Type"); + switch (alloc.m_PackedData.GetResourceDimension()) { + case D3D12_RESOURCE_DIMENSION_UNKNOWN: + WriteString(L"UNKNOWN"); + break; + case D3D12_RESOURCE_DIMENSION_BUFFER: + WriteString(L"BUFFER"); + break; + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + WriteString(L"TEXTURE1D"); + break; + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + WriteString(L"TEXTURE2D"); + break; + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + WriteString(L"TEXTURE3D"); + break; + default: D3D12MA_ASSERT(0); break; + } + + WriteString(L"Size"); + WriteNumber(alloc.GetSize()); + WriteString(L"Usage"); + WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags()); + + void* privateData = alloc.GetPrivateData(); + if (privateData) + { + WriteString(L"CustomData"); + BeginString(); + ContinueString_Pointer(privateData); + EndString(); + } + + LPCWSTR name = alloc.GetName(); + if (name != NULL) + { + WriteString(L"Name"); + WriteString(name); + } + if (alloc.m_PackedData.GetTextureLayout()) + { + WriteString(L"Layout"); + WriteNumber((UINT)alloc.m_PackedData.GetTextureLayout()); + } +} + +void JsonWriter::AddDetailedStatisticsInfoObject(const DetailedStatistics& stats) +{ + BeginObject(); + + WriteString(L"BlockCount"); + WriteNumber(stats.Stats.BlockCount); + WriteString(L"BlockBytes"); + WriteNumber(stats.Stats.BlockBytes); + WriteString(L"AllocationCount"); + WriteNumber(stats.Stats.AllocationCount); + WriteString(L"AllocationBytes"); + WriteNumber(stats.Stats.AllocationBytes); + WriteString(L"UnusedRangeCount"); + WriteNumber(stats.UnusedRangeCount); + + if (stats.Stats.AllocationCount > 1) + { + WriteString(L"AllocationSizeMin"); + WriteNumber(stats.AllocationSizeMin); + WriteString(L"AllocationSizeMax"); + WriteNumber(stats.AllocationSizeMax); + } + if (stats.UnusedRangeCount > 1) + { + WriteString(L"UnusedRangeSizeMin"); + WriteNumber(stats.UnusedRangeSizeMin); + WriteString(L"UnusedRangeSizeMax"); + WriteNumber(stats.UnusedRangeSizeMax); + } + EndObject(); +} + +void JsonWriter::BeginValue(bool isString) +{ + if (!m_Stack.empty()) + { + StackItem& currItem = m_Stack.back(); + if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 0) + { + D3D12MA_ASSERT(isString); + } + + if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 1) + { + m_SB.Add(L':'); m_SB.Add(L' '); + } + else if (currItem.valueCount > 0) + { + m_SB.Add(L','); m_SB.Add(L' '); + WriteIndent(); + } + else + { + WriteIndent(); + } + ++currItem.valueCount; + } +} + +void JsonWriter::WriteIndent(bool oneLess) +{ + if (!m_Stack.empty() && !m_Stack.back().singleLineMode) + { + m_SB.AddNewLine(); + + size_t count = m_Stack.size(); + if (count > 0 && oneLess) + { + --count; + } + for (size_t i = 0; i < count; ++i) + { + m_SB.Add(INDENT); + } + } +} +#endif // _D3D12MA_JSON_WRITER_FUNCTIONS +#endif // _D3D12MA_JSON_WRITER + +#ifndef _D3D12MA_POOL_ALLOCATOR +/* +Allocator for objects of type T using a list of arrays (pools) to speed up +allocation. Number of elements that can be allocated is not bounded because +allocator can create multiple blocks. +T should be POD because constructor and destructor is not called in Alloc or +Free. +*/ +template +class PoolAllocator +{ + D3D12MA_CLASS_NO_COPY(PoolAllocator) +public: + // allocationCallbacks externally owned, must outlive this object. + PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity); + ~PoolAllocator() { Clear(); } + + void Clear(); + template + T* Alloc(Types... args); + void Free(T* ptr); + +private: + union Item + { + UINT NextFreeIndex; // UINT32_MAX means end of list. + alignas(T) char Value[sizeof(T)]; + }; + + struct ItemBlock + { + Item* pItems; + UINT Capacity; + UINT FirstFreeIndex; + }; + + const ALLOCATION_CALLBACKS& m_AllocationCallbacks; + const UINT m_FirstBlockCapacity; + Vector m_ItemBlocks; + + ItemBlock& CreateNewBlock(); +}; + +#ifndef _D3D12MA_POOL_ALLOCATOR_FUNCTIONS +template +PoolAllocator::PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity) + : m_AllocationCallbacks(allocationCallbacks), + m_FirstBlockCapacity(firstBlockCapacity), + m_ItemBlocks(allocationCallbacks) +{ + D3D12MA_ASSERT(m_FirstBlockCapacity > 1); +} + +template +void PoolAllocator::Clear() +{ + for(size_t i = m_ItemBlocks.size(); i--; ) + { + D3D12MA_DELETE_ARRAY(m_AllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity); + } + m_ItemBlocks.clear(true); +} + +template template +T* PoolAllocator::Alloc(Types... args) +{ + for(size_t i = m_ItemBlocks.size(); i--; ) + { + ItemBlock& block = m_ItemBlocks[i]; + // This block has some free items: Use first one. + if(block.FirstFreeIndex != UINT32_MAX) + { + Item* const pItem = &block.pItems[block.FirstFreeIndex]; + block.FirstFreeIndex = pItem->NextFreeIndex; + T* result = (T*)&pItem->Value; + new(result)T(std::forward(args)...); // Explicit constructor call. + return result; + } + } + + // No block has free item: Create new one and use it. + ItemBlock& newBlock = CreateNewBlock(); + Item* const pItem = &newBlock.pItems[0]; + newBlock.FirstFreeIndex = pItem->NextFreeIndex; + T* result = (T*)pItem->Value; + new(result)T(std::forward(args)...); // Explicit constructor call. + return result; +} + +template +void PoolAllocator::Free(T* ptr) +{ + // Search all memory blocks to find ptr. + for(size_t i = m_ItemBlocks.size(); i--; ) + { + ItemBlock& block = m_ItemBlocks[i]; + + Item* pItemPtr; + memcpy(&pItemPtr, &ptr, sizeof(pItemPtr)); + + // Check if pItemPtr is in address range of this block. + if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity)) + { + ptr->~T(); // Explicit destructor call. + const UINT index = static_cast(pItemPtr - block.pItems); + pItemPtr->NextFreeIndex = block.FirstFreeIndex; + block.FirstFreeIndex = index; + return; + } + } + D3D12MA_ASSERT(0 && "Pointer doesn't belong to this memory pool."); +} + +template +typename PoolAllocator::ItemBlock& PoolAllocator::CreateNewBlock() +{ + const UINT newBlockCapacity = m_ItemBlocks.empty() ? + m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2; + + const ItemBlock newBlock = { + D3D12MA_NEW_ARRAY(m_AllocationCallbacks, Item, newBlockCapacity), + newBlockCapacity, + 0 }; + + m_ItemBlocks.push_back(newBlock); + + // Setup singly-linked list of all free items in this block. + for(UINT i = 0; i < newBlockCapacity - 1; ++i) + { + newBlock.pItems[i].NextFreeIndex = i + 1; + } + newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX; + return m_ItemBlocks.back(); +} +#endif // _D3D12MA_POOL_ALLOCATOR_FUNCTIONS +#endif // _D3D12MA_POOL_ALLOCATOR + +#ifndef _D3D12MA_LIST +/* +Doubly linked list, with elements allocated out of PoolAllocator. +Has custom interface, as well as STL-style interface, including iterator and +const_iterator. +*/ +template +class List +{ + D3D12MA_CLASS_NO_COPY(List) +public: + struct Item + { + Item* pPrev; + Item* pNext; + T Value; + }; + + class reverse_iterator; + class const_reverse_iterator; + class iterator + { + friend class List; + friend class const_iterator; + + public: + iterator() = default; + iterator(const reverse_iterator& src) + : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + T& operator*() const; + T* operator->() const; + + iterator& operator++(); + iterator& operator--(); + iterator operator++(int); + iterator operator--(int); + + bool operator==(const iterator& rhs) const; + bool operator!=(const iterator& rhs) const; + + private: + List* m_pList = NULL; + Item* m_pItem = NULL; + + iterator(List* pList, Item* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + + class reverse_iterator + { + friend class List; + friend class const_reverse_iterator; + + public: + reverse_iterator() = default; + reverse_iterator(const iterator& src) + : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + T& operator*() const; + T* operator->() const; + + reverse_iterator& operator++(); + reverse_iterator& operator--(); + reverse_iterator operator++(int); + reverse_iterator operator--(int); + + bool operator==(const reverse_iterator& rhs) const; + bool operator!=(const reverse_iterator& rhs) const; + + private: + List* m_pList = NULL; + Item* m_pItem = NULL; + + reverse_iterator(List* pList, Item* pItem) + : m_pList(pList), m_pItem(pItem) {} + }; + + class const_iterator + { + friend class List; + + public: + const_iterator() = default; + const_iterator(const iterator& src) + : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_iterator(const reverse_iterator& src) + : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_iterator(const const_reverse_iterator& src) + : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + iterator dropConst() const; + const T& operator*() const; + const T* operator->() const; + + const_iterator& operator++(); + const_iterator& operator--(); + const_iterator operator++(int); + const_iterator operator--(int); + + bool operator==(const const_iterator& rhs) const; + bool operator!=(const const_iterator& rhs) const; + + private: + const List* m_pList = NULL; + const Item* m_pItem = NULL; + + const_iterator(const List* pList, const Item* pItem) + : m_pList(pList), m_pItem(pItem) {} + }; + + class const_reverse_iterator + { + friend class List; + + public: + const_reverse_iterator() = default; + const_reverse_iterator(const iterator& src) + : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_reverse_iterator(const reverse_iterator& src) + : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_reverse_iterator(const const_iterator& src) + : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + reverse_iterator dropConst() const; + const T& operator*() const; + const T* operator->() const; + + const_reverse_iterator& operator++(); + const_reverse_iterator& operator--(); + const_reverse_iterator operator++(int); + const_reverse_iterator operator--(int); + + bool operator==(const const_reverse_iterator& rhs) const; + bool operator!=(const const_reverse_iterator& rhs) const; + + private: + const List* m_pList = NULL; + const Item* m_pItem = NULL; + + const_reverse_iterator(const List* pList, const Item* pItem) + : m_pList(pList), m_pItem(pItem) {} + }; + + // allocationCallbacks externally owned, must outlive this object. + List(const ALLOCATION_CALLBACKS& allocationCallbacks); + // Intentionally not calling Clear, because that would be unnecessary + // computations to return all items to m_ItemAllocator as free. + ~List() = default; + + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + + Item* Front() { return m_pFront; } + const Item* Front() const { return m_pFront; } + Item* Back() { return m_pBack; } + const Item* Back() const { return m_pBack; } + + bool empty() const { return IsEmpty(); } + size_t size() const { return GetCount(); } + void push_back(const T& value) { PushBack(value); } + iterator insert(iterator it, const T& value) { return iterator(this, InsertBefore(it.m_pItem, value)); } + void clear() { Clear(); } + void erase(iterator it) { Remove(it.m_pItem); } + + iterator begin() { return iterator(this, Front()); } + iterator end() { return iterator(this, NULL); } + reverse_iterator rbegin() { return reverse_iterator(this, Back()); } + reverse_iterator rend() { return reverse_iterator(this, NULL); } + + const_iterator cbegin() const { return const_iterator(this, Front()); } + const_iterator cend() const { return const_iterator(this, NULL); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + + const_reverse_iterator crbegin() const { return const_reverse_iterator(this, Back()); } + const_reverse_iterator crend() const { return const_reverse_iterator(this, NULL); } + const_reverse_iterator rbegin() const { return crbegin(); } + const_reverse_iterator rend() const { return crend(); } + + Item* PushBack(); + Item* PushFront(); + Item* PushBack(const T& value); + Item* PushFront(const T& value); + void PopBack(); + void PopFront(); + + // Item can be null - it means PushBack. + Item* InsertBefore(Item* pItem); + // Item can be null - it means PushFront. + Item* InsertAfter(Item* pItem); + Item* InsertBefore(Item* pItem, const T& value); + Item* InsertAfter(Item* pItem, const T& value); + + void Clear(); + void Remove(Item* pItem); + +private: + const ALLOCATION_CALLBACKS& m_AllocationCallbacks; + PoolAllocator m_ItemAllocator; + Item* m_pFront; + Item* m_pBack; + size_t m_Count; +}; + +#ifndef _D3D12MA_LIST_ITERATOR_FUNCTIONS +template +T& List::iterator::operator*() const +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + return m_pItem->Value; +} + +template +T* List::iterator::operator->() const +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + return &m_pItem->Value; +} + +template +typename List::iterator& List::iterator::operator++() +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + m_pItem = m_pItem->pNext; + return *this; +} + +template +typename List::iterator& List::iterator::operator--() +{ + if (m_pItem != NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} + +template +typename List::iterator List::iterator::operator++(int) +{ + iterator result = *this; + ++* this; + return result; +} + +template +typename List::iterator List::iterator::operator--(int) +{ + iterator result = *this; + --* this; + return result; +} + +template +bool List::iterator::operator==(const iterator& rhs) const +{ + D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem == rhs.m_pItem; +} + +template +bool List::iterator::operator!=(const iterator& rhs) const +{ + D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem != rhs.m_pItem; +} +#endif // _D3D12MA_LIST_ITERATOR_FUNCTIONS + +#ifndef _D3D12MA_LIST_REVERSE_ITERATOR_FUNCTIONS +template +T& List::reverse_iterator::operator*() const +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + return m_pItem->Value; +} + +template +T* List::reverse_iterator::operator->() const +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + return &m_pItem->Value; +} + +template +typename List::reverse_iterator& List::reverse_iterator::operator++() +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + m_pItem = m_pItem->pPrev; + return *this; +} + +template +typename List::reverse_iterator& List::reverse_iterator::operator--() +{ + if (m_pItem != NULL) + { + m_pItem = m_pItem->pNext; + } + else + { + D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Front(); + } + return *this; +} + +template +typename List::reverse_iterator List::reverse_iterator::operator++(int) +{ + reverse_iterator result = *this; + ++* this; + return result; +} + +template +typename List::reverse_iterator List::reverse_iterator::operator--(int) +{ + reverse_iterator result = *this; + --* this; + return result; +} + +template +bool List::reverse_iterator::operator==(const reverse_iterator& rhs) const +{ + D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem == rhs.m_pItem; +} + +template +bool List::reverse_iterator::operator!=(const reverse_iterator& rhs) const +{ + D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem != rhs.m_pItem; +} +#endif // _D3D12MA_LIST_REVERSE_ITERATOR_FUNCTIONS + +#ifndef _D3D12MA_LIST_CONST_ITERATOR_FUNCTIONS +template +typename List::iterator List::const_iterator::dropConst() const +{ + return iterator(const_cast*>(m_pList), const_cast(m_pItem)); +} + +template +const T& List::const_iterator::operator*() const +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + return m_pItem->Value; +} + +template +const T* List::const_iterator::operator->() const +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + return &m_pItem->Value; +} + +template +typename List::const_iterator& List::const_iterator::operator++() +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + m_pItem = m_pItem->pNext; + return *this; +} + +template +typename List::const_iterator& List::const_iterator::operator--() +{ + if (m_pItem != NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} + +template +typename List::const_iterator List::const_iterator::operator++(int) +{ + const_iterator result = *this; + ++* this; + return result; +} + +template +typename List::const_iterator List::const_iterator::operator--(int) +{ + const_iterator result = *this; + --* this; + return result; +} + +template +bool List::const_iterator::operator==(const const_iterator& rhs) const +{ + D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem == rhs.m_pItem; +} + +template +bool List::const_iterator::operator!=(const const_iterator& rhs) const +{ + D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem != rhs.m_pItem; +} +#endif // _D3D12MA_LIST_CONST_ITERATOR_FUNCTIONS + +#ifndef _D3D12MA_LIST_CONST_REVERSE_ITERATOR_FUNCTIONS +template +typename List::reverse_iterator List::const_reverse_iterator::dropConst() const +{ + return reverse_iterator(const_cast*>(m_pList), const_cast(m_pItem)); +} + +template +const T& List::const_reverse_iterator::operator*() const +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + return m_pItem->Value; +} + +template +const T* List::const_reverse_iterator::operator->() const +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + return &m_pItem->Value; +} + +template +typename List::const_reverse_iterator& List::const_reverse_iterator::operator++() +{ + D3D12MA_HEAVY_ASSERT(m_pItem != NULL); + m_pItem = m_pItem->pPrev; + return *this; +} + +template +typename List::const_reverse_iterator& List::const_reverse_iterator::operator--() +{ + if (m_pItem != NULL) + { + m_pItem = m_pItem->pNext; + } + else + { + D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Front(); + } + return *this; +} + +template +typename List::const_reverse_iterator List::const_reverse_iterator::operator++(int) +{ + const_reverse_iterator result = *this; + ++* this; + return result; +} + +template +typename List::const_reverse_iterator List::const_reverse_iterator::operator--(int) +{ + const_reverse_iterator result = *this; + --* this; + return result; +} + +template +bool List::const_reverse_iterator::operator==(const const_reverse_iterator& rhs) const +{ + D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem == rhs.m_pItem; +} + +template +bool List::const_reverse_iterator::operator!=(const const_reverse_iterator& rhs) const +{ + D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem != rhs.m_pItem; +} +#endif // _D3D12MA_LIST_CONST_REVERSE_ITERATOR_FUNCTIONS + +#ifndef _D3D12MA_LIST_FUNCTIONS +template +List::List(const ALLOCATION_CALLBACKS& allocationCallbacks) + : m_AllocationCallbacks(allocationCallbacks), + m_ItemAllocator(allocationCallbacks, 128), + m_pFront(NULL), + m_pBack(NULL), + m_Count(0) {} + +template +void List::Clear() +{ + if(!IsEmpty()) + { + Item* pItem = m_pBack; + while(pItem != NULL) + { + Item* const pPrevItem = pItem->pPrev; + m_ItemAllocator.Free(pItem); + pItem = pPrevItem; + } + m_pFront = NULL; + m_pBack = NULL; + m_Count = 0; + } +} + +template +typename List::Item* List::PushBack() +{ + Item* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pNext = NULL; + if(IsEmpty()) + { + pNewItem->pPrev = NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; + } + else + { + pNewItem->pPrev = m_pBack; + m_pBack->pNext = pNewItem; + m_pBack = pNewItem; + ++m_Count; + } + return pNewItem; +} + +template +typename List::Item* List::PushFront() +{ + Item* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pPrev = NULL; + if(IsEmpty()) + { + pNewItem->pNext = NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; + } + else + { + pNewItem->pNext = m_pFront; + m_pFront->pPrev = pNewItem; + m_pFront = pNewItem; + ++m_Count; + } + return pNewItem; +} + +template +typename List::Item* List::PushBack(const T& value) +{ + Item* const pNewItem = PushBack(); + pNewItem->Value = value; + return pNewItem; +} + +template +typename List::Item* List::PushFront(const T& value) +{ + Item* const pNewItem = PushFront(); + pNewItem->Value = value; + return pNewItem; +} + +template +void List::PopBack() +{ + D3D12MA_HEAVY_ASSERT(m_Count > 0); + Item* const pBackItem = m_pBack; + Item* const pPrevItem = pBackItem->pPrev; + if(pPrevItem != NULL) + { + pPrevItem->pNext = NULL; + } + m_pBack = pPrevItem; + m_ItemAllocator.Free(pBackItem); + --m_Count; +} + +template +void List::PopFront() +{ + D3D12MA_HEAVY_ASSERT(m_Count > 0); + Item* const pFrontItem = m_pFront; + Item* const pNextItem = pFrontItem->pNext; + if(pNextItem != NULL) + { + pNextItem->pPrev = NULL; + } + m_pFront = pNextItem; + m_ItemAllocator.Free(pFrontItem); + --m_Count; +} + +template +void List::Remove(Item* pItem) +{ + D3D12MA_HEAVY_ASSERT(pItem != NULL); + D3D12MA_HEAVY_ASSERT(m_Count > 0); + + if(pItem->pPrev != NULL) + { + pItem->pPrev->pNext = pItem->pNext; + } + else + { + D3D12MA_HEAVY_ASSERT(m_pFront == pItem); + m_pFront = pItem->pNext; + } + + if(pItem->pNext != NULL) + { + pItem->pNext->pPrev = pItem->pPrev; + } + else + { + D3D12MA_HEAVY_ASSERT(m_pBack == pItem); + m_pBack = pItem->pPrev; + } + + m_ItemAllocator.Free(pItem); + --m_Count; +} + +template +typename List::Item* List::InsertBefore(Item* pItem) +{ + if(pItem != NULL) + { + Item* const prevItem = pItem->pPrev; + Item* const newItem = m_ItemAllocator.Alloc(); + newItem->pPrev = prevItem; + newItem->pNext = pItem; + pItem->pPrev = newItem; + if(prevItem != NULL) + { + prevItem->pNext = newItem; + } + else + { + D3D12MA_HEAVY_ASSERT(m_pFront == pItem); + m_pFront = newItem; + } + ++m_Count; + return newItem; + } + else + { + return PushBack(); + } +} + +template +typename List::Item* List::InsertAfter(Item* pItem) +{ + if(pItem != NULL) + { + Item* const nextItem = pItem->pNext; + Item* const newItem = m_ItemAllocator.Alloc(); + newItem->pNext = nextItem; + newItem->pPrev = pItem; + pItem->pNext = newItem; + if(nextItem != NULL) + { + nextItem->pPrev = newItem; + } + else + { + D3D12MA_HEAVY_ASSERT(m_pBack == pItem); + m_pBack = newItem; + } + ++m_Count; + return newItem; + } + else + return PushFront(); +} + +template +typename List::Item* List::InsertBefore(Item* pItem, const T& value) +{ + Item* const newItem = InsertBefore(pItem); + newItem->Value = value; + return newItem; +} + +template +typename List::Item* List::InsertAfter(Item* pItem, const T& value) +{ + Item* const newItem = InsertAfter(pItem); + newItem->Value = value; + return newItem; +} +#endif // _D3D12MA_LIST_FUNCTIONS +#endif // _D3D12MA_LIST + +#ifndef _D3D12MA_INTRUSIVE_LINKED_LIST +/* +Expected interface of ItemTypeTraits: +struct MyItemTypeTraits +{ + using ItemType = MyItem; + static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; } + static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; } + static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; } + static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; } +}; +*/ +template +class IntrusiveLinkedList +{ +public: + using ItemType = typename ItemTypeTraits::ItemType; + static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); } + static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); } + + // Movable, not copyable. + IntrusiveLinkedList() = default; + IntrusiveLinkedList(const IntrusiveLinkedList&) = delete; + IntrusiveLinkedList(IntrusiveLinkedList&& src); + IntrusiveLinkedList& operator=(const IntrusiveLinkedList&) = delete; + IntrusiveLinkedList& operator=(IntrusiveLinkedList&& src); + ~IntrusiveLinkedList() { D3D12MA_HEAVY_ASSERT(IsEmpty()); } + + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + + ItemType* Front() { return m_Front; } + ItemType* Back() { return m_Back; } + const ItemType* Front() const { return m_Front; } + const ItemType* Back() const { return m_Back; } + + void PushBack(ItemType* item); + void PushFront(ItemType* item); + ItemType* PopBack(); + ItemType* PopFront(); + + // MyItem can be null - it means PushBack. + void InsertBefore(ItemType* existingItem, ItemType* newItem); + // MyItem can be null - it means PushFront. + void InsertAfter(ItemType* existingItem, ItemType* newItem); + + void Remove(ItemType* item); + void RemoveAll(); + +private: + ItemType* m_Front = NULL; + ItemType* m_Back = NULL; + size_t m_Count = 0; +}; + +#ifndef _D3D12MA_INTRUSIVE_LINKED_LIST_FUNCTIONS +template +IntrusiveLinkedList::IntrusiveLinkedList(IntrusiveLinkedList&& src) + : m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count) +{ + src.m_Front = src.m_Back = NULL; + src.m_Count = 0; +} + +template +IntrusiveLinkedList& IntrusiveLinkedList::operator=(IntrusiveLinkedList&& src) +{ + if (&src != this) + { + D3D12MA_HEAVY_ASSERT(IsEmpty()); + m_Front = src.m_Front; + m_Back = src.m_Back; + m_Count = src.m_Count; + src.m_Front = src.m_Back = NULL; + src.m_Count = 0; + } + return *this; +} + +template +void IntrusiveLinkedList::PushBack(ItemType* item) +{ + D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL); + if (IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessPrev(item) = m_Back; + ItemTypeTraits::AccessNext(m_Back) = item; + m_Back = item; + ++m_Count; + } +} + +template +void IntrusiveLinkedList::PushFront(ItemType* item) +{ + D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL); + if (IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessNext(item) = m_Front; + ItemTypeTraits::AccessPrev(m_Front) = item; + m_Front = item; + ++m_Count; + } +} + +template +typename IntrusiveLinkedList::ItemType* IntrusiveLinkedList::PopBack() +{ + D3D12MA_HEAVY_ASSERT(m_Count > 0); + ItemType* const backItem = m_Back; + ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem); + if (prevItem != NULL) + { + ItemTypeTraits::AccessNext(prevItem) = NULL; + } + m_Back = prevItem; + --m_Count; + ItemTypeTraits::AccessPrev(backItem) = NULL; + ItemTypeTraits::AccessNext(backItem) = NULL; + return backItem; +} + +template +typename IntrusiveLinkedList::ItemType* IntrusiveLinkedList::PopFront() +{ + D3D12MA_HEAVY_ASSERT(m_Count > 0); + ItemType* const frontItem = m_Front; + ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem); + if (nextItem != NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = NULL; + } + m_Front = nextItem; + --m_Count; + ItemTypeTraits::AccessPrev(frontItem) = NULL; + ItemTypeTraits::AccessNext(frontItem) = NULL; + return frontItem; +} + +template +void IntrusiveLinkedList::InsertBefore(ItemType* existingItem, ItemType* newItem) +{ + D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL); + if (existingItem != NULL) + { + ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem); + ItemTypeTraits::AccessPrev(newItem) = prevItem; + ItemTypeTraits::AccessNext(newItem) = existingItem; + ItemTypeTraits::AccessPrev(existingItem) = newItem; + if (prevItem != NULL) + { + ItemTypeTraits::AccessNext(prevItem) = newItem; + } + else + { + D3D12MA_HEAVY_ASSERT(m_Front == existingItem); + m_Front = newItem; + } + ++m_Count; + } + else + PushBack(newItem); +} + +template +void IntrusiveLinkedList::InsertAfter(ItemType* existingItem, ItemType* newItem) +{ + D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL); + if (existingItem != NULL) + { + ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem); + ItemTypeTraits::AccessNext(newItem) = nextItem; + ItemTypeTraits::AccessPrev(newItem) = existingItem; + ItemTypeTraits::AccessNext(existingItem) = newItem; + if (nextItem != NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = newItem; + } + else + { + D3D12MA_HEAVY_ASSERT(m_Back == existingItem); + m_Back = newItem; + } + ++m_Count; + } + else + return PushFront(newItem); +} + +template +void IntrusiveLinkedList::Remove(ItemType* item) +{ + D3D12MA_HEAVY_ASSERT(item != NULL && m_Count > 0); + if (ItemTypeTraits::GetPrev(item) != NULL) + { + ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item); + } + else + { + D3D12MA_HEAVY_ASSERT(m_Front == item); + m_Front = ItemTypeTraits::GetNext(item); + } + + if (ItemTypeTraits::GetNext(item) != NULL) + { + ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item); + } + else + { + D3D12MA_HEAVY_ASSERT(m_Back == item); + m_Back = ItemTypeTraits::GetPrev(item); + } + ItemTypeTraits::AccessPrev(item) = NULL; + ItemTypeTraits::AccessNext(item) = NULL; + --m_Count; +} + +template +void IntrusiveLinkedList::RemoveAll() +{ + if (!IsEmpty()) + { + ItemType* item = m_Back; + while (item != NULL) + { + ItemType* const prevItem = ItemTypeTraits::AccessPrev(item); + ItemTypeTraits::AccessPrev(item) = NULL; + ItemTypeTraits::AccessNext(item) = NULL; + item = prevItem; + } + m_Front = NULL; + m_Back = NULL; + m_Count = 0; + } +} +#endif // _D3D12MA_INTRUSIVE_LINKED_LIST_FUNCTIONS +#endif // _D3D12MA_INTRUSIVE_LINKED_LIST + +#ifndef _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR +/* +Thread-safe wrapper over PoolAllocator free list, for allocation of Allocation objects. +*/ +class AllocationObjectAllocator +{ + D3D12MA_CLASS_NO_COPY(AllocationObjectAllocator); +public: + AllocationObjectAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks) + : m_Allocator(allocationCallbacks, 1024) {} + + template + Allocation* Allocate(Types... args); + void Free(Allocation* alloc); + +private: + D3D12MA_MUTEX m_Mutex; + PoolAllocator m_Allocator; +}; + +#ifndef _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR_FUNCTIONS +template +Allocation* AllocationObjectAllocator::Allocate(Types... args) +{ + MutexLock mutexLock(m_Mutex); + return m_Allocator.Alloc(std::forward(args)...); +} + +void AllocationObjectAllocator::Free(Allocation* alloc) +{ + MutexLock mutexLock(m_Mutex); + m_Allocator.Free(alloc); +} +#endif // _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR_FUNCTIONS +#endif // _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR + +#ifndef _D3D12MA_SUBALLOCATION +/* +Represents a region of NormalBlock that is either assigned and returned as +allocated memory block or free. +*/ +struct Suballocation +{ + UINT64 offset; + UINT64 size; + void* privateData; + SuballocationType type; +}; +using SuballocationList = List; + +// Comparator for offsets. +struct SuballocationOffsetLess +{ + bool operator()(const Suballocation& lhs, const Suballocation& rhs) const + { + return lhs.offset < rhs.offset; + } +}; + +struct SuballocationOffsetGreater +{ + bool operator()(const Suballocation& lhs, const Suballocation& rhs) const + { + return lhs.offset > rhs.offset; + } +}; + +struct SuballocationItemSizeLess +{ + bool operator()(const SuballocationList::iterator lhs, const SuballocationList::iterator rhs) const + { + return lhs->size < rhs->size; + } + bool operator()(const SuballocationList::iterator lhs, UINT64 rhsSize) const + { + return lhs->size < rhsSize; + } +}; +#endif // _D3D12MA_SUBALLOCATION + +#ifndef _D3D12MA_ALLOCATION_REQUEST +/* +Parameters of planned allocation inside a NormalBlock. +*/ +struct AllocationRequest +{ + AllocHandle allocHandle; + UINT64 size; + UINT64 algorithmData; + UINT64 sumFreeSize; // Sum size of free items that overlap with proposed allocation. + UINT64 sumItemSize; // Sum size of items to make lost that overlap with proposed allocation. + SuballocationList::iterator item; + BOOL zeroInitialized; +}; +#endif // _D3D12MA_ALLOCATION_REQUEST + +#ifndef _D3D12MA_ZERO_INITIALIZED_RANGE +/* +Keeps track of the range of bytes that are surely initialized with zeros. +Everything outside of it is considered uninitialized memory that may contain +garbage data. + +The range is left-inclusive. +*/ +class ZeroInitializedRange +{ +public: + void Reset(UINT64 size); + BOOL IsRangeZeroInitialized(UINT64 beg, UINT64 end) const; + void MarkRangeAsUsed(UINT64 usedBeg, UINT64 usedEnd); + +private: + UINT64 m_ZeroBeg = 0, m_ZeroEnd = 0; +}; + +#ifndef _D3D12MA_ZERO_INITIALIZED_RANGE_FUNCTIONS +void ZeroInitializedRange::Reset(UINT64 size) +{ + D3D12MA_ASSERT(size > 0); + m_ZeroBeg = 0; + m_ZeroEnd = size; +} + +BOOL ZeroInitializedRange::IsRangeZeroInitialized(UINT64 beg, UINT64 end) const +{ + D3D12MA_ASSERT(beg < end); + return m_ZeroBeg <= beg && end <= m_ZeroEnd; +} + +void ZeroInitializedRange::MarkRangeAsUsed(UINT64 usedBeg, UINT64 usedEnd) +{ + D3D12MA_ASSERT(usedBeg < usedEnd); + // No new bytes marked. + if (usedEnd <= m_ZeroBeg || m_ZeroEnd <= usedBeg) + { + return; + } + // All bytes marked. + if (usedBeg <= m_ZeroBeg && m_ZeroEnd <= usedEnd) + { + m_ZeroBeg = m_ZeroEnd = 0; + } + // Some bytes marked. + else + { + const UINT64 remainingZeroBefore = usedBeg > m_ZeroBeg ? usedBeg - m_ZeroBeg : 0; + const UINT64 remainingZeroAfter = usedEnd < m_ZeroEnd ? m_ZeroEnd - usedEnd : 0; + D3D12MA_ASSERT(remainingZeroBefore > 0 || remainingZeroAfter > 0); + if (remainingZeroBefore > remainingZeroAfter) + { + m_ZeroEnd = usedBeg; + } + else + { + m_ZeroBeg = usedEnd; + } + } +} +#endif // _D3D12MA_ZERO_INITIALIZED_RANGE_FUNCTIONS +#endif // _D3D12MA_ZERO_INITIALIZED_RANGE + +#ifndef _D3D12MA_BLOCK_METADATA +/* +Data structure used for bookkeeping of allocations and unused ranges of memory +in a single ID3D12Heap memory block. +*/ +class BlockMetadata +{ +public: + BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual); + virtual ~BlockMetadata() = default; + + virtual void Init(UINT64 size) { m_Size = size; } + // Validates all data structures inside this object. If not valid, returns false. + virtual bool Validate() const = 0; + UINT64 GetSize() const { return m_Size; } + bool IsVirtual() const { return m_IsVirtual; } + virtual size_t GetAllocationCount() const = 0; + virtual size_t GetFreeRegionsCount() const = 0; + virtual UINT64 GetSumFreeSize() const = 0; + virtual UINT64 GetAllocationOffset(AllocHandle allocHandle) const = 0; + // Returns true if this block is empty - contains only single free suballocation. + virtual bool IsEmpty() const = 0; + + virtual void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const = 0; + + // Tries to find a place for suballocation with given parameters inside this block. + // If succeeded, fills pAllocationRequest and returns true. + // If failed, returns false. + virtual bool CreateAllocationRequest( + UINT64 allocSize, + UINT64 allocAlignment, + bool upperAddress, + UINT32 strategy, + AllocationRequest* pAllocationRequest) = 0; + + // Makes actual allocation based on request. Request must already be checked and valid. + virtual void Alloc( + const AllocationRequest& request, + UINT64 allocSize, + void* PrivateData) = 0; + + virtual void Free(AllocHandle allocHandle) = 0; + // Frees all allocations. + // Careful! Don't call it if there are Allocation objects owned by pPrivateData of of cleared allocations! + virtual void Clear() = 0; + + virtual AllocHandle GetAllocationListBegin() const = 0; + virtual AllocHandle GetNextAllocation(AllocHandle prevAlloc) const = 0; + virtual UINT64 GetNextFreeRegionSize(AllocHandle alloc) const = 0; + virtual void* GetAllocationPrivateData(AllocHandle allocHandle) const = 0; + virtual void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) = 0; + + virtual void AddStatistics(Statistics& inoutStats) const = 0; + virtual void AddDetailedStatistics(DetailedStatistics& inoutStats) const = 0; + virtual void WriteAllocationInfoToJson(JsonWriter& json) const = 0; + +protected: + const ALLOCATION_CALLBACKS* GetAllocs() const { return m_pAllocationCallbacks; } + UINT64 GetDebugMargin() const { return IsVirtual() ? 0 : D3D12MA_DEBUG_MARGIN; } + + void PrintDetailedMap_Begin(JsonWriter& json, + UINT64 unusedBytes, + size_t allocationCount, + size_t unusedRangeCount) const; + void PrintDetailedMap_Allocation(JsonWriter& json, + UINT64 offset, UINT64 size, void* privateData) const; + void PrintDetailedMap_UnusedRange(JsonWriter& json, + UINT64 offset, UINT64 size) const; + void PrintDetailedMap_End(JsonWriter& json) const; + +private: + UINT64 m_Size; + bool m_IsVirtual; + const ALLOCATION_CALLBACKS* m_pAllocationCallbacks; + + D3D12MA_CLASS_NO_COPY(BlockMetadata); +}; + +#ifndef _D3D12MA_BLOCK_METADATA_FUNCTIONS +BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual) + : m_Size(0), + m_IsVirtual(isVirtual), + m_pAllocationCallbacks(allocationCallbacks) +{ + D3D12MA_ASSERT(allocationCallbacks); +} + +void BlockMetadata::PrintDetailedMap_Begin(JsonWriter& json, + UINT64 unusedBytes, size_t allocationCount, size_t unusedRangeCount) const +{ + json.WriteString(L"TotalBytes"); + json.WriteNumber(GetSize()); + + json.WriteString(L"UnusedBytes"); + json.WriteNumber(unusedBytes); + + json.WriteString(L"Allocations"); + json.WriteNumber(allocationCount); + + json.WriteString(L"UnusedRanges"); + json.WriteNumber(unusedRangeCount); + + json.WriteString(L"Suballocations"); + json.BeginArray(); +} + +void BlockMetadata::PrintDetailedMap_Allocation(JsonWriter& json, + UINT64 offset, UINT64 size, void* privateData) const +{ + json.BeginObject(true); + + json.WriteString(L"Offset"); + json.WriteNumber(offset); + + if (IsVirtual()) + { + json.WriteString(L"Size"); + json.WriteNumber(size); + if (privateData) + { + json.WriteString(L"CustomData"); + json.WriteNumber((uintptr_t)privateData); + } + } + else + { + const Allocation* const alloc = (const Allocation*)privateData; + D3D12MA_ASSERT(alloc); + json.AddAllocationToObject(*alloc); + } + json.EndObject(); +} + +void BlockMetadata::PrintDetailedMap_UnusedRange(JsonWriter& json, + UINT64 offset, UINT64 size) const +{ + json.BeginObject(true); + + json.WriteString(L"Offset"); + json.WriteNumber(offset); + + json.WriteString(L"Type"); + json.WriteString(L"FREE"); + + json.WriteString(L"Size"); + json.WriteNumber(size); + + json.EndObject(); +} + +void BlockMetadata::PrintDetailedMap_End(JsonWriter& json) const +{ + json.EndArray(); +} +#endif // _D3D12MA_BLOCK_METADATA_FUNCTIONS +#endif // _D3D12MA_BLOCK_METADATA + +#if 0 +#ifndef _D3D12MA_BLOCK_METADATA_GENERIC +class BlockMetadata_Generic : public BlockMetadata +{ +public: + BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual); + virtual ~BlockMetadata_Generic() = default; + + size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; } + UINT64 GetSumFreeSize() const override { return m_SumFreeSize; } + UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return (UINT64)allocHandle - 1; } + + void Init(UINT64 size) override; + bool Validate() const override; + bool IsEmpty() const override; + void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override; + + bool CreateAllocationRequest( + UINT64 allocSize, + UINT64 allocAlignment, + bool upperAddress, + AllocationRequest* pAllocationRequest) override; + + void Alloc( + const AllocationRequest& request, + UINT64 allocSize, + void* privateData) override; + + void Free(AllocHandle allocHandle) override; + void Clear() override; + + void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override; + + void AddStatistics(Statistics& inoutStats) const override; + void AddDetailedStatistics(DetailedStatistics& inoutStats) const override; + void WriteAllocationInfoToJson(JsonWriter& json) const override; + +private: + UINT m_FreeCount; + UINT64 m_SumFreeSize; + SuballocationList m_Suballocations; + // Suballocations that are free and have size greater than certain threshold. + // Sorted by size, ascending. + Vector m_FreeSuballocationsBySize; + ZeroInitializedRange m_ZeroInitializedRange; + + SuballocationList::const_iterator FindAtOffset(UINT64 offset) const; + bool ValidateFreeSuballocationList() const; + + // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem. + // If yes, fills pOffset and returns true. If no, returns false. + bool CheckAllocation( + UINT64 allocSize, + UINT64 allocAlignment, + SuballocationList::const_iterator suballocItem, + AllocHandle* pAllocHandle, + UINT64* pSumFreeSize, + UINT64* pSumItemSize, + BOOL *pZeroInitialized) const; + // Given free suballocation, it merges it with following one, which must also be free. + void MergeFreeWithNext(SuballocationList::iterator item); + // Releases given suballocation, making it free. + // Merges it with adjacent free suballocations if applicable. + // Returns iterator to new free suballocation at this place. + SuballocationList::iterator FreeSuballocation(SuballocationList::iterator suballocItem); + // Given free suballocation, it inserts it into sorted list of + // m_FreeSuballocationsBySize if it's suitable. + void RegisterFreeSuballocation(SuballocationList::iterator item); + // Given free suballocation, it removes it from sorted list of + // m_FreeSuballocationsBySize if it's suitable. + void UnregisterFreeSuballocation(SuballocationList::iterator item); + + D3D12MA_CLASS_NO_COPY(BlockMetadata_Generic) +}; + +#ifndef _D3D12MA_BLOCK_METADATA_GENERIC_FUNCTIONS +BlockMetadata_Generic::BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual) + : BlockMetadata(allocationCallbacks, isVirtual), + m_FreeCount(0), + m_SumFreeSize(0), + m_Suballocations(*allocationCallbacks), + m_FreeSuballocationsBySize(*allocationCallbacks) +{ + D3D12MA_ASSERT(allocationCallbacks); +} + +void BlockMetadata_Generic::Init(UINT64 size) +{ + BlockMetadata::Init(size); + m_ZeroInitializedRange.Reset(size); + + m_FreeCount = 1; + m_SumFreeSize = size; + + Suballocation suballoc = {}; + suballoc.offset = 0; + suballoc.size = size; + suballoc.type = SUBALLOCATION_TYPE_FREE; + suballoc.privateData = NULL; + + D3D12MA_ASSERT(size > MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER); + m_Suballocations.push_back(suballoc); + SuballocationList::iterator suballocItem = m_Suballocations.end(); + --suballocItem; + m_FreeSuballocationsBySize.push_back(suballocItem); +} + +bool BlockMetadata_Generic::Validate() const +{ + D3D12MA_VALIDATE(!m_Suballocations.empty()); + + // Expected offset of new suballocation as calculated from previous ones. + UINT64 calculatedOffset = 0; + // Expected number of free suballocations as calculated from traversing their list. + UINT calculatedFreeCount = 0; + // Expected sum size of free suballocations as calculated from traversing their list. + UINT64 calculatedSumFreeSize = 0; + // Expected number of free suballocations that should be registered in + // m_FreeSuballocationsBySize calculated from traversing their list. + size_t freeSuballocationsToRegister = 0; + // True if previous visited suballocation was free. + bool prevFree = false; + + for (const auto& subAlloc : m_Suballocations) + { + // Actual offset of this suballocation doesn't match expected one. + D3D12MA_VALIDATE(subAlloc.offset == calculatedOffset); + + const bool currFree = (subAlloc.type == SUBALLOCATION_TYPE_FREE); + // Two adjacent free suballocations are invalid. They should be merged. + D3D12MA_VALIDATE(!prevFree || !currFree); + + const Allocation* const alloc = (Allocation*)subAlloc.privateData; + if (!IsVirtual()) + { + D3D12MA_VALIDATE(currFree == (alloc == NULL)); + } + + if (currFree) + { + calculatedSumFreeSize += subAlloc.size; + ++calculatedFreeCount; + if (subAlloc.size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) + { + ++freeSuballocationsToRegister; + } + + // Margin required between allocations - every free space must be at least that large. + D3D12MA_VALIDATE(subAlloc.size >= GetDebugMargin()); + } + else + { + if (!IsVirtual()) + { + D3D12MA_VALIDATE(alloc->GetOffset() == subAlloc.offset); + D3D12MA_VALIDATE(alloc->GetSize() == subAlloc.size); + } + + // Margin required between allocations - previous allocation must be free. + D3D12MA_VALIDATE(GetDebugMargin() == 0 || prevFree); + } + + calculatedOffset += subAlloc.size; + prevFree = currFree; + } + + // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't + // match expected one. + D3D12MA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister); + + UINT64 lastSize = 0; + for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i) + { + SuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i]; + + // Only free suballocations can be registered in m_FreeSuballocationsBySize. + D3D12MA_VALIDATE(suballocItem->type == SUBALLOCATION_TYPE_FREE); + // They must be sorted by size ascending. + D3D12MA_VALIDATE(suballocItem->size >= lastSize); + + lastSize = suballocItem->size; + } + + // Check if totals match calculacted values. + D3D12MA_VALIDATE(ValidateFreeSuballocationList()); + D3D12MA_VALIDATE(calculatedOffset == GetSize()); + D3D12MA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize); + D3D12MA_VALIDATE(calculatedFreeCount == m_FreeCount); + + return true; +} + +bool BlockMetadata_Generic::IsEmpty() const +{ + return (m_Suballocations.size() == 1) && (m_FreeCount == 1); +} + +void BlockMetadata_Generic::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const +{ + Suballocation& suballoc = *FindAtOffset((UINT64)allocHandle - 1).dropConst(); + outInfo.Offset = suballoc.offset; + outInfo.Size = suballoc.size; + outInfo.pPrivateData = suballoc.privateData; +} + +bool BlockMetadata_Generic::CreateAllocationRequest( + UINT64 allocSize, + UINT64 allocAlignment, + bool upperAddress, + AllocationRequest* pAllocationRequest) +{ + D3D12MA_ASSERT(allocSize > 0); + D3D12MA_ASSERT(!upperAddress && "ALLOCATION_FLAG_UPPER_ADDRESS can be used only with linear algorithm."); + D3D12MA_ASSERT(pAllocationRequest != NULL); + D3D12MA_HEAVY_ASSERT(Validate()); + + // There is not enough total free space in this block to fullfill the request: Early return. + if (m_SumFreeSize < allocSize + GetDebugMargin()) + { + return false; + } + + // New algorithm, efficiently searching freeSuballocationsBySize. + const size_t freeSuballocCount = m_FreeSuballocationsBySize.size(); + if (freeSuballocCount > 0) + { + // Find first free suballocation with size not less than allocSize + GetDebugMargin(). + SuballocationList::iterator* const it = BinaryFindFirstNotLess( + m_FreeSuballocationsBySize.data(), + m_FreeSuballocationsBySize.data() + freeSuballocCount, + allocSize + GetDebugMargin(), + SuballocationItemSizeLess()); + size_t index = it - m_FreeSuballocationsBySize.data(); + for (; index < freeSuballocCount; ++index) + { + if (CheckAllocation( + allocSize, + allocAlignment, + m_FreeSuballocationsBySize[index], + &pAllocationRequest->allocHandle, + &pAllocationRequest->sumFreeSize, + &pAllocationRequest->sumItemSize, + &pAllocationRequest->zeroInitialized)) + { + pAllocationRequest->item = m_FreeSuballocationsBySize[index]; + return true; + } + } + } + + return false; +} + +void BlockMetadata_Generic::Alloc( + const AllocationRequest& request, + UINT64 allocSize, + void* privateData) +{ + D3D12MA_ASSERT(request.item != m_Suballocations.end()); + Suballocation& suballoc = *request.item; + // Given suballocation is a free block. + D3D12MA_ASSERT(suballoc.type == SUBALLOCATION_TYPE_FREE); + // Given offset is inside this suballocation. + UINT64 offset = (UINT64)request.allocHandle - 1; + D3D12MA_ASSERT(offset >= suballoc.offset); + const UINT64 paddingBegin = offset - suballoc.offset; + D3D12MA_ASSERT(suballoc.size >= paddingBegin + allocSize); + const UINT64 paddingEnd = suballoc.size - paddingBegin - allocSize; + + // Unregister this free suballocation from m_FreeSuballocationsBySize and update + // it to become used. + UnregisterFreeSuballocation(request.item); + + suballoc.offset = offset; + suballoc.size = allocSize; + suballoc.type = SUBALLOCATION_TYPE_ALLOCATION; + suballoc.privateData = privateData; + + // If there are any free bytes remaining at the end, insert new free suballocation after current one. + if (paddingEnd) + { + Suballocation paddingSuballoc = {}; + paddingSuballoc.offset = offset + allocSize; + paddingSuballoc.size = paddingEnd; + paddingSuballoc.type = SUBALLOCATION_TYPE_FREE; + SuballocationList::iterator next = request.item; + ++next; + const SuballocationList::iterator paddingEndItem = + m_Suballocations.insert(next, paddingSuballoc); + RegisterFreeSuballocation(paddingEndItem); + } + + // If there are any free bytes remaining at the beginning, insert new free suballocation before current one. + if (paddingBegin) + { + Suballocation paddingSuballoc = {}; + paddingSuballoc.offset = offset - paddingBegin; + paddingSuballoc.size = paddingBegin; + paddingSuballoc.type = SUBALLOCATION_TYPE_FREE; + const SuballocationList::iterator paddingBeginItem = + m_Suballocations.insert(request.item, paddingSuballoc); + RegisterFreeSuballocation(paddingBeginItem); + } + + // Update totals. + m_FreeCount = m_FreeCount - 1; + if (paddingBegin > 0) + { + ++m_FreeCount; + } + if (paddingEnd > 0) + { + ++m_FreeCount; + } + m_SumFreeSize -= allocSize; + + m_ZeroInitializedRange.MarkRangeAsUsed(offset, offset + allocSize); +} + +void BlockMetadata_Generic::Free(AllocHandle allocHandle) +{ + FreeSuballocation(FindAtOffset((UINT64)allocHandle - 1).dropConst()); +} + +void BlockMetadata_Generic::Clear() +{ + m_FreeCount = 1; + m_SumFreeSize = GetSize(); + + m_Suballocations.clear(); + Suballocation suballoc = {}; + suballoc.offset = 0; + suballoc.size = GetSize(); + suballoc.type = SUBALLOCATION_TYPE_FREE; + m_Suballocations.push_back(suballoc); + + m_FreeSuballocationsBySize.clear(); + m_FreeSuballocationsBySize.push_back(m_Suballocations.begin()); +} + +SuballocationList::const_iterator BlockMetadata_Generic::FindAtOffset(UINT64 offset) const +{ + const UINT64 last = m_Suballocations.crbegin()->offset; + if (last == offset) + return m_Suballocations.crbegin(); + const UINT64 first = m_Suballocations.cbegin()->offset; + if (first == offset) + return m_Suballocations.cbegin(); + + const size_t suballocCount = m_Suballocations.size(); + const UINT64 step = (last - first + m_Suballocations.cbegin()->size) / suballocCount; + auto findSuballocation = [&](auto begin, auto end) -> SuballocationList::const_iterator + { + for (auto suballocItem = begin; + suballocItem != end; + ++suballocItem) + { + const Suballocation& suballoc = *suballocItem; + if (suballoc.offset == offset) + return suballocItem; + } + D3D12MA_ASSERT(false && "Not found!"); + return m_Suballocations.end(); + }; + // If requested offset is closer to the end of range, search from the end + if ((offset - first) > suballocCount * step / 2) + { + return findSuballocation(m_Suballocations.crbegin(), m_Suballocations.crend()); + } + return findSuballocation(m_Suballocations.cbegin(), m_Suballocations.cend()); +} + +bool BlockMetadata_Generic::ValidateFreeSuballocationList() const +{ + UINT64 lastSize = 0; + for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i) + { + const SuballocationList::iterator it = m_FreeSuballocationsBySize[i]; + + D3D12MA_VALIDATE(it->type == SUBALLOCATION_TYPE_FREE); + D3D12MA_VALIDATE(it->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER); + D3D12MA_VALIDATE(it->size >= lastSize); + lastSize = it->size; + } + return true; +} + +bool BlockMetadata_Generic::CheckAllocation( + UINT64 allocSize, + UINT64 allocAlignment, + SuballocationList::const_iterator suballocItem, + AllocHandle* pAllocHandle, + UINT64* pSumFreeSize, + UINT64* pSumItemSize, + BOOL* pZeroInitialized) const +{ + D3D12MA_ASSERT(allocSize > 0); + D3D12MA_ASSERT(suballocItem != m_Suballocations.cend()); + D3D12MA_ASSERT(pAllocHandle != NULL && pZeroInitialized != NULL); + + *pSumFreeSize = 0; + *pSumItemSize = 0; + *pZeroInitialized = FALSE; + + const Suballocation& suballoc = *suballocItem; + D3D12MA_ASSERT(suballoc.type == SUBALLOCATION_TYPE_FREE); + + *pSumFreeSize = suballoc.size; + + // Size of this suballocation is too small for this request: Early return. + if (suballoc.size < allocSize) + { + return false; + } + + // Start from offset equal to beginning of this suballocation and debug margin of previous allocation if present. + UINT64 offset = suballoc.offset + (suballocItem == m_Suballocations.cbegin() ? 0 : GetDebugMargin()); + + // Apply alignment. + offset = AlignUp(offset, allocAlignment); + + // Calculate padding at the beginning based on current offset. + const UINT64 paddingBegin = offset - suballoc.offset; + + // Fail if requested size plus margin after is bigger than size of this suballocation. + if (paddingBegin + allocSize + GetDebugMargin() > suballoc.size) + { + return false; + } + + // All tests passed: Success. Offset is already filled. + *pZeroInitialized = m_ZeroInitializedRange.IsRangeZeroInitialized(offset, offset + allocSize); + *pAllocHandle = (AllocHandle)(offset + 1); + return true; +} + +void BlockMetadata_Generic::MergeFreeWithNext(SuballocationList::iterator item) +{ + D3D12MA_ASSERT(item != m_Suballocations.end()); + D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE); + + SuballocationList::iterator nextItem = item; + ++nextItem; + D3D12MA_ASSERT(nextItem != m_Suballocations.end()); + D3D12MA_ASSERT(nextItem->type == SUBALLOCATION_TYPE_FREE); + + item->size += nextItem->size; + --m_FreeCount; + m_Suballocations.erase(nextItem); +} + +SuballocationList::iterator BlockMetadata_Generic::FreeSuballocation(SuballocationList::iterator suballocItem) +{ + // Change this suballocation to be marked as free. + Suballocation& suballoc = *suballocItem; + suballoc.type = SUBALLOCATION_TYPE_FREE; + suballoc.privateData = NULL; + + // Update totals. + ++m_FreeCount; + m_SumFreeSize += suballoc.size; + + // Merge with previous and/or next suballocation if it's also free. + bool mergeWithNext = false; + bool mergeWithPrev = false; + + SuballocationList::iterator nextItem = suballocItem; + ++nextItem; + if ((nextItem != m_Suballocations.end()) && (nextItem->type == SUBALLOCATION_TYPE_FREE)) + { + mergeWithNext = true; + } + + SuballocationList::iterator prevItem = suballocItem; + if (suballocItem != m_Suballocations.begin()) + { + --prevItem; + if (prevItem->type == SUBALLOCATION_TYPE_FREE) + { + mergeWithPrev = true; + } + } + + if (mergeWithNext) + { + UnregisterFreeSuballocation(nextItem); + MergeFreeWithNext(suballocItem); + } + + if (mergeWithPrev) + { + UnregisterFreeSuballocation(prevItem); + MergeFreeWithNext(prevItem); + RegisterFreeSuballocation(prevItem); + return prevItem; + } + else + { + RegisterFreeSuballocation(suballocItem); + return suballocItem; + } +} + +void BlockMetadata_Generic::RegisterFreeSuballocation(SuballocationList::iterator item) +{ + D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE); + D3D12MA_ASSERT(item->size > 0); + + // You may want to enable this validation at the beginning or at the end of + // this function, depending on what do you want to check. + D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + + if (item->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) + { + if (m_FreeSuballocationsBySize.empty()) + { + m_FreeSuballocationsBySize.push_back(item); + } + else + { + m_FreeSuballocationsBySize.InsertSorted(item, SuballocationItemSizeLess()); + } + } + + //D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList()); +} + +void BlockMetadata_Generic::UnregisterFreeSuballocation(SuballocationList::iterator item) +{ + D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE); + D3D12MA_ASSERT(item->size > 0); + + // You may want to enable this validation at the beginning or at the end of + // this function, depending on what do you want to check. + D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + + if (item->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) + { + SuballocationList::iterator* const it = BinaryFindFirstNotLess( + m_FreeSuballocationsBySize.data(), + m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(), + item, + SuballocationItemSizeLess()); + for (size_t index = it - m_FreeSuballocationsBySize.data(); + index < m_FreeSuballocationsBySize.size(); + ++index) + { + if (m_FreeSuballocationsBySize[index] == item) + { + m_FreeSuballocationsBySize.remove(index); + return; + } + D3D12MA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found."); + } + D3D12MA_ASSERT(0 && "Not found."); + } + + //D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList()); +} + +void BlockMetadata_Generic::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) +{ + Suballocation& suballoc = *FindAtOffset((UINT64)allocHandle - 1).dropConst(); + suballoc.privateData = privateData; +} + +void BlockMetadata_Generic::AddStatistics(Statistics& inoutStats) const +{ + inoutStats.BlockCount++; + inoutStats.AllocationCount += (UINT)m_Suballocations.size() - m_FreeCount; + inoutStats.BlockBytes += GetSize(); + inoutStats.AllocationBytes += GetSize() - m_SumFreeSize; +} + +void BlockMetadata_Generic::AddDetailedStatistics(DetailedStatistics& inoutStats) const +{ + inoutStats.Stats.BlockCount++; + inoutStats.Stats.BlockBytes += GetSize(); + + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type == SUBALLOCATION_TYPE_FREE) + AddDetailedStatisticsUnusedRange(inoutStats, suballoc.size); + else + AddDetailedStatisticsAllocation(inoutStats, suballoc.size); + } +} + +void BlockMetadata_Generic::WriteAllocationInfoToJson(JsonWriter& json) const +{ + PrintDetailedMap_Begin(json, GetSumFreeSize(), GetAllocationCount(), m_FreeCount); + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type == SUBALLOCATION_TYPE_FREE) + PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size); + else + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData); + } + PrintDetailedMap_End(json); +} +#endif // _D3D12MA_BLOCK_METADATA_GENERIC_FUNCTIONS +#endif // _D3D12MA_BLOCK_METADATA_GENERIC +#endif // #if 0 + +#ifndef _D3D12MA_BLOCK_METADATA_LINEAR +class BlockMetadata_Linear : public BlockMetadata +{ +public: + BlockMetadata_Linear(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual); + virtual ~BlockMetadata_Linear() = default; + + UINT64 GetSumFreeSize() const override { return m_SumFreeSize; } + bool IsEmpty() const override { return GetAllocationCount() == 0; } + UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return (UINT64)allocHandle - 1; }; + + void Init(UINT64 size) override; + bool Validate() const override; + size_t GetAllocationCount() const override; + size_t GetFreeRegionsCount() const override; + void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override; + + bool CreateAllocationRequest( + UINT64 allocSize, + UINT64 allocAlignment, + bool upperAddress, + UINT32 strategy, + AllocationRequest* pAllocationRequest) override; + + void Alloc( + const AllocationRequest& request, + UINT64 allocSize, + void* privateData) override; + + void Free(AllocHandle allocHandle) override; + void Clear() override; + + AllocHandle GetAllocationListBegin() const override; + AllocHandle GetNextAllocation(AllocHandle prevAlloc) const override; + UINT64 GetNextFreeRegionSize(AllocHandle alloc) const override; + void* GetAllocationPrivateData(AllocHandle allocHandle) const override; + void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override; + + void AddStatistics(Statistics& inoutStats) const override; + void AddDetailedStatistics(DetailedStatistics& inoutStats) const override; + void WriteAllocationInfoToJson(JsonWriter& json) const override; + +private: + /* + There are two suballocation vectors, used in ping-pong way. + The one with index m_1stVectorIndex is called 1st. + The one with index (m_1stVectorIndex ^ 1) is called 2nd. + 2nd can be non-empty only when 1st is not empty. + When 2nd is not empty, m_2ndVectorMode indicates its mode of operation. + */ + typedef Vector SuballocationVectorType; + + enum ALLOC_REQUEST_TYPE + { + ALLOC_REQUEST_UPPER_ADDRESS, + ALLOC_REQUEST_END_OF_1ST, + ALLOC_REQUEST_END_OF_2ND, + }; + + enum SECOND_VECTOR_MODE + { + SECOND_VECTOR_EMPTY, + /* + Suballocations in 2nd vector are created later than the ones in 1st, but they + all have smaller offset. + */ + SECOND_VECTOR_RING_BUFFER, + /* + Suballocations in 2nd vector are upper side of double stack. + They all have offsets higher than those in 1st vector. + Top of this stack means smaller offsets, but higher indices in this vector. + */ + SECOND_VECTOR_DOUBLE_STACK, + }; + + UINT64 m_SumFreeSize; + SuballocationVectorType m_Suballocations0, m_Suballocations1; + UINT32 m_1stVectorIndex; + SECOND_VECTOR_MODE m_2ndVectorMode; + // Number of items in 1st vector with hAllocation = null at the beginning. + size_t m_1stNullItemsBeginCount; + // Number of other items in 1st vector with hAllocation = null somewhere in the middle. + size_t m_1stNullItemsMiddleCount; + // Number of items in 2nd vector with hAllocation = null. + size_t m_2ndNullItemsCount; + + SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + + Suballocation& FindSuballocation(UINT64 offset) const; + bool ShouldCompact1st() const; + void CleanupAfterFree(); + + bool CreateAllocationRequest_LowerAddress( + UINT64 allocSize, + UINT64 allocAlignment, + AllocationRequest* pAllocationRequest); + bool CreateAllocationRequest_UpperAddress( + UINT64 allocSize, + UINT64 allocAlignment, + AllocationRequest* pAllocationRequest); + + D3D12MA_CLASS_NO_COPY(BlockMetadata_Linear) +}; + +#ifndef _D3D12MA_BLOCK_METADATA_LINEAR_FUNCTIONS +BlockMetadata_Linear::BlockMetadata_Linear(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual) + : BlockMetadata(allocationCallbacks, isVirtual), + m_SumFreeSize(0), + m_Suballocations0(*allocationCallbacks), + m_Suballocations1(*allocationCallbacks), + m_1stVectorIndex(0), + m_2ndVectorMode(SECOND_VECTOR_EMPTY), + m_1stNullItemsBeginCount(0), + m_1stNullItemsMiddleCount(0), + m_2ndNullItemsCount(0) +{ + D3D12MA_ASSERT(allocationCallbacks); +} + +void BlockMetadata_Linear::Init(UINT64 size) +{ + BlockMetadata::Init(size); + m_SumFreeSize = size; +} + +bool BlockMetadata_Linear::Validate() const +{ + D3D12MA_VALIDATE(GetSumFreeSize() <= GetSize()); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + D3D12MA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY)); + D3D12MA_VALIDATE(!suballocations1st.empty() || + suballocations2nd.empty() || + m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER); + + if (!suballocations1st.empty()) + { + // Null item at the beginning should be accounted into m_1stNullItemsBeginCount. + D3D12MA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != SUBALLOCATION_TYPE_FREE); + // Null item at the end should be just pop_back(). + D3D12MA_VALIDATE(suballocations1st.back().type != SUBALLOCATION_TYPE_FREE); + } + if (!suballocations2nd.empty()) + { + // Null item at the end should be just pop_back(). + D3D12MA_VALIDATE(suballocations2nd.back().type != SUBALLOCATION_TYPE_FREE); + } + + D3D12MA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size()); + D3D12MA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size()); + + UINT64 sumUsedSize = 0; + const size_t suballoc1stCount = suballocations1st.size(); + UINT64 offset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for (size_t i = 0; i < suballoc2ndCount; ++i) + { + const Suballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE); + + const Allocation* alloc = (Allocation*)suballoc.privateData; + if (!IsVirtual()) + { + D3D12MA_VALIDATE(currFree == (alloc == NULL)); + } + D3D12MA_VALIDATE(suballoc.offset >= offset); + + if (!currFree) + { + if (!IsVirtual()) + { + D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset); + D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + GetDebugMargin(); + } + + D3D12MA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i) + { + const Suballocation& suballoc = suballocations1st[i]; + D3D12MA_VALIDATE(suballoc.type == SUBALLOCATION_TYPE_FREE && + suballoc.privateData == NULL); + } + + size_t nullItem1stCount = m_1stNullItemsBeginCount; + + for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i) + { + const Suballocation& suballoc = suballocations1st[i]; + const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE); + + const Allocation* alloc = (Allocation*)suballoc.privateData; + if (!IsVirtual()) + { + D3D12MA_VALIDATE(currFree == (alloc == NULL)); + } + D3D12MA_VALIDATE(suballoc.offset >= offset); + D3D12MA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree); + + if (!currFree) + { + if (!IsVirtual()) + { + D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset); + D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem1stCount; + } + + offset = suballoc.offset + suballoc.size + GetDebugMargin(); + } + D3D12MA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount); + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for (size_t i = suballoc2ndCount; i--; ) + { + const Suballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE); + + const Allocation* alloc = (Allocation*)suballoc.privateData; + if (!IsVirtual()) + { + D3D12MA_VALIDATE(currFree == (alloc == NULL)); + } + D3D12MA_VALIDATE(suballoc.offset >= offset); + + if (!currFree) + { + if (!IsVirtual()) + { + D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset); + D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + GetDebugMargin(); + } + + D3D12MA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + D3D12MA_VALIDATE(offset <= GetSize()); + D3D12MA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize); + + return true; +} + +size_t BlockMetadata_Linear::GetAllocationCount() const +{ + return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount + + AccessSuballocations2nd().size() - m_2ndNullItemsCount; +} + +size_t BlockMetadata_Linear::GetFreeRegionsCount() const +{ + // Function only used for defragmentation, which is disabled for this algorithm + D3D12MA_ASSERT(0); + return SIZE_MAX; +} + +void BlockMetadata_Linear::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const +{ + const Suballocation& suballoc = FindSuballocation((UINT64)allocHandle - 1); + outInfo.Offset = suballoc.offset; + outInfo.Size = suballoc.size; + outInfo.pPrivateData = suballoc.privateData; +} + +bool BlockMetadata_Linear::CreateAllocationRequest( + UINT64 allocSize, + UINT64 allocAlignment, + bool upperAddress, + UINT32 strategy, + AllocationRequest* pAllocationRequest) +{ + D3D12MA_ASSERT(allocSize > 0 && "Cannot allocate empty block!"); + D3D12MA_ASSERT(pAllocationRequest != NULL); + D3D12MA_HEAVY_ASSERT(Validate()); + pAllocationRequest->size = allocSize; + return upperAddress ? + CreateAllocationRequest_UpperAddress( + allocSize, allocAlignment, pAllocationRequest) : + CreateAllocationRequest_LowerAddress( + allocSize, allocAlignment, pAllocationRequest); +} + +void BlockMetadata_Linear::Alloc( + const AllocationRequest& request, + UINT64 allocSize, + void* privateData) +{ + UINT64 offset = (UINT64)request.allocHandle - 1; + const Suballocation newSuballoc = { offset, request.size, privateData, SUBALLOCATION_TYPE_ALLOCATION }; + + switch (request.algorithmData) + { + case ALLOC_REQUEST_UPPER_ADDRESS: + { + D3D12MA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER && + "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer."); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + suballocations2nd.push_back(newSuballoc); + m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK; + break; + } + case ALLOC_REQUEST_END_OF_1ST: + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + + D3D12MA_ASSERT(suballocations1st.empty() || + offset >= suballocations1st.back().offset + suballocations1st.back().size); + // Check if it fits before the end of the block. + D3D12MA_ASSERT(offset + request.size <= GetSize()); + + suballocations1st.push_back(newSuballoc); + break; + } + case ALLOC_REQUEST_END_OF_2ND: + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector. + D3D12MA_ASSERT(!suballocations1st.empty() && + offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + switch (m_2ndVectorMode) + { + case SECOND_VECTOR_EMPTY: + // First allocation from second part ring buffer. + D3D12MA_ASSERT(suballocations2nd.empty()); + m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER; + break; + case SECOND_VECTOR_RING_BUFFER: + // 2-part ring buffer is already started. + D3D12MA_ASSERT(!suballocations2nd.empty()); + break; + case SECOND_VECTOR_DOUBLE_STACK: + D3D12MA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack."); + break; + default: + D3D12MA_ASSERT(0); + } + + suballocations2nd.push_back(newSuballoc); + break; + } + default: + D3D12MA_ASSERT(0 && "CRITICAL INTERNAL ERROR."); + } + m_SumFreeSize -= newSuballoc.size; +} + +void BlockMetadata_Linear::Free(AllocHandle allocHandle) +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + UINT64 offset = (UINT64)allocHandle - 1; + + if (!suballocations1st.empty()) + { + // First allocation: Mark it as next empty at the beginning. + Suballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount]; + if (firstSuballoc.offset == offset) + { + firstSuballoc.type = SUBALLOCATION_TYPE_FREE; + firstSuballoc.privateData = NULL; + m_SumFreeSize += firstSuballoc.size; + ++m_1stNullItemsBeginCount; + CleanupAfterFree(); + return; + } + } + + // Last allocation in 2-part ring buffer or top of upper stack (same logic). + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER || + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + Suballocation& lastSuballoc = suballocations2nd.back(); + if (lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations2nd.pop_back(); + CleanupAfterFree(); + return; + } + } + // Last allocation in 1st vector. + else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY) + { + Suballocation& lastSuballoc = suballocations1st.back(); + if (lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations1st.pop_back(); + CleanupAfterFree(); + return; + } + } + + Suballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + + // Item from the middle of 1st vector. + { + const SuballocationVectorType::iterator it = BinaryFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), + refSuballoc, + SuballocationOffsetLess()); + if (it != suballocations1st.end()) + { + it->type = SUBALLOCATION_TYPE_FREE; + it->privateData = NULL; + ++m_1stNullItemsMiddleCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Item from the middle of 2nd vector. + const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetLess()) : + BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetGreater()); + if (it != suballocations2nd.end()) + { + it->type = SUBALLOCATION_TYPE_FREE; + it->privateData = NULL; + ++m_2ndNullItemsCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + D3D12MA_ASSERT(0 && "Allocation to free not found in linear allocator!"); +} + +void BlockMetadata_Linear::Clear() +{ + m_SumFreeSize = GetSize(); + m_Suballocations0.clear(); + m_Suballocations1.clear(); + // Leaving m_1stVectorIndex unchanged - it doesn't matter. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; +} + +AllocHandle BlockMetadata_Linear::GetAllocationListBegin() const +{ + // Function only used for defragmentation, which is disabled for this algorithm + D3D12MA_ASSERT(0); + return (AllocHandle)0; +} + +AllocHandle BlockMetadata_Linear::GetNextAllocation(AllocHandle prevAlloc) const +{ + // Function only used for defragmentation, which is disabled for this algorithm + D3D12MA_ASSERT(0); + return (AllocHandle)0; +} + +UINT64 BlockMetadata_Linear::GetNextFreeRegionSize(AllocHandle alloc) const +{ + // Function only used for defragmentation, which is disabled for this algorithm + D3D12MA_ASSERT(0); + return 0; +} + +void* BlockMetadata_Linear::GetAllocationPrivateData(AllocHandle allocHandle) const +{ + return FindSuballocation((UINT64)allocHandle - 1).privateData; +} + +void BlockMetadata_Linear::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) +{ + Suballocation& suballoc = FindSuballocation((UINT64)allocHandle - 1); + suballoc.privateData = privateData; +} + +void BlockMetadata_Linear::AddStatistics(Statistics& inoutStats) const +{ + inoutStats.BlockCount++; + inoutStats.AllocationCount += (UINT)GetAllocationCount(); + inoutStats.BlockBytes += GetSize(); + inoutStats.AllocationBytes += GetSize() - m_SumFreeSize; +} + +void BlockMetadata_Linear::AddDetailedStatistics(DetailedStatistics& inoutStats) const +{ + inoutStats.Stats.BlockCount++; + inoutStats.Stats.BlockBytes += GetSize(); + + const UINT64 size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + UINT64 lastOffset = 0; + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].privateData == NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const UINT64 unusedRangeSize = suballoc.offset - lastOffset; + AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + AddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + if (lastOffset < freeSpace2ndTo1stEnd) + { + const UINT64 unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const UINT64 freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].privateData == NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const UINT64 unusedRangeSize = suballoc.offset - lastOffset; + AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + AddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + if (lastOffset < freeSpace1stTo2ndEnd) + { + const UINT64 unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].privateData == NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const UINT64 unusedRangeSize = suballoc.offset - lastOffset; + AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + AddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to size. + if (lastOffset < size) + { + const UINT64 unusedRangeSize = size - lastOffset; + AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } +} + +void BlockMetadata_Linear::WriteAllocationInfoToJson(JsonWriter& json) const +{ + const UINT64 size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + // FIRST PASS + + size_t unusedRangeCount = 0; + UINT64 usedBytes = 0; + + UINT64 lastOffset = 0; + + size_t alloc2ndCount = 0; + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].privateData == NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + size_t alloc1stCount = 0; + const UINT64 freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].privateData == NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc1stCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].privateData == NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = size; + } + } + } + + const UINT64 unusedBytes = size - usedBytes; + PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount); + + // SECOND PASS + lastOffset = 0; + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].privateData == NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const UINT64 unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + const UINT64 unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + nextAlloc1stIndex = m_1stNullItemsBeginCount; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].privateData == NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const UINT64 unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace1stTo2ndEnd) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + const UINT64 unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].privateData == NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const UINT64 unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + const UINT64 unusedRangeSize = size - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } + + PrintDetailedMap_End(json); +} + +Suballocation& BlockMetadata_Linear::FindSuballocation(UINT64 offset) const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + Suballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + + // Item from the 1st vector. + { + const SuballocationVectorType::iterator it = BinaryFindSorted( + suballocations1st.cbegin() + m_1stNullItemsBeginCount, + suballocations1st.cend(), + refSuballoc, + SuballocationOffsetLess()); + if (it != suballocations1st.cend()) + { + return *it; + } + } + + if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Rest of members stays uninitialized intentionally for better performance. + const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + BinaryFindSorted(suballocations2nd.cbegin(), suballocations2nd.cend(), refSuballoc, SuballocationOffsetLess()) : + BinaryFindSorted(suballocations2nd.cbegin(), suballocations2nd.cend(), refSuballoc, SuballocationOffsetGreater()); + if (it != suballocations2nd.cend()) + { + return *it; + } + } + + D3D12MA_ASSERT(0 && "Allocation not found in linear allocator!"); + return *suballocations1st.crbegin(); // Should never occur. +} + +bool BlockMetadata_Linear::ShouldCompact1st() const +{ + const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + const size_t suballocCount = AccessSuballocations1st().size(); + return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3; +} + +void BlockMetadata_Linear::CleanupAfterFree() +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (IsEmpty()) + { + suballocations1st.clear(); + suballocations2nd.clear(); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + else + { + const size_t suballoc1stCount = suballocations1st.size(); + const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + D3D12MA_ASSERT(nullItem1stCount <= suballoc1stCount); + + // Find more null items at the beginning of 1st vector. + while (m_1stNullItemsBeginCount < suballoc1stCount && + suballocations1st[m_1stNullItemsBeginCount].type == SUBALLOCATION_TYPE_FREE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + + // Find more null items at the end of 1st vector. + while (m_1stNullItemsMiddleCount > 0 && + suballocations1st.back().type == SUBALLOCATION_TYPE_FREE) + { + --m_1stNullItemsMiddleCount; + suballocations1st.pop_back(); + } + + // Find more null items at the end of 2nd vector. + while (m_2ndNullItemsCount > 0 && + suballocations2nd.back().type == SUBALLOCATION_TYPE_FREE) + { + --m_2ndNullItemsCount; + suballocations2nd.pop_back(); + } + + // Find more null items at the beginning of 2nd vector. + while (m_2ndNullItemsCount > 0 && + suballocations2nd[0].type == SUBALLOCATION_TYPE_FREE) + { + --m_2ndNullItemsCount; + suballocations2nd.remove(0); + } + + if (ShouldCompact1st()) + { + const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount; + size_t srcIndex = m_1stNullItemsBeginCount; + for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex) + { + while (suballocations1st[srcIndex].type == SUBALLOCATION_TYPE_FREE) + { + ++srcIndex; + } + if (dstIndex != srcIndex) + { + suballocations1st[dstIndex] = suballocations1st[srcIndex]; + } + ++srcIndex; + } + suballocations1st.resize(nonNullItemCount); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + } + + // 2nd vector became empty. + if (suballocations2nd.empty()) + { + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + + // 1st vector became empty. + if (suballocations1st.size() - m_1stNullItemsBeginCount == 0) + { + suballocations1st.clear(); + m_1stNullItemsBeginCount = 0; + + if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + // Swap 1st with 2nd. Now 2nd is empty. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsMiddleCount = m_2ndNullItemsCount; + while (m_1stNullItemsBeginCount < suballocations2nd.size() && + suballocations2nd[m_1stNullItemsBeginCount].type == SUBALLOCATION_TYPE_FREE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + m_2ndNullItemsCount = 0; + m_1stVectorIndex ^= 1; + } + } + } + + D3D12MA_HEAVY_ASSERT(Validate()); +} + +bool BlockMetadata_Linear::CreateAllocationRequest_LowerAddress( + UINT64 allocSize, + UINT64 allocAlignment, + AllocationRequest* pAllocationRequest) +{ + const UINT64 blockSize = GetSize(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + // Try to allocate at the end of 1st vector. + + UINT64 resultBaseOffset = 0; + if (!suballocations1st.empty()) + { + const Suballocation& lastSuballoc = suballocations1st.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + GetDebugMargin(); + } + + // Start from offset equal to beginning of free space. + UINT64 resultOffset = resultBaseOffset; + // Apply alignment. + resultOffset = AlignUp(resultOffset, allocAlignment); + + const UINT64 freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? + suballocations2nd.back().offset : blockSize; + + // There is enough free space at the end after alignment. + if (resultOffset + allocSize + GetDebugMargin() <= freeSpaceEnd) + { + // All tests passed: Success. + pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1); + // pAllocationRequest->item, customData unused. + pAllocationRequest->algorithmData = ALLOC_REQUEST_END_OF_1ST; + return true; + } + } + + // Wrap-around to end of 2nd vector. Try to allocate there, watching for the + // beginning of 1st vector as the end of free space. + if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + D3D12MA_ASSERT(!suballocations1st.empty()); + + UINT64 resultBaseOffset = 0; + if (!suballocations2nd.empty()) + { + const Suballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + GetDebugMargin(); + } + + // Start from offset equal to beginning of free space. + UINT64 resultOffset = resultBaseOffset; + + // Apply alignment. + resultOffset = AlignUp(resultOffset, allocAlignment); + + size_t index1st = m_1stNullItemsBeginCount; + // There is enough free space at the end after alignment. + if ((index1st == suballocations1st.size() && resultOffset + allocSize + GetDebugMargin() <= blockSize) || + (index1st < suballocations1st.size() && resultOffset + allocSize + GetDebugMargin() <= suballocations1st[index1st].offset)) + { + // All tests passed: Success. + pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1); + pAllocationRequest->algorithmData = ALLOC_REQUEST_END_OF_2ND; + // pAllocationRequest->item, customData unused. + return true; + } + } + return false; +} + +bool BlockMetadata_Linear::CreateAllocationRequest_UpperAddress( + UINT64 allocSize, + UINT64 allocAlignment, + AllocationRequest* pAllocationRequest) +{ + const UINT64 blockSize = GetSize(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + D3D12MA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer."); + return false; + } + + // Try to allocate before 2nd.back(), or end of block if 2nd.empty(). + if (allocSize > blockSize) + { + return false; + } + UINT64 resultBaseOffset = blockSize - allocSize; + if (!suballocations2nd.empty()) + { + const Suballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset - allocSize; + if (allocSize > lastSuballoc.offset) + { + return false; + } + } + + // Start from offset equal to end of free space. + UINT64 resultOffset = resultBaseOffset; + // Apply debugMargin at the end. + if (GetDebugMargin() > 0) + { + if (resultOffset < GetDebugMargin()) + { + return false; + } + resultOffset -= GetDebugMargin(); + } + + // Apply alignment. + resultOffset = AlignDown(resultOffset, allocAlignment); + // There is enough free space. + const UINT64 endOf1st = !suballocations1st.empty() ? + suballocations1st.back().offset + suballocations1st.back().size : 0; + + if (endOf1st + GetDebugMargin() <= resultOffset) + { + // All tests passed: Success. + pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1); + // pAllocationRequest->item unused. + pAllocationRequest->algorithmData = ALLOC_REQUEST_UPPER_ADDRESS; + return true; + } + return false; +} +#endif // _D3D12MA_BLOCK_METADATA_LINEAR_FUNCTIONS +#endif // _D3D12MA_BLOCK_METADATA_LINEAR + +#ifndef _D3D12MA_BLOCK_METADATA_TLSF +class BlockMetadata_TLSF : public BlockMetadata +{ +public: + BlockMetadata_TLSF(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual); + virtual ~BlockMetadata_TLSF(); + + size_t GetAllocationCount() const override { return m_AllocCount; } + size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; } + UINT64 GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; } + bool IsEmpty() const override { return m_NullBlock->offset == 0; } + UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; }; + + void Init(UINT64 size) override; + bool Validate() const override; + void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override; + + bool CreateAllocationRequest( + UINT64 allocSize, + UINT64 allocAlignment, + bool upperAddress, + UINT32 strategy, + AllocationRequest* pAllocationRequest) override; + + void Alloc( + const AllocationRequest& request, + UINT64 allocSize, + void* privateData) override; + + void Free(AllocHandle allocHandle) override; + void Clear() override; + + AllocHandle GetAllocationListBegin() const override; + AllocHandle GetNextAllocation(AllocHandle prevAlloc) const override; + UINT64 GetNextFreeRegionSize(AllocHandle alloc) const override; + void* GetAllocationPrivateData(AllocHandle allocHandle) const override; + void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override; + + void AddStatistics(Statistics& inoutStats) const override; + void AddDetailedStatistics(DetailedStatistics& inoutStats) const override; + void WriteAllocationInfoToJson(JsonWriter& json) const override; + +private: + // According to original paper it should be preferable 4 or 5: + // M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems" + // http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf + static const UINT8 SECOND_LEVEL_INDEX = 5; + static const UINT16 SMALL_BUFFER_SIZE = 256; + static const UINT INITIAL_BLOCK_ALLOC_COUNT = 16; + static const UINT8 MEMORY_CLASS_SHIFT = 7; + static const UINT8 MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT; + + class Block + { + public: + UINT64 offset; + UINT64 size; + Block* prevPhysical; + Block* nextPhysical; + + void MarkFree() { prevFree = NULL; } + void MarkTaken() { prevFree = this; } + bool IsFree() const { return prevFree != this; } + void*& PrivateData() { D3D12MA_HEAVY_ASSERT(!IsFree()); return privateData; } + Block*& PrevFree() { return prevFree; } + Block*& NextFree() { D3D12MA_HEAVY_ASSERT(IsFree()); return nextFree; } + + private: + Block* prevFree; // Address of the same block here indicates that block is taken + union + { + Block* nextFree; + void* privateData; + }; + }; + + size_t m_AllocCount = 0; + // Total number of free blocks besides null block + size_t m_BlocksFreeCount = 0; + // Total size of free blocks excluding null block + UINT64 m_BlocksFreeSize = 0; + UINT32 m_IsFreeBitmap = 0; + UINT8 m_MemoryClasses = 0; + UINT32 m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES]; + UINT32 m_ListsCount = 0; + /* + * 0: 0-3 lists for small buffers + * 1+: 0-(2^SLI-1) lists for normal buffers + */ + Block** m_FreeList = NULL; + PoolAllocator m_BlockAllocator; + Block* m_NullBlock = NULL; + + UINT8 SizeToMemoryClass(UINT64 size) const; + UINT16 SizeToSecondIndex(UINT64 size, UINT8 memoryClass) const; + UINT32 GetListIndex(UINT8 memoryClass, UINT16 secondIndex) const; + UINT32 GetListIndex(UINT64 size) const; + + void RemoveFreeBlock(Block* block); + void InsertFreeBlock(Block* block); + void MergeBlock(Block* block, Block* prev); + + Block* FindFreeBlock(UINT64 size, UINT32& listIndex) const; + bool CheckBlock( + Block& block, + UINT32 listIndex, + UINT64 allocSize, + UINT64 allocAlignment, + AllocationRequest* pAllocationRequest); + + D3D12MA_CLASS_NO_COPY(BlockMetadata_TLSF) +}; + +#ifndef _D3D12MA_BLOCK_METADATA_TLSF_FUNCTIONS +BlockMetadata_TLSF::BlockMetadata_TLSF(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual) + : BlockMetadata(allocationCallbacks, isVirtual), + m_BlockAllocator(*allocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT) +{ + D3D12MA_ASSERT(allocationCallbacks); +} + +BlockMetadata_TLSF::~BlockMetadata_TLSF() +{ + D3D12MA_DELETE_ARRAY(*GetAllocs(), m_FreeList, m_ListsCount); +} + +void BlockMetadata_TLSF::Init(UINT64 size) +{ + BlockMetadata::Init(size); + + m_NullBlock = m_BlockAllocator.Alloc(); + m_NullBlock->size = size; + m_NullBlock->offset = 0; + m_NullBlock->prevPhysical = NULL; + m_NullBlock->nextPhysical = NULL; + m_NullBlock->MarkFree(); + m_NullBlock->NextFree() = NULL; + m_NullBlock->PrevFree() = NULL; + UINT8 memoryClass = SizeToMemoryClass(size); + UINT16 sli = SizeToSecondIndex(size, memoryClass); + m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1; + if (IsVirtual()) + m_ListsCount += 1UL << SECOND_LEVEL_INDEX; + else + m_ListsCount += 4; + + m_MemoryClasses = memoryClass + 2; + memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(UINT32)); + + m_FreeList = D3D12MA_NEW_ARRAY(*GetAllocs(), Block*, m_ListsCount); + memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); +} + +bool BlockMetadata_TLSF::Validate() const +{ + D3D12MA_VALIDATE(GetSumFreeSize() <= GetSize()); + + UINT64 calculatedSize = m_NullBlock->size; + UINT64 calculatedFreeSize = m_NullBlock->size; + size_t allocCount = 0; + size_t freeCount = 0; + + // Check integrity of free lists + for (UINT32 list = 0; list < m_ListsCount; ++list) + { + Block* block = m_FreeList[list]; + if (block != NULL) + { + D3D12MA_VALIDATE(block->IsFree()); + D3D12MA_VALIDATE(block->PrevFree() == NULL); + while (block->NextFree()) + { + D3D12MA_VALIDATE(block->NextFree()->IsFree()); + D3D12MA_VALIDATE(block->NextFree()->PrevFree() == block); + block = block->NextFree(); + } + } + } + + D3D12MA_VALIDATE(m_NullBlock->nextPhysical == NULL); + if (m_NullBlock->prevPhysical) + { + D3D12MA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock); + } + + // Check all blocks + UINT64 nextOffset = m_NullBlock->offset; + for (Block* prev = m_NullBlock->prevPhysical; prev != NULL; prev = prev->prevPhysical) + { + D3D12MA_VALIDATE(prev->offset + prev->size == nextOffset); + nextOffset = prev->offset; + calculatedSize += prev->size; + + UINT32 listIndex = GetListIndex(prev->size); + if (prev->IsFree()) + { + ++freeCount; + // Check if free block belongs to free list + Block* freeBlock = m_FreeList[listIndex]; + D3D12MA_VALIDATE(freeBlock != NULL); + + bool found = false; + do + { + if (freeBlock == prev) + found = true; + + freeBlock = freeBlock->NextFree(); + } while (!found && freeBlock != NULL); + + D3D12MA_VALIDATE(found); + calculatedFreeSize += prev->size; + } + else + { + ++allocCount; + // Check if taken block is not on a free list + Block* freeBlock = m_FreeList[listIndex]; + while (freeBlock) + { + D3D12MA_VALIDATE(freeBlock != prev); + freeBlock = freeBlock->NextFree(); + } + } + + if (prev->prevPhysical) + { + D3D12MA_VALIDATE(prev->prevPhysical->nextPhysical == prev); + } + } + + D3D12MA_VALIDATE(nextOffset == 0); + D3D12MA_VALIDATE(calculatedSize == GetSize()); + D3D12MA_VALIDATE(calculatedFreeSize == GetSumFreeSize()); + D3D12MA_VALIDATE(allocCount == m_AllocCount); + D3D12MA_VALIDATE(freeCount == m_BlocksFreeCount); + + return true; +} + +void BlockMetadata_TLSF::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const +{ + Block* block = (Block*)allocHandle; + D3D12MA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!"); + outInfo.Offset = block->offset; + outInfo.Size = block->size; + outInfo.pPrivateData = block->PrivateData(); +} + +bool BlockMetadata_TLSF::CreateAllocationRequest( + UINT64 allocSize, + UINT64 allocAlignment, + bool upperAddress, + UINT32 strategy, + AllocationRequest* pAllocationRequest) +{ + D3D12MA_ASSERT(allocSize > 0 && "Cannot allocate empty block!"); + D3D12MA_ASSERT(!upperAddress && "ALLOCATION_FLAG_UPPER_ADDRESS can be used only with linear algorithm."); + D3D12MA_ASSERT(pAllocationRequest != NULL); + D3D12MA_HEAVY_ASSERT(Validate()); + + allocSize += GetDebugMargin(); + // Quick check for too small pool + if (allocSize > GetSumFreeSize()) + return false; + + // If no free blocks in pool then check only null block + if (m_BlocksFreeCount == 0) + return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest); + + // Round up to the next block + UINT64 sizeForNextList = allocSize; + UINT64 smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4); + if (allocSize > SMALL_BUFFER_SIZE) + { + sizeForNextList += (1ULL << (BitScanMSB(allocSize) - SECOND_LEVEL_INDEX)); + } + else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep) + sizeForNextList = SMALL_BUFFER_SIZE + 1; + else + sizeForNextList += smallSizeStep; + + UINT32 nextListIndex = 0; + UINT32 prevListIndex = 0; + Block* nextListBlock = NULL; + Block* prevListBlock = NULL; + + // Check blocks according to strategies + if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_TIME) + { + // Quick check for larger block first + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + if (nextListBlock != NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest)) + return true; + + // If not fitted then null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest)) + return true; + + // Null block failed, search larger bucket + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + + // Failed again, check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + } + else if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_MEMORY) + { + // Check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest)) + return true; + + // Check larger bucket + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + } + else if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_OFFSET) + { + // Perform search from the start + Vector blockList(m_BlocksFreeCount, *GetAllocs()); + + size_t i = m_BlocksFreeCount; + for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical) + { + if (block->IsFree() && block->size >= allocSize) + blockList[--i] = block; + } + + for (; i < m_BlocksFreeCount; ++i) + { + Block& block = *blockList[i]; + if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, pAllocationRequest)) + return true; + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest)) + return true; + + // Whole range searched, no more memory + return false; + } + else + { + // Check larger bucket + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest)) + return true; + + // Check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + } + + // Worst case, full search has to be done + while (++nextListIndex < m_ListsCount) + { + nextListBlock = m_FreeList[nextListIndex]; + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + } + + // No more memory sadly + return false; +} + +void BlockMetadata_TLSF::Alloc( + const AllocationRequest& request, + UINT64 allocSize, + void* privateData) +{ + // Get block and pop it from the free list + Block* currentBlock = (Block*)request.allocHandle; + UINT64 offset = request.algorithmData; + D3D12MA_ASSERT(currentBlock != NULL); + D3D12MA_ASSERT(currentBlock->offset <= offset); + + if (currentBlock != m_NullBlock) + RemoveFreeBlock(currentBlock); + + // Append missing alignment to prev block or create new one + UINT64 misssingAlignment = offset - currentBlock->offset; + if (misssingAlignment) + { + Block* prevBlock = currentBlock->prevPhysical; + D3D12MA_ASSERT(prevBlock != NULL && "There should be no missing alignment at offset 0!"); + + if (prevBlock->IsFree() && prevBlock->size != GetDebugMargin()) + { + UINT32 oldList = GetListIndex(prevBlock->size); + prevBlock->size += misssingAlignment; + // Check if new size crosses list bucket + if (oldList != GetListIndex(prevBlock->size)) + { + prevBlock->size -= misssingAlignment; + RemoveFreeBlock(prevBlock); + prevBlock->size += misssingAlignment; + InsertFreeBlock(prevBlock); + } + else + m_BlocksFreeSize += misssingAlignment; + } + else + { + Block* newBlock = m_BlockAllocator.Alloc(); + currentBlock->prevPhysical = newBlock; + prevBlock->nextPhysical = newBlock; + newBlock->prevPhysical = prevBlock; + newBlock->nextPhysical = currentBlock; + newBlock->size = misssingAlignment; + newBlock->offset = currentBlock->offset; + newBlock->MarkTaken(); + + InsertFreeBlock(newBlock); + } + + currentBlock->size -= misssingAlignment; + currentBlock->offset += misssingAlignment; + } + + UINT64 size = request.size + GetDebugMargin(); + if (currentBlock->size == size) + { + if (currentBlock == m_NullBlock) + { + // Setup new null block + m_NullBlock = m_BlockAllocator.Alloc(); + m_NullBlock->size = 0; + m_NullBlock->offset = currentBlock->offset + size; + m_NullBlock->prevPhysical = currentBlock; + m_NullBlock->nextPhysical = NULL; + m_NullBlock->MarkFree(); + m_NullBlock->PrevFree() = NULL; + m_NullBlock->NextFree() = NULL; + currentBlock->nextPhysical = m_NullBlock; + currentBlock->MarkTaken(); + } + } + else + { + D3D12MA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!"); + + // Create new free block + Block* newBlock = m_BlockAllocator.Alloc(); + newBlock->size = currentBlock->size - size; + newBlock->offset = currentBlock->offset + size; + newBlock->prevPhysical = currentBlock; + newBlock->nextPhysical = currentBlock->nextPhysical; + currentBlock->nextPhysical = newBlock; + currentBlock->size = size; + + if (currentBlock == m_NullBlock) + { + m_NullBlock = newBlock; + m_NullBlock->MarkFree(); + m_NullBlock->NextFree() = NULL; + m_NullBlock->PrevFree() = NULL; + currentBlock->MarkTaken(); + } + else + { + newBlock->nextPhysical->prevPhysical = newBlock; + newBlock->MarkTaken(); + InsertFreeBlock(newBlock); + } + } + currentBlock->PrivateData() = privateData; + + if (GetDebugMargin() > 0) + { + currentBlock->size -= GetDebugMargin(); + Block* newBlock = m_BlockAllocator.Alloc(); + newBlock->size = GetDebugMargin(); + newBlock->offset = currentBlock->offset + currentBlock->size; + newBlock->prevPhysical = currentBlock; + newBlock->nextPhysical = currentBlock->nextPhysical; + newBlock->MarkTaken(); + currentBlock->nextPhysical->prevPhysical = newBlock; + currentBlock->nextPhysical = newBlock; + InsertFreeBlock(newBlock); + } + ++m_AllocCount; +} + +void BlockMetadata_TLSF::Free(AllocHandle allocHandle) +{ + Block* block = (Block*)allocHandle; + Block* next = block->nextPhysical; + D3D12MA_ASSERT(!block->IsFree() && "Block is already free!"); + + --m_AllocCount; + if (GetDebugMargin() > 0) + { + RemoveFreeBlock(next); + MergeBlock(next, block); + block = next; + next = next->nextPhysical; + } + + // Try merging + Block* prev = block->prevPhysical; + if (prev != NULL && prev->IsFree() && prev->size != GetDebugMargin()) + { + RemoveFreeBlock(prev); + MergeBlock(block, prev); + } + + if (!next->IsFree()) + InsertFreeBlock(block); + else if (next == m_NullBlock) + MergeBlock(m_NullBlock, block); + else + { + RemoveFreeBlock(next); + MergeBlock(next, block); + InsertFreeBlock(next); + } +} + +void BlockMetadata_TLSF::Clear() +{ + m_AllocCount = 0; + m_BlocksFreeCount = 0; + m_BlocksFreeSize = 0; + m_IsFreeBitmap = 0; + m_NullBlock->offset = 0; + m_NullBlock->size = GetSize(); + Block* block = m_NullBlock->prevPhysical; + m_NullBlock->prevPhysical = NULL; + while (block) + { + Block* prev = block->prevPhysical; + m_BlockAllocator.Free(block); + block = prev; + } + memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); + memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(UINT32)); +} + +AllocHandle BlockMetadata_TLSF::GetAllocationListBegin() const +{ + if (m_AllocCount == 0) + return (AllocHandle)0; + + for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical) + { + if (!block->IsFree()) + return (AllocHandle)block; + } + D3D12MA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!"); + return (AllocHandle)0; +} + +AllocHandle BlockMetadata_TLSF::GetNextAllocation(AllocHandle prevAlloc) const +{ + Block* startBlock = (Block*)prevAlloc; + D3D12MA_ASSERT(!startBlock->IsFree() && "Incorrect block!"); + + for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical) + { + if (!block->IsFree()) + return (AllocHandle)block; + } + return (AllocHandle)0; +} + +UINT64 BlockMetadata_TLSF::GetNextFreeRegionSize(AllocHandle alloc) const +{ + Block* block = (Block*)alloc; + D3D12MA_ASSERT(!block->IsFree() && "Incorrect block!"); + + if (block->prevPhysical) + return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0; + return 0; +} + +void* BlockMetadata_TLSF::GetAllocationPrivateData(AllocHandle allocHandle) const +{ + Block* block = (Block*)allocHandle; + D3D12MA_ASSERT(!block->IsFree() && "Cannot get user data for free block!"); + return block->PrivateData(); +} + +void BlockMetadata_TLSF::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) +{ + Block* block = (Block*)allocHandle; + D3D12MA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!"); + block->PrivateData() = privateData; +} + +void BlockMetadata_TLSF::AddStatistics(Statistics& inoutStats) const +{ + inoutStats.BlockCount++; + inoutStats.AllocationCount += static_cast(m_AllocCount); + inoutStats.BlockBytes += GetSize(); + inoutStats.AllocationBytes += GetSize() - GetSumFreeSize(); +} + +void BlockMetadata_TLSF::AddDetailedStatistics(DetailedStatistics& inoutStats) const +{ + inoutStats.Stats.BlockCount++; + inoutStats.Stats.BlockBytes += GetSize(); + + for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical) + { + if (block->IsFree()) + AddDetailedStatisticsUnusedRange(inoutStats, block->size); + else + AddDetailedStatisticsAllocation(inoutStats, block->size); + } + + if (m_NullBlock->size > 0) + AddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size); +} + +void BlockMetadata_TLSF::WriteAllocationInfoToJson(JsonWriter& json) const +{ + size_t blockCount = m_AllocCount + m_BlocksFreeCount; + Vector blockList(blockCount, *GetAllocs()); + + size_t i = blockCount; + if (m_NullBlock->size > 0) + { + ++blockCount; + blockList.push_back(m_NullBlock); + } + for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical) + { + blockList[--i] = block; + } + D3D12MA_ASSERT(i == 0); + + PrintDetailedMap_Begin(json, GetSumFreeSize(), GetAllocationCount(), m_BlocksFreeCount + static_cast(m_NullBlock->size)); + for (; i < blockCount; ++i) + { + Block* block = blockList[i]; + if (block->IsFree()) + PrintDetailedMap_UnusedRange(json, block->offset, block->size); + else + PrintDetailedMap_Allocation(json, block->offset, block->size, block->PrivateData()); + } + PrintDetailedMap_End(json); +} + +UINT8 BlockMetadata_TLSF::SizeToMemoryClass(UINT64 size) const +{ + if (size > SMALL_BUFFER_SIZE) + return BitScanMSB(size) - MEMORY_CLASS_SHIFT; + return 0; +} + +UINT16 BlockMetadata_TLSF::SizeToSecondIndex(UINT64 size, UINT8 memoryClass) const +{ + if (memoryClass == 0) + { + if (IsVirtual()) + return static_cast((size - 1) / 8); + else + return static_cast((size - 1) / 64); + } + return static_cast((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX)); +} + +UINT32 BlockMetadata_TLSF::GetListIndex(UINT8 memoryClass, UINT16 secondIndex) const +{ + if (memoryClass == 0) + return secondIndex; + + const UINT32 index = static_cast(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex; + if (IsVirtual()) + return index + (1 << SECOND_LEVEL_INDEX); + else + return index + 4; +} + +UINT32 BlockMetadata_TLSF::GetListIndex(UINT64 size) const +{ + UINT8 memoryClass = SizeToMemoryClass(size); + return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass)); +} + +void BlockMetadata_TLSF::RemoveFreeBlock(Block* block) +{ + D3D12MA_ASSERT(block != m_NullBlock); + D3D12MA_ASSERT(block->IsFree()); + + if (block->NextFree() != NULL) + block->NextFree()->PrevFree() = block->PrevFree(); + if (block->PrevFree() != NULL) + block->PrevFree()->NextFree() = block->NextFree(); + else + { + UINT8 memClass = SizeToMemoryClass(block->size); + UINT16 secondIndex = SizeToSecondIndex(block->size, memClass); + UINT32 index = GetListIndex(memClass, secondIndex); + m_FreeList[index] = block->NextFree(); + if (block->NextFree() == NULL) + { + m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex); + if (m_InnerIsFreeBitmap[memClass] == 0) + m_IsFreeBitmap &= ~(1UL << memClass); + } + } + block->MarkTaken(); + block->PrivateData() = NULL; + --m_BlocksFreeCount; + m_BlocksFreeSize -= block->size; +} + +void BlockMetadata_TLSF::InsertFreeBlock(Block* block) +{ + D3D12MA_ASSERT(block != m_NullBlock); + D3D12MA_ASSERT(!block->IsFree() && "Cannot insert block twice!"); + + UINT8 memClass = SizeToMemoryClass(block->size); + UINT16 secondIndex = SizeToSecondIndex(block->size, memClass); + UINT32 index = GetListIndex(memClass, secondIndex); + block->PrevFree() = NULL; + block->NextFree() = m_FreeList[index]; + m_FreeList[index] = block; + if (block->NextFree() != NULL) + block->NextFree()->PrevFree() = block; + else + { + m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex; + m_IsFreeBitmap |= 1UL << memClass; + } + ++m_BlocksFreeCount; + m_BlocksFreeSize += block->size; +} + +void BlockMetadata_TLSF::MergeBlock(Block* block, Block* prev) +{ + D3D12MA_ASSERT(block->prevPhysical == prev && "Cannot merge seperate physical regions!"); + D3D12MA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!"); + + block->offset = prev->offset; + block->size += prev->size; + block->prevPhysical = prev->prevPhysical; + if (block->prevPhysical) + block->prevPhysical->nextPhysical = block; + m_BlockAllocator.Free(prev); +} + +BlockMetadata_TLSF::Block* BlockMetadata_TLSF::FindFreeBlock(UINT64 size, UINT32& listIndex) const +{ + UINT8 memoryClass = SizeToMemoryClass(size); + UINT32 innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass)); + if (!innerFreeMap) + { + // Check higher levels for avaiable blocks + UINT32 freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1)); + if (!freeMap) + return NULL; // No more memory avaible + + // Find lowest free region + memoryClass = BitScanLSB(freeMap); + innerFreeMap = m_InnerIsFreeBitmap[memoryClass]; + D3D12MA_ASSERT(innerFreeMap != 0); + } + // Find lowest free subregion + listIndex = GetListIndex(memoryClass, BitScanLSB(innerFreeMap)); + return m_FreeList[listIndex]; +} + +bool BlockMetadata_TLSF::CheckBlock( + Block& block, + UINT32 listIndex, + UINT64 allocSize, + UINT64 allocAlignment, + AllocationRequest* pAllocationRequest) +{ + D3D12MA_ASSERT(block.IsFree() && "Block is already taken!"); + + UINT64 alignedOffset = AlignUp(block.offset, allocAlignment); + if (block.size < allocSize + alignedOffset - block.offset) + return false; + + // Alloc successful + pAllocationRequest->allocHandle = (AllocHandle)█ + pAllocationRequest->size = allocSize - GetDebugMargin(); + pAllocationRequest->algorithmData = alignedOffset; + + // Place block at the start of list if it's normal block + if (listIndex != m_ListsCount && block.PrevFree()) + { + block.PrevFree()->NextFree() = block.NextFree(); + if (block.NextFree()) + block.NextFree()->PrevFree() = block.PrevFree(); + block.PrevFree() = NULL; + block.NextFree() = m_FreeList[listIndex]; + m_FreeList[listIndex] = █ + if (block.NextFree()) + block.NextFree()->PrevFree() = █ + } + + return true; +} +#endif // _D3D12MA_BLOCK_METADATA_TLSF_FUNCTIONS +#endif // _D3D12MA_BLOCK_METADATA_TLSF + +#ifndef _D3D12MA_MEMORY_BLOCK +/* +Represents a single block of device memory (heap). +Base class for inheritance. +Thread-safety: This class must be externally synchronized. +*/ +class MemoryBlock +{ +public: + // Creates the ID3D12Heap. + MemoryBlock( + AllocatorPimpl* allocator, + const D3D12_HEAP_PROPERTIES& heapProps, + D3D12_HEAP_FLAGS heapFlags, + UINT64 size, + UINT id); + virtual ~MemoryBlock(); + + const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; } + D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; } + UINT64 GetSize() const { return m_Size; } + UINT GetId() const { return m_Id; } + ID3D12Heap* GetHeap() const { return m_Heap; } + +protected: + AllocatorPimpl* const m_Allocator; + const D3D12_HEAP_PROPERTIES m_HeapProps; + const D3D12_HEAP_FLAGS m_HeapFlags; + const UINT64 m_Size; + const UINT m_Id; + + HRESULT Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures); + +private: + ID3D12Heap* m_Heap = NULL; + + D3D12MA_CLASS_NO_COPY(MemoryBlock) +}; +#endif // _D3D12MA_MEMORY_BLOCK + +#ifndef _D3D12MA_NORMAL_BLOCK +/* +Represents a single block of device memory (heap) with all the data about its +regions (aka suballocations, Allocation), assigned and free. +Thread-safety: This class must be externally synchronized. +*/ +class NormalBlock : public MemoryBlock +{ +public: + BlockMetadata* m_pMetadata; + + NormalBlock( + AllocatorPimpl* allocator, + BlockVector* blockVector, + const D3D12_HEAP_PROPERTIES& heapProps, + D3D12_HEAP_FLAGS heapFlags, + UINT64 size, + UINT id); + virtual ~NormalBlock(); + + BlockVector* GetBlockVector() const { return m_BlockVector; } + + // 'algorithm' should be one of the *_ALGORITHM_* flags in enums POOL_FLAGS or VIRTUAL_BLOCK_FLAGS + HRESULT Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures); + + // Validates all data structures inside this object. If not valid, returns false. + bool Validate() const; + +private: + BlockVector* m_BlockVector; + + D3D12MA_CLASS_NO_COPY(NormalBlock) +}; +#endif // _D3D12MA_NORMAL_BLOCK + +#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST_ITEM_TRAITS +struct CommittedAllocationListItemTraits +{ + using ItemType = Allocation; + + static ItemType* GetPrev(const ItemType* item) + { + D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP); + return item->m_Committed.prev; + } + static ItemType* GetNext(const ItemType* item) + { + D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP); + return item->m_Committed.next; + } + static ItemType*& AccessPrev(ItemType* item) + { + D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP); + return item->m_Committed.prev; + } + static ItemType*& AccessNext(ItemType* item) + { + D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP); + return item->m_Committed.next; + } +}; +#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST_ITEM_TRAITS + +#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST +/* +Stores linked list of Allocation objects that are of TYPE_COMMITTED or TYPE_HEAP. +Thread-safe, synchronized internally. +*/ +class CommittedAllocationList +{ +public: + CommittedAllocationList() = default; + void Init(bool useMutex, D3D12_HEAP_TYPE heapType, PoolPimpl* pool); + ~CommittedAllocationList(); + + D3D12_HEAP_TYPE GetHeapType() const { return m_HeapType; } + PoolPimpl* GetPool() const { return m_Pool; } + UINT GetMemorySegmentGroup(AllocatorPimpl* allocator) const; + + void AddStatistics(Statistics& inoutStats); + void AddDetailedStatistics(DetailedStatistics& inoutStats); + // Writes JSON array with the list of allocations. + void BuildStatsString(JsonWriter& json); + + void Register(Allocation* alloc); + void Unregister(Allocation* alloc); + +private: + using CommittedAllocationLinkedList = IntrusiveLinkedList; + + bool m_UseMutex = true; + D3D12_HEAP_TYPE m_HeapType = D3D12_HEAP_TYPE_CUSTOM; + PoolPimpl* m_Pool = NULL; + + D3D12MA_RW_MUTEX m_Mutex; + CommittedAllocationLinkedList m_AllocationList; +}; +#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST + +#ifndef _D3D12M_COMMITTED_ALLOCATION_PARAMETERS +struct CommittedAllocationParameters +{ + CommittedAllocationList* m_List = NULL; + D3D12_HEAP_PROPERTIES m_HeapProperties = {}; + D3D12_HEAP_FLAGS m_HeapFlags = D3D12_HEAP_FLAG_NONE; + ID3D12ProtectedResourceSession* m_ProtectedSession = NULL; + bool m_CanAlias = false; + + bool IsValid() const { return m_List != NULL; } +}; +#endif // _D3D12M_COMMITTED_ALLOCATION_PARAMETERS + +#ifndef _D3D12MA_BLOCK_VECTOR +/* +Sequence of NormalBlock. Represents memory blocks allocated for a specific +heap type and possibly resource type (if only Tier 1 is supported). + +Synchronized internally with a mutex. +*/ +class BlockVector +{ + friend class DefragmentationContextPimpl; + D3D12MA_CLASS_NO_COPY(BlockVector) +public: + BlockVector( + AllocatorPimpl* hAllocator, + const D3D12_HEAP_PROPERTIES& heapProps, + D3D12_HEAP_FLAGS heapFlags, + UINT64 preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + bool explicitBlockSize, + UINT64 minAllocationAlignment, + UINT32 algorithm, + bool denyMsaaTextures, + ID3D12ProtectedResourceSession* pProtectedSession); + ~BlockVector(); + + const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; } + D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; } + UINT64 GetPreferredBlockSize() const { return m_PreferredBlockSize; } + UINT32 GetAlgorithm() const { return m_Algorithm; } + bool DeniesMsaaTextures() const { return m_DenyMsaaTextures; } + // To be used only while the m_Mutex is locked. Used during defragmentation. + size_t GetBlockCount() const { return m_Blocks.size(); } + // To be used only while the m_Mutex is locked. Used during defragmentation. + NormalBlock* GetBlock(size_t index) const { return m_Blocks[index]; } + D3D12MA_RW_MUTEX& GetMutex() { return m_Mutex; } + + HRESULT CreateMinBlocks(); + bool IsEmpty(); + + HRESULT Allocate( + UINT64 size, + UINT64 alignment, + const ALLOCATION_DESC& allocDesc, + size_t allocationCount, + Allocation** pAllocations); + + void Free(Allocation* hAllocation); + + HRESULT CreateResource( + UINT64 size, + UINT64 alignment, + const ALLOCATION_DESC& allocDesc, + const D3D12_RESOURCE_DESC& resourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE *pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource); + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + HRESULT CreateResource2( + UINT64 size, + UINT64 alignment, + const ALLOCATION_DESC& allocDesc, + const D3D12_RESOURCE_DESC1& resourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE *pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource); +#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ + + void AddStatistics(Statistics& inoutStats); + void AddDetailedStatistics(DetailedStatistics& inoutStats); + + void WriteBlockInfoToJson(JsonWriter& json); + +private: + AllocatorPimpl* const m_hAllocator; + const D3D12_HEAP_PROPERTIES m_HeapProps; + const D3D12_HEAP_FLAGS m_HeapFlags; + const UINT64 m_PreferredBlockSize; + const size_t m_MinBlockCount; + const size_t m_MaxBlockCount; + const bool m_ExplicitBlockSize; + const UINT64 m_MinAllocationAlignment; + const UINT32 m_Algorithm; + const bool m_DenyMsaaTextures; + ID3D12ProtectedResourceSession* const m_ProtectedSession; + /* There can be at most one allocation that is completely empty - a + hysteresis to avoid pessimistic case of alternating creation and destruction + of a ID3D12Heap. */ + bool m_HasEmptyBlock; + D3D12MA_RW_MUTEX m_Mutex; + // Incrementally sorted by sumFreeSize, ascending. + Vector m_Blocks; + UINT m_NextBlockId; + bool m_IncrementalSort = true; + + // Disable incremental sorting when freeing allocations + void SetIncrementalSort(bool val) { m_IncrementalSort = val; } + + UINT64 CalcSumBlockSize() const; + UINT64 CalcMaxBlockSize() const; + + // Finds and removes given block from vector. + void Remove(NormalBlock* pBlock); + + // Performs single step in sorting m_Blocks. They may not be fully sorted + // after this call. + void IncrementallySortBlocks(); + void SortByFreeSize(); + + HRESULT AllocatePage( + UINT64 size, + UINT64 alignment, + const ALLOCATION_DESC& allocDesc, + Allocation** pAllocation); + + HRESULT AllocateFromBlock( + NormalBlock* pBlock, + UINT64 size, + UINT64 alignment, + ALLOCATION_FLAGS allocFlags, + void* pPrivateData, + UINT32 strategy, + Allocation** pAllocation); + + HRESULT CommitAllocationRequest( + AllocationRequest& allocRequest, + NormalBlock* pBlock, + UINT64 size, + UINT64 alignment, + void* pPrivateData, + Allocation** pAllocation); + + HRESULT CreateBlock( + UINT64 blockSize, + size_t* pNewBlockIndex); +}; +#endif // _D3D12MA_BLOCK_VECTOR + +#ifndef _D3D12MA_CURRENT_BUDGET_DATA +class CurrentBudgetData +{ +public: + bool ShouldUpdateBudget() const { return m_OperationsSinceBudgetFetch >= 30; } + + void GetStatistics(Statistics& outStats, UINT group) const; + void GetBudget(bool useMutex, + UINT64* outLocalUsage, UINT64* outLocalBudget, + UINT64* outNonLocalUsage, UINT64* outNonLocalBudget); + +#if D3D12MA_DXGI_1_4 + HRESULT UpdateBudget(IDXGIAdapter3* adapter3, bool useMutex); +#endif + + void AddAllocation(UINT group, UINT64 allocationBytes); + void RemoveAllocation(UINT group, UINT64 allocationBytes); + + void AddBlock(UINT group, UINT64 blockBytes); + void RemoveBlock(UINT group, UINT64 blockBytes); + +private: + D3D12MA_ATOMIC_UINT32 m_BlockCount[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; + D3D12MA_ATOMIC_UINT32 m_AllocationCount[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; + D3D12MA_ATOMIC_UINT64 m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; + D3D12MA_ATOMIC_UINT64 m_AllocationBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; + + D3D12MA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch = 0; + D3D12MA_RW_MUTEX m_BudgetMutex; + UINT64 m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; + UINT64 m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; + UINT64 m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; +}; + +#ifndef _D3D12MA_CURRENT_BUDGET_DATA_FUNCTIONS +void CurrentBudgetData::GetStatistics(Statistics& outStats, UINT group) const +{ + outStats.BlockCount = m_BlockCount[group]; + outStats.AllocationCount = m_AllocationCount[group]; + outStats.BlockBytes = m_BlockBytes[group]; + outStats.AllocationBytes = m_AllocationBytes[group]; +} + +void CurrentBudgetData::GetBudget(bool useMutex, + UINT64* outLocalUsage, UINT64* outLocalBudget, + UINT64* outNonLocalUsage, UINT64* outNonLocalBudget) +{ + MutexLockRead lockRead(m_BudgetMutex, useMutex); + + if (outLocalUsage) + { + const UINT64 D3D12Usage = m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY]; + const UINT64 blockBytes = m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY]; + const UINT64 blockBytesAtD3D12Fetch = m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY]; + *outLocalUsage = D3D12Usage + blockBytes > blockBytesAtD3D12Fetch ? + D3D12Usage + blockBytes - blockBytesAtD3D12Fetch : 0; + } + if (outLocalBudget) + *outLocalBudget = m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY]; + + if (outNonLocalUsage) + { + const UINT64 D3D12Usage = m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY]; + const UINT64 blockBytes = m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY]; + const UINT64 blockBytesAtD3D12Fetch = m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY]; + *outNonLocalUsage = D3D12Usage + blockBytes > blockBytesAtD3D12Fetch ? + D3D12Usage + blockBytes - blockBytesAtD3D12Fetch : 0; + } + if (outNonLocalBudget) + *outNonLocalBudget = m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY]; +} + +#if D3D12MA_DXGI_1_4 +HRESULT CurrentBudgetData::UpdateBudget(IDXGIAdapter3* adapter3, bool useMutex) +{ + D3D12MA_ASSERT(adapter3); + + DXGI_QUERY_VIDEO_MEMORY_INFO infoLocal = {}; + DXGI_QUERY_VIDEO_MEMORY_INFO infoNonLocal = {}; + const HRESULT hrLocal = adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &infoLocal); + const HRESULT hrNonLocal = adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &infoNonLocal); + + if (SUCCEEDED(hrLocal) || SUCCEEDED(hrNonLocal)) + { + MutexLockWrite lockWrite(m_BudgetMutex, useMutex); + + if (SUCCEEDED(hrLocal)) + { + m_D3D12Usage[0] = infoLocal.CurrentUsage; + m_D3D12Budget[0] = infoLocal.Budget; + } + if (SUCCEEDED(hrNonLocal)) + { + m_D3D12Usage[1] = infoNonLocal.CurrentUsage; + m_D3D12Budget[1] = infoNonLocal.Budget; + } + + m_BlockBytesAtD3D12Fetch[0] = m_BlockBytes[0]; + m_BlockBytesAtD3D12Fetch[1] = m_BlockBytes[1]; + m_OperationsSinceBudgetFetch = 0; + } + + return FAILED(hrLocal) ? hrLocal : hrNonLocal; +} +#endif // #if D3D12MA_DXGI_1_4 + +void CurrentBudgetData::AddAllocation(UINT group, UINT64 allocationBytes) +{ + ++m_AllocationCount[group]; + m_AllocationBytes[group] += allocationBytes; + ++m_OperationsSinceBudgetFetch; +} + +void CurrentBudgetData::RemoveAllocation(UINT group, UINT64 allocationBytes) +{ + D3D12MA_ASSERT(m_AllocationBytes[group] >= allocationBytes); + D3D12MA_ASSERT(m_AllocationCount[group] > 0); + m_AllocationBytes[group] -= allocationBytes; + --m_AllocationCount[group]; + ++m_OperationsSinceBudgetFetch; +} + +void CurrentBudgetData::AddBlock(UINT group, UINT64 blockBytes) +{ + ++m_BlockCount[group]; + m_BlockBytes[group] += blockBytes; + ++m_OperationsSinceBudgetFetch; +} + +void CurrentBudgetData::RemoveBlock(UINT group, UINT64 blockBytes) +{ + D3D12MA_ASSERT(m_BlockBytes[group] >= blockBytes); + D3D12MA_ASSERT(m_BlockCount[group] > 0); + m_BlockBytes[group] -= blockBytes; + --m_BlockCount[group]; + ++m_OperationsSinceBudgetFetch; +} +#endif // _D3D12MA_CURRENT_BUDGET_DATA_FUNCTIONS +#endif // _D3D12MA_CURRENT_BUDGET_DATA + +#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL +class DefragmentationContextPimpl +{ + D3D12MA_CLASS_NO_COPY(DefragmentationContextPimpl) +public: + DefragmentationContextPimpl( + AllocatorPimpl* hAllocator, + const DEFRAGMENTATION_DESC& desc, + BlockVector* poolVector); + ~DefragmentationContextPimpl(); + + void GetStats(DEFRAGMENTATION_STATS& outStats) { outStats = m_GlobalStats; } + const ALLOCATION_CALLBACKS& GetAllocs() const { return m_Moves.GetAllocs(); } + + HRESULT DefragmentPassBegin(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo); + HRESULT DefragmentPassEnd(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo); + +private: + // Max number of allocations to ignore due to size constraints before ending single pass + static const UINT8 MAX_ALLOCS_TO_IGNORE = 16; + enum class CounterStatus { Pass, Ignore, End }; + + struct FragmentedBlock + { + UINT32 data; + NormalBlock* block; + }; + struct StateBalanced + { + UINT64 avgFreeSize = 0; + UINT64 avgAllocSize = UINT64_MAX; + }; + struct MoveAllocationData + { + UINT64 size; + UINT64 alignment; + ALLOCATION_FLAGS flags; + DEFRAGMENTATION_MOVE move = {}; + }; + + const UINT64 m_MaxPassBytes; + const UINT32 m_MaxPassAllocations; + + Vector m_Moves; + + UINT8 m_IgnoredAllocs = 0; + UINT32 m_Algorithm; + UINT32 m_BlockVectorCount; + BlockVector* m_PoolBlockVector; + BlockVector** m_pBlockVectors; + size_t m_ImmovableBlockCount = 0; + DEFRAGMENTATION_STATS m_GlobalStats = { 0 }; + DEFRAGMENTATION_STATS m_PassStats = { 0 }; + void* m_AlgorithmState = NULL; + + static MoveAllocationData GetMoveData(AllocHandle handle, BlockMetadata* metadata); + CounterStatus CheckCounters(UINT64 bytes); + bool IncrementCounters(UINT64 bytes); + bool ReallocWithinBlock(BlockVector& vector, NormalBlock* block); + bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, BlockVector& vector); + + bool ComputeDefragmentation(BlockVector& vector, size_t index); + bool ComputeDefragmentation_Fast(BlockVector& vector); + bool ComputeDefragmentation_Balanced(BlockVector& vector, size_t index, bool update); + bool ComputeDefragmentation_Full(BlockVector& vector); + + void UpdateVectorStatistics(BlockVector& vector, StateBalanced& state); +}; +#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL + +#ifndef _D3D12MA_POOL_PIMPL +class PoolPimpl +{ + friend class Allocator; + friend struct PoolListItemTraits; +public: + PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc); + ~PoolPimpl(); + + AllocatorPimpl* GetAllocator() const { return m_Allocator; } + const POOL_DESC& GetDesc() const { return m_Desc; } + bool SupportsCommittedAllocations() const { return m_Desc.BlockSize == 0; } + LPCWSTR GetName() const { return m_Name; } + + BlockVector* GetBlockVector() { return m_BlockVector; } + CommittedAllocationList* GetCommittedAllocationList() { return SupportsCommittedAllocations() ? &m_CommittedAllocations : NULL; } + + HRESULT Init(); + void GetStatistics(Statistics& outStats); + void CalculateStatistics(DetailedStatistics& outStats); + void AddDetailedStatistics(DetailedStatistics& inoutStats); + void SetName(LPCWSTR Name); + +private: + AllocatorPimpl* m_Allocator; // Externally owned object. + POOL_DESC m_Desc; + BlockVector* m_BlockVector; // Owned object. + CommittedAllocationList m_CommittedAllocations; + wchar_t* m_Name; + PoolPimpl* m_PrevPool = NULL; + PoolPimpl* m_NextPool = NULL; + + void FreeName(); +}; + +struct PoolListItemTraits +{ + using ItemType = PoolPimpl; + static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; } + static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; } + static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; } + static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; } +}; +#endif // _D3D12MA_POOL_PIMPL + + +#ifndef _D3D12MA_ALLOCATOR_PIMPL +class AllocatorPimpl +{ + friend class Allocator; + friend class Pool; +public: + std::atomic_uint32_t m_RefCount = 1; + CurrentBudgetData m_Budget; + + AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc); + ~AllocatorPimpl(); + + ID3D12Device* GetDevice() const { return m_Device; } +#ifdef __ID3D12Device4_INTERFACE_DEFINED__ + ID3D12Device4* GetDevice4() const { return m_Device4; } +#endif +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + ID3D12Device8* GetDevice8() const { return m_Device8; } +#endif + // Shortcut for "Allocation Callbacks", because this function is called so often. + const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; } + const D3D12_FEATURE_DATA_D3D12_OPTIONS& GetD3D12Options() const { return m_D3D12Options; } + BOOL IsUMA() const { return m_D3D12Architecture.UMA; } + BOOL IsCacheCoherentUMA() const { return m_D3D12Architecture.CacheCoherentUMA; } + bool SupportsResourceHeapTier2() const { return m_D3D12Options.ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2; } + bool UseMutex() const { return m_UseMutex; } + AllocationObjectAllocator& GetAllocationObjectAllocator() { return m_AllocationObjectAllocator; } + UINT GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); } + /* + If SupportsResourceHeapTier2(): + 0: D3D12_HEAP_TYPE_DEFAULT + 1: D3D12_HEAP_TYPE_UPLOAD + 2: D3D12_HEAP_TYPE_READBACK + else: + 0: D3D12_HEAP_TYPE_DEFAULT + buffer + 1: D3D12_HEAP_TYPE_DEFAULT + texture + 2: D3D12_HEAP_TYPE_DEFAULT + texture RT or DS + 3: D3D12_HEAP_TYPE_UPLOAD + buffer + 4: D3D12_HEAP_TYPE_UPLOAD + texture + 5: D3D12_HEAP_TYPE_UPLOAD + texture RT or DS + 6: D3D12_HEAP_TYPE_READBACK + buffer + 7: D3D12_HEAP_TYPE_READBACK + texture + 8: D3D12_HEAP_TYPE_READBACK + texture RT or DS + */ + UINT GetDefaultPoolCount() const { return SupportsResourceHeapTier2() ? 3 : 9; } + BlockVector** GetDefaultPools() { return m_BlockVectors; } + + HRESULT Init(const ALLOCATOR_DESC& desc); + bool HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const; + UINT StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE heapType) const; + UINT HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const; + UINT64 GetMemoryCapacity(UINT memorySegmentGroup) const; + + HRESULT CreateResource( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE *pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource); + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + HRESULT CreateResource2( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE *pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource); +#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ + + HRESULT CreateAliasingResource( + Allocation* pAllocation, + UINT64 AllocationLocalOffset, + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE *pOptimizedClearValue, + REFIID riidResource, + void** ppvResource); + + HRESULT AllocateMemory( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo, + Allocation** ppAllocation); + + // Unregisters allocation from the collection of dedicated allocations. + // Allocation object must be deleted externally afterwards. + void FreeCommittedMemory(Allocation* allocation); + // Unregisters allocation from the collection of placed allocations. + // Allocation object must be deleted externally afterwards. + void FreePlacedMemory(Allocation* allocation); + // Unregisters allocation from the collection of dedicated allocations and destroys associated heap. + // Allocation object must be deleted externally afterwards. + void FreeHeapMemory(Allocation* allocation); + + void SetCurrentFrameIndex(UINT frameIndex); + // For more deailed stats use outCutomHeaps to access statistics divided into L0 and L1 group + void CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCutomHeaps[2] = NULL); + + void GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget); + void GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType); + + void BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap); + void FreeStatsString(WCHAR* pStatsString); + +private: + using PoolList = IntrusiveLinkedList; + + const bool m_UseMutex; + const bool m_AlwaysCommitted; + const bool m_MsaaAlwaysCommitted; + ID3D12Device* m_Device; // AddRef +#ifdef __ID3D12Device4_INTERFACE_DEFINED__ + ID3D12Device4* m_Device4 = NULL; // AddRef, optional +#endif +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + ID3D12Device8* m_Device8 = NULL; // AddRef, optional +#endif + IDXGIAdapter* m_Adapter; // AddRef +#if D3D12MA_DXGI_1_4 + IDXGIAdapter3* m_Adapter3 = NULL; // AddRef, optional +#endif + UINT64 m_PreferredBlockSize; + ALLOCATION_CALLBACKS m_AllocationCallbacks; + D3D12MA_ATOMIC_UINT32 m_CurrentFrameIndex; + DXGI_ADAPTER_DESC m_AdapterDesc; + D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Options; + D3D12_FEATURE_DATA_ARCHITECTURE m_D3D12Architecture; + AllocationObjectAllocator m_AllocationObjectAllocator; + + D3D12MA_RW_MUTEX m_PoolsMutex[HEAP_TYPE_COUNT]; + PoolList m_Pools[HEAP_TYPE_COUNT]; + // Default pools. + BlockVector* m_BlockVectors[DEFAULT_POOL_MAX_COUNT]; + CommittedAllocationList m_CommittedAllocations[STANDARD_HEAP_TYPE_COUNT]; + + /* + Heuristics that decides whether a resource should better be placed in its own, + dedicated allocation (committed resource rather than placed resource). + */ + template + static bool PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc); + + // Allocates and registers new committed resource with implicit heap, as dedicated allocation. + // Creates and returns Allocation object and optionally D3D12 resource. + HRESULT AllocateCommittedResource( + const CommittedAllocationParameters& committedAllocParams, + UINT64 resourceSize, bool withinBudget, void* pPrivateData, + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, const D3D12_CLEAR_VALUE *pOptimizedClearValue, + Allocation** ppAllocation, REFIID riidResource, void** ppvResource); + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + HRESULT AllocateCommittedResource2( + const CommittedAllocationParameters& committedAllocParams, + UINT64 resourceSize, bool withinBudget, void* pPrivateData, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, const D3D12_CLEAR_VALUE *pOptimizedClearValue, + Allocation** ppAllocation, REFIID riidResource, void** ppvResource); +#endif + + // Allocates and registers new heap without any resources placed in it, as dedicated allocation. + // Creates and returns Allocation object. + HRESULT AllocateHeap( + const CommittedAllocationParameters& committedAllocParams, + const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo, bool withinBudget, + void* pPrivateData, Allocation** ppAllocation); + + template + HRESULT CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize, + const D3D12_RESOURCE_DESC_T* resDesc, // Optional + BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted); + + // Returns UINT32_MAX if index cannot be calculcated. + UINT CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const; + void CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const; + + // Registers Pool object in m_Pools. + void RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType); + // Unregisters Pool object from m_Pools. + void UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType); + + HRESULT UpdateD3D12Budget(); + + D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const; +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC1& resourceDesc) const; +#endif + + template + D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const; + + bool NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size); + + // Writes object { } with data of given budget. + static void WriteBudgetToJson(JsonWriter& json, const Budget& budget); +}; + +#ifndef _D3D12MA_ALLOCATOR_PIMPL_FUNCTINOS +AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc) + : m_UseMutex((desc.Flags & ALLOCATOR_FLAG_SINGLETHREADED) == 0), + m_AlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_ALWAYS_COMMITTED) != 0), + m_MsaaAlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0), + m_Device(desc.pDevice), + m_Adapter(desc.pAdapter), + m_PreferredBlockSize(desc.PreferredBlockSize != 0 ? desc.PreferredBlockSize : D3D12MA_DEFAULT_BLOCK_SIZE), + m_AllocationCallbacks(allocationCallbacks), + m_CurrentFrameIndex(0), + // Below this line don't use allocationCallbacks but m_AllocationCallbacks!!! + m_AllocationObjectAllocator(m_AllocationCallbacks) +{ + // desc.pAllocationCallbacks intentionally ignored here, preprocessed by CreateAllocator. + ZeroMemory(&m_D3D12Options, sizeof(m_D3D12Options)); + ZeroMemory(&m_D3D12Architecture, sizeof(m_D3D12Architecture)); + + ZeroMemory(m_BlockVectors, sizeof(m_BlockVectors)); + + for (UINT i = 0; i < STANDARD_HEAP_TYPE_COUNT; ++i) + { + m_CommittedAllocations[i].Init( + m_UseMutex, + (D3D12_HEAP_TYPE)(D3D12_HEAP_TYPE_DEFAULT + i), + NULL); // pool + } + + m_Device->AddRef(); + m_Adapter->AddRef(); +} + +HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc) +{ +#if D3D12MA_DXGI_1_4 + desc.pAdapter->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Adapter3)); +#endif + +#ifdef __ID3D12Device4_INTERFACE_DEFINED__ + m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device4)); +#endif + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device8)); +#endif + + HRESULT hr = m_Adapter->GetDesc(&m_AdapterDesc); + if (FAILED(hr)) + { + return hr; + } + + hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &m_D3D12Options, sizeof(m_D3D12Options)); + if (FAILED(hr)) + { + return hr; + } +#ifdef D3D12MA_FORCE_RESOURCE_HEAP_TIER + m_D3D12Options.ResourceHeapTier = (D3D12MA_FORCE_RESOURCE_HEAP_TIER); +#endif + + hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &m_D3D12Architecture, sizeof(m_D3D12Architecture)); + if (FAILED(hr)) + { + m_D3D12Architecture.UMA = FALSE; + m_D3D12Architecture.CacheCoherentUMA = FALSE; + } + + D3D12_HEAP_PROPERTIES heapProps = {}; + const UINT defaultPoolCount = GetDefaultPoolCount(); + for (UINT i = 0; i < defaultPoolCount; ++i) + { + D3D12_HEAP_FLAGS heapFlags; + CalcDefaultPoolParams(heapProps.Type, heapFlags, i); + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + if (desc.Flags & ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED) + heapFlags |= D3D12_HEAP_FLAG_CREATE_NOT_ZEROED; +#endif + + m_BlockVectors[i] = D3D12MA_NEW(GetAllocs(), BlockVector)( + this, // hAllocator + heapProps, // heapType + heapFlags, // heapFlags + m_PreferredBlockSize, + 0, // minBlockCount + SIZE_MAX, // maxBlockCount + false, // explicitBlockSize + D3D12MA_DEBUG_ALIGNMENT, // minAllocationAlignment + 0, // Default algorithm, + m_MsaaAlwaysCommitted, + NULL); // pProtectedSession + // No need to call m_pBlockVectors[i]->CreateMinBlocks here, becase minBlockCount is 0. + } + +#if D3D12MA_DXGI_1_4 + UpdateD3D12Budget(); +#endif + + return S_OK; +} + +AllocatorPimpl::~AllocatorPimpl() +{ +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + SAFE_RELEASE(m_Device8); +#endif +#ifdef __ID3D12Device4_INTERFACE_DEFINED__ + SAFE_RELEASE(m_Device4); +#endif +#if D3D12MA_DXGI_1_4 + SAFE_RELEASE(m_Adapter3); +#endif + SAFE_RELEASE(m_Adapter); + SAFE_RELEASE(m_Device); + + for (UINT i = DEFAULT_POOL_MAX_COUNT; i--; ) + { + D3D12MA_DELETE(GetAllocs(), m_BlockVectors[i]); + } + + for (UINT i = HEAP_TYPE_COUNT; i--; ) + { + if (!m_Pools[i].IsEmpty()) + { + D3D12MA_ASSERT(0 && "Unfreed pools found!"); + } + } +} + +bool AllocatorPimpl::HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const +{ + if (SupportsResourceHeapTier2()) + { + return true; + } + else + { + const bool allowBuffers = (flags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0; + const bool allowRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0; + const bool allowNonRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0; + const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0); + return allowedGroupCount == 1; + } +} + +UINT AllocatorPimpl::StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE heapType) const +{ + D3D12MA_ASSERT(IsHeapTypeStandard(heapType)); + if (IsUMA()) + return DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY; + return heapType == D3D12_HEAP_TYPE_DEFAULT ? + DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY; +} + +UINT AllocatorPimpl::HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const +{ + if (IsUMA()) + return DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY; + if (heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_UNKNOWN) + return StandardHeapTypeToMemorySegmentGroup(heapProps.Type); + return heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_L1 ? + DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY; +} + +UINT64 AllocatorPimpl::GetMemoryCapacity(UINT memorySegmentGroup) const +{ + switch (memorySegmentGroup) + { + case DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY: + return IsUMA() ? + m_AdapterDesc.DedicatedVideoMemory + m_AdapterDesc.SharedSystemMemory : m_AdapterDesc.DedicatedVideoMemory; + case DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY: + return IsUMA() ? 0 : m_AdapterDesc.SharedSystemMemory; + default: + D3D12MA_ASSERT(0); + return UINT64_MAX; + } +} + +HRESULT AllocatorPimpl::CreateResource( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource) +{ + D3D12MA_ASSERT(pAllocDesc && pResourceDesc && ppAllocation); + + *ppAllocation = NULL; + if (ppvResource) + { + *ppvResource = NULL; + } + + D3D12_RESOURCE_DESC finalResourceDesc = *pResourceDesc; + D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(finalResourceDesc); + D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment)); + D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0); + + BlockVector* blockVector = NULL; + CommittedAllocationParameters committedAllocationParams = {}; + bool preferCommitted = false; + HRESULT hr = CalcAllocationParams(*pAllocDesc, resAllocInfo.SizeInBytes, + pResourceDesc, + blockVector, committedAllocationParams, preferCommitted); + if (FAILED(hr)) + return hr; + + const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0; + hr = E_INVALIDARG; + if (committedAllocationParams.IsValid() && preferCommitted) + { + hr = AllocateCommittedResource(committedAllocationParams, + resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData, + &finalResourceDesc, InitialResourceState, pOptimizedClearValue, + ppAllocation, riidResource, ppvResource); + if (SUCCEEDED(hr)) + return hr; + } + if (blockVector != NULL) + { + hr = blockVector->CreateResource(resAllocInfo.SizeInBytes, resAllocInfo.Alignment, + *pAllocDesc, finalResourceDesc, + InitialResourceState, pOptimizedClearValue, + ppAllocation, riidResource, ppvResource); + if (SUCCEEDED(hr)) + return hr; + } + if (committedAllocationParams.IsValid() && !preferCommitted) + { + hr = AllocateCommittedResource(committedAllocationParams, + resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData, + &finalResourceDesc, InitialResourceState, pOptimizedClearValue, + ppAllocation, riidResource, ppvResource); + if (SUCCEEDED(hr)) + return hr; + } + return hr; +} + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ +HRESULT AllocatorPimpl::CreateResource2( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource) +{ + D3D12MA_ASSERT(pAllocDesc && pResourceDesc && ppAllocation); + + *ppAllocation = NULL; + if (ppvResource) + { + *ppvResource = NULL; + } + if (m_Device8 == NULL) + { + return E_NOINTERFACE; + } + + D3D12_RESOURCE_DESC1 finalResourceDesc = *pResourceDesc; + D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(finalResourceDesc); + D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment)); + D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0); + + BlockVector* blockVector = NULL; + CommittedAllocationParameters committedAllocationParams = {}; + bool preferCommitted = false; + HRESULT hr = CalcAllocationParams(*pAllocDesc, resAllocInfo.SizeInBytes, + pResourceDesc, + blockVector, committedAllocationParams, preferCommitted); + if (FAILED(hr)) + return hr; + + const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0; + hr = E_INVALIDARG; + if (committedAllocationParams.IsValid() && preferCommitted) + { + hr = AllocateCommittedResource2(committedAllocationParams, + resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData, + &finalResourceDesc, InitialResourceState, pOptimizedClearValue, + ppAllocation, riidResource, ppvResource); + if (SUCCEEDED(hr)) + return hr; + } + if (blockVector != NULL) + { + hr = blockVector->CreateResource2(resAllocInfo.SizeInBytes, resAllocInfo.Alignment, + *pAllocDesc, finalResourceDesc, + InitialResourceState, pOptimizedClearValue, + ppAllocation, riidResource, ppvResource); + if (SUCCEEDED(hr)) + return hr; + } + if (committedAllocationParams.IsValid() && !preferCommitted) + { + hr = AllocateCommittedResource2(committedAllocationParams, + resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData, + &finalResourceDesc, InitialResourceState, pOptimizedClearValue, + ppAllocation, riidResource, ppvResource); + if (SUCCEEDED(hr)) + return hr; + } + return hr; +} +#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ + +HRESULT AllocatorPimpl::AllocateMemory( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo, + Allocation** ppAllocation) +{ + *ppAllocation = NULL; + + BlockVector* blockVector = NULL; + CommittedAllocationParameters committedAllocationParams = {}; + bool preferCommitted = false; + HRESULT hr = CalcAllocationParams(*pAllocDesc, pAllocInfo->SizeInBytes, + NULL, // pResDesc + blockVector, committedAllocationParams, preferCommitted); + if (FAILED(hr)) + return hr; + + const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0; + hr = E_INVALIDARG; + if (committedAllocationParams.IsValid() && preferCommitted) + { + hr = AllocateHeap(committedAllocationParams, *pAllocInfo, withinBudget, pAllocDesc->pPrivateData, ppAllocation); + if (SUCCEEDED(hr)) + return hr; + } + if (blockVector != NULL) + { + hr = blockVector->Allocate(pAllocInfo->SizeInBytes, pAllocInfo->Alignment, + *pAllocDesc, 1, (Allocation**)ppAllocation); + if (SUCCEEDED(hr)) + return hr; + } + if (committedAllocationParams.IsValid() && !preferCommitted) + { + hr = AllocateHeap(committedAllocationParams, *pAllocInfo, withinBudget, pAllocDesc->pPrivateData, ppAllocation); + if (SUCCEEDED(hr)) + return hr; + } + return hr; +} + +HRESULT AllocatorPimpl::CreateAliasingResource( + Allocation* pAllocation, + UINT64 AllocationLocalOffset, + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + REFIID riidResource, + void** ppvResource) +{ + *ppvResource = NULL; + + D3D12_RESOURCE_DESC resourceDesc2 = *pResourceDesc; + D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(resourceDesc2); + D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment)); + D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0); + + ID3D12Heap* const existingHeap = pAllocation->GetHeap(); + const UINT64 existingOffset = pAllocation->GetOffset(); + const UINT64 existingSize = pAllocation->GetSize(); + const UINT64 newOffset = existingOffset + AllocationLocalOffset; + + if (existingHeap == NULL || + AllocationLocalOffset + resAllocInfo.SizeInBytes > existingSize || + newOffset % resAllocInfo.Alignment != 0) + { + return E_INVALIDARG; + } + + return m_Device->CreatePlacedResource( + existingHeap, + newOffset, + &resourceDesc2, + InitialResourceState, + pOptimizedClearValue, + riidResource, + ppvResource); +} + +void AllocatorPimpl::FreeCommittedMemory(Allocation* allocation) +{ + D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_COMMITTED); + + CommittedAllocationList* const allocList = allocation->m_Committed.list; + allocList->Unregister(allocation); + + const UINT memSegmentGroup = allocList->GetMemorySegmentGroup(this); + const UINT64 allocSize = allocation->GetSize(); + m_Budget.RemoveAllocation(memSegmentGroup, allocSize); + m_Budget.RemoveBlock(memSegmentGroup, allocSize); +} + +void AllocatorPimpl::FreePlacedMemory(Allocation* allocation) +{ + D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_PLACED); + + NormalBlock* const block = allocation->m_Placed.block; + D3D12MA_ASSERT(block); + BlockVector* const blockVector = block->GetBlockVector(); + D3D12MA_ASSERT(blockVector); + m_Budget.RemoveAllocation(HeapPropertiesToMemorySegmentGroup(block->GetHeapProperties()), allocation->GetSize()); + blockVector->Free(allocation); +} + +void AllocatorPimpl::FreeHeapMemory(Allocation* allocation) +{ + D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_HEAP); + + CommittedAllocationList* const allocList = allocation->m_Committed.list; + allocList->Unregister(allocation); + SAFE_RELEASE(allocation->m_Heap.heap); + + const UINT memSegmentGroup = allocList->GetMemorySegmentGroup(this); + const UINT64 allocSize = allocation->GetSize(); + m_Budget.RemoveAllocation(memSegmentGroup, allocSize); + m_Budget.RemoveBlock(memSegmentGroup, allocSize); +} + +void AllocatorPimpl::SetCurrentFrameIndex(UINT frameIndex) +{ + m_CurrentFrameIndex.store(frameIndex); + +#if D3D12MA_DXGI_1_4 + UpdateD3D12Budget(); +#endif +} + +void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCutomHeaps[2]) +{ + // Init stats + for (size_t i = 0; i < HEAP_TYPE_COUNT; i++) + ClearDetailedStatistics(outStats.HeapType[i]); + for (size_t i = 0; i < DXGI_MEMORY_SEGMENT_GROUP_COUNT; i++) + ClearDetailedStatistics(outStats.MemorySegmentGroup[i]); + ClearDetailedStatistics(outStats.Total); + if (outCutomHeaps) + { + ClearDetailedStatistics(outCutomHeaps[0]); + ClearDetailedStatistics(outCutomHeaps[1]); + } + + // Process default pools. 3 standard heap types only. Add them to outStats.HeapType[i]. + if (SupportsResourceHeapTier2()) + { + // DEFAULT, UPLOAD, READBACK. + for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex) + { + BlockVector* const pBlockVector = m_BlockVectors[heapTypeIndex]; + D3D12MA_ASSERT(pBlockVector); + pBlockVector->AddDetailedStatistics(outStats.HeapType[heapTypeIndex]); + } + } + else + { + // DEFAULT, UPLOAD, READBACK. + for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex) + { + for (size_t heapSubType = 0; heapSubType < 3; ++heapSubType) + { + BlockVector* const pBlockVector = m_BlockVectors[heapTypeIndex * 3 + heapSubType]; + D3D12MA_ASSERT(pBlockVector); + pBlockVector->AddDetailedStatistics(outStats.HeapType[heapTypeIndex]); + } + } + } + + // Sum them up to memory segment groups. + AddDetailedStatistics( + outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_DEFAULT)], + outStats.HeapType[0]); + AddDetailedStatistics( + outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_UPLOAD)], + outStats.HeapType[1]); + AddDetailedStatistics( + outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_READBACK)], + outStats.HeapType[2]); + + // Process custom pools. + DetailedStatistics tmpStats; + for (size_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex) + { + MutexLockRead lock(m_PoolsMutex[heapTypeIndex], m_UseMutex); + PoolList& poolList = m_Pools[heapTypeIndex]; + for (PoolPimpl* pool = poolList.Front(); pool != NULL; pool = poolList.GetNext(pool)) + { + const D3D12_HEAP_PROPERTIES& poolHeapProps = pool->GetDesc().HeapProperties; + ClearDetailedStatistics(tmpStats); + pool->AddDetailedStatistics(tmpStats); + AddDetailedStatistics( + outStats.HeapType[heapTypeIndex], tmpStats); + + UINT memorySegment = HeapPropertiesToMemorySegmentGroup(poolHeapProps); + AddDetailedStatistics( + outStats.MemorySegmentGroup[memorySegment], tmpStats); + + if (outCutomHeaps) + AddDetailedStatistics(outCutomHeaps[memorySegment], tmpStats); + } + } + + // Process committed allocations. 3 standard heap types only. + for (UINT heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex) + { + ClearDetailedStatistics(tmpStats); + m_CommittedAllocations[heapTypeIndex].AddDetailedStatistics(tmpStats); + AddDetailedStatistics( + outStats.HeapType[heapTypeIndex], tmpStats); + AddDetailedStatistics( + outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(IndexToHeapType(heapTypeIndex))], tmpStats); + } + + // Sum up memory segment groups to totals. + AddDetailedStatistics(outStats.Total, outStats.MemorySegmentGroup[0]); + AddDetailedStatistics(outStats.Total, outStats.MemorySegmentGroup[1]); + + D3D12MA_ASSERT(outStats.Total.Stats.BlockCount == + outStats.MemorySegmentGroup[0].Stats.BlockCount + outStats.MemorySegmentGroup[1].Stats.BlockCount); + D3D12MA_ASSERT(outStats.Total.Stats.AllocationCount == + outStats.MemorySegmentGroup[0].Stats.AllocationCount + outStats.MemorySegmentGroup[1].Stats.AllocationCount); + D3D12MA_ASSERT(outStats.Total.Stats.BlockBytes == + outStats.MemorySegmentGroup[0].Stats.BlockBytes + outStats.MemorySegmentGroup[1].Stats.BlockBytes); + D3D12MA_ASSERT(outStats.Total.Stats.AllocationBytes == + outStats.MemorySegmentGroup[0].Stats.AllocationBytes + outStats.MemorySegmentGroup[1].Stats.AllocationBytes); + D3D12MA_ASSERT(outStats.Total.UnusedRangeCount == + outStats.MemorySegmentGroup[0].UnusedRangeCount + outStats.MemorySegmentGroup[1].UnusedRangeCount); + + D3D12MA_ASSERT(outStats.Total.Stats.BlockCount == + outStats.HeapType[0].Stats.BlockCount + outStats.HeapType[1].Stats.BlockCount + + outStats.HeapType[2].Stats.BlockCount + outStats.HeapType[3].Stats.BlockCount); + D3D12MA_ASSERT(outStats.Total.Stats.AllocationCount == + outStats.HeapType[0].Stats.AllocationCount + outStats.HeapType[1].Stats.AllocationCount + + outStats.HeapType[2].Stats.AllocationCount + outStats.HeapType[3].Stats.AllocationCount); + D3D12MA_ASSERT(outStats.Total.Stats.BlockBytes == + outStats.HeapType[0].Stats.BlockBytes + outStats.HeapType[1].Stats.BlockBytes + + outStats.HeapType[2].Stats.BlockBytes + outStats.HeapType[3].Stats.BlockBytes); + D3D12MA_ASSERT(outStats.Total.Stats.AllocationBytes == + outStats.HeapType[0].Stats.AllocationBytes + outStats.HeapType[1].Stats.AllocationBytes + + outStats.HeapType[2].Stats.AllocationBytes + outStats.HeapType[3].Stats.AllocationBytes); + D3D12MA_ASSERT(outStats.Total.UnusedRangeCount == + outStats.HeapType[0].UnusedRangeCount + outStats.HeapType[1].UnusedRangeCount + + outStats.HeapType[2].UnusedRangeCount + outStats.HeapType[3].UnusedRangeCount); +} + +void AllocatorPimpl::GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget) +{ + if (outLocalBudget) + m_Budget.GetStatistics(outLocalBudget->Stats, DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY); + if (outNonLocalBudget) + m_Budget.GetStatistics(outNonLocalBudget->Stats, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY); + +#if D3D12MA_DXGI_1_4 + if (m_Adapter3) + { + if (!m_Budget.ShouldUpdateBudget()) + { + m_Budget.GetBudget(m_UseMutex, + outLocalBudget ? &outLocalBudget->UsageBytes : NULL, + outLocalBudget ? &outLocalBudget->BudgetBytes : NULL, + outNonLocalBudget ? &outNonLocalBudget->UsageBytes : NULL, + outNonLocalBudget ? &outNonLocalBudget->BudgetBytes : NULL); + } + else + { + UpdateD3D12Budget(); + GetBudget(outLocalBudget, outNonLocalBudget); // Recursion + } + } + else +#endif + { + if (outLocalBudget) + { + outLocalBudget->UsageBytes = outLocalBudget->Stats.BlockBytes; + outLocalBudget->BudgetBytes = GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY) * 8 / 10; // 80% heuristics. + } + if (outNonLocalBudget) + { + outNonLocalBudget->UsageBytes = outNonLocalBudget->Stats.BlockBytes; + outNonLocalBudget->BudgetBytes = GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY) * 8 / 10; // 80% heuristics. + } + } +} + +void AllocatorPimpl::GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType) +{ + switch (heapType) + { + case D3D12_HEAP_TYPE_DEFAULT: + GetBudget(&outBudget, NULL); + break; + case D3D12_HEAP_TYPE_UPLOAD: + case D3D12_HEAP_TYPE_READBACK: + GetBudget(NULL, &outBudget); + break; + default: D3D12MA_ASSERT(0); + } +} + +void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap) +{ + StringBuilder sb(GetAllocs()); + { + Budget localBudget = {}, nonLocalBudget = {}; + GetBudget(&localBudget, &nonLocalBudget); + + TotalStatistics stats; + DetailedStatistics customHeaps[2]; + CalculateStatistics(stats, customHeaps); + + JsonWriter json(GetAllocs(), sb); + json.BeginObject(); + { + json.WriteString(L"General"); + json.BeginObject(); + { + json.WriteString(L"API"); + json.WriteString(L"Direct3D 12"); + + json.WriteString(L"GPU"); + json.WriteString(m_AdapterDesc.Description); + + json.WriteString(L"DedicatedVideoMemory"); + json.WriteNumber(m_AdapterDesc.DedicatedVideoMemory); + json.WriteString(L"DedicatedSystemMemory"); + json.WriteNumber(m_AdapterDesc.DedicatedSystemMemory); + json.WriteString(L"SharedSystemMemory"); + json.WriteNumber(m_AdapterDesc.SharedSystemMemory); + + json.WriteString(L"ResourceHeapTier"); + json.WriteNumber(static_cast(m_D3D12Options.ResourceHeapTier)); + + json.WriteString(L"ResourceBindingTier"); + json.WriteNumber(static_cast(m_D3D12Options.ResourceBindingTier)); + + json.WriteString(L"TiledResourcesTier"); + json.WriteNumber(static_cast(m_D3D12Options.TiledResourcesTier)); + + json.WriteString(L"TileBasedRenderer"); + json.WriteBool(m_D3D12Architecture.TileBasedRenderer); + + json.WriteString(L"UMA"); + json.WriteBool(m_D3D12Architecture.UMA); + json.WriteString(L"CacheCoherentUMA"); + json.WriteBool(m_D3D12Architecture.CacheCoherentUMA); + } + json.EndObject(); + } + { + json.WriteString(L"Total"); + json.AddDetailedStatisticsInfoObject(stats.Total); + } + { + json.WriteString(L"MemoryInfo"); + json.BeginObject(); + { + json.WriteString(L"L0"); + json.BeginObject(); + { + json.WriteString(L"Budget"); + WriteBudgetToJson(json, IsUMA() ? localBudget : nonLocalBudget); // When UMA device only L0 present as local + + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[!IsUMA()]); + + json.WriteString(L"MemoryPools"); + json.BeginObject(); + { + if (IsUMA()) + { + json.WriteString(L"DEFAULT"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.HeapType[0]); + } + json.EndObject(); + } + json.WriteString(L"UPLOAD"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.HeapType[1]); + } + json.EndObject(); + + json.WriteString(L"READBACK"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.HeapType[2]); + } + json.EndObject(); + + json.WriteString(L"CUSTOM"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(customHeaps[!IsUMA()]); + } + json.EndObject(); + } + json.EndObject(); + } + json.EndObject(); + if (!IsUMA()) + { + json.WriteString(L"L1"); + json.BeginObject(); + { + json.WriteString(L"Budget"); + WriteBudgetToJson(json, localBudget); + + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[0]); + + json.WriteString(L"MemoryPools"); + json.BeginObject(); + { + json.WriteString(L"DEFAULT"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.HeapType[0]); + } + json.EndObject(); + + json.WriteString(L"CUSTOM"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(customHeaps[0]); + } + json.EndObject(); + } + json.EndObject(); + } + json.EndObject(); + } + } + json.EndObject(); + } + + if (detailedMap) + { + const auto writeHeapInfo = [&](BlockVector* blockVector, CommittedAllocationList* committedAllocs, bool customHeap) + { + D3D12MA_ASSERT(blockVector); + + D3D12_HEAP_FLAGS flags = blockVector->GetHeapFlags(); + json.WriteString(L"Flags"); + json.BeginArray(true); + { + if (flags & D3D12_HEAP_FLAG_SHARED) + json.WriteString(L"HEAP_FLAG_SHARED"); + if (flags & D3D12_HEAP_FLAG_ALLOW_DISPLAY) + json.WriteString(L"HEAP_FLAG_ALLOW_DISPLAY"); + if (flags & D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER) + json.WriteString(L"HEAP_FLAG_CROSS_ADAPTER"); + if (flags & D3D12_HEAP_FLAG_HARDWARE_PROTECTED) + json.WriteString(L"HEAP_FLAG_HARDWARE_PROTECTED"); + if (flags & D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH) + json.WriteString(L"HEAP_FLAG_ALLOW_WRITE_WATCH"); + if (flags & D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS) + json.WriteString(L"HEAP_FLAG_ALLOW_SHADER_ATOMICS"); +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + if (flags & D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT) + json.WriteString(L"HEAP_FLAG_CREATE_NOT_RESIDENT"); + if (flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) + json.WriteString(L"HEAP_FLAG_CREATE_NOT_ZEROED"); +#endif + + if (flags & D3D12_HEAP_FLAG_DENY_BUFFERS) + json.WriteString(L"HEAP_FLAG_DENY_BUFFERS"); + if (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) + json.WriteString(L"HEAP_FLAG_DENY_RT_DS_TEXTURES"); + if (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) + json.WriteString(L"HEAP_FLAG_DENY_NON_RT_DS_TEXTURES"); + + flags &= ~(D3D12_HEAP_FLAG_SHARED + | D3D12_HEAP_FLAG_DENY_BUFFERS + | D3D12_HEAP_FLAG_ALLOW_DISPLAY + | D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER + | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES + | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES + | D3D12_HEAP_FLAG_HARDWARE_PROTECTED + | D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH + | D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS); +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + flags &= ~(D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT + | D3D12_HEAP_FLAG_CREATE_NOT_ZEROED); +#endif + if (flags != 0) + json.WriteNumber((UINT)flags); + + if (customHeap) + { + const D3D12_HEAP_PROPERTIES& properties = blockVector->GetHeapProperties(); + switch (properties.MemoryPoolPreference) + { + default: + D3D12MA_ASSERT(0); + case D3D12_MEMORY_POOL_UNKNOWN: + json.WriteString(L"MEMORY_POOL_UNKNOWN"); + break; + case D3D12_MEMORY_POOL_L0: + json.WriteString(L"MEMORY_POOL_L0"); + break; + case D3D12_MEMORY_POOL_L1: + json.WriteString(L"MEMORY_POOL_L1"); + break; + } + switch (properties.CPUPageProperty) + { + default: + D3D12MA_ASSERT(0); + case D3D12_CPU_PAGE_PROPERTY_UNKNOWN: + json.WriteString(L"CPU_PAGE_PROPERTY_UNKNOWN"); + break; + case D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE: + json.WriteString(L"CPU_PAGE_PROPERTY_NOT_AVAILABLE"); + break; + case D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE: + json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_COMBINE"); + break; + case D3D12_CPU_PAGE_PROPERTY_WRITE_BACK: + json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_BACK"); + break; + } + } + } + json.EndArray(); + + json.WriteString(L"PreferredBlockSize"); + json.WriteNumber(blockVector->GetPreferredBlockSize()); + + json.WriteString(L"Blocks"); + blockVector->WriteBlockInfoToJson(json); + + json.WriteString(L"DedicatedAllocations"); + json.BeginArray(); + if (committedAllocs) + committedAllocs->BuildStatsString(json); + json.EndArray(); + }; + + json.WriteString(L"DefaultPools"); + json.BeginObject(); + { + if (SupportsResourceHeapTier2()) + { + for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType) + { + json.WriteString(HeapTypeNames[heapType]); + json.BeginObject(); + writeHeapInfo(m_BlockVectors[heapType], m_CommittedAllocations + heapType, false); + json.EndObject(); + } + } + else + { + for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType) + { + for (uint8_t heapSubType = 0; heapSubType < 3; ++heapSubType) + { + static const WCHAR* const heapSubTypeName[] = { + L" - Buffers", + L" - Textures", + L" - Textures RT/DS", + }; + json.BeginString(HeapTypeNames[heapType]); + json.EndString(heapSubTypeName[heapSubType]); + + json.BeginObject(); + writeHeapInfo(m_BlockVectors[heapType + heapSubType], m_CommittedAllocations + heapType, false); + json.EndObject(); + } + } + } + } + json.EndObject(); + + json.WriteString(L"CustomPools"); + json.BeginObject(); + for (uint8_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex) + { + MutexLockRead mutex(m_PoolsMutex[heapTypeIndex], m_UseMutex); + auto* item = m_Pools[heapTypeIndex].Front(); + if (item != NULL) + { + size_t index = 0; + json.WriteString(HeapTypeNames[heapTypeIndex]); + json.BeginArray(); + do + { + json.BeginObject(); + json.WriteString(L"Name"); + json.BeginString(); + json.ContinueString(index++); + if (item->GetName()) + { + json.ContinueString(L" - "); + json.ContinueString(item->GetName()); + } + json.EndString(); + + writeHeapInfo(item->GetBlockVector(), item->GetCommittedAllocationList(), heapTypeIndex == 3); + json.EndObject(); + } while ((item = PoolList::GetNext(item)) != NULL); + json.EndArray(); + } + } + json.EndObject(); + } + json.EndObject(); + } + + const size_t length = sb.GetLength(); + WCHAR* result = AllocateArray(GetAllocs(), length + 2); + result[0] = 0xFEFF; + memcpy(result + 1, sb.GetData(), length * sizeof(WCHAR)); + result[length + 1] = L'\0'; + *ppStatsString = result; +} + +void AllocatorPimpl::FreeStatsString(WCHAR* pStatsString) +{ + D3D12MA_ASSERT(pStatsString); + Free(GetAllocs(), pStatsString); +} + +template +bool AllocatorPimpl::PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc) +{ + // Intentional. It may change in the future. + return false; +} + +HRESULT AllocatorPimpl::AllocateCommittedResource( + const CommittedAllocationParameters& committedAllocParams, + UINT64 resourceSize, bool withinBudget, void* pPrivateData, + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, const D3D12_CLEAR_VALUE* pOptimizedClearValue, + Allocation** ppAllocation, REFIID riidResource, void** ppvResource) +{ + D3D12MA_ASSERT(committedAllocParams.IsValid()); + + HRESULT hr; + ID3D12Resource* res = NULL; + // Allocate aliasing memory with explicit heap + if (committedAllocParams.m_CanAlias) + { + D3D12_RESOURCE_ALLOCATION_INFO heapAllocInfo = {}; + heapAllocInfo.SizeInBytes = resourceSize; + heapAllocInfo.Alignment = HeapFlagsToAlignment(committedAllocParams.m_HeapFlags, m_MsaaAlwaysCommitted); + hr = AllocateHeap(committedAllocParams, heapAllocInfo, withinBudget, pPrivateData, ppAllocation); + if (SUCCEEDED(hr)) + { + hr = m_Device->CreatePlacedResource((*ppAllocation)->GetHeap(), 0, + pResourceDesc, InitialResourceState, + pOptimizedClearValue, D3D12MA_IID_PPV_ARGS(&res)); + if (SUCCEEDED(hr)) + { + if (ppvResource != NULL) + hr = res->QueryInterface(riidResource, ppvResource); + if (SUCCEEDED(hr)) + { + (*ppAllocation)->SetResourcePointer(res, pResourceDesc); + return hr; + } + res->Release(); + } + FreeHeapMemory(*ppAllocation); + } + return hr; + } + + if (withinBudget && + !NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, resourceSize)) + { + return E_OUTOFMEMORY; + } + + /* D3D12 ERROR: + * ID3D12Device::CreateCommittedResource: + * When creating a committed resource, D3D12_HEAP_FLAGS must not have either + * D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES, + * D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES, + * nor D3D12_HEAP_FLAG_DENY_BUFFERS set. + * These flags will be set automatically to correspond with the committed resource type. + * + * [ STATE_CREATION ERROR #640: CREATERESOURCEANDHEAP_INVALIDHEAPMISCFLAGS] + */ +#ifdef __ID3D12Device4_INTERFACE_DEFINED__ + if (m_Device4) + { + hr = m_Device4->CreateCommittedResource1( + &committedAllocParams.m_HeapProperties, + committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS, + pResourceDesc, InitialResourceState, + pOptimizedClearValue, committedAllocParams.m_ProtectedSession, D3D12MA_IID_PPV_ARGS(&res)); + } + else +#endif + { + if (committedAllocParams.m_ProtectedSession == NULL) + { + hr = m_Device->CreateCommittedResource( + &committedAllocParams.m_HeapProperties, + committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS, + pResourceDesc, InitialResourceState, + pOptimizedClearValue, D3D12MA_IID_PPV_ARGS(&res)); + } + else + hr = E_NOINTERFACE; + } + + if (SUCCEEDED(hr)) + { + if (ppvResource != NULL) + { + hr = res->QueryInterface(riidResource, ppvResource); + } + if (SUCCEEDED(hr)) + { + const BOOL wasZeroInitialized = TRUE; + Allocation* alloc = m_AllocationObjectAllocator.Allocate(this, resourceSize, pResourceDesc->Alignment, wasZeroInitialized); + alloc->InitCommitted(committedAllocParams.m_List); + alloc->SetResourcePointer(res, pResourceDesc); + alloc->SetPrivateData(pPrivateData); + + *ppAllocation = alloc; + + committedAllocParams.m_List->Register(alloc); + + const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties); + m_Budget.AddBlock(memSegmentGroup, resourceSize); + m_Budget.AddAllocation(memSegmentGroup, resourceSize); + } + else + { + res->Release(); + } + } + return hr; +} + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ +HRESULT AllocatorPimpl::AllocateCommittedResource2( + const CommittedAllocationParameters& committedAllocParams, + UINT64 resourceSize, bool withinBudget, void* pPrivateData, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, const D3D12_CLEAR_VALUE* pOptimizedClearValue, + Allocation** ppAllocation, REFIID riidResource, void** ppvResource) +{ + D3D12MA_ASSERT(committedAllocParams.IsValid()); + + if (m_Device8 == NULL) + { + return E_NOINTERFACE; + } + + HRESULT hr; + ID3D12Resource* res = NULL; + // Allocate aliasing memory with explicit heap + if (committedAllocParams.m_CanAlias) + { + D3D12_RESOURCE_ALLOCATION_INFO heapAllocInfo = {}; + heapAllocInfo.SizeInBytes = resourceSize; + heapAllocInfo.Alignment = HeapFlagsToAlignment(committedAllocParams.m_HeapFlags, m_MsaaAlwaysCommitted); + hr = AllocateHeap(committedAllocParams, heapAllocInfo, withinBudget, pPrivateData, ppAllocation); + if (SUCCEEDED(hr)) + { + hr = m_Device8->CreatePlacedResource1((*ppAllocation)->GetHeap(), 0, + pResourceDesc, InitialResourceState, + pOptimizedClearValue, D3D12MA_IID_PPV_ARGS(&res)); + if (SUCCEEDED(hr)) + { + if (ppvResource != NULL) + hr = res->QueryInterface(riidResource, ppvResource); + if (SUCCEEDED(hr)) + { + (*ppAllocation)->SetResourcePointer(res, pResourceDesc); + return hr; + } + res->Release(); + } + FreeHeapMemory(*ppAllocation); + } + return hr; + } + + if (withinBudget && + !NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, resourceSize)) + { + return E_OUTOFMEMORY; + } + + hr = m_Device8->CreateCommittedResource2( + &committedAllocParams.m_HeapProperties, + committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS, // D3D12 ERROR: ID3D12Device::CreateCommittedResource: When creating a committed resource, D3D12_HEAP_FLAGS must not have either D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES, D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES, nor D3D12_HEAP_FLAG_DENY_BUFFERS set. These flags will be set automatically to correspond with the committed resource type. [ STATE_CREATION ERROR #640: CREATERESOURCEANDHEAP_INVALIDHEAPMISCFLAGS] + pResourceDesc, InitialResourceState, + pOptimizedClearValue, committedAllocParams.m_ProtectedSession, D3D12MA_IID_PPV_ARGS(&res)); + if (SUCCEEDED(hr)) + { + if (ppvResource != NULL) + { + hr = res->QueryInterface(riidResource, ppvResource); + } + if (SUCCEEDED(hr)) + { + const BOOL wasZeroInitialized = TRUE; + Allocation* alloc = m_AllocationObjectAllocator.Allocate(this, resourceSize, pResourceDesc->Alignment, wasZeroInitialized); + alloc->InitCommitted(committedAllocParams.m_List); + alloc->SetResourcePointer(res, pResourceDesc); + alloc->SetPrivateData(pPrivateData); + + *ppAllocation = alloc; + + committedAllocParams.m_List->Register(alloc); + + const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties); + m_Budget.AddBlock(memSegmentGroup, resourceSize); + m_Budget.AddAllocation(memSegmentGroup, resourceSize); + } + else + { + res->Release(); + } + } + return hr; +} +#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ + +HRESULT AllocatorPimpl::AllocateHeap( + const CommittedAllocationParameters& committedAllocParams, + const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo, bool withinBudget, + void* pPrivateData, Allocation** ppAllocation) +{ + D3D12MA_ASSERT(committedAllocParams.IsValid()); + + *ppAllocation = nullptr; + + if (withinBudget && + !NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, allocInfo.SizeInBytes)) + { + return E_OUTOFMEMORY; + } + + D3D12_HEAP_DESC heapDesc = {}; + heapDesc.SizeInBytes = allocInfo.SizeInBytes; + heapDesc.Properties = committedAllocParams.m_HeapProperties; + heapDesc.Alignment = allocInfo.Alignment; + heapDesc.Flags = committedAllocParams.m_HeapFlags; + + HRESULT hr; + ID3D12Heap* heap = nullptr; +#ifdef __ID3D12Device4_INTERFACE_DEFINED__ + if (m_Device4) + hr = m_Device4->CreateHeap1(&heapDesc, committedAllocParams.m_ProtectedSession, D3D12MA_IID_PPV_ARGS(&heap)); + else +#endif + { + if (committedAllocParams.m_ProtectedSession == NULL) + hr = m_Device->CreateHeap(&heapDesc, D3D12MA_IID_PPV_ARGS(&heap)); + else + hr = E_NOINTERFACE; + } + + if (SUCCEEDED(hr)) + { + const BOOL wasZeroInitialized = TRUE; + (*ppAllocation) = m_AllocationObjectAllocator.Allocate(this, allocInfo.SizeInBytes, allocInfo.Alignment, wasZeroInitialized); + (*ppAllocation)->InitHeap(committedAllocParams.m_List, heap); + (*ppAllocation)->SetPrivateData(pPrivateData); + committedAllocParams.m_List->Register(*ppAllocation); + + const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties); + m_Budget.AddBlock(memSegmentGroup, allocInfo.SizeInBytes); + m_Budget.AddAllocation(memSegmentGroup, allocInfo.SizeInBytes); + } + return hr; +} + +template +HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize, + const D3D12_RESOURCE_DESC_T* resDesc, + BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted) +{ + outBlockVector = NULL; + outCommittedAllocationParams = CommittedAllocationParameters(); + outPreferCommitted = false; + + bool msaaAlwaysCommitted; + if (allocDesc.CustomPool != NULL) + { + PoolPimpl* const pool = allocDesc.CustomPool->m_Pimpl; + + msaaAlwaysCommitted = pool->GetBlockVector()->DeniesMsaaTextures(); + outBlockVector = pool->GetBlockVector(); + + outCommittedAllocationParams.m_ProtectedSession = pool->GetDesc().pProtectedSession; + outCommittedAllocationParams.m_HeapProperties = pool->GetDesc().HeapProperties; + outCommittedAllocationParams.m_HeapFlags = pool->GetDesc().HeapFlags; + outCommittedAllocationParams.m_List = pool->GetCommittedAllocationList(); + } + else + { + if (!IsHeapTypeStandard(allocDesc.HeapType)) + { + return E_INVALIDARG; + } + msaaAlwaysCommitted = m_MsaaAlwaysCommitted; + + outCommittedAllocationParams.m_HeapProperties = StandardHeapTypeToHeapProperties(allocDesc.HeapType); + outCommittedAllocationParams.m_HeapFlags = allocDesc.ExtraHeapFlags; + outCommittedAllocationParams.m_List = &m_CommittedAllocations[HeapTypeToIndex(allocDesc.HeapType)]; + + const ResourceClass resourceClass = (resDesc != NULL) ? + ResourceDescToResourceClass(*resDesc) : HeapFlagsToResourceClass(allocDesc.ExtraHeapFlags); + const UINT defaultPoolIndex = CalcDefaultPoolIndex(allocDesc, resourceClass); + if (defaultPoolIndex != UINT32_MAX) + { + outBlockVector = m_BlockVectors[defaultPoolIndex]; + const UINT64 preferredBlockSize = outBlockVector->GetPreferredBlockSize(); + if (allocSize > preferredBlockSize) + { + outBlockVector = NULL; + } + else if (allocSize > preferredBlockSize / 2) + { + // Heuristics: Allocate committed memory if requested size if greater than half of preferred block size. + outPreferCommitted = true; + } + } + + const D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS; + if (outBlockVector != NULL && extraHeapFlags != 0) + { + outBlockVector = NULL; + } + } + + if ((allocDesc.Flags & ALLOCATION_FLAG_COMMITTED) != 0 || + m_AlwaysCommitted) + { + outBlockVector = NULL; + } + if ((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) != 0) + { + outCommittedAllocationParams.m_List = NULL; + } + outCommittedAllocationParams.m_CanAlias = allocDesc.Flags & ALLOCATION_FLAG_CAN_ALIAS; + + if (resDesc != NULL) + { + if (resDesc->SampleDesc.Count > 1 && msaaAlwaysCommitted) + outBlockVector = NULL; + if (!outPreferCommitted && PrefersCommittedAllocation(*resDesc)) + outPreferCommitted = true; + } + + return (outBlockVector != NULL || outCommittedAllocationParams.m_List != NULL) ? S_OK : E_INVALIDARG; +} + +UINT AllocatorPimpl::CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const +{ + const D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS; + if (extraHeapFlags != 0) + { + return UINT32_MAX; + } + + UINT poolIndex = UINT_MAX; + switch (allocDesc.HeapType) + { + case D3D12_HEAP_TYPE_DEFAULT: poolIndex = 0; break; + case D3D12_HEAP_TYPE_UPLOAD: poolIndex = 1; break; + case D3D12_HEAP_TYPE_READBACK: poolIndex = 2; break; + default: D3D12MA_ASSERT(0); + } + + if (SupportsResourceHeapTier2()) + return poolIndex; + else + { + switch (resourceClass) + { + case ResourceClass::Buffer: + return poolIndex * 3; + case ResourceClass::Non_RT_DS_Texture: + return poolIndex * 3 + 1; + case ResourceClass::RT_DS_Texture: + return poolIndex * 3 + 2; + default: + return UINT32_MAX; + } + } +} + +void AllocatorPimpl::CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const +{ + outHeapType = D3D12_HEAP_TYPE_DEFAULT; + outHeapFlags = D3D12_HEAP_FLAG_NONE; + + if (!SupportsResourceHeapTier2()) + { + switch (index % 3) + { + case 0: + outHeapFlags = D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES; + break; + case 1: + outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES; + break; + case 2: + outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES; + break; + } + + index /= 3; + } + + switch (index) + { + case 0: + outHeapType = D3D12_HEAP_TYPE_DEFAULT; + break; + case 1: + outHeapType = D3D12_HEAP_TYPE_UPLOAD; + break; + case 2: + outHeapType = D3D12_HEAP_TYPE_READBACK; + break; + default: + D3D12MA_ASSERT(0); + } +} + +void AllocatorPimpl::RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType) +{ + const UINT heapTypeIndex = HeapTypeToIndex(heapType); + + MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex); + m_Pools[heapTypeIndex].PushBack(pool->m_Pimpl); +} + +void AllocatorPimpl::UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType) +{ + const UINT heapTypeIndex = HeapTypeToIndex(heapType); + + MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex); + m_Pools[heapTypeIndex].Remove(pool->m_Pimpl); +} + +HRESULT AllocatorPimpl::UpdateD3D12Budget() +{ +#if D3D12MA_DXGI_1_4 + if (m_Adapter3) + return m_Budget.UpdateBudget(m_Adapter3, m_UseMutex); + else + return E_NOINTERFACE; +#else + return S_OK; +#endif +} + +D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const +{ + return m_Device->GetResourceAllocationInfo(0, 1, &resourceDesc); +} + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ +D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC1& resourceDesc) const +{ + D3D12MA_ASSERT(m_Device8 != NULL); + D3D12_RESOURCE_ALLOCATION_INFO1 info1Unused; + return m_Device8->GetResourceAllocationInfo2(0, 1, &resourceDesc, &info1Unused); +} +#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ + +template +D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const +{ + /* Optional optimization: Microsoft documentation says: + https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-getresourceallocationinfo + + Your application can forgo using GetResourceAllocationInfo for buffer resources + (D3D12_RESOURCE_DIMENSION_BUFFER). Buffers have the same size on all adapters, + which is merely the smallest multiple of 64KB that's greater or equal to + D3D12_RESOURCE_DESC::Width. + */ + if (inOutResourceDesc.Alignment == 0 && + inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) + { + return { + AlignUp(inOutResourceDesc.Width, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), // SizeInBytes + D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT }; // Alignment + } + +#if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT + if (inOutResourceDesc.Alignment == 0 && + inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D && + (inOutResourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 0 +#if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT == 1 + && CanUseSmallAlignment(inOutResourceDesc) +#endif + ) + { + /* + The algorithm here is based on Microsoft sample: "Small Resources Sample" + https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12SmallResources + */ + const UINT64 smallAlignmentToTry = inOutResourceDesc.SampleDesc.Count > 1 ? + D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT : + D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT; + inOutResourceDesc.Alignment = smallAlignmentToTry; + const D3D12_RESOURCE_ALLOCATION_INFO smallAllocInfo = GetResourceAllocationInfoNative(inOutResourceDesc); + // Check if alignment requested has been granted. + if (smallAllocInfo.Alignment == smallAlignmentToTry) + { + return smallAllocInfo; + } + inOutResourceDesc.Alignment = 0; // Restore original + } +#endif // #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT + + return GetResourceAllocationInfoNative(inOutResourceDesc); +} + +bool AllocatorPimpl::NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size) +{ + Budget budget = {}; + GetBudgetForHeapType(budget, heapType); + return budget.UsageBytes + size <= budget.BudgetBytes; +} + +void AllocatorPimpl::WriteBudgetToJson(JsonWriter& json, const Budget& budget) +{ + json.BeginObject(); + { + json.WriteString(L"BudgetBytes"); + json.WriteNumber(budget.BudgetBytes); + json.WriteString(L"UsageBytes"); + json.WriteNumber(budget.UsageBytes); + } + json.EndObject(); +} +#endif // _D3D12MA_ALLOCATOR_PIMPL +#endif // _D3D12MA_ALLOCATOR_PIMPL + +#ifndef _D3D12MA_VIRTUAL_BLOCK_PIMPL +class VirtualBlockPimpl +{ +public: + const ALLOCATION_CALLBACKS m_AllocationCallbacks; + const UINT64 m_Size; + BlockMetadata* m_Metadata; + + VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc); + ~VirtualBlockPimpl(); +}; + +#ifndef _D3D12MA_VIRTUAL_BLOCK_PIMPL_FUNCTIONS +VirtualBlockPimpl::VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc) + : m_AllocationCallbacks(allocationCallbacks), m_Size(desc.Size) +{ + switch (desc.Flags & VIRTUAL_BLOCK_FLAG_ALGORITHM_MASK) + { + case VIRTUAL_BLOCK_FLAG_ALGORITHM_LINEAR: + m_Metadata = D3D12MA_NEW(allocationCallbacks, BlockMetadata_Linear)(&m_AllocationCallbacks, true); + break; + default: + D3D12MA_ASSERT(0); + case 0: + m_Metadata = D3D12MA_NEW(allocationCallbacks, BlockMetadata_TLSF)(&m_AllocationCallbacks, true); + break; + } + m_Metadata->Init(m_Size); +} + +VirtualBlockPimpl::~VirtualBlockPimpl() +{ + D3D12MA_DELETE(m_AllocationCallbacks, m_Metadata); +} +#endif // _D3D12MA_VIRTUAL_BLOCK_PIMPL_FUNCTIONS +#endif // _D3D12MA_VIRTUAL_BLOCK_PIMPL + + +#ifndef _D3D12MA_MEMORY_BLOCK_FUNCTIONS +MemoryBlock::MemoryBlock( + AllocatorPimpl* allocator, + const D3D12_HEAP_PROPERTIES& heapProps, + D3D12_HEAP_FLAGS heapFlags, + UINT64 size, + UINT id) + : m_Allocator(allocator), + m_HeapProps(heapProps), + m_HeapFlags(heapFlags), + m_Size(size), + m_Id(id) {} + +MemoryBlock::~MemoryBlock() +{ + if (m_Heap) + { + m_Heap->Release(); + m_Allocator->m_Budget.RemoveBlock( + m_Allocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), m_Size); + } +} + +HRESULT MemoryBlock::Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures) +{ + D3D12MA_ASSERT(m_Heap == NULL && m_Size > 0); + + D3D12_HEAP_DESC heapDesc = {}; + heapDesc.SizeInBytes = m_Size; + heapDesc.Properties = m_HeapProps; + heapDesc.Alignment = HeapFlagsToAlignment(m_HeapFlags, denyMsaaTextures); + heapDesc.Flags = m_HeapFlags; + + HRESULT hr; +#ifdef __ID3D12Device4_INTERFACE_DEFINED__ + ID3D12Device4* const device4 = m_Allocator->GetDevice4(); + if (device4) + hr = m_Allocator->GetDevice4()->CreateHeap1(&heapDesc, pProtectedSession, D3D12MA_IID_PPV_ARGS(&m_Heap)); + else +#endif + { + if (pProtectedSession == NULL) + hr = m_Allocator->GetDevice()->CreateHeap(&heapDesc, D3D12MA_IID_PPV_ARGS(&m_Heap)); + else + hr = E_NOINTERFACE; + } + + if (SUCCEEDED(hr)) + { + m_Allocator->m_Budget.AddBlock( + m_Allocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), m_Size); + } + return hr; +} +#endif // _D3D12MA_MEMORY_BLOCK_FUNCTIONS + +#ifndef _D3D12MA_NORMAL_BLOCK_FUNCTIONS +NormalBlock::NormalBlock( + AllocatorPimpl* allocator, + BlockVector* blockVector, + const D3D12_HEAP_PROPERTIES& heapProps, + D3D12_HEAP_FLAGS heapFlags, + UINT64 size, + UINT id) + : MemoryBlock(allocator, heapProps, heapFlags, size, id), + m_pMetadata(NULL), + m_BlockVector(blockVector) {} + +NormalBlock::~NormalBlock() +{ + if (m_pMetadata != NULL) + { + // THIS IS THE MOST IMPORTANT ASSERT IN THE ENTIRE LIBRARY! + // Hitting it means you have some memory leak - unreleased Allocation objects. + D3D12MA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!"); + + D3D12MA_DELETE(m_Allocator->GetAllocs(), m_pMetadata); + } +} + +HRESULT NormalBlock::Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures) +{ + HRESULT hr = MemoryBlock::Init(pProtectedSession, denyMsaaTextures); + if (FAILED(hr)) + { + return hr; + } + + switch (algorithm) + { + case POOL_FLAG_ALGORITHM_LINEAR: + m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_Linear)(&m_Allocator->GetAllocs(), false); + break; + default: + D3D12MA_ASSERT(0); + case 0: + m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_TLSF)(&m_Allocator->GetAllocs(), false); + break; + } + m_pMetadata->Init(m_Size); + + return hr; +} + +bool NormalBlock::Validate() const +{ + D3D12MA_VALIDATE(GetHeap() && + m_pMetadata && + m_pMetadata->GetSize() != 0 && + m_pMetadata->GetSize() == GetSize()); + return m_pMetadata->Validate(); +} +#endif // _D3D12MA_NORMAL_BLOCK_FUNCTIONS + +#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST_FUNCTIONS +void CommittedAllocationList::Init(bool useMutex, D3D12_HEAP_TYPE heapType, PoolPimpl* pool) +{ + m_UseMutex = useMutex; + m_HeapType = heapType; + m_Pool = pool; +} + +CommittedAllocationList::~CommittedAllocationList() +{ + if (!m_AllocationList.IsEmpty()) + { + D3D12MA_ASSERT(0 && "Unfreed committed allocations found!"); + } +} + +UINT CommittedAllocationList::GetMemorySegmentGroup(AllocatorPimpl* allocator) const +{ + if (m_Pool) + return allocator->HeapPropertiesToMemorySegmentGroup(m_Pool->GetDesc().HeapProperties); + else + return allocator->StandardHeapTypeToMemorySegmentGroup(m_HeapType); +} + +void CommittedAllocationList::AddStatistics(Statistics& inoutStats) +{ + MutexLockRead lock(m_Mutex, m_UseMutex); + + for (Allocation* alloc = m_AllocationList.Front(); + alloc != NULL; alloc = m_AllocationList.GetNext(alloc)) + { + const UINT64 size = alloc->GetSize(); + inoutStats.BlockCount++; + inoutStats.AllocationCount++; + inoutStats.BlockBytes += size; + inoutStats.AllocationBytes += size; + } +} + +void CommittedAllocationList::AddDetailedStatistics(DetailedStatistics& inoutStats) +{ + MutexLockRead lock(m_Mutex, m_UseMutex); + + for (Allocation* alloc = m_AllocationList.Front(); + alloc != NULL; alloc = m_AllocationList.GetNext(alloc)) + { + const UINT64 size = alloc->GetSize(); + inoutStats.Stats.BlockCount++; + inoutStats.Stats.BlockBytes += size; + AddDetailedStatisticsAllocation(inoutStats, size); + } +} + +void CommittedAllocationList::BuildStatsString(JsonWriter& json) +{ + MutexLockRead lock(m_Mutex, m_UseMutex); + + for (Allocation* alloc = m_AllocationList.Front(); + alloc != NULL; alloc = m_AllocationList.GetNext(alloc)) + { + json.BeginObject(true); + json.AddAllocationToObject(*alloc); + json.EndObject(); + } +} + +void CommittedAllocationList::Register(Allocation* alloc) +{ + MutexLockWrite lock(m_Mutex, m_UseMutex); + m_AllocationList.PushBack(alloc); +} + +void CommittedAllocationList::Unregister(Allocation* alloc) +{ + MutexLockWrite lock(m_Mutex, m_UseMutex); + m_AllocationList.Remove(alloc); +} +#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST_FUNCTIONS + +#ifndef _D3D12MA_BLOCK_VECTOR_FUNCTIONS +BlockVector::BlockVector( + AllocatorPimpl* hAllocator, + const D3D12_HEAP_PROPERTIES& heapProps, + D3D12_HEAP_FLAGS heapFlags, + UINT64 preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + bool explicitBlockSize, + UINT64 minAllocationAlignment, + UINT32 algorithm, + bool denyMsaaTextures, + ID3D12ProtectedResourceSession* pProtectedSession) + : m_hAllocator(hAllocator), + m_HeapProps(heapProps), + m_HeapFlags(heapFlags), + m_PreferredBlockSize(preferredBlockSize), + m_MinBlockCount(minBlockCount), + m_MaxBlockCount(maxBlockCount), + m_ExplicitBlockSize(explicitBlockSize), + m_MinAllocationAlignment(minAllocationAlignment), + m_Algorithm(algorithm), + m_DenyMsaaTextures(denyMsaaTextures), + m_ProtectedSession(pProtectedSession), + m_HasEmptyBlock(false), + m_Blocks(hAllocator->GetAllocs()), + m_NextBlockId(0) {} + +BlockVector::~BlockVector() +{ + for (size_t i = m_Blocks.size(); i--; ) + { + D3D12MA_DELETE(m_hAllocator->GetAllocs(), m_Blocks[i]); + } +} + +HRESULT BlockVector::CreateMinBlocks() +{ + for (size_t i = 0; i < m_MinBlockCount; ++i) + { + HRESULT hr = CreateBlock(m_PreferredBlockSize, NULL); + if (FAILED(hr)) + { + return hr; + } + } + return S_OK; +} + +bool BlockVector::IsEmpty() +{ + MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex()); + return m_Blocks.empty(); +} + +HRESULT BlockVector::Allocate( + UINT64 size, + UINT64 alignment, + const ALLOCATION_DESC& allocDesc, + size_t allocationCount, + Allocation** pAllocations) +{ + size_t allocIndex; + HRESULT hr = S_OK; + + { + MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex()); + for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + hr = AllocatePage( + size, + alignment, + allocDesc, + pAllocations + allocIndex); + if (FAILED(hr)) + { + break; + } + } + } + + if (FAILED(hr)) + { + // Free all already created allocations. + while (allocIndex--) + { + Free(pAllocations[allocIndex]); + } + ZeroMemory(pAllocations, sizeof(Allocation*) * allocationCount); + } + + return hr; +} + +void BlockVector::Free(Allocation* hAllocation) +{ + NormalBlock* pBlockToDelete = NULL; + + bool budgetExceeded = false; + if (IsHeapTypeStandard(m_HeapProps.Type)) + { + Budget budget = {}; + m_hAllocator->GetBudgetForHeapType(budget, m_HeapProps.Type); + budgetExceeded = budget.UsageBytes >= budget.BudgetBytes; + } + + // Scope for lock. + { + MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex()); + + NormalBlock* pBlock = hAllocation->m_Placed.block; + + pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle()); + D3D12MA_HEAVY_ASSERT(pBlock->Validate()); + + const size_t blockCount = m_Blocks.size(); + // pBlock became empty after this deallocation. + if (pBlock->m_pMetadata->IsEmpty()) + { + // Already has empty Allocation. We don't want to have two, so delete this one. + if ((m_HasEmptyBlock || budgetExceeded) && + blockCount > m_MinBlockCount) + { + pBlockToDelete = pBlock; + Remove(pBlock); + } + // We now have first empty block. + else + { + m_HasEmptyBlock = true; + } + } + // pBlock didn't become empty, but we have another empty block - find and free that one. + // (This is optional, heuristics.) + else if (m_HasEmptyBlock && blockCount > m_MinBlockCount) + { + NormalBlock* pLastBlock = m_Blocks.back(); + if (pLastBlock->m_pMetadata->IsEmpty()) + { + pBlockToDelete = pLastBlock; + m_Blocks.pop_back(); + m_HasEmptyBlock = false; + } + } + + IncrementallySortBlocks(); + } + + // Destruction of a free Allocation. Deferred until this point, outside of mutex + // lock, for performance reason. + if (pBlockToDelete != NULL) + { + D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlockToDelete); + } +} + +HRESULT BlockVector::CreateResource( + UINT64 size, + UINT64 alignment, + const ALLOCATION_DESC& allocDesc, + const D3D12_RESOURCE_DESC& resourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource) +{ + HRESULT hr = Allocate(size, alignment, allocDesc, 1, ppAllocation); + if (SUCCEEDED(hr)) + { + ID3D12Resource* res = NULL; + hr = m_hAllocator->GetDevice()->CreatePlacedResource( + (*ppAllocation)->m_Placed.block->GetHeap(), + (*ppAllocation)->GetOffset(), + &resourceDesc, + InitialResourceState, + pOptimizedClearValue, + D3D12MA_IID_PPV_ARGS(&res)); + if (SUCCEEDED(hr)) + { + if (ppvResource != NULL) + { + hr = res->QueryInterface(riidResource, ppvResource); + } + if (SUCCEEDED(hr)) + { + (*ppAllocation)->SetResourcePointer(res, &resourceDesc); + } + else + { + res->Release(); + SAFE_RELEASE(*ppAllocation); + } + } + else + { + SAFE_RELEASE(*ppAllocation); + } + } + return hr; +} + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ +HRESULT BlockVector::CreateResource2( + UINT64 size, + UINT64 alignment, + const ALLOCATION_DESC& allocDesc, + const D3D12_RESOURCE_DESC1& resourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource) +{ + ID3D12Device8* const device8 = m_hAllocator->GetDevice8(); + if (device8 == NULL) + { + return E_NOINTERFACE; + } + + HRESULT hr = Allocate(size, alignment, allocDesc, 1, ppAllocation); + if (SUCCEEDED(hr)) + { + ID3D12Resource* res = NULL; + hr = device8->CreatePlacedResource1( + (*ppAllocation)->m_Placed.block->GetHeap(), + (*ppAllocation)->GetOffset(), + &resourceDesc, + InitialResourceState, + pOptimizedClearValue, + D3D12MA_IID_PPV_ARGS(&res)); + if (SUCCEEDED(hr)) + { + if (ppvResource != NULL) + { + hr = res->QueryInterface(riidResource, ppvResource); + } + if (SUCCEEDED(hr)) + { + (*ppAllocation)->SetResourcePointer(res, &resourceDesc); + } + else + { + res->Release(); + SAFE_RELEASE(*ppAllocation); + } + } + else + { + SAFE_RELEASE(*ppAllocation); + } + } + return hr; +} +#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ + +void BlockVector::AddStatistics(Statistics& inoutStats) +{ + MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex()); + + for (size_t i = 0; i < m_Blocks.size(); ++i) + { + const NormalBlock* const pBlock = m_Blocks[i]; + D3D12MA_ASSERT(pBlock); + D3D12MA_HEAVY_ASSERT(pBlock->Validate()); + pBlock->m_pMetadata->AddStatistics(inoutStats); + } +} + +void BlockVector::AddDetailedStatistics(DetailedStatistics& inoutStats) +{ + MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex()); + + for (size_t i = 0; i < m_Blocks.size(); ++i) + { + const NormalBlock* const pBlock = m_Blocks[i]; + D3D12MA_ASSERT(pBlock); + D3D12MA_HEAVY_ASSERT(pBlock->Validate()); + pBlock->m_pMetadata->AddDetailedStatistics(inoutStats); + } +} + +void BlockVector::WriteBlockInfoToJson(JsonWriter& json) +{ + MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex()); + + json.BeginObject(); + + for (size_t i = 0, count = m_Blocks.size(); i < count; ++i) + { + const NormalBlock* const pBlock = m_Blocks[i]; + D3D12MA_ASSERT(pBlock); + D3D12MA_HEAVY_ASSERT(pBlock->Validate()); + json.BeginString(); + json.ContinueString(pBlock->GetId()); + json.EndString(); + + json.BeginObject(); + pBlock->m_pMetadata->WriteAllocationInfoToJson(json); + json.EndObject(); + } + + json.EndObject(); +} + +UINT64 BlockVector::CalcSumBlockSize() const +{ + UINT64 result = 0; + for (size_t i = m_Blocks.size(); i--; ) + { + result += m_Blocks[i]->m_pMetadata->GetSize(); + } + return result; +} + +UINT64 BlockVector::CalcMaxBlockSize() const +{ + UINT64 result = 0; + for (size_t i = m_Blocks.size(); i--; ) + { + result = D3D12MA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize()); + if (result >= m_PreferredBlockSize) + { + break; + } + } + return result; +} + +void BlockVector::Remove(NormalBlock* pBlock) +{ + for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + if (m_Blocks[blockIndex] == pBlock) + { + m_Blocks.remove(blockIndex); + return; + } + } + D3D12MA_ASSERT(0); +} + +void BlockVector::IncrementallySortBlocks() +{ + if (!m_IncrementalSort) + return; + // Bubble sort only until first swap. + for (size_t i = 1; i < m_Blocks.size(); ++i) + { + if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize()) + { + D3D12MA_SWAP(m_Blocks[i - 1], m_Blocks[i]); + return; + } + } +} + +void BlockVector::SortByFreeSize() +{ + D3D12MA_SORT(m_Blocks.begin(), m_Blocks.end(), + [](auto* b1, auto* b2) + { + return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize(); + }); +} + +HRESULT BlockVector::AllocatePage( + UINT64 size, + UINT64 alignment, + const ALLOCATION_DESC& allocDesc, + Allocation** pAllocation) +{ + // Early reject: requested allocation size is larger that maximum block size for this block vector. + if (size + D3D12MA_DEBUG_MARGIN > m_PreferredBlockSize) + { + return E_OUTOFMEMORY; + } + + UINT64 freeMemory = UINT64_MAX; + if (IsHeapTypeStandard(m_HeapProps.Type)) + { + Budget budget = {}; + m_hAllocator->GetBudgetForHeapType(budget, m_HeapProps.Type); + freeMemory = (budget.UsageBytes < budget.BudgetBytes) ? (budget.BudgetBytes - budget.UsageBytes) : 0; + } + + const bool canCreateNewBlock = + ((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0) && + (m_Blocks.size() < m_MaxBlockCount) && + // Even if we don't have to stay within budget with this allocation, when the + // budget would be exceeded, we don't want to allocate new blocks, but always + // create resources as committed. + freeMemory >= size; + + // 1. Search existing allocations + { + // Forward order in m_Blocks - prefer blocks with smallest amount of free space. + for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + NormalBlock* const pCurrBlock = m_Blocks[blockIndex]; + D3D12MA_ASSERT(pCurrBlock); + HRESULT hr = AllocateFromBlock( + pCurrBlock, + size, + alignment, + allocDesc.Flags, + allocDesc.pPrivateData, + allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK, + pAllocation); + if (SUCCEEDED(hr)) + { + return hr; + } + } + } + + // 2. Try to create new block. + if (canCreateNewBlock) + { + // Calculate optimal size for new block. + UINT64 newBlockSize = m_PreferredBlockSize; + UINT newBlockSizeShift = 0; + + if (!m_ExplicitBlockSize) + { + // Allocate 1/8, 1/4, 1/2 as first blocks. + const UINT64 maxExistingBlockSize = CalcMaxBlockSize(); + for (UINT i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i) + { + const UINT64 smallerNewBlockSize = newBlockSize / 2; + if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + } + else + { + break; + } + } + } + + size_t newBlockIndex = 0; + HRESULT hr = newBlockSize <= freeMemory ? + CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY; + // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize. + if (!m_ExplicitBlockSize) + { + while (FAILED(hr) && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX) + { + const UINT64 smallerNewBlockSize = newBlockSize / 2; + if (smallerNewBlockSize >= size) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + hr = newBlockSize <= freeMemory ? + CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY; + } + else + { + break; + } + } + } + + if (SUCCEEDED(hr)) + { + NormalBlock* const pBlock = m_Blocks[newBlockIndex]; + D3D12MA_ASSERT(pBlock->m_pMetadata->GetSize() >= size); + + hr = AllocateFromBlock( + pBlock, + size, + alignment, + allocDesc.Flags, + allocDesc.pPrivateData, + allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK, + pAllocation); + if (SUCCEEDED(hr)) + { + return hr; + } + else + { + // Allocation from new block failed, possibly due to D3D12MA_DEBUG_MARGIN or alignment. + return E_OUTOFMEMORY; + } + } + } + + return E_OUTOFMEMORY; +} + +HRESULT BlockVector::AllocateFromBlock( + NormalBlock* pBlock, + UINT64 size, + UINT64 alignment, + ALLOCATION_FLAGS allocFlags, + void* pPrivateData, + UINT32 strategy, + Allocation** pAllocation) +{ + alignment = D3D12MA_MAX(alignment, m_MinAllocationAlignment); + + AllocationRequest currRequest = {}; + if (pBlock->m_pMetadata->CreateAllocationRequest( + size, + alignment, + allocFlags & ALLOCATION_FLAG_UPPER_ADDRESS, + strategy, + &currRequest)) + { + return CommitAllocationRequest(currRequest, pBlock, size, alignment, pPrivateData, pAllocation); + } + return E_OUTOFMEMORY; +} + +HRESULT BlockVector::CommitAllocationRequest( + AllocationRequest& allocRequest, + NormalBlock* pBlock, + UINT64 size, + UINT64 alignment, + void* pPrivateData, + Allocation** pAllocation) +{ + // We no longer have an empty Allocation. + if (pBlock->m_pMetadata->IsEmpty()) + m_HasEmptyBlock = false; + + *pAllocation = m_hAllocator->GetAllocationObjectAllocator().Allocate(m_hAllocator, size, alignment, allocRequest.zeroInitialized); + pBlock->m_pMetadata->Alloc(allocRequest, size, *pAllocation); + + (*pAllocation)->InitPlaced(allocRequest.allocHandle, pBlock); + (*pAllocation)->SetPrivateData(pPrivateData); + + D3D12MA_HEAVY_ASSERT(pBlock->Validate()); + m_hAllocator->m_Budget.AddAllocation(m_hAllocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), size); + + return S_OK; +} + +HRESULT BlockVector::CreateBlock( + UINT64 blockSize, + size_t* pNewBlockIndex) +{ + NormalBlock* const pBlock = D3D12MA_NEW(m_hAllocator->GetAllocs(), NormalBlock)( + m_hAllocator, + this, + m_HeapProps, + m_HeapFlags, + blockSize, + m_NextBlockId++); + HRESULT hr = pBlock->Init(m_Algorithm, m_ProtectedSession, m_DenyMsaaTextures); + if (FAILED(hr)) + { + D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlock); + return hr; + } + + m_Blocks.push_back(pBlock); + if (pNewBlockIndex != NULL) + { + *pNewBlockIndex = m_Blocks.size() - 1; + } + + return hr; +} +#endif // _D3D12MA_BLOCK_VECTOR_FUNCTIONS + +#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL_FUNCTIONS +DefragmentationContextPimpl::DefragmentationContextPimpl( + AllocatorPimpl* hAllocator, + const DEFRAGMENTATION_DESC& desc, + BlockVector* poolVector) + : m_MaxPassBytes(desc.MaxBytesPerPass == 0 ? UINT64_MAX : desc.MaxBytesPerPass), + m_MaxPassAllocations(desc.MaxAllocationsPerPass == 0 ? UINT32_MAX : desc.MaxAllocationsPerPass), + m_Moves(hAllocator->GetAllocs()) +{ + m_Algorithm = desc.Flags & DEFRAGMENTATION_FLAG_ALGORITHM_MASK; + + if (poolVector != NULL) + { + m_BlockVectorCount = 1; + m_PoolBlockVector = poolVector; + m_pBlockVectors = &m_PoolBlockVector; + m_PoolBlockVector->SetIncrementalSort(false); + m_PoolBlockVector->SortByFreeSize(); + } + else + { + m_BlockVectorCount = hAllocator->GetDefaultPoolCount(); + m_PoolBlockVector = NULL; + m_pBlockVectors = hAllocator->GetDefaultPools(); + for (UINT32 i = 0; i < m_BlockVectorCount; ++i) + { + BlockVector* vector = m_pBlockVectors[i]; + if (vector != NULL) + { + vector->SetIncrementalSort(false); + vector->SortByFreeSize(); + } + } + } + + switch (m_Algorithm) + { + case 0: // Default algorithm + m_Algorithm = DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED; + case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED: + { + m_AlgorithmState = D3D12MA_NEW_ARRAY(hAllocator->GetAllocs(), StateBalanced, m_BlockVectorCount); + break; + } + } +} + +DefragmentationContextPimpl::~DefragmentationContextPimpl() +{ + if (m_PoolBlockVector != NULL) + m_PoolBlockVector->SetIncrementalSort(true); + else + { + for (UINT32 i = 0; i < m_BlockVectorCount; ++i) + { + BlockVector* vector = m_pBlockVectors[i]; + if (vector != NULL) + vector->SetIncrementalSort(true); + } + } + + if (m_AlgorithmState) + { + switch (m_Algorithm) + { + case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED: + D3D12MA_DELETE_ARRAY(m_Moves.GetAllocs(), reinterpret_cast(m_AlgorithmState), m_BlockVectorCount); + break; + default: + D3D12MA_ASSERT(0); + } + } +} + +HRESULT DefragmentationContextPimpl::DefragmentPassBegin(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo) +{ + if (m_PoolBlockVector != NULL) + { + MutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->m_hAllocator->UseMutex()); + + if (m_PoolBlockVector->GetBlockCount() > 1) + ComputeDefragmentation(*m_PoolBlockVector, 0); + else if (m_PoolBlockVector->GetBlockCount() == 1) + ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0)); + + // Setup index into block vector + for (size_t i = 0; i < m_Moves.size(); ++i) + m_Moves[i].pDstTmpAllocation->SetPrivateData(0); + } + else + { + for (UINT32 i = 0; i < m_BlockVectorCount; ++i) + { + if (m_pBlockVectors[i] != NULL) + { + MutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->m_hAllocator->UseMutex()); + + bool end = false; + size_t movesOffset = m_Moves.size(); + if (m_pBlockVectors[i]->GetBlockCount() > 1) + { + end = ComputeDefragmentation(*m_pBlockVectors[i], i); + } + else if (m_pBlockVectors[i]->GetBlockCount() == 1) + { + end = ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0)); + } + + // Setup index into block vector + for (; movesOffset < m_Moves.size(); ++movesOffset) + m_Moves[movesOffset].pDstTmpAllocation->SetPrivateData(reinterpret_cast(static_cast(i))); + + if (end) + break; + } + } + } + + moveInfo.MoveCount = static_cast(m_Moves.size()); + if (moveInfo.MoveCount > 0) + { + moveInfo.pMoves = m_Moves.data(); + return S_FALSE; + } + + moveInfo.pMoves = NULL; + return S_OK; +} + +HRESULT DefragmentationContextPimpl::DefragmentPassEnd(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo) +{ + D3D12MA_ASSERT(moveInfo.MoveCount > 0 ? moveInfo.pMoves != NULL : true); + + HRESULT result = S_OK; + Vector immovableBlocks(m_Moves.GetAllocs()); + + for (uint32_t i = 0; i < moveInfo.MoveCount; ++i) + { + DEFRAGMENTATION_MOVE& move = moveInfo.pMoves[i]; + size_t prevCount = 0, currentCount = 0; + UINT64 freedBlockSize = 0; + + UINT32 vectorIndex; + BlockVector* vector; + if (m_PoolBlockVector != NULL) + { + vectorIndex = 0; + vector = m_PoolBlockVector; + } + else + { + vectorIndex = static_cast(reinterpret_cast(move.pDstTmpAllocation->GetPrivateData())); + vector = m_pBlockVectors[vectorIndex]; + D3D12MA_ASSERT(vector != NULL); + } + + switch (move.Operation) + { + case DEFRAGMENTATION_MOVE_OPERATION_COPY: + { + move.pSrcAllocation->SwapBlockAllocation(move.pDstTmpAllocation); + + // Scope for locks, Free have it's own lock + { + MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex()); + prevCount = vector->GetBlockCount(); + freedBlockSize = move.pDstTmpAllocation->GetBlock()->m_pMetadata->GetSize(); + } + move.pDstTmpAllocation->Release(); + { + MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex()); + currentCount = vector->GetBlockCount(); + } + + result = S_FALSE; + break; + } + case DEFRAGMENTATION_MOVE_OPERATION_IGNORE: + { + m_PassStats.BytesMoved -= move.pSrcAllocation->GetSize(); + --m_PassStats.AllocationsMoved; + move.pDstTmpAllocation->Release(); + + NormalBlock* newBlock = move.pSrcAllocation->GetBlock(); + bool notPresent = true; + for (const FragmentedBlock& block : immovableBlocks) + { + if (block.block == newBlock) + { + notPresent = false; + break; + } + } + if (notPresent) + immovableBlocks.push_back({ vectorIndex, newBlock }); + break; + } + case DEFRAGMENTATION_MOVE_OPERATION_DESTROY: + { + m_PassStats.BytesMoved -= move.pSrcAllocation->GetSize(); + --m_PassStats.AllocationsMoved; + // Scope for locks, Free have it's own lock + { + MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex()); + prevCount = vector->GetBlockCount(); + freedBlockSize = move.pSrcAllocation->GetBlock()->m_pMetadata->GetSize(); + } + move.pSrcAllocation->Release(); + { + MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex()); + currentCount = vector->GetBlockCount(); + } + freedBlockSize *= prevCount - currentCount; + + UINT64 dstBlockSize; + { + MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex()); + dstBlockSize = move.pDstTmpAllocation->GetBlock()->m_pMetadata->GetSize(); + } + move.pDstTmpAllocation->Release(); + { + MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex()); + freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount()); + currentCount = vector->GetBlockCount(); + } + + result = S_FALSE; + break; + } + default: + D3D12MA_ASSERT(0); + } + + if (prevCount > currentCount) + { + size_t freedBlocks = prevCount - currentCount; + m_PassStats.HeapsFreed += static_cast(freedBlocks); + m_PassStats.BytesFreed += freedBlockSize; + } + } + moveInfo.MoveCount = 0; + moveInfo.pMoves = NULL; + m_Moves.clear(); + + // Update stats + m_GlobalStats.AllocationsMoved += m_PassStats.AllocationsMoved; + m_GlobalStats.BytesFreed += m_PassStats.BytesFreed; + m_GlobalStats.BytesMoved += m_PassStats.BytesMoved; + m_GlobalStats.HeapsFreed += m_PassStats.HeapsFreed; + m_PassStats = { 0 }; + + // Move blocks with immovable allocations according to algorithm + if (immovableBlocks.size() > 0) + { + // Move to the begining + for (const FragmentedBlock& block : immovableBlocks) + { + BlockVector* vector = m_pBlockVectors[block.data]; + MutexLockWrite lock(vector->GetMutex(), vector->m_hAllocator->UseMutex()); + + for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i) + { + if (vector->GetBlock(i) == block.block) + { + D3D12MA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]); + break; + } + } + } + } + return result; +} + +bool DefragmentationContextPimpl::ComputeDefragmentation(BlockVector& vector, size_t index) +{ + switch (m_Algorithm) + { + case DEFRAGMENTATION_FLAG_ALGORITHM_FAST: + return ComputeDefragmentation_Fast(vector); + default: + D3D12MA_ASSERT(0); + case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED: + return ComputeDefragmentation_Balanced(vector, index, true); + case DEFRAGMENTATION_FLAG_ALGORITHM_FULL: + return ComputeDefragmentation_Full(vector); + } +} + +DefragmentationContextPimpl::MoveAllocationData DefragmentationContextPimpl::GetMoveData( + AllocHandle handle, BlockMetadata* metadata) +{ + MoveAllocationData moveData; + moveData.move.pSrcAllocation = (Allocation*)metadata->GetAllocationPrivateData(handle); + moveData.size = moveData.move.pSrcAllocation->GetSize(); + moveData.alignment = moveData.move.pSrcAllocation->GetAlignment(); + moveData.flags = ALLOCATION_FLAG_NONE; + + return moveData; +} + +DefragmentationContextPimpl::CounterStatus DefragmentationContextPimpl::CheckCounters(UINT64 bytes) +{ + // Ignore allocation if will exceed max size for copy + if (m_PassStats.BytesMoved + bytes > m_MaxPassBytes) + { + if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE) + return CounterStatus::Ignore; + else + return CounterStatus::End; + } + return CounterStatus::Pass; +} + +bool DefragmentationContextPimpl::IncrementCounters(UINT64 bytes) +{ + m_PassStats.BytesMoved += bytes; + // Early return when max found + if (++m_PassStats.AllocationsMoved >= m_MaxPassAllocations || m_PassStats.BytesMoved >= m_MaxPassBytes) + { + D3D12MA_ASSERT(m_PassStats.AllocationsMoved == m_MaxPassAllocations || + m_PassStats.BytesMoved == m_MaxPassBytes && "Exceeded maximal pass threshold!"); + return true; + } + return false; +} + +bool DefragmentationContextPimpl::ReallocWithinBlock(BlockVector& vector, NormalBlock* block) +{ + BlockMetadata* metadata = block->m_pMetadata; + + for (AllocHandle handle = metadata->GetAllocationListBegin(); + handle != (AllocHandle)0; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.pSrcAllocation->GetPrivateData() == this) + continue; + switch (CheckCounters(moveData.move.pSrcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + default: + D3D12MA_ASSERT(0); + case CounterStatus::Pass: + break; + } + + UINT64 offset = moveData.move.pSrcAllocation->GetOffset(); + if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + AllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + ALLOCATION_FLAG_STRATEGY_MIN_OFFSET, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (SUCCEEDED(vector.CommitAllocationRequest( + request, + block, + moveData.size, + moveData.alignment, + this, + &moveData.move.pDstTmpAllocation))) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + return false; +} + +bool DefragmentationContextPimpl::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, BlockVector& vector) +{ + for (; start < end; ++start) + { + NormalBlock* dstBlock = vector.GetBlock(start); + if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size) + { + if (SUCCEEDED(vector.AllocateFromBlock(dstBlock, + data.size, + data.alignment, + data.flags, + this, + 0, + &data.move.pDstTmpAllocation))) + { + m_Moves.push_back(data.move); + if (IncrementCounters(data.size)) + return true; + break; + } + } + } + return false; +} + +bool DefragmentationContextPimpl::ComputeDefragmentation_Fast(BlockVector& vector) +{ + // Move only between blocks + + // Go through allocations in last blocks and try to fit them inside first ones + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + BlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata; + + for (AllocHandle handle = metadata->GetAllocationListBegin(); + handle != (AllocHandle)0; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.pSrcAllocation->GetPrivateData() == this) + continue; + switch (CheckCounters(moveData.move.pSrcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + default: + D3D12MA_ASSERT(0); + case CounterStatus::Pass: + break; + } + + // Check all previous blocks for free space + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + } + } + return false; +} + +bool DefragmentationContextPimpl::ComputeDefragmentation_Balanced(BlockVector& vector, size_t index, bool update) +{ + // Go over every allocation and try to fit it in previous blocks at lowest offsets, + // if not possible: realloc within single block to minimize offset (exclude offset == 0), + // but only if there are noticable gaps between them (some heuristic, ex. average size of allocation in block) + D3D12MA_ASSERT(m_AlgorithmState != NULL); + + StateBalanced& vectorState = reinterpret_cast(m_AlgorithmState)[index]; + if (update && vectorState.avgAllocSize == UINT64_MAX) + UpdateVectorStatistics(vector, vectorState); + + const size_t startMoveCount = m_Moves.size(); + UINT64 minimalFreeRegion = vectorState.avgFreeSize / 2; + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + NormalBlock* block = vector.GetBlock(i); + BlockMetadata* metadata = block->m_pMetadata; + UINT64 prevFreeRegionSize = 0; + + for (AllocHandle handle = metadata->GetAllocationListBegin(); + handle != (AllocHandle)0; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.pSrcAllocation->GetPrivateData() == this) + continue; + switch (CheckCounters(moveData.move.pSrcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + default: + D3D12MA_ASSERT(0); + case CounterStatus::Pass: + break; + } + + // Check all previous blocks for free space + const size_t prevMoveCount = m_Moves.size(); + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + + UINT64 nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle); + // If no room found then realloc within block for lower offset + UINT64 offset = moveData.move.pSrcAllocation->GetOffset(); + if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + // Check if realloc will make sense + if (prevFreeRegionSize >= minimalFreeRegion || + nextFreeRegionSize >= minimalFreeRegion || + moveData.size <= vectorState.avgFreeSize || + moveData.size <= vectorState.avgAllocSize) + { + AllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + ALLOCATION_FLAG_STRATEGY_MIN_OFFSET, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (SUCCEEDED(vector.CommitAllocationRequest( + request, + block, + moveData.size, + moveData.alignment, + this, + &moveData.move.pDstTmpAllocation))) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + prevFreeRegionSize = nextFreeRegionSize; + } + } + + // No moves perfomed, update statistics to current vector state + if (startMoveCount == m_Moves.size() && !update) + { + vectorState.avgAllocSize = UINT64_MAX; + return ComputeDefragmentation_Balanced(vector, index, false); + } + return false; +} + +bool DefragmentationContextPimpl::ComputeDefragmentation_Full(BlockVector& vector) +{ + // Go over every allocation and try to fit it in previous blocks at lowest offsets, + // if not possible: realloc within single block to minimize offset (exclude offset == 0) + + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + NormalBlock* block = vector.GetBlock(i); + BlockMetadata* metadata = block->m_pMetadata; + + for (AllocHandle handle = metadata->GetAllocationListBegin(); + handle != (AllocHandle)0; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.pSrcAllocation->GetPrivateData() == this) + continue; + switch (CheckCounters(moveData.move.pSrcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + default: + D3D12MA_ASSERT(0); + case CounterStatus::Pass: + break; + } + + // Check all previous blocks for free space + const size_t prevMoveCount = m_Moves.size(); + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + + // If no room found then realloc within block for lower offset + UINT64 offset = moveData.move.pSrcAllocation->GetOffset(); + if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + AllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + ALLOCATION_FLAG_STRATEGY_MIN_OFFSET, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (SUCCEEDED(vector.CommitAllocationRequest( + request, + block, + moveData.size, + moveData.alignment, + this, + &moveData.move.pDstTmpAllocation))) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + } + return false; +} + +void DefragmentationContextPimpl::UpdateVectorStatistics(BlockVector& vector, StateBalanced& state) +{ + size_t allocCount = 0; + size_t freeCount = 0; + state.avgFreeSize = 0; + state.avgAllocSize = 0; + + for (size_t i = 0; i < vector.GetBlockCount(); ++i) + { + BlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata; + + allocCount += metadata->GetAllocationCount(); + freeCount += metadata->GetFreeRegionsCount(); + state.avgFreeSize += metadata->GetSumFreeSize(); + state.avgAllocSize += metadata->GetSize(); + } + + state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount; + state.avgFreeSize /= freeCount; +} +#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL_FUNCTIONS + +#ifndef _D3D12MA_POOL_PIMPL_FUNCTIONS +PoolPimpl::PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc) + : m_Allocator(allocator), + m_Desc(desc), + m_BlockVector(NULL), + m_Name(NULL) +{ + const bool explicitBlockSize = desc.BlockSize != 0; + const UINT64 preferredBlockSize = explicitBlockSize ? desc.BlockSize : D3D12MA_DEFAULT_BLOCK_SIZE; + UINT maxBlockCount = desc.MaxBlockCount != 0 ? desc.MaxBlockCount : UINT_MAX; + +#ifndef __ID3D12Device4_INTERFACE_DEFINED__ + D3D12MA_ASSERT(m_Desc.pProtectedSession == NULL); +#endif + + m_BlockVector = D3D12MA_NEW(allocator->GetAllocs(), BlockVector)( + allocator, desc.HeapProperties, desc.HeapFlags, + preferredBlockSize, + desc.MinBlockCount, maxBlockCount, + explicitBlockSize, + D3D12MA_MAX(desc.MinAllocationAlignment, (UINT64)D3D12MA_DEBUG_ALIGNMENT), + desc.Flags & POOL_FLAG_ALGORITHM_MASK, + desc.Flags & POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED, + desc.pProtectedSession); +} + +PoolPimpl::~PoolPimpl() +{ + D3D12MA_ASSERT(m_PrevPool == NULL && m_NextPool == NULL); + FreeName(); + D3D12MA_DELETE(m_Allocator->GetAllocs(), m_BlockVector); +} + +HRESULT PoolPimpl::Init() +{ + m_CommittedAllocations.Init(m_Allocator->UseMutex(), m_Desc.HeapProperties.Type, this); + return m_BlockVector->CreateMinBlocks(); +} + +void PoolPimpl::GetStatistics(Statistics& outStats) +{ + ClearStatistics(outStats); + m_BlockVector->AddStatistics(outStats); + m_CommittedAllocations.AddStatistics(outStats); +} + +void PoolPimpl::CalculateStatistics(DetailedStatistics& outStats) +{ + ClearDetailedStatistics(outStats); + AddDetailedStatistics(outStats); +} + +void PoolPimpl::AddDetailedStatistics(DetailedStatistics& inoutStats) +{ + m_BlockVector->AddDetailedStatistics(inoutStats); + m_CommittedAllocations.AddDetailedStatistics(inoutStats); +} + +void PoolPimpl::SetName(LPCWSTR Name) +{ + FreeName(); + + if (Name) + { + const size_t nameCharCount = wcslen(Name) + 1; + m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount); + memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR)); + } +} + +void PoolPimpl::FreeName() +{ + if (m_Name) + { + const size_t nameCharCount = wcslen(m_Name) + 1; + D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount); + m_Name = NULL; + } +} +#endif // _D3D12MA_POOL_PIMPL_FUNCTIONS + + +#ifndef _D3D12MA_PUBLIC_INTERFACE +HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator) +{ + if (!pDesc || !ppAllocator || !pDesc->pDevice || !pDesc->pAdapter || + !(pDesc->PreferredBlockSize == 0 || (pDesc->PreferredBlockSize >= 16 && pDesc->PreferredBlockSize < 0x10000000000ull))) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateAllocator."); + return E_INVALIDARG; + } + + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + + ALLOCATION_CALLBACKS allocationCallbacks; + SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks); + + *ppAllocator = D3D12MA_NEW(allocationCallbacks, Allocator)(allocationCallbacks, *pDesc); + HRESULT hr = (*ppAllocator)->m_Pimpl->Init(*pDesc); + if (FAILED(hr)) + { + D3D12MA_DELETE(allocationCallbacks, *ppAllocator); + *ppAllocator = NULL; + } + return hr; +} + +HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock) +{ + if (!pDesc || !ppVirtualBlock) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateVirtualBlock."); + return E_INVALIDARG; + } + + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + + ALLOCATION_CALLBACKS allocationCallbacks; + SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks); + + *ppVirtualBlock = D3D12MA_NEW(allocationCallbacks, VirtualBlock)(allocationCallbacks, *pDesc); + return S_OK; +} + +#ifndef _D3D12MA_IUNKNOWN_IMPL_FUNCTIONS +HRESULT STDMETHODCALLTYPE IUnknownImpl::QueryInterface(REFIID riid, void** ppvObject) +{ + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown) + { + ++m_RefCount; + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef() +{ + return ++m_RefCount; +} + +ULONG STDMETHODCALLTYPE IUnknownImpl::Release() +{ + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + + const uint32_t newRefCount = --m_RefCount; + if (newRefCount == 0) + ReleaseThis(); + return newRefCount; +} +#endif // _D3D12MA_IUNKNOWN_IMPL_FUNCTIONS + +#ifndef _D3D12MA_ALLOCATION_FUNCTIONS +void Allocation::PackedData::SetType(Type type) +{ + const UINT u = (UINT)type; + D3D12MA_ASSERT(u < (1u << 2)); + m_Type = u; +} + +void Allocation::PackedData::SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension) +{ + const UINT u = (UINT)resourceDimension; + D3D12MA_ASSERT(u < (1u << 3)); + m_ResourceDimension = u; +} + +void Allocation::PackedData::SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags) +{ + const UINT u = (UINT)resourceFlags; + D3D12MA_ASSERT(u < (1u << 24)); + m_ResourceFlags = u; +} + +void Allocation::PackedData::SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout) +{ + const UINT u = (UINT)textureLayout; + D3D12MA_ASSERT(u < (1u << 9)); + m_TextureLayout = u; +} + +UINT64 Allocation::GetOffset() const +{ + switch (m_PackedData.GetType()) + { + case TYPE_COMMITTED: + case TYPE_HEAP: + return 0; + case TYPE_PLACED: + return m_Placed.block->m_pMetadata->GetAllocationOffset(m_Placed.allocHandle); + default: + D3D12MA_ASSERT(0); + return 0; + } +} + +void Allocation::SetResource(ID3D12Resource* pResource) +{ + if (pResource != m_Resource) + { + if (m_Resource) + m_Resource->Release(); + m_Resource = pResource; + if (m_Resource) + m_Resource->AddRef(); + } +} + +ID3D12Heap* Allocation::GetHeap() const +{ + switch (m_PackedData.GetType()) + { + case TYPE_COMMITTED: + return NULL; + case TYPE_PLACED: + return m_Placed.block->GetHeap(); + case TYPE_HEAP: + return m_Heap.heap; + default: + D3D12MA_ASSERT(0); + return 0; + } +} + +void Allocation::SetName(LPCWSTR Name) +{ + FreeName(); + + if (Name) + { + const size_t nameCharCount = wcslen(Name) + 1; + m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount); + memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR)); + } +} + +void Allocation::ReleaseThis() +{ + if (this == NULL) + { + return; + } + + SAFE_RELEASE(m_Resource); + + switch (m_PackedData.GetType()) + { + case TYPE_COMMITTED: + m_Allocator->FreeCommittedMemory(this); + break; + case TYPE_PLACED: + m_Allocator->FreePlacedMemory(this); + break; + case TYPE_HEAP: + m_Allocator->FreeHeapMemory(this); + break; + } + + FreeName(); + + m_Allocator->GetAllocationObjectAllocator().Free(this); +} + +Allocation::Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment, BOOL wasZeroInitialized) + : m_Allocator{ allocator }, + m_Size{ size }, + m_Alignment{ alignment }, + m_Resource{ NULL }, + m_Name{ NULL } +{ + D3D12MA_ASSERT(allocator); + + m_PackedData.SetType(TYPE_COUNT); + m_PackedData.SetResourceDimension(D3D12_RESOURCE_DIMENSION_UNKNOWN); + m_PackedData.SetResourceFlags(D3D12_RESOURCE_FLAG_NONE); + m_PackedData.SetTextureLayout(D3D12_TEXTURE_LAYOUT_UNKNOWN); + m_PackedData.SetWasZeroInitialized(wasZeroInitialized); +} + +void Allocation::InitCommitted(CommittedAllocationList* list) +{ + m_PackedData.SetType(TYPE_COMMITTED); + m_Committed.list = list; + m_Committed.prev = NULL; + m_Committed.next = NULL; +} + +void Allocation::InitPlaced(AllocHandle allocHandle, NormalBlock* block) +{ + m_PackedData.SetType(TYPE_PLACED); + m_Placed.allocHandle = allocHandle; + m_Placed.block = block; +} + +void Allocation::InitHeap(CommittedAllocationList* list, ID3D12Heap* heap) +{ + m_PackedData.SetType(TYPE_HEAP); + m_Heap.list = list; + m_Committed.prev = NULL; + m_Committed.next = NULL; + m_Heap.heap = heap; +} + +void Allocation::SwapBlockAllocation(Allocation* allocation) +{ + D3D12MA_ASSERT(allocation != NULL); + D3D12MA_ASSERT(m_PackedData.GetType() == TYPE_PLACED); + D3D12MA_ASSERT(allocation->m_PackedData.GetType() == TYPE_PLACED); + + D3D12MA_SWAP(m_Resource, allocation->m_Resource); + m_PackedData.SetWasZeroInitialized(allocation->m_PackedData.WasZeroInitialized()); + m_Placed.block->m_pMetadata->SetAllocationPrivateData(m_Placed.allocHandle, allocation); + D3D12MA_SWAP(m_Placed, allocation->m_Placed); + m_Placed.block->m_pMetadata->SetAllocationPrivateData(m_Placed.allocHandle, this); +} + +AllocHandle Allocation::GetAllocHandle() const +{ + switch (m_PackedData.GetType()) + { + case TYPE_COMMITTED: + case TYPE_HEAP: + return (AllocHandle)0; + case TYPE_PLACED: + return m_Placed.allocHandle; + default: + D3D12MA_ASSERT(0); + return (AllocHandle)0; + } +} + +NormalBlock* Allocation::GetBlock() +{ + switch (m_PackedData.GetType()) + { + case TYPE_COMMITTED: + case TYPE_HEAP: + return NULL; + case TYPE_PLACED: + return m_Placed.block; + default: + D3D12MA_ASSERT(0); + return NULL; + } +} + +template +void Allocation::SetResourcePointer(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc) +{ + D3D12MA_ASSERT(m_Resource == NULL && pResourceDesc); + m_Resource = resource; + m_PackedData.SetResourceDimension(pResourceDesc->Dimension); + m_PackedData.SetResourceFlags(pResourceDesc->Flags); + m_PackedData.SetTextureLayout(pResourceDesc->Layout); +} + +void Allocation::FreeName() +{ + if (m_Name) + { + const size_t nameCharCount = wcslen(m_Name) + 1; + D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount); + m_Name = NULL; + } +} +#endif // _D3D12MA_ALLOCATION_FUNCTIONS + +#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_FUNCTIONS +HRESULT DefragmentationContext::BeginPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo) +{ + D3D12MA_ASSERT(pPassInfo); + return m_Pimpl->DefragmentPassBegin(*pPassInfo); +} + +HRESULT DefragmentationContext::EndPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo) +{ + D3D12MA_ASSERT(pPassInfo); + return m_Pimpl->DefragmentPassEnd(*pPassInfo); +} + +void DefragmentationContext::GetStats(DEFRAGMENTATION_STATS* pStats) +{ + D3D12MA_ASSERT(pStats); + m_Pimpl->GetStats(*pStats); +} + +void DefragmentationContext::ReleaseThis() +{ + if (this == NULL) + { + return; + } + + D3D12MA_DELETE(m_Pimpl->GetAllocs(), this); +} + +DefragmentationContext::DefragmentationContext(AllocatorPimpl* allocator, + const DEFRAGMENTATION_DESC& desc, + BlockVector* poolVector) + : m_Pimpl(D3D12MA_NEW(allocator->GetAllocs(), DefragmentationContextPimpl)(allocator, desc, poolVector)) {} + +DefragmentationContext::~DefragmentationContext() +{ + D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl); +} +#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_FUNCTIONS + +#ifndef _D3D12MA_POOL_FUNCTIONS +POOL_DESC Pool::GetDesc() const +{ + return m_Pimpl->GetDesc(); +} + +void Pool::GetStatistics(Statistics* pStats) +{ + D3D12MA_ASSERT(pStats); + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + m_Pimpl->GetStatistics(*pStats); +} + +void Pool::CalculateStatistics(DetailedStatistics* pStats) +{ + D3D12MA_ASSERT(pStats); + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + m_Pimpl->CalculateStatistics(*pStats); +} + +void Pool::SetName(LPCWSTR Name) +{ + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + m_Pimpl->SetName(Name); +} + +LPCWSTR Pool::GetName() const +{ + return m_Pimpl->GetName(); +} + +HRESULT Pool::BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext) +{ + D3D12MA_ASSERT(pDesc && ppContext); + + // Check for support + if (m_Pimpl->GetBlockVector()->GetAlgorithm() & POOL_FLAG_ALGORITHM_LINEAR) + return E_NOINTERFACE; + + AllocatorPimpl* allocator = m_Pimpl->GetAllocator(); + *ppContext = D3D12MA_NEW(allocator->GetAllocs(), DefragmentationContext)(allocator, *pDesc, m_Pimpl->GetBlockVector()); + return S_OK; +} + +void Pool::ReleaseThis() +{ + if (this == NULL) + { + return; + } + + D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), this); +} + +Pool::Pool(Allocator* allocator, const POOL_DESC& desc) + : m_Pimpl(D3D12MA_NEW(allocator->m_Pimpl->GetAllocs(), PoolPimpl)(allocator->m_Pimpl, desc)) {} + +Pool::~Pool() +{ + m_Pimpl->GetAllocator()->UnregisterPool(this, m_Pimpl->GetDesc().HeapProperties.Type); + + D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), m_Pimpl); +} +#endif // _D3D12MA_POOL_FUNCTIONS + +#ifndef _D3D12MA_ALLOCATOR_FUNCTIONS +const D3D12_FEATURE_DATA_D3D12_OPTIONS& Allocator::GetD3D12Options() const +{ + return m_Pimpl->GetD3D12Options(); +} + +BOOL Allocator::IsUMA() const +{ + return m_Pimpl->IsUMA(); +} + +BOOL Allocator::IsCacheCoherentUMA() const +{ + return m_Pimpl->IsCacheCoherentUMA(); +} + +UINT64 Allocator::GetMemoryCapacity(UINT memorySegmentGroup) const +{ + return m_Pimpl->GetMemoryCapacity(memorySegmentGroup); +} + +HRESULT Allocator::CreateResource( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource) +{ + if (!pAllocDesc || !pResourceDesc || !ppAllocation) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource."); + return E_INVALIDARG; + } + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + return m_Pimpl->CreateResource(pAllocDesc, pResourceDesc, InitialResourceState, pOptimizedClearValue, ppAllocation, riidResource, ppvResource); +} + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ +HRESULT Allocator::CreateResource2( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource) +{ + if (!pAllocDesc || !pResourceDesc || !ppAllocation) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource2."); + return E_INVALIDARG; + } + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + return m_Pimpl->CreateResource2(pAllocDesc, pResourceDesc, InitialResourceState, pOptimizedClearValue, ppAllocation, riidResource, ppvResource); +} +#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ + +HRESULT Allocator::AllocateMemory( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo, + Allocation** ppAllocation) +{ + if (!ValidateAllocateMemoryParameters(pAllocDesc, pAllocInfo, ppAllocation)) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::AllocateMemory."); + return E_INVALIDARG; + } + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + return m_Pimpl->AllocateMemory(pAllocDesc, pAllocInfo, ppAllocation); +} + +HRESULT Allocator::CreateAliasingResource( + Allocation* pAllocation, + UINT64 AllocationLocalOffset, + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + REFIID riidResource, + void** ppvResource) +{ + if (!pAllocation || !pResourceDesc || !ppvResource) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource."); + return E_INVALIDARG; + } + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + return m_Pimpl->CreateAliasingResource(pAllocation, AllocationLocalOffset, pResourceDesc, InitialResourceState, pOptimizedClearValue, riidResource, ppvResource); +} + +HRESULT Allocator::CreatePool( + const POOL_DESC* pPoolDesc, + Pool** ppPool) +{ + if (!pPoolDesc || !ppPool || + (pPoolDesc->MaxBlockCount > 0 && pPoolDesc->MaxBlockCount < pPoolDesc->MinBlockCount) || + (pPoolDesc->MinAllocationAlignment > 0 && !IsPow2(pPoolDesc->MinAllocationAlignment))) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreatePool."); + return E_INVALIDARG; + } + if (!m_Pimpl->HeapFlagsFulfillResourceHeapTier(pPoolDesc->HeapFlags)) + { + D3D12MA_ASSERT(0 && "Invalid pPoolDesc->HeapFlags passed to Allocator::CreatePool. Did you forget to handle ResourceHeapTier=1?"); + return E_INVALIDARG; + } + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + * ppPool = D3D12MA_NEW(m_Pimpl->GetAllocs(), Pool)(this, *pPoolDesc); + HRESULT hr = (*ppPool)->m_Pimpl->Init(); + if (SUCCEEDED(hr)) + { + m_Pimpl->RegisterPool(*ppPool, pPoolDesc->HeapProperties.Type); + } + else + { + D3D12MA_DELETE(m_Pimpl->GetAllocs(), *ppPool); + *ppPool = NULL; + } + return hr; +} + +void Allocator::SetCurrentFrameIndex(UINT frameIndex) +{ + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + m_Pimpl->SetCurrentFrameIndex(frameIndex); +} + +void Allocator::GetBudget(Budget* pLocalBudget, Budget* pNonLocalBudget) +{ + if (pLocalBudget == NULL && pNonLocalBudget == NULL) + { + return; + } + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + m_Pimpl->GetBudget(pLocalBudget, pNonLocalBudget); +} + +void Allocator::CalculateStatistics(TotalStatistics* pStats) +{ + D3D12MA_ASSERT(pStats); + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + m_Pimpl->CalculateStatistics(*pStats); +} + +void Allocator::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const +{ + D3D12MA_ASSERT(ppStatsString); + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + m_Pimpl->BuildStatsString(ppStatsString, DetailedMap); +} + +void Allocator::FreeStatsString(WCHAR* pStatsString) const +{ + if (pStatsString != NULL) + { + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + m_Pimpl->FreeStatsString(pStatsString); + } +} + +void Allocator::BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext) +{ + D3D12MA_ASSERT(pDesc && ppContext); + + *ppContext = D3D12MA_NEW(m_Pimpl->GetAllocs(), DefragmentationContext)(m_Pimpl, *pDesc, NULL); +} + +void Allocator::ReleaseThis() +{ + // Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory. + const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->GetAllocs(); + D3D12MA_DELETE(allocationCallbacksCopy, this); +} + +Allocator::Allocator(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc) + : m_Pimpl(D3D12MA_NEW(allocationCallbacks, AllocatorPimpl)(allocationCallbacks, desc)) {} + +Allocator::~Allocator() +{ + D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl); +} +#endif // _D3D12MA_ALLOCATOR_FUNCTIONS + +#ifndef _D3D12MA_VIRTUAL_BLOCK_FUNCTIONS +BOOL VirtualBlock::IsEmpty() const +{ + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + + return m_Pimpl->m_Metadata->IsEmpty() ? TRUE : FALSE; +} + +void VirtualBlock::GetAllocationInfo(VirtualAllocation allocation, VIRTUAL_ALLOCATION_INFO* pInfo) const +{ + D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0 && pInfo); + + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + + m_Pimpl->m_Metadata->GetAllocationInfo(allocation.AllocHandle, *pInfo); +} + +HRESULT VirtualBlock::Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, VirtualAllocation* pAllocation, UINT64* pOffset) +{ + if (!pDesc || !pAllocation || pDesc->Size == 0 || !IsPow2(pDesc->Alignment)) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to VirtualBlock::Allocate."); + return E_INVALIDARG; + } + + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + + const UINT64 alignment = pDesc->Alignment != 0 ? pDesc->Alignment : 1; + AllocationRequest allocRequest = {}; + if (m_Pimpl->m_Metadata->CreateAllocationRequest( + pDesc->Size, + alignment, + pDesc->Flags & VIRTUAL_ALLOCATION_FLAG_UPPER_ADDRESS, + pDesc->Flags & VIRTUAL_ALLOCATION_FLAG_STRATEGY_MASK, + &allocRequest)) + { + m_Pimpl->m_Metadata->Alloc(allocRequest, pDesc->Size, pDesc->pPrivateData); + D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); + pAllocation->AllocHandle = allocRequest.allocHandle; + + if (pOffset) + *pOffset = m_Pimpl->m_Metadata->GetAllocationOffset(allocRequest.allocHandle); + return S_OK; + } + + pAllocation->AllocHandle = (AllocHandle)0; + if (pOffset) + *pOffset = UINT64_MAX; + + return E_OUTOFMEMORY; +} + +void VirtualBlock::FreeAllocation(VirtualAllocation allocation) +{ + if (allocation.AllocHandle == (AllocHandle)0) + return; + + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + + m_Pimpl->m_Metadata->Free(allocation.AllocHandle); + D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); +} + +void VirtualBlock::Clear() +{ + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + + m_Pimpl->m_Metadata->Clear(); + D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); +} + +void VirtualBlock::SetAllocationPrivateData(VirtualAllocation allocation, void* pPrivateData) +{ + D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0); + + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + + m_Pimpl->m_Metadata->SetAllocationPrivateData(allocation.AllocHandle, pPrivateData); +} + +void VirtualBlock::GetStatistics(Statistics* pStats) const +{ + D3D12MA_ASSERT(pStats); + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); + ClearStatistics(*pStats); + m_Pimpl->m_Metadata->AddStatistics(*pStats); +} + +void VirtualBlock::CalculateStatistics(DetailedStatistics* pStats) const +{ + D3D12MA_ASSERT(pStats); + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); + ClearDetailedStatistics(*pStats); + m_Pimpl->m_Metadata->AddDetailedStatistics(*pStats); +} + +void VirtualBlock::BuildStatsString(WCHAR** ppStatsString) const +{ + D3D12MA_ASSERT(ppStatsString); + + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + + StringBuilder sb(m_Pimpl->m_AllocationCallbacks); + { + JsonWriter json(m_Pimpl->m_AllocationCallbacks, sb); + D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); + m_Pimpl->m_Metadata->WriteAllocationInfoToJson(json); + } // Scope for JsonWriter + + const size_t length = sb.GetLength(); + WCHAR* result = AllocateArray(m_Pimpl->m_AllocationCallbacks, length + 1); + memcpy(result, sb.GetData(), length * sizeof(WCHAR)); + result[length] = L'\0'; + *ppStatsString = result; +} + +void VirtualBlock::FreeStatsString(WCHAR* pStatsString) const +{ + if (pStatsString != NULL) + { + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + D3D12MA::Free(m_Pimpl->m_AllocationCallbacks, pStatsString); + } +} + +void VirtualBlock::ReleaseThis() +{ + // Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory. + const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->m_AllocationCallbacks; + D3D12MA_DELETE(allocationCallbacksCopy, this); +} + +VirtualBlock::VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc) + : m_Pimpl(D3D12MA_NEW(allocationCallbacks, VirtualBlockPimpl)(allocationCallbacks, desc)) {} + +VirtualBlock::~VirtualBlock() +{ + // THIS IS AN IMPORTANT ASSERT! + // Hitting it means you have some memory leak - unreleased allocations in this virtual block. + D3D12MA_ASSERT(m_Pimpl->m_Metadata->IsEmpty() && "Some allocations were not freed before destruction of this virtual block!"); + + D3D12MA_DELETE(m_Pimpl->m_AllocationCallbacks, m_Pimpl); +} +#endif // _D3D12MA_VIRTUAL_BLOCK_FUNCTIONS +#endif // _D3D12MA_PUBLIC_INTERFACE +} // namespace D3D12MA diff --git a/vendor/rmlui_backend/RmlUi_DirectX/D3D12MemAlloc.h b/vendor/rmlui_backend/RmlUi_DirectX/D3D12MemAlloc.h new file mode 100644 index 0000000..43e6ca4 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_DirectX/D3D12MemAlloc.h @@ -0,0 +1,2533 @@ +// +// Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +/** \mainpage D3D12 Memory Allocator + +Version 2.0.1 (2022-04-05) + +Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. \n +License: MIT + +Documentation of all members: D3D12MemAlloc.h + +\section main_table_of_contents Table of contents + +- \subpage quick_start + - [Project setup](@ref quick_start_project_setup) + - [Creating resources](@ref quick_start_creating_resources) + - [Resource reference counting](@ref quick_start_resource_reference_counting) + - [Mapping memory](@ref quick_start_mapping_memory) +- \subpage custom_pools +- \subpage defragmentation +- \subpage statistics +- \subpage resource_aliasing +- \subpage linear_algorithm +- \subpage virtual_allocator +- \subpage configuration + - [Custom CPU memory allocator](@ref custom_memory_allocator) + - [Debug margins](@ref debug_margins) +- \subpage general_considerations + - [Thread safety](@ref general_considerations_thread_safety) + - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility) + - [Features not supported](@ref general_considerations_features_not_supported) + +\section main_see_also See also + +- [Product page on GPUOpen](https://gpuopen.com/gaming-product/d3d12-memory-allocator/) +- [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator) +*/ + +// If using this library on a platform different than Windows PC or want to use different version of DXGI, +// you should include D3D12-compatible headers before this library on your own and define this macro. +#ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED + #include + #include +#endif + +// Define this macro to 0 to disable usage of DXGI 1.4 (needed for IDXGIAdapter3 and query for memory budget). +#ifndef D3D12MA_DXGI_1_4 + #ifdef __IDXGIAdapter3_INTERFACE_DEFINED__ + #define D3D12MA_DXGI_1_4 1 + #else + #define D3D12MA_DXGI_1_4 0 + #endif +#endif + +/* +When defined to value other than 0, the library will try to use +D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT or D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT +for created textures when possible, which can save memory because some small textures +may get their alignment 4K and their size a multiply of 4K instead of 64K. + +#define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 0 + Disables small texture alignment. +#define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 1 + Enables conservative algorithm that will use small alignment only for some textures + that are surely known to support it. +#define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 2 + Enables query for small alignment to D3D12 (based on Microsoft sample) which will + enable small alignment for more textures, but will also generate D3D Debug Layer + error #721 on call to ID3D12Device::GetResourceAllocationInfo, which you should just + ignore. +*/ +#ifndef D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT + #define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 1 +#endif + +/// \cond INTERNAL + +#define D3D12MA_CLASS_NO_COPY(className) \ + private: \ + className(const className&) = delete; \ + className(className&&) = delete; \ + className& operator=(const className&) = delete; \ + className& operator=(className&&) = delete; + +// To be used with MAKE_HRESULT to define custom error codes. +#define FACILITY_D3D12MA 3542 + +/* +If providing your own implementation, you need to implement a subset of std::atomic. +*/ +#if !defined(D3D12MA_ATOMIC_UINT32) || !defined(D3D12MA_ATOMIC_UINT64) + #include +#endif + +#ifndef D3D12MA_ATOMIC_UINT32 + #define D3D12MA_ATOMIC_UINT32 std::atomic +#endif + +#ifndef D3D12MA_ATOMIC_UINT64 + #define D3D12MA_ATOMIC_UINT64 std::atomic +#endif + +#ifdef D3D12MA_EXPORTS + #define D3D12MA_API __declspec(dllexport) +#elif defined(D3D12MA_IMPORTS) + #define D3D12MA_API __declspec(dllimport) +#else + #define D3D12MA_API +#endif + +// Forward declaration if ID3D12ProtectedResourceSession is not defined inside the headers (older SDK, pre ID3D12Device4) +struct ID3D12ProtectedResourceSession; + +namespace D3D12MA +{ +class D3D12MA_API IUnknownImpl : public IUnknown +{ +public: + virtual ~IUnknownImpl() = default; + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); +protected: + virtual void ReleaseThis() { delete this; } +private: + D3D12MA_ATOMIC_UINT32 m_RefCount = 1; +}; +} // namespace D3D12MA + +/// \endcond + +namespace D3D12MA +{ + +/// \cond INTERNAL +class DefragmentationContextPimpl; +class AllocatorPimpl; +class PoolPimpl; +class NormalBlock; +class BlockVector; +class CommittedAllocationList; +class JsonWriter; +class VirtualBlockPimpl; +/// \endcond + +class Pool; +class Allocator; +struct Statistics; +struct DetailedStatistics; +struct TotalStatistics; + +/// \brief Unique identifier of single allocation done inside the memory heap. +typedef UINT64 AllocHandle; + +/// Pointer to custom callback function that allocates CPU memory. +using ALLOCATE_FUNC_PTR = void* (*)(size_t Size, size_t Alignment, void* pPrivateData); +/** +\brief Pointer to custom callback function that deallocates CPU memory. + +`pMemory = null` should be accepted and ignored. +*/ +using FREE_FUNC_PTR = void (*)(void* pMemory, void* pPrivateData); + +/// Custom callbacks to CPU memory allocation functions. +struct ALLOCATION_CALLBACKS +{ + /// %Allocation function. + ALLOCATE_FUNC_PTR pAllocate; + /// Dellocation function. + FREE_FUNC_PTR pFree; + /// Custom data that will be passed to allocation and deallocation functions as `pUserData` parameter. + void* pPrivateData; +}; + + +/// \brief Bit flags to be used with ALLOCATION_DESC::Flags. +enum ALLOCATION_FLAGS +{ + /// Zero + ALLOCATION_FLAG_NONE = 0, + + /** + Set this flag if the allocation should have its own dedicated memory allocation (committed resource with implicit heap). + + Use it for special, big resources, like fullscreen textures used as render targets. + + - When used with functions like D3D12MA::Allocator::CreateResource, it will use `ID3D12Device::CreateCommittedResource`, + so the created allocation will contain a resource (D3D12MA::Allocation::GetResource() `!= NULL`) but will not have + a heap (D3D12MA::Allocation::GetHeap() `== NULL`), as the heap is implicit. + - When used with raw memory allocation like D3D12MA::Allocator::AllocateMemory, it will use `ID3D12Device::CreateHeap`, + so the created allocation will contain a heap (D3D12MA::Allocation::GetHeap() `!= NULL`) and its offset will always be 0. + */ + ALLOCATION_FLAG_COMMITTED = 0x1, + + /** + Set this flag to only try to allocate from existing memory heaps and never create new such heap. + + If new allocation cannot be placed in any of the existing heaps, allocation + fails with `E_OUTOFMEMORY` error. + + You should not use D3D12MA::ALLOCATION_FLAG_COMMITTED and + D3D12MA::ALLOCATION_FLAG_NEVER_ALLOCATE at the same time. It makes no sense. + */ + ALLOCATION_FLAG_NEVER_ALLOCATE = 0x2, + + /** Create allocation only if additional memory required for it, if any, won't exceed + memory budget. Otherwise return `E_OUTOFMEMORY`. + */ + ALLOCATION_FLAG_WITHIN_BUDGET = 0x4, + + /** Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for custom pools created with #POOL_FLAG_ALGORITHM_LINEAR flag. + */ + ALLOCATION_FLAG_UPPER_ADDRESS = 0x8, + + /** Set this flag if the allocated memory will have aliasing resources. + + Use this when calling D3D12MA::Allocator::CreateResource() and similar to + guarantee creation of explicit heap for desired allocation and prevent it from using `CreateCommittedResource`, + so that new allocation object will always have `allocation->GetHeap() != NULL`. + */ + ALLOCATION_FLAG_CAN_ALIAS = 0x10, + + /** Allocation strategy that chooses smallest possible free range for the allocation + to minimize memory usage and fragmentation, possibly at the expense of allocation time. + */ + ALLOCATION_FLAG_STRATEGY_MIN_MEMORY = 0x00010000, + + /** Allocation strategy that chooses first suitable free range for the allocation - + not necessarily in terms of the smallest offset but the one that is easiest and fastest to find + to minimize allocation time, possibly at the expense of allocation quality. + */ + ALLOCATION_FLAG_STRATEGY_MIN_TIME = 0x00020000, + + /** Allocation strategy that chooses always the lowest offset in available space. + This is not the most efficient strategy but achieves highly packed data. + Used internally by defragmentation, not recomended in typical usage. + */ + ALLOCATION_FLAG_STRATEGY_MIN_OFFSET = 0x0004000, + + /// Alias to #ALLOCATION_FLAG_STRATEGY_MIN_MEMORY. + ALLOCATION_FLAG_STRATEGY_BEST_FIT = ALLOCATION_FLAG_STRATEGY_MIN_MEMORY, + /// Alias to #ALLOCATION_FLAG_STRATEGY_MIN_TIME. + ALLOCATION_FLAG_STRATEGY_FIRST_FIT = ALLOCATION_FLAG_STRATEGY_MIN_TIME, + + /// A bit mask to extract only `STRATEGY` bits from entire set of flags. + ALLOCATION_FLAG_STRATEGY_MASK = + ALLOCATION_FLAG_STRATEGY_MIN_MEMORY | + ALLOCATION_FLAG_STRATEGY_MIN_TIME | + ALLOCATION_FLAG_STRATEGY_MIN_OFFSET, +}; + +/// \brief Parameters of created D3D12MA::Allocation object. To be used with Allocator::CreateResource. +struct ALLOCATION_DESC +{ + /// Flags. + ALLOCATION_FLAGS Flags; + /** \brief The type of memory heap where the new allocation should be placed. + + It must be one of: `D3D12_HEAP_TYPE_DEFAULT`, `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`. + + When D3D12MA::ALLOCATION_DESC::CustomPool != NULL this member is ignored. + */ + D3D12_HEAP_TYPE HeapType; + /** \brief Additional heap flags to be used when allocating memory. + + In most cases it can be 0. + + - If you use D3D12MA::Allocator::CreateResource(), you don't need to care. + Necessary flag `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`, `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`, + or `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES` is added automatically. + - If you use D3D12MA::Allocator::AllocateMemory(), you should specify one of those `ALLOW_ONLY` flags. + Except when you validate that D3D12MA::Allocator::GetD3D12Options()`.ResourceHeapTier == D3D12_RESOURCE_HEAP_TIER_1` - + then you can leave it 0. + - You can specify additional flags if needed. Then the memory will always be allocated as + separate block using `D3D12Device::CreateCommittedResource` or `CreateHeap`, not as part of an existing larget block. + + When D3D12MA::ALLOCATION_DESC::CustomPool != NULL this member is ignored. + */ + D3D12_HEAP_FLAGS ExtraHeapFlags; + /** \brief Custom pool to place the new resource in. Optional. + + When not NULL, the resource will be created inside specified custom pool. + It will then never be created as committed. + */ + Pool* CustomPool; + /// Custom general-purpose pointer that will be stored in D3D12MA::Allocation. + void* pPrivateData; +}; + +/** \brief Calculated statistics of memory usage e.g. in a specific memory heap type, +memory segment group, custom pool, or total. + +These are fast to calculate. +See functions: D3D12MA::Allocator::GetBudget(), D3D12MA::Pool::GetStatistics(). +*/ +struct Statistics +{ + /** \brief Number of D3D12 memory blocks allocated - `ID3D12Heap` objects and committed resources. + */ + UINT BlockCount; + /** \brief Number of D3D12MA::Allocation objects allocated. + + Committed allocations have their own blocks, so each one adds 1 to `AllocationCount` as well as `BlockCount`. + */ + UINT AllocationCount; + /** \brief Number of bytes allocated in memory blocks. + */ + UINT64 BlockBytes; + /** \brief Total number of bytes occupied by all D3D12MA::Allocation objects. + + Always less or equal than `BlockBytes`. + Difference `(BlockBytes - AllocationBytes)` is the amount of memory allocated from D3D12 + but unused by any D3D12MA::Allocation. + */ + UINT64 AllocationBytes; +}; + +/** \brief More detailed statistics than D3D12MA::Statistics. + +These are slower to calculate. Use for debugging purposes. +See functions: D3D12MA::Allocator::CalculateStatistics(), D3D12MA::Pool::CalculateStatistics(). + +Averages are not provided because they can be easily calculated as: + +\code +UINT64 AllocationSizeAvg = DetailedStats.Statistics.AllocationBytes / detailedStats.Statistics.AllocationCount; +UINT64 UnusedBytes = DetailedStats.Statistics.BlockBytes - DetailedStats.Statistics.AllocationBytes; +UINT64 UnusedRangeSizeAvg = UnusedBytes / DetailedStats.UnusedRangeCount; +\endcode +*/ +struct DetailedStatistics +{ + /// Basic statistics. + Statistics Stats; + /// Number of free ranges of memory between allocations. + UINT UnusedRangeCount; + /// Smallest allocation size. `UINT64_MAX` if there are 0 allocations. + UINT64 AllocationSizeMin; + /// Largest allocation size. 0 if there are 0 allocations. + UINT64 AllocationSizeMax; + /// Smallest empty range size. `UINT64_MAX` if there are 0 empty ranges. + UINT64 UnusedRangeSizeMin; + /// Largest empty range size. 0 if there are 0 empty ranges. + UINT64 UnusedRangeSizeMax; +}; + +/** \brief General statistics from current state of the allocator - +total memory usage across all memory heaps and segments. + +These are slower to calculate. Use for debugging purposes. +See function D3D12MA::Allocator::CalculateStatistics(). +*/ +struct TotalStatistics +{ + /** \brief One element for each type of heap located at the following indices: + + - 0 = `D3D12_HEAP_TYPE_DEFAULT` + - 1 = `D3D12_HEAP_TYPE_UPLOAD` + - 2 = `D3D12_HEAP_TYPE_READBACK` + - 3 = `D3D12_HEAP_TYPE_CUSTOM` + */ + DetailedStatistics HeapType[4]; + /** \brief One element for each memory segment group located at the following indices: + + - 0 = `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` + - 1 = `DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL` + + Meaning of these segment groups is: + + - When `IsUMA() == FALSE` (discrete graphics card): + - `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` (index 0) represents GPU memory + (resources allocated in `D3D12_HEAP_TYPE_DEFAULT` or `D3D12_MEMORY_POOL_L1`). + - `DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL` (index 1) represents system memory + (resources allocated in `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`, or `D3D12_MEMORY_POOL_L0`). + - When `IsUMA() == TRUE` (integrated graphics chip): + - `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` = (index 0) represents memory shared for all the resources. + - `DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL` = (index 1) is unused and always 0. + */ + DetailedStatistics MemorySegmentGroup[2]; + /// Total statistics from all memory allocated from D3D12. + DetailedStatistics Total; +}; + +/** \brief %Statistics of current memory usage and available budget for a specific memory segment group. + +These are fast to calculate. See function D3D12MA::Allocator::GetBudget(). +*/ +struct Budget +{ + /** \brief %Statistics fetched from the library. + */ + Statistics Stats; + /** \brief Estimated current memory usage of the program. + + Fetched from system using `IDXGIAdapter3::QueryVideoMemoryInfo` if possible. + + It might be different than `BlockBytes` (usually higher) due to additional implicit objects + also occupying the memory, like swapchain, pipeline state objects, descriptor heaps, command lists, or + heaps and resources allocated outside of this library, if any. + */ + UINT64 UsageBytes; + /** \brief Estimated amount of memory available to the program. + + Fetched from system using `IDXGIAdapter3::QueryVideoMemoryInfo` if possible. + + It might be different (most probably smaller) than memory capacity returned + by D3D12MA::Allocator::GetMemoryCapacity() due to factors + external to the program, decided by the operating system. + Difference `BudgetBytes - UsageBytes` is the amount of additional memory that can probably + be allocated without problems. Exceeding the budget may result in various problems. + */ + UINT64 BudgetBytes; +}; + + +/// \brief Represents single memory allocation done inside VirtualBlock. +struct D3D12MA_API VirtualAllocation +{ + /// \brief Unique idenitfier of current allocation. 0 means null/invalid. + AllocHandle AllocHandle; +}; + +/** \brief Represents single memory allocation. + +It may be either implicit memory heap dedicated to a single resource or a +specific region of a bigger heap plus unique offset. + +To create such object, fill structure D3D12MA::ALLOCATION_DESC and call function +Allocator::CreateResource. + +The object remembers size and some other information. +To retrieve this information, use methods of this class. + +The object also remembers `ID3D12Resource` and "owns" a reference to it, +so it calls `%Release()` on the resource when destroyed. +*/ +class D3D12MA_API Allocation : public IUnknownImpl +{ +public: + /** \brief Returns offset in bytes from the start of memory heap. + + You usually don't need to use this offset. If you create a buffer or a texture together with the allocation using function + D3D12MA::Allocator::CreateResource, functions that operate on that resource refer to the beginning of the resource, + not entire memory heap. + + If the Allocation represents committed resource with implicit heap, returns 0. + */ + UINT64 GetOffset() const; + + /// Returns alignment that resource was created with. + UINT64 GetAlignment() const { return m_Alignment; } + + /** \brief Returns size in bytes of the allocation. + + - If you created a buffer or a texture together with the allocation using function D3D12MA::Allocator::CreateResource, + this is the size of the resource returned by `ID3D12Device::GetResourceAllocationInfo`. + - For allocations made out of bigger memory blocks, this also is the size of the memory region assigned exclusively to this allocation. + - For resources created as committed, this value may not be accurate. DirectX implementation may optimize memory usage internally + so that you may even observe regions of `ID3D12Resource::GetGPUVirtualAddress()` + Allocation::GetSize() to overlap in memory and still work correctly. + */ + UINT64 GetSize() const { return m_Size; } + + /** \brief Returns D3D12 resource associated with this object. + + Calling this method doesn't increment resource's reference counter. + */ + ID3D12Resource* GetResource() const { return m_Resource; } + + /// Releases the resource currently pointed by the allocation (if any), sets it to new one, incrementing its reference counter (if not null). + void SetResource(ID3D12Resource* pResource); + + /** \brief Returns memory heap that the resource is created in. + + If the Allocation represents committed resource with implicit heap, returns NULL. + */ + ID3D12Heap* GetHeap() const; + + /// Changes custom pointer for an allocation to a new value. + void SetPrivateData(void* pPrivateData) { m_pPrivateData = pPrivateData; } + + /// Get custom pointer associated with the allocation. + void* GetPrivateData() const { return m_pPrivateData; } + + /** \brief Associates a name with the allocation object. This name is for use in debug diagnostics and tools. + + Internal copy of the string is made, so the memory pointed by the argument can be + changed of freed immediately after this call. + + `Name` can be null. + */ + void SetName(LPCWSTR Name); + + /** \brief Returns the name associated with the allocation object. + + Returned string points to an internal copy. + + If no name was associated with the allocation, returns null. + */ + LPCWSTR GetName() const { return m_Name; } + + /** \brief Returns `TRUE` if the memory of the allocation was filled with zeros when the allocation was created. + + Returns `TRUE` only if the allocator is sure that the entire memory where the + allocation was created was filled with zeros at the moment the allocation was made. + + Returns `FALSE` if the memory could potentially contain garbage data. + If it's a render-target or depth-stencil texture, it then needs proper + initialization with `ClearRenderTargetView`, `ClearDepthStencilView`, `DiscardResource`, + or a copy operation, as described on page + "ID3D12Device::CreatePlacedResource method - Notes on the required resource initialization" in Microsoft documentation. + Please note that rendering a fullscreen triangle or quad to the texture as + a render target is not a proper way of initialization! + + See also articles: + + - "Coming to DirectX 12: More control over memory allocation" on DirectX Developer Blog + - ["Initializing DX12 Textures After Allocation and Aliasing"](https://asawicki.info/news_1724_initializing_dx12_textures_after_allocation_and_aliasing). + */ + BOOL WasZeroInitialized() const { return m_PackedData.WasZeroInitialized(); } + +protected: + void ReleaseThis() override; + +private: + friend class AllocatorPimpl; + friend class BlockVector; + friend class CommittedAllocationList; + friend class JsonWriter; + friend class BlockMetadata_Linear; + friend class DefragmentationContextPimpl; + friend struct CommittedAllocationListItemTraits; + template friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*); + template friend class PoolAllocator; + + enum Type + { + TYPE_COMMITTED, + TYPE_PLACED, + TYPE_HEAP, + TYPE_COUNT + }; + + AllocatorPimpl* m_Allocator; + UINT64 m_Size; + UINT64 m_Alignment; + ID3D12Resource* m_Resource; + void* m_pPrivateData; + wchar_t* m_Name; + + union + { + struct + { + CommittedAllocationList* list; + Allocation* prev; + Allocation* next; + } m_Committed; + + struct + { + AllocHandle allocHandle; + NormalBlock* block; + } m_Placed; + + struct + { + // Beginning must be compatible with m_Committed. + CommittedAllocationList* list; + Allocation* prev; + Allocation* next; + ID3D12Heap* heap; + } m_Heap; + }; + + struct PackedData + { + public: + PackedData() : + m_Type(0), m_ResourceDimension(0), m_ResourceFlags(0), m_TextureLayout(0), m_WasZeroInitialized(0) { } + + Type GetType() const { return (Type)m_Type; } + D3D12_RESOURCE_DIMENSION GetResourceDimension() const { return (D3D12_RESOURCE_DIMENSION)m_ResourceDimension; } + D3D12_RESOURCE_FLAGS GetResourceFlags() const { return (D3D12_RESOURCE_FLAGS)m_ResourceFlags; } + D3D12_TEXTURE_LAYOUT GetTextureLayout() const { return (D3D12_TEXTURE_LAYOUT)m_TextureLayout; } + BOOL WasZeroInitialized() const { return (BOOL)m_WasZeroInitialized; } + + void SetType(Type type); + void SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension); + void SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags); + void SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout); + void SetWasZeroInitialized(BOOL wasZeroInitialized) { m_WasZeroInitialized = wasZeroInitialized ? 1 : 0; } + + private: + UINT m_Type : 2; // enum Type + UINT m_ResourceDimension : 3; // enum D3D12_RESOURCE_DIMENSION + UINT m_ResourceFlags : 24; // flags D3D12_RESOURCE_FLAGS + UINT m_TextureLayout : 9; // enum D3D12_TEXTURE_LAYOUT + UINT m_WasZeroInitialized : 1; // BOOL + } m_PackedData; + + Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment, BOOL wasZeroInitialized); + // Nothing here, everything already done in Release. + virtual ~Allocation() = default; + + void InitCommitted(CommittedAllocationList* list); + void InitPlaced(AllocHandle allocHandle, NormalBlock* block); + void InitHeap(CommittedAllocationList* list, ID3D12Heap* heap); + void SwapBlockAllocation(Allocation* allocation); + // If the Allocation represents committed resource with implicit heap, returns UINT64_MAX. + AllocHandle GetAllocHandle() const; + NormalBlock* GetBlock(); + template + void SetResourcePointer(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc); + void FreeName(); + + D3D12MA_CLASS_NO_COPY(Allocation) +}; + + +/// Flags to be passed as DEFRAGMENTATION_DESC::Flags. +enum DEFRAGMENTATION_FLAGS +{ + /** Use simple but fast algorithm for defragmentation. + May not achieve best results but will require least time to compute and least allocations to copy. + */ + DEFRAGMENTATION_FLAG_ALGORITHM_FAST = 0x1, + /** Default defragmentation algorithm, applied also when no `ALGORITHM` flag is specified. + Offers a balance between defragmentation quality and the amount of allocations and bytes that need to be moved. + */ + DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED = 0x2, + /** Perform full defragmentation of memory. + Can result in notably more time to compute and allocations to copy, but will achieve best memory packing. + */ + DEFRAGMENTATION_FLAG_ALGORITHM_FULL = 0x4, + + /// A bit mask to extract only `ALGORITHM` bits from entire set of flags. + DEFRAGMENTATION_FLAG_ALGORITHM_MASK = + DEFRAGMENTATION_FLAG_ALGORITHM_FAST | + DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED | + DEFRAGMENTATION_FLAG_ALGORITHM_FULL +}; + +/** \brief Parameters for defragmentation. + +To be used with functions Allocator::BeginDefragmentation() and Pool::BeginDefragmentation(). +*/ +struct DEFRAGMENTATION_DESC +{ + /// Flags. + DEFRAGMENTATION_FLAGS Flags; + /** \brief Maximum numbers of bytes that can be copied during single pass, while moving allocations to different places. + + 0 means no limit. + */ + UINT64 MaxBytesPerPass; + /** \brief Maximum number of allocations that can be moved during single pass to a different place. + + 0 means no limit. + */ + UINT32 MaxAllocationsPerPass; +}; + +/// Operation performed on single defragmentation move. +enum DEFRAGMENTATION_MOVE_OPERATION +{ + /** Resource has been recreated at `pDstTmpAllocation`, data has been copied, old resource has been destroyed. + `pSrcAllocation` will be changed to point to the new place. This is the default value set by DefragmentationContext::BeginPass(). + */ + DEFRAGMENTATION_MOVE_OPERATION_COPY = 0, + /// Set this value if you cannot move the allocation. New place reserved at `pDstTmpAllocation` will be freed. `pSrcAllocation` will remain unchanged. + DEFRAGMENTATION_MOVE_OPERATION_IGNORE = 1, + /// Set this value if you decide to abandon the allocation and you destroyed the resource. New place reserved `pDstTmpAllocation` will be freed, along with `pSrcAllocation`. + DEFRAGMENTATION_MOVE_OPERATION_DESTROY = 2, +}; + +/// Single move of an allocation to be done for defragmentation. +struct DEFRAGMENTATION_MOVE +{ + /** \brief Operation to be performed on the allocation by DefragmentationContext::EndPass(). + Default value is #DEFRAGMENTATION_MOVE_OPERATION_COPY. You can modify it. + */ + DEFRAGMENTATION_MOVE_OPERATION Operation; + /// %Allocation that should be moved. + Allocation* pSrcAllocation; + /** \brief Temporary allocation pointing to destination memory that will replace `pSrcAllocation`. + + Use it to retrieve new `ID3D12Heap` and offset to create new `ID3D12Resource` and then store it here via Allocation::SetResource(). + + \warning Do not store this allocation in your data structures! It exists only temporarily, for the duration of the defragmentation pass, + to be used for storing newly created resource. DefragmentationContext::EndPass() will destroy it and make `pSrcAllocation` point to this memory. + */ + Allocation* pDstTmpAllocation; +}; + +/** \brief Parameters for incremental defragmentation steps. + +To be used with function DefragmentationContext::BeginPass(). +*/ +struct DEFRAGMENTATION_PASS_MOVE_INFO +{ + /// Number of elements in the `pMoves` array. + UINT32 MoveCount; + /** \brief Array of moves to be performed by the user in the current defragmentation pass. + + Pointer to an array of `MoveCount` elements, owned by %D3D12MA, created in DefragmentationContext::BeginPass(), destroyed in DefragmentationContext::EndPass(). + + For each element, you should: + + 1. Create a new resource in the place pointed by `pMoves[i].pDstTmpAllocation->GetHeap()` + `pMoves[i].pDstTmpAllocation->GetOffset()`. + 2. Store new resource in `pMoves[i].pDstTmpAllocation` by using Allocation::SetResource(). It will later replace old resource from `pMoves[i].pSrcAllocation`. + 3. Copy data from the `pMoves[i].pSrcAllocation` e.g. using `D3D12GraphicsCommandList::CopyResource`. + 4. Make sure these commands finished executing on the GPU. + + Only then you can finish defragmentation pass by calling DefragmentationContext::EndPass(). + After this call, the allocation will point to the new place in memory. + + Alternatively, if you cannot move specific allocation, + you can set DEFRAGMENTATION_MOVE::Operation to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_IGNORE. + + Alternatively, if you decide you want to completely remove the allocation, + set DEFRAGMENTATION_MOVE::Operation to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_DESTROY. + Then, after DefragmentationContext::EndPass() the allocation will be released. + */ + DEFRAGMENTATION_MOVE* pMoves; +}; + +/// %Statistics returned for defragmentation process by function DefragmentationContext::GetStats(). +struct DEFRAGMENTATION_STATS +{ + /// Total number of bytes that have been copied while moving allocations to different places. + UINT64 BytesMoved; + /// Total number of bytes that have been released to the system by freeing empty heaps. + UINT64 BytesFreed; + /// Number of allocations that have been moved to different places. + UINT32 AllocationsMoved; + /// Number of empty `ID3D12Heap` objects that have been released to the system. + UINT32 HeapsFreed; +}; + +/** \brief Represents defragmentation process in progress. + +You can create this object using Allocator::BeginDefragmentation (for default pools) or +Pool::BeginDefragmentation (for a custom pool). +*/ +class D3D12MA_API DefragmentationContext : public IUnknownImpl +{ +public: + /** \brief Starts single defragmentation pass. + + \param[out] pPassInfo Computed informations for current pass. + \returns + - `S_OK` if no more moves are possible. Then you can omit call to DefragmentationContext::EndPass() and simply end whole defragmentation. + - `S_FALSE` if there are pending moves returned in `pPassInfo`. You need to perform them, call DefragmentationContext::EndPass(), + and then preferably try another pass with DefragmentationContext::BeginPass(). + */ + HRESULT BeginPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo); + /** \brief Ends single defragmentation pass. + + \param pPassInfo Computed informations for current pass filled by DefragmentationContext::BeginPass() and possibly modified by you. + \return Returns `S_OK` if no more moves are possible or `S_FALSE` if more defragmentations are possible. + + Ends incremental defragmentation pass and commits all defragmentation moves from `pPassInfo`. + After this call: + + - %Allocation at `pPassInfo[i].pSrcAllocation` that had `pPassInfo[i].Operation ==` #DEFRAGMENTATION_MOVE_OPERATION_COPY + (which is the default) will be pointing to the new destination place. + - %Allocation at `pPassInfo[i].pSrcAllocation` that had `pPassInfo[i].operation ==` #DEFRAGMENTATION_MOVE_OPERATION_DESTROY + will be released. + + If no more moves are possible you can end whole defragmentation. + */ + HRESULT EndPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo); + /** \brief Returns statistics of the defragmentation performed so far. + */ + void GetStats(DEFRAGMENTATION_STATS* pStats); + +protected: + void ReleaseThis() override; + +private: + friend class Pool; + friend class Allocator; + template friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*); + + DefragmentationContextPimpl* m_Pimpl; + + DefragmentationContext(AllocatorPimpl* allocator, + const DEFRAGMENTATION_DESC& desc, + BlockVector* poolVector); + ~DefragmentationContext(); + + D3D12MA_CLASS_NO_COPY(DefragmentationContext) +}; + +/// \brief Bit flags to be used with POOL_DESC::Flags. +enum POOL_FLAGS +{ + /// Zero + POOL_FLAG_NONE = 0, + + /** \brief Enables alternative, linear allocation algorithm in this pool. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. + For details, see documentation chapter \ref linear_algorithm. + */ + POOL_FLAG_ALGORITHM_LINEAR = 0x1, + + /** \brief Optimization, allocate MSAA textures as committed resources always. + + Specify this flag to create MSAA textures with implicit heaps, as if they were created + with flag ALLOCATION_FLAG_COMMITTED. Usage of this flags enables pool to create its heaps + on smaller alignment not suitable for MSAA textures. + */ + POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED = 0x2, + + // Bit mask to extract only `ALGORITHM` bits from entire set of flags. + POOL_FLAG_ALGORITHM_MASK = POOL_FLAG_ALGORITHM_LINEAR +}; + +/// \brief Parameters of created D3D12MA::Pool object. To be used with D3D12MA::Allocator::CreatePool. +struct POOL_DESC +{ + /// Flags. + POOL_FLAGS Flags; + /** \brief The parameters of memory heap where allocations of this pool should be placed. + + In the simplest case, just fill it with zeros and set `Type` to one of: `D3D12_HEAP_TYPE_DEFAULT`, + `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`. Additional parameters can be used e.g. to utilize UMA. + */ + D3D12_HEAP_PROPERTIES HeapProperties; + /** \brief Heap flags to be used when allocating heaps of this pool. + + It should contain one of these values, depending on type of resources you are going to create in this heap: + `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`, + `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`, + `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES`. + Except if ResourceHeapTier = 2, then it may be `D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES` = 0. + + You can specify additional flags if needed. + */ + D3D12_HEAP_FLAGS HeapFlags; + /** \brief Size of a single heap (memory block) to be allocated as part of this pool, in bytes. Optional. + + Specify nonzero to set explicit, constant size of memory blocks used by this pool. + Leave 0 to use default and let the library manage block sizes automatically. + Then sizes of particular blocks may vary. + */ + UINT64 BlockSize; + /** \brief Minimum number of heaps (memory blocks) to be always allocated in this pool, even if they stay empty. Optional. + + Set to 0 to have no preallocated blocks and allow the pool be completely empty. + */ + UINT MinBlockCount; + /** \brief Maximum number of heaps (memory blocks) that can be allocated in this pool. Optional. + + Set to 0 to use default, which is `UINT64_MAX`, which means no limit. + + Set to same value as D3D12MA::POOL_DESC::MinBlockCount to have fixed amount of memory allocated + throughout whole lifetime of this pool. + */ + UINT MaxBlockCount; + /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0. + + Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two. + */ + UINT64 MinAllocationAlignment; + /** \brief Additional parameter allowing pool to create resources with passed protected session. + + If not null then all the heaps and committed resources will be created with this parameter. + Valid only if ID3D12Device4 interface is present in current Windows SDK! + */ + ID3D12ProtectedResourceSession* pProtectedSession; +}; + +/** \brief Custom memory pool + +Represents a separate set of heaps (memory blocks) that can be used to create +D3D12MA::Allocation-s and resources in it. Usually there is no need to create custom +pools - creating resources in default pool is sufficient. + +To create custom pool, fill D3D12MA::POOL_DESC and call D3D12MA::Allocator::CreatePool. +*/ +class D3D12MA_API Pool : public IUnknownImpl +{ +public: + /** \brief Returns copy of parameters of the pool. + + These are the same parameters as passed to D3D12MA::Allocator::CreatePool. + */ + POOL_DESC GetDesc() const; + + /** \brief Retrieves basic statistics of the custom pool that are fast to calculate. + + \param[out] pStats %Statistics of the current pool. + */ + void GetStatistics(Statistics* pStats); + + /** \brief Retrieves detailed statistics of the custom pool that are slower to calculate. + + \param[out] pStats %Statistics of the current pool. + */ + void CalculateStatistics(DetailedStatistics* pStats); + + /** \brief Associates a name with the pool. This name is for use in debug diagnostics and tools. + + Internal copy of the string is made, so the memory pointed by the argument can be + changed of freed immediately after this call. + + `Name` can be NULL. + */ + void SetName(LPCWSTR Name); + + /** \brief Returns the name associated with the pool object. + + Returned string points to an internal copy. + + If no name was associated with the allocation, returns NULL. + */ + LPCWSTR GetName() const; + + /** \brief Begins defragmentation process of the current pool. + + \param pDesc Structure filled with parameters of defragmentation. + \param[out] ppContext Context object that will manage defragmentation. + \returns + - `S_OK` if defragmentation can begin. + - `E_NOINTERFACE` if defragmentation is not supported. + + For more information about defragmentation, see documentation chapter: + [Defragmentation](@ref defragmentation). + */ + HRESULT BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext); + +protected: + void ReleaseThis() override; + +private: + friend class Allocator; + friend class AllocatorPimpl; + template friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*); + + PoolPimpl* m_Pimpl; + + Pool(Allocator* allocator, const POOL_DESC &desc); + ~Pool(); + + D3D12MA_CLASS_NO_COPY(Pool) +}; + + +/// \brief Bit flags to be used with ALLOCATOR_DESC::Flags. +enum ALLOCATOR_FLAGS +{ + /// Zero + ALLOCATOR_FLAG_NONE = 0, + + /** + Allocator and all objects created from it will not be synchronized internally, + so you must guarantee they are used from only one thread at a time or + synchronized by you. + + Using this flag may increase performance because internal mutexes are not used. + */ + ALLOCATOR_FLAG_SINGLETHREADED = 0x1, + + /** + Every allocation will have its own memory block. + To be used for debugging purposes. + */ + ALLOCATOR_FLAG_ALWAYS_COMMITTED = 0x2, + + /** + Heaps created for the default pools will be created with flag `D3D12_HEAP_FLAG_CREATE_NOT_ZEROED`, + allowing for their memory to be not zeroed by the system if possible, + which can speed up allocation. + + Only affects default pools. + To use the flag with @ref custom_pools, you need to add it manually: + + \code + poolDesc.heapFlags |= D3D12_HEAP_FLAG_CREATE_NOT_ZEROED; + \endcode + + Only avaiable if `ID3D12Device8` is present. Otherwise, the flag is ignored. + */ + ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED = 0x4, + + /** \brief Optimization, allocate MSAA textures as committed resources always. + + Specify this flag to create MSAA textures with implicit heaps, as if they were created + with flag ALLOCATION_FLAG_COMMITTED. Usage of this flags enables all default pools + to create its heaps on smaller alignment not suitable for MSAA textures. + */ + ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED = 0x8, +}; + +/// \brief Parameters of created Allocator object. To be used with CreateAllocator(). +struct ALLOCATOR_DESC +{ + /// Flags. + ALLOCATOR_FLAGS Flags; + + /** Direct3D device object that the allocator should be attached to. + + Allocator is doing `AddRef`/`Release` on this object. + */ + ID3D12Device* pDevice; + + /** \brief Preferred size of a single `ID3D12Heap` block to be allocated. + + Set to 0 to use default, which is currently 64 MiB. + */ + UINT64 PreferredBlockSize; + + /** \brief Custom CPU memory allocation callbacks. Optional. + + Optional, can be null. When specified, will be used for all CPU-side memory allocations. + */ + const ALLOCATION_CALLBACKS* pAllocationCallbacks; + + /** DXGI Adapter object that you use for D3D12 and this allocator. + + Allocator is doing `AddRef`/`Release` on this object. + */ + IDXGIAdapter* pAdapter; +}; + +/** +\brief Represents main object of this library initialized for particular `ID3D12Device`. + +Fill structure D3D12MA::ALLOCATOR_DESC and call function CreateAllocator() to create it. +Call method `Release()` to destroy it. + +It is recommended to create just one object of this type per `ID3D12Device` object, +right after Direct3D 12 is initialized and keep it alive until before Direct3D device is destroyed. +*/ +class D3D12MA_API Allocator : public IUnknownImpl +{ +public: + /// Returns cached options retrieved from D3D12 device. + const D3D12_FEATURE_DATA_D3D12_OPTIONS& GetD3D12Options() const; + /** \brief Returns true if `D3D12_FEATURE_DATA_ARCHITECTURE1::UMA` was found to be true. + + For more information about how to use it, see articles in Microsoft Docs articles: + + - "UMA Optimizations: CPU Accessible Textures and Standard Swizzle" + - "D3D12_FEATURE_DATA_ARCHITECTURE structure (d3d12.h)" + - "ID3D12Device::GetCustomHeapProperties method (d3d12.h)" + */ + BOOL IsUMA() const; + /** \brief Returns true if `D3D12_FEATURE_DATA_ARCHITECTURE1::CacheCoherentUMA` was found to be true. + + For more information about how to use it, see articles in Microsoft Docs articles: + + - "UMA Optimizations: CPU Accessible Textures and Standard Swizzle" + - "D3D12_FEATURE_DATA_ARCHITECTURE structure (d3d12.h)" + - "ID3D12Device::GetCustomHeapProperties method (d3d12.h)" + */ + BOOL IsCacheCoherentUMA() const; + /** \brief Returns total amount of memory of specific segment group, in bytes. + + \param memorySegmentGroup use `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` or DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL`. + + This information is taken from `DXGI_ADAPTER_DESC`. + It is not recommended to use this number. + You should preferably call GetBudget() and limit memory usage to D3D12MA::Budget::BudgetBytes instead. + + - When IsUMA() `== FALSE` (discrete graphics card): + - `GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_LOCAL)` returns the size of the video memory. + - `GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL)` returns the size of the system memory available for D3D12 resources. + - When IsUMA() `== TRUE` (integrated graphics chip): + - `GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_LOCAL)` returns the size of the shared memory available for all D3D12 resources. + All memory is considered "local". + - `GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL)` is not applicable and returns 0. + */ + UINT64 GetMemoryCapacity(UINT memorySegmentGroup) const; + + /** \brief Allocates memory and creates a D3D12 resource (buffer or texture). This is the main allocation function. + + The function is similar to `ID3D12Device::CreateCommittedResource`, but it may + really call `ID3D12Device::CreatePlacedResource` to assign part of a larger, + existing memory heap to the new resource, which is the main purpose of this + whole library. + + If `ppvResource` is null, you receive only `ppAllocation` object from this function. + It holds pointer to `ID3D12Resource` that can be queried using function D3D12MA::Allocation::GetResource(). + Reference count of the resource object is 1. + It is automatically destroyed when you destroy the allocation object. + + If `ppvResource` is not null, you receive pointer to the resource next to allocation object. + Reference count of the resource object is then increased by calling `QueryInterface`, so you need to manually `Release` it + along with the allocation. + + \param pAllocDesc Parameters of the allocation. + \param pResourceDesc Description of created resource. + \param InitialResourceState Initial resource state. + \param pOptimizedClearValue Optional. Either null or optimized clear value. + \param[out] ppAllocation Filled with pointer to new allocation object created. + \param riidResource IID of a resource to be returned via `ppvResource`. + \param[out] ppvResource Optional. If not null, filled with pointer to new resouce created. + + \note This function creates a new resource. Sub-allocation of parts of one large buffer, + although recommended as a good practice, is out of scope of this library and could be implemented + by the user as a higher-level logic on top of it, e.g. using the \ref virtual_allocator feature. + */ + HRESULT CreateResource( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE *pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource); + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + /** \brief Similar to Allocator::CreateResource, but supports new structure `D3D12_RESOURCE_DESC1`. + + It internally uses `ID3D12Device8::CreateCommittedResource2` or `ID3D12Device8::CreatePlacedResource1`. + + To work correctly, `ID3D12Device8` interface must be available in the current system. Otherwise, `E_NOINTERFACE` is returned. + */ + HRESULT CreateResource2( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE *pOptimizedClearValue, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource); +#endif // #ifdef __ID3D12Device4_INTERFACE_DEFINED__ + + /** \brief Allocates memory without creating any resource placed in it. + + This function is similar to `ID3D12Device::CreateHeap`, but it may really assign + part of a larger, existing heap to the allocation. + + `pAllocDesc->heapFlags` should contain one of these values, depending on type of resources you are going to create in this memory: + `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`, + `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`, + `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES`. + Except if you validate that ResourceHeapTier = 2 - then `heapFlags` + may be `D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES` = 0. + Additional flags in `heapFlags` are allowed as well. + + `pAllocInfo->SizeInBytes` must be multiply of 64KB. + `pAllocInfo->Alignment` must be one of the legal values as described in documentation of `D3D12_HEAP_DESC`. + + If you use D3D12MA::ALLOCATION_FLAG_COMMITTED you will get a separate memory block - + a heap that always has offset 0. + */ + HRESULT AllocateMemory( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo, + Allocation** ppAllocation); + + /** \brief Creates a new resource in place of an existing allocation. This is useful for memory aliasing. + + \param pAllocation Existing allocation indicating the memory where the new resource should be created. + It can be created using D3D12MA::Allocator::CreateResource and already have a resource bound to it, + or can be a raw memory allocated with D3D12MA::Allocator::AllocateMemory. + It must not be created as committed so that `ID3D12Heap` is available and not implicit. + \param AllocationLocalOffset Additional offset in bytes to be applied when allocating the resource. + Local from the start of `pAllocation`, not the beginning of the whole `ID3D12Heap`! + If the new resource should start from the beginning of the `pAllocation` it should be 0. + \param pResourceDesc Description of the new resource to be created. + \param InitialResourceState + \param pOptimizedClearValue + \param riidResource + \param[out] ppvResource Returns pointer to the new resource. + The resource is not bound with `pAllocation`. + This pointer must not be null - you must get the resource pointer and `Release` it when no longer needed. + + Memory requirements of the new resource are checked for validation. + If its size exceeds the end of `pAllocation` or required alignment is not fulfilled + considering `pAllocation->GetOffset() + AllocationLocalOffset`, the function + returns `E_INVALIDARG`. + */ + HRESULT CreateAliasingResource( + Allocation* pAllocation, + UINT64 AllocationLocalOffset, + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE *pOptimizedClearValue, + REFIID riidResource, + void** ppvResource); + + /** \brief Creates custom pool. + */ + HRESULT CreatePool( + const POOL_DESC* pPoolDesc, + Pool** ppPool); + + /** \brief Sets the index of the current frame. + + This function is used to set the frame index in the allocator when a new game frame begins. + */ + void SetCurrentFrameIndex(UINT frameIndex); + + /** \brief Retrieves information about current memory usage and budget. + + \param[out] pLocalBudget Optional, can be null. + \param[out] pNonLocalBudget Optional, can be null. + + - When IsUMA() `== FALSE` (discrete graphics card): + - `pLocalBudget` returns the budget of the video memory. + - `pNonLocalBudget` returns the budget of the system memory available for D3D12 resources. + - When IsUMA() `== TRUE` (integrated graphics chip): + - `pLocalBudget` returns the budget of the shared memory available for all D3D12 resources. + All memory is considered "local". + - `pNonLocalBudget` is not applicable and returns zeros. + + This function is called "get" not "calculate" because it is very fast, suitable to be called + every frame or every allocation. For more detailed statistics use CalculateStatistics(). + + Note that when using allocator from multiple threads, returned information may immediately + become outdated. + */ + void GetBudget(Budget* pLocalBudget, Budget* pNonLocalBudget); + + /** \brief Retrieves statistics from current state of the allocator. + + This function is called "calculate" not "get" because it has to traverse all + internal data structures, so it may be quite slow. Use it for debugging purposes. + For faster but more brief statistics suitable to be called every frame or every allocation, + use GetBudget(). + + Note that when using allocator from multiple threads, returned information may immediately + become outdated. + */ + void CalculateStatistics(TotalStatistics* pStats); + + /** \brief Builds and returns statistics as a string in JSON format. + * + @param[out] ppStatsString Must be freed using Allocator::FreeStatsString. + @param DetailedMap `TRUE` to include full list of allocations (can make the string quite long), `FALSE` to only return statistics. + */ + void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const; + + /// Frees memory of a string returned from Allocator::BuildStatsString. + void FreeStatsString(WCHAR* pStatsString) const; + + /** \brief Begins defragmentation process of the default pools. + + \param pDesc Structure filled with parameters of defragmentation. + \param[out] ppContext Context object that will manage defragmentation. + + For more information about defragmentation, see documentation chapter: + [Defragmentation](@ref defragmentation). + */ + void BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext); + +protected: + void ReleaseThis() override; + +private: + friend D3D12MA_API HRESULT CreateAllocator(const ALLOCATOR_DESC*, Allocator**); + template friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*); + friend class DefragmentationContext; + friend class Pool; + + Allocator(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc); + ~Allocator(); + + AllocatorPimpl* m_Pimpl; + + D3D12MA_CLASS_NO_COPY(Allocator) +}; + + +/// \brief Bit flags to be used with VIRTUAL_BLOCK_DESC::Flags. +enum VIRTUAL_BLOCK_FLAGS +{ + /// Zero + VIRTUAL_BLOCK_FLAG_NONE = 0, + + /** \brief Enables alternative, linear allocation algorithm in this virtual block. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. + For details, see documentation chapter \ref linear_algorithm. + */ + VIRTUAL_BLOCK_FLAG_ALGORITHM_LINEAR = POOL_FLAG_ALGORITHM_LINEAR, + + // Bit mask to extract only `ALGORITHM` bits from entire set of flags. + VIRTUAL_BLOCK_FLAG_ALGORITHM_MASK = POOL_FLAG_ALGORITHM_MASK +}; + +/// Parameters of created D3D12MA::VirtualBlock object to be passed to CreateVirtualBlock(). +struct VIRTUAL_BLOCK_DESC +{ + /// Flags. + VIRTUAL_BLOCK_FLAGS Flags; + /** \brief Total size of the block. + + Sizes can be expressed in bytes or any units you want as long as you are consistent in using them. + For example, if you allocate from some array of structures, 1 can mean single instance of entire structure. + */ + UINT64 Size; + /** \brief Custom CPU memory allocation callbacks. Optional. + + Optional, can be null. When specified, will be used for all CPU-side memory allocations. + */ + const ALLOCATION_CALLBACKS* pAllocationCallbacks; +}; + +/// \brief Bit flags to be used with VIRTUAL_ALLOCATION_DESC::Flags. +enum VIRTUAL_ALLOCATION_FLAGS +{ + /// Zero + VIRTUAL_ALLOCATION_FLAG_NONE = 0, + + /** \brief Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for virtual blocks created with #VIRTUAL_BLOCK_FLAG_ALGORITHM_LINEAR flag. + */ + VIRTUAL_ALLOCATION_FLAG_UPPER_ADDRESS = ALLOCATION_FLAG_UPPER_ADDRESS, + + /// Allocation strategy that tries to minimize memory usage. + VIRTUAL_ALLOCATION_FLAG_STRATEGY_MIN_MEMORY = ALLOCATION_FLAG_STRATEGY_MIN_MEMORY, + /// Allocation strategy that tries to minimize allocation time. + VIRTUAL_ALLOCATION_FLAG_STRATEGY_MIN_TIME = ALLOCATION_FLAG_STRATEGY_MIN_TIME, + /** \brief Allocation strategy that chooses always the lowest offset in available space. + This is not the most efficient strategy but achieves highly packed data. + */ + VIRTUAL_ALLOCATION_FLAG_STRATEGY_MIN_OFFSET = ALLOCATION_FLAG_STRATEGY_MIN_OFFSET, + /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags. + + These strategy flags are binary compatible with equivalent flags in #ALLOCATION_FLAGS. + */ + VIRTUAL_ALLOCATION_FLAG_STRATEGY_MASK = ALLOCATION_FLAG_STRATEGY_MASK, +}; + +/// Parameters of created virtual allocation to be passed to VirtualBlock::Allocate(). +struct VIRTUAL_ALLOCATION_DESC +{ + /// Flags. + VIRTUAL_ALLOCATION_FLAGS Flags; + /** \brief Size of the allocation. + + Cannot be zero. + */ + UINT64 Size; + /** \brief Required alignment of the allocation. + + Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset. + */ + UINT64 Alignment; + /** \brief Custom pointer to be associated with the allocation. + + It can be fetched or changed later. + */ + void* pPrivateData; +}; + +/// Parameters of an existing virtual allocation, returned by VirtualBlock::GetAllocationInfo(). +struct VIRTUAL_ALLOCATION_INFO +{ + /// \brief Offset of the allocation. + UINT64 Offset; + /** \brief Size of the allocation. + + Same value as passed in VIRTUAL_ALLOCATION_DESC::Size. + */ + UINT64 Size; + /** \brief Custom pointer associated with the allocation. + + Same value as passed in VIRTUAL_ALLOCATION_DESC::pPrivateData or VirtualBlock::SetAllocationPrivateData(). + */ + void* pPrivateData; +}; + +/** \brief Represents pure allocation algorithm and a data structure with allocations in some memory block, without actually allocating any GPU memory. + +This class allows to use the core algorithm of the library custom allocations e.g. CPU memory or +sub-allocation regions inside a single GPU buffer. + +To create this object, fill in D3D12MA::VIRTUAL_BLOCK_DESC and call CreateVirtualBlock(). +To destroy it, call its method `VirtualBlock::Release()`. +You need to free all the allocations within this block or call Clear() before destroying it. + +This object is not thread-safe - should not be used from multiple threads simultaneously, must be synchronized externally. +*/ +class D3D12MA_API VirtualBlock : public IUnknownImpl +{ +public: + /** \brief Returns true if the block is empty - contains 0 allocations. + */ + BOOL IsEmpty() const; + /** \brief Returns information about an allocation - its offset, size and custom pointer. + */ + void GetAllocationInfo(VirtualAllocation allocation, VIRTUAL_ALLOCATION_INFO* pInfo) const; + + /** \brief Creates new allocation. + \param pDesc + \param[out] pAllocation Unique indentifier of the new allocation within single block. + \param[out] pOffset Returned offset of the new allocation. Optional, can be null. + \return `S_OK` if allocation succeeded, `E_OUTOFMEMORY` if it failed. + + If the allocation failed, `pAllocation->AllocHandle` is set to 0 and `pOffset`, if not null, is set to `UINT64_MAX`. + */ + HRESULT Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, VirtualAllocation* pAllocation, UINT64* pOffset); + /** \brief Frees the allocation. + + Calling this function with `allocation.AllocHandle == 0` is correct and does nothing. + */ + void FreeAllocation(VirtualAllocation allocation); + /** \brief Frees all the allocations. + */ + void Clear(); + /** \brief Changes custom pointer for an allocation to a new value. + */ + void SetAllocationPrivateData(VirtualAllocation allocation, void* pPrivateData); + /** \brief Retrieves basic statistics of the virtual block that are fast to calculate. + + \param[out] pStats %Statistics of the virtual block. + */ + void GetStatistics(Statistics* pStats) const; + /** \brief Retrieves detailed statistics of the virtual block that are slower to calculate. + + \param[out] pStats %Statistics of the virtual block. + */ + void CalculateStatistics(DetailedStatistics* pStats) const; + + /** \brief Builds and returns statistics as a string in JSON format, including the list of allocations with their parameters. + @param[out] ppStatsString Must be freed using VirtualBlock::FreeStatsString. + */ + void BuildStatsString(WCHAR** ppStatsString) const; + + /** \brief Frees memory of a string returned from VirtualBlock::BuildStatsString. + */ + void FreeStatsString(WCHAR* pStatsString) const; + +protected: + void ReleaseThis() override; + +private: + friend D3D12MA_API HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC*, VirtualBlock**); + template friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*); + + VirtualBlockPimpl* m_Pimpl; + + VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc); + ~VirtualBlock(); + + D3D12MA_CLASS_NO_COPY(VirtualBlock) +}; + + +/** \brief Creates new main D3D12MA::Allocator object and returns it through `ppAllocator`. + +You normally only need to call it once and keep a single Allocator object for your `ID3D12Device`. +*/ +D3D12MA_API HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator); + +/** \brief Creates new D3D12MA::VirtualBlock object and returns it through `ppVirtualBlock`. + +Note you don't need to create D3D12MA::Allocator to use virtual blocks. +*/ +D3D12MA_API HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock); + +} // namespace D3D12MA + +/// \cond INTERNAL +DEFINE_ENUM_FLAG_OPERATORS(D3D12MA::ALLOCATION_FLAGS); +DEFINE_ENUM_FLAG_OPERATORS(D3D12MA::DEFRAGMENTATION_FLAGS); +DEFINE_ENUM_FLAG_OPERATORS(D3D12MA::ALLOCATOR_FLAGS); +DEFINE_ENUM_FLAG_OPERATORS(D3D12MA::POOL_FLAGS); +DEFINE_ENUM_FLAG_OPERATORS(D3D12MA::VIRTUAL_BLOCK_FLAGS); +DEFINE_ENUM_FLAG_OPERATORS(D3D12MA::VIRTUAL_ALLOCATION_FLAGS); +/// \endcond + +/** +\page quick_start Quick start + +\section quick_start_project_setup Project setup and initialization + +This is a small, standalone C++ library. It consists of a pair of 2 files: +"D3D12MemAlloc.h" header file with public interface and "D3D12MemAlloc.cpp" with +internal implementation. The only external dependencies are WinAPI, Direct3D 12, +and parts of C/C++ standard library (but STL containers, exceptions, or RTTI are +not used). + +The library is developed and tested using Microsoft Visual Studio 2019, but it +should work with other compilers as well. It is designed for 64-bit code. + +To use the library in your project: + +(1.) Copy files `D3D12MemAlloc.cpp`, `%D3D12MemAlloc.h` to your project. + +(2.) Make `D3D12MemAlloc.cpp` compiling as part of the project, as C++ code. + +(3.) Include library header in each CPP file that needs to use the library. + +\code +#include "D3D12MemAlloc.h" +\endcode + +(4.) Right after you created `ID3D12Device`, fill D3D12MA::ALLOCATOR_DESC +structure and call function D3D12MA::CreateAllocator to create the main +D3D12MA::Allocator object. + +Please note that all symbols of the library are declared inside #D3D12MA namespace. + +\code +IDXGIAdapter* adapter = (...) +ID3D12Device* device = (...) + +D3D12MA::ALLOCATOR_DESC allocatorDesc = {}; +allocatorDesc.pDevice = device; +allocatorDesc.pAdapter = adapter; + +D3D12MA::Allocator* allocator; +HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator); +\endcode + +(5.) Right before destroying the D3D12 device, destroy the allocator object. + +Objects of this library must be destroyed by calling `Release` method. +They are somewhat compatible with COM: they implement `IUnknown` interface with its virtual methods: `AddRef`, `Release`, `QueryInterface`, +and they are reference-counted internally. +You can use smart pointers designed for COM with objects of this library - e.g. `CComPtr` or `Microsoft::WRL::ComPtr`. +The reference counter is thread-safe. +`QueryInterface` method supports only `IUnknown`, as classes of this library don't define their own GUIDs. + +\code +allocator->Release(); +\endcode + + +\section quick_start_creating_resources Creating resources + +To use the library for creating resources (textures and buffers), call method +D3D12MA::Allocator::CreateResource in the place where you would previously call +`ID3D12Device::CreateCommittedResource`. + +The function has similar syntax, but it expects structure D3D12MA::ALLOCATION_DESC +to be passed along with `D3D12_RESOURCE_DESC` and other parameters for created +resource. This structure describes parameters of the desired memory allocation, +including choice of `D3D12_HEAP_TYPE`. + +The function returns a new object of type D3D12MA::Allocation. +It represents allocated memory and can be queried for size, offset, `ID3D12Heap`. +It also holds a reference to the `ID3D12Resource`, which can be accessed by calling D3D12MA::Allocation::GetResource(). + +\code +D3D12_RESOURCE_DESC resourceDesc = {}; +resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; +resourceDesc.Alignment = 0; +resourceDesc.Width = 1024; +resourceDesc.Height = 1024; +resourceDesc.DepthOrArraySize = 1; +resourceDesc.MipLevels = 1; +resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; +resourceDesc.SampleDesc.Count = 1; +resourceDesc.SampleDesc.Quality = 0; +resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; +resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + +D3D12MA::ALLOCATION_DESC allocationDesc = {}; +allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + +D3D12MA::Allocation* allocation; +HRESULT hr = allocator->CreateResource( + &allocationDesc, + &resourceDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + NULL, + &allocation, + IID_NULL, NULL); + +// Use allocation->GetResource()... +\endcode + +You need to release the allocation object when no longer needed. +This will also release the D3D12 resource. + +\code +allocation->Release(); +\endcode + +The advantage of using the allocator instead of creating committed resource, and +the main purpose of this library, is that it can decide to allocate bigger memory +heap internally using `ID3D12Device::CreateHeap` and place multiple resources in +it, at different offsets, using `ID3D12Device::CreatePlacedResource`. The library +manages its own collection of allocated memory blocks (heaps) and remembers which +parts of them are occupied and which parts are free to be used for new resources. + +It is important to remember that resources created as placed don't have their memory +initialized to zeros, but may contain garbage data, so they need to be fully initialized +before usage, e.g. using Clear (`ClearRenderTargetView`), Discard (`DiscardResource`), +or copy (`CopyResource`). + +The library also automatically handles resource heap tier. +When `D3D12_FEATURE_DATA_D3D12_OPTIONS::ResourceHeapTier` equals `D3D12_RESOURCE_HEAP_TIER_1`, +resources of 3 types: buffers, textures that are render targets or depth-stencil, +and other textures must be kept in separate heaps. When `D3D12_RESOURCE_HEAP_TIER_2`, +they can be kept together. By using this library, you don't need to handle this +manually. + + +\section quick_start_resource_reference_counting Resource reference counting + +`ID3D12Resource` and other interfaces of Direct3D 12 use COM, so they are reference-counted. +Objects of this library are reference-counted as well. +An object of type D3D12MA::Allocation remembers the resource (buffer or texture) +that was created together with this memory allocation +and holds a reference to the `ID3D12Resource` object. +(Note this is a difference to Vulkan Memory Allocator, where a `VmaAllocation` object has no connection +with the buffer or image that was created with it.) +Thus, it is important to manage the resource reference counter properly. + +The simplest use case is shown in the code snippet above. +When only D3D12MA::Allocation object is obtained from a function call like D3D12MA::Allocator::CreateResource, +it remembers the `ID3D12Resource` that was created with it and holds a reference to it. +The resource can be obtained by calling `allocation->GetResource()`, which doesn't increment the resource +reference counter. +Calling `allocation->Release()` will decrease the resource reference counter, which is = 1 in this case, +so the resource will be released. + +Second option is to retrieve a pointer to the resource along with D3D12MA::Allocation. +Last parameters of the resource creation function can be used for this purpose. + +\code +D3D12MA::Allocation* allocation; +ID3D12Resource* resource; +HRESULT hr = allocator->CreateResource( + &allocationDesc, + &resourceDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + NULL, + &allocation, + IID_PPV_ARGS(&resource)); + +// Use resource... +\endcode + +In this case, returned pointer `resource` is equal to `allocation->GetResource()`, +but the creation function additionally increases resource reference counter for the purpose of returning it from this call +(it actually calls `QueryInterface` internally), so the resource will have the counter = 2. +The resource then need to be released along with the allocation, in this particular order, +to make sure the resource is destroyed before its memory heap can potentially be freed. + +\code +resource->Release(); +allocation->Release(); +\endcode + +More advanced use cases are possible when we consider that an D3D12MA::Allocation object can just hold +a reference to any resource. +It can be changed by calling D3D12MA::Allocation::SetResource. This function +releases the old resource and calls `AddRef` on the new one. + +Special care must be taken when performing defragmentation. +The new resource created at the destination place should be set as `pass.pMoves[i].pDstTmpAllocation->SetResource(newRes)`, +but it is moved to the source allocation at end of the defragmentation pass, +while the old resource accessible through `pass.pMoves[i].pSrcAllocation->GetResource()` is then released. +For more information, see documentation chapter \ref defragmentation. + + +\section quick_start_mapping_memory Mapping memory + +The process of getting regular CPU-side pointer to the memory of a resource in +Direct3D is called "mapping". There are rules and restrictions to this process, +as described in D3D12 documentation of `ID3D12Resource::Map` method. + +Mapping happens on the level of particular resources, not entire memory heaps, +and so it is out of scope of this library. Just as the documentation of the `Map` function says: + +- Returned pointer refers to data of particular subresource, not entire memory heap. +- You can map same resource multiple times. It is ref-counted internally. +- Mapping is thread-safe. +- Unmapping is not required before resource destruction. +- Unmapping may not be required before using written data - some heap types on + some platforms support resources persistently mapped. + +When using this library, you can map and use your resources normally without +considering whether they are created as committed resources or placed resources in one large heap. + +Example for buffer created and filled in `UPLOAD` heap type: + +\code +const UINT64 bufSize = 65536; +const float* bufData = (...); + +D3D12_RESOURCE_DESC resourceDesc = {}; +resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; +resourceDesc.Alignment = 0; +resourceDesc.Width = bufSize; +resourceDesc.Height = 1; +resourceDesc.DepthOrArraySize = 1; +resourceDesc.MipLevels = 1; +resourceDesc.Format = DXGI_FORMAT_UNKNOWN; +resourceDesc.SampleDesc.Count = 1; +resourceDesc.SampleDesc.Quality = 0; +resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; +resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + +D3D12MA::ALLOCATION_DESC allocationDesc = {}; +allocationDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + +D3D12Resource* resource; +D3D12MA::Allocation* allocation; +HRESULT hr = allocator->CreateResource( + &allocationDesc, + &resourceDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + NULL, + &allocation, + IID_PPV_ARGS(&resource)); + +void* mappedPtr; +hr = resource->Map(0, NULL, &mappedPtr); + +memcpy(mappedPtr, bufData, bufSize); + +resource->Unmap(0, NULL); +\endcode + + +\page custom_pools Custom memory pools + +A "pool" is a collection of memory blocks that share certain properties. +Allocator creates 3 default pools: for `D3D12_HEAP_TYPE_DEFAULT`, `UPLOAD`, `READBACK`. +A default pool automatically grows in size. Size of allocated blocks is also variable and managed automatically. +Typical allocations are created in these pools. You can also create custom pools. + +\section custom_pools_usage Usage + +To create a custom pool, fill in structure D3D12MA::POOL_DESC and call function D3D12MA::Allocator::CreatePool +to obtain object D3D12MA::Pool. Example: + +\code +POOL_DESC poolDesc = {}; +poolDesc.HeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + +Pool* pool; +HRESULT hr = allocator->CreatePool(&poolDesc, &pool); +\endcode + +To allocate resources out of a custom pool, only set member D3D12MA::ALLOCATION_DESC::CustomPool. +Example: + +\code +ALLOCATION_DESC allocDesc = {}; +allocDesc.CustomPool = pool; + +D3D12_RESOURCE_DESC resDesc = ... +Allocation* alloc; +hr = allocator->CreateResource(&allocDesc, &resDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &alloc, IID_NULL, NULL); +\endcode + +All allocations must be released before releasing the pool. +The pool must be released before relasing the allocator. + +\code +alloc->Release(); +pool->Release(); +\endcode + +\section custom_pools_features_and_benefits Features and benefits + +While it is recommended to use default pools whenever possible for simplicity and to give the allocator +more opportunities for internal optimizations, custom pools may be useful in following cases: + +- To keep some resources separate from others in memory. +- To keep track of memory usage of just a specific group of resources. %Statistics can be queried using + D3D12MA::Pool::CalculateStatistics. +- To use specific size of a memory block (`ID3D12Heap`). To set it, use member D3D12MA::POOL_DESC::BlockSize. + When set to 0, the library uses automatically determined, variable block sizes. +- To reserve some minimum amount of memory allocated. To use it, set member D3D12MA::POOL_DESC::MinBlockCount. +- To limit maximum amount of memory allocated. To use it, set member D3D12MA::POOL_DESC::MaxBlockCount. +- To use extended parameters of the D3D12 memory allocation. While resources created from default pools + can only specify `D3D12_HEAP_TYPE_DEFAULT`, `UPLOAD`, `READBACK`, a custom pool may use non-standard + `D3D12_HEAP_PROPERTIES` (member D3D12MA::POOL_DESC::HeapProperties) and `D3D12_HEAP_FLAGS` + (D3D12MA::POOL_DESC::HeapFlags), which is useful e.g. for cross-adapter sharing or UMA + (see also D3D12MA::Allocator::IsUMA). + +New versions of this library support creating **committed allocations in custom pools**. +It is supported only when D3D12MA::POOL_DESC::BlockSize = 0. +To use this feature, set D3D12MA::ALLOCATION_DESC::CustomPool to the pointer to your custom pool and +D3D12MA::ALLOCATION_DESC::Flags to D3D12MA::ALLOCATION_FLAG_COMMITTED. Example: + +\code +ALLOCATION_DESC allocDesc = {}; +allocDesc.CustomPool = pool; +allocDesc.Flags = ALLOCATION_FLAG_COMMITTED; + +D3D12_RESOURCE_DESC resDesc = ... +Allocation* alloc; +ID3D12Resource* res; +hr = allocator->CreateResource(&allocDesc, &resDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &alloc, IID_PPV_ARGS(&res)); +\endcode + +This feature may seem unnecessary, but creating committed allocations from custom pools may be useful +in some cases, e.g. to have separate memory usage statistics for some group of resources or to use +extended allocation parameters, like custom `D3D12_HEAP_PROPERTIES`, which are available only in custom pools. + + +\page defragmentation Defragmentation + +Interleaved allocations and deallocations of many objects of varying size can +cause fragmentation over time, which can lead to a situation where the library is unable +to find a continuous range of free memory for a new allocation despite there is +enough free space, just scattered across many small free ranges between existing +allocations. + +To mitigate this problem, you can use defragmentation feature. +It doesn't happen automatically though and needs your cooperation, +because %D3D12MA is a low level library that only allocates memory. +It cannot recreate buffers and textures in a new place as it doesn't remember the contents of `D3D12_RESOURCE_DESC` structure. +It cannot copy their contents as it doesn't record any commands to a command list. + +Example: + +\code +D3D12MA::DEFRAGMENTATION_DESC defragDesc = {}; +defragDesc.Flags = D3D12MA::DEFRAGMENTATION_FLAG_ALGORITHM_FAST; + +D3D12MA::DefragmentationContext* defragCtx; +allocator->BeginDefragmentation(&defragDesc, &defragCtx); + +for(;;) +{ + D3D12MA::DEFRAGMENTATION_PASS_MOVE_INFO pass; + HRESULT hr = defragCtx->BeginPass(&pass); + if(hr == S_OK) + break; + else if(hr != S_FALSE) + // Handle error... + + for(UINT i = 0; i < pass.MoveCount; ++i) + { + // Inspect pass.pMoves[i].pSrcAllocation, identify what buffer/texture it represents. + MyEngineResourceData* resData = (MyEngineResourceData*)pMoves[i].pSrcAllocation->GetPrivateData(); + + // Recreate this buffer/texture as placed at pass.pMoves[i].pDstTmpAllocation. + D3D12_RESOURCE_DESC resDesc = ... + ID3D12Resource* newRes; + hr = device->CreatePlacedResource( + pass.pMoves[i].pDstTmpAllocation->GetHeap(), + pass.pMoves[i].pDstTmpAllocation->GetOffset(), &resDesc, + D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&newRes)); + // Check hr... + + // Store new resource in the pDstTmpAllocation. + pass.pMoves[i].pDstTmpAllocation->SetResource(newRes); + + // Copy its content to the new place. + cmdList->CopyResource( + pass.pMoves[i].pDstTmpAllocation->GetResource(), + pass.pMoves[i].pSrcAllocation->GetResource()); + } + + // Make sure the copy commands finished executing. + cmdQueue->ExecuteCommandLists(...); + // ... + WaitForSingleObject(fenceEvent, INFINITE); + + // Update appropriate descriptors to point to the new places... + + hr = defragCtx->EndPass(&pass); + if(hr == S_OK) + break; + else if(hr != S_FALSE) + // Handle error... +} + +defragCtx->Release(); +\endcode + +Although functions like D3D12MA::Allocator::CreateResource() +create an allocation and a buffer/texture at once, these are just a shortcut for +allocating memory and creating a placed resource. +Defragmentation works on memory allocations only. You must handle the rest manually. +Defragmentation is an iterative process that should repreat "passes" as long as related functions +return `S_FALSE` not `S_OK`. +In each pass: + +1. D3D12MA::DefragmentationContext::BeginPass() function call: + - Calculates and returns the list of allocations to be moved in this pass. + Note this can be a time-consuming process. + - Reserves destination memory for them by creating temporary destination allocations + that you can query for their `ID3D12Heap` + offset using methods like D3D12MA::Allocation::GetHeap(). +2. Inside the pass, **you should**: + - Inspect the returned list of allocations to be moved. + - Create new buffers/textures as placed at the returned destination temporary allocations. + - Copy data from source to destination resources if necessary. + - Store the pointer to the new resource in the temporary destination allocation. +3. D3D12MA::DefragmentationContext::EndPass() function call: + - Frees the source memory reserved for the allocations that are moved. + - Modifies source D3D12MA::Allocation objects that are moved to point to the destination reserved memory + and destination resource, while source resource is released. + - Frees `ID3D12Heap` blocks that became empty. + +Defragmentation algorithm tries to move all suitable allocations. +You can, however, refuse to move some of them inside a defragmentation pass, by setting +`pass.pMoves[i].Operation` to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_IGNORE. +This is not recommended and may result in suboptimal packing of the allocations after defragmentation. +If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool. + +Inside a pass, for each allocation that should be moved: + +- You should copy its data from the source to the destination place by calling e.g. `CopyResource()`. + - You need to make sure these commands finished executing before the source buffers/textures are released by D3D12MA::DefragmentationContext::EndPass(). +- If a resource doesn't contain any meaningful data, e.g. it is a transient render-target texture to be cleared, + filled, and used temporarily in each rendering frame, you can just recreate this texture + without copying its data. +- If the resource is in `D3D12_HEAP_TYPE_READBACK` memory, you can copy its data on the CPU + using `memcpy()`. +- If you cannot move the allocation, you can set `pass.pMoves[i].Operation` to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_IGNORE. + This will cancel the move. + - D3D12MA::DefragmentationContext::EndPass() will then free the destination memory + not the source memory of the allocation, leaving it unchanged. +- If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time), + you can set `pass.pMoves[i].Operation` to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_DESTROY. + - D3D12MA::DefragmentationContext::EndPass() will then free both source and destination memory, and will destroy the source D3D12MA::Allocation object. + +You can defragment a specific custom pool by calling D3D12MA::Pool::BeginDefragmentation +or all the default pools by calling D3D12MA::Allocator::BeginDefragmentation (like in the example above). + +Defragmentation is always performed in each pool separately. +Allocations are never moved between different heap types. +The size of the destination memory reserved for a moved allocation is the same as the original one. +Alignment of an allocation as it was determined using `GetResourceAllocationInfo()` is also respected after defragmentation. +Buffers/textures should be recreated with the same `D3D12_RESOURCE_DESC` parameters as the original ones. + +You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved +in each pass, e.g. to call it in sync with render frames and not to experience too big hitches. +See members: D3D12MA::DEFRAGMENTATION_DESC::MaxBytesPerPass, D3D12MA::DEFRAGMENTATION_DESC::MaxAllocationsPerPass. + +It is also safe to perform the defragmentation asynchronously to render frames and other Direct3D 12 and %D3D12MA +usage, possibly from multiple threads, with the exception that allocations +returned in D3D12MA::DEFRAGMENTATION_PASS_MOVE_INFO::pMoves shouldn't be released until the defragmentation pass is ended. + +Mapping is out of scope of this library and so it is not preserved after an allocation is moved during defragmentation. +You need to map the new resource yourself if needed. + +\note Defragmentation is not supported in custom pools created with D3D12MA::POOL_FLAG_ALGORITHM_LINEAR. + + +\page statistics Statistics + +This library contains several functions that return information about its internal state, +especially the amount of memory allocated from D3D12. + +\section statistics_numeric_statistics Numeric statistics + +If you need to obtain basic statistics about memory usage per memory segment group, together with current budget, +you can call function D3D12MA::Allocator::GetBudget() and inspect structure D3D12MA::Budget. +This is useful to keep track of memory usage and stay withing budget. +Example: + +\code +D3D12MA::Budget localBudget; +allocator->GetBudget(&localBudget, NULL); + +printf("My GPU memory currently has %u allocations taking %llu B,\n", + localBudget.Statistics.AllocationCount, + localBudget.Statistics.AllocationBytes); +printf("allocated out of %u D3D12 memory heaps taking %llu B,\n", + localBudget.Statistics.BlockCount, + localBudget.Statistics.BlockBytes); +printf("D3D12 reports total usage %llu B with budget %llu B.\n", + localBudget.UsageBytes, + localBudget.BudgetBytes); +\endcode + +You can query for more detailed statistics per heap type, memory segment group, and totals, +including minimum and maximum allocation size and unused range size, +by calling function D3D12MA::Allocator::CalculateStatistics() and inspecting structure D3D12MA::TotalStatistics. +This function is slower though, as it has to traverse all the internal data structures, +so it should be used only for debugging purposes. + +You can query for statistics of a custom pool using function D3D12MA::Pool::GetStatistics() +or D3D12MA::Pool::CalculateStatistics(). + +You can query for information about a specific allocation using functions of the D3D12MA::Allocation class, +e.g. `GetSize()`, `GetOffset()`, `GetHeap()`. + +\section statistics_json_dump JSON dump + +You can dump internal state of the allocator to a string in JSON format using function D3D12MA::Allocator::BuildStatsString(). +The result is guaranteed to be correct JSON. +It uses Windows Unicode (UTF-16) encoding. +Any strings provided by user (see D3D12MA::Allocation::SetName()) +are copied as-is and properly escaped for JSON. +It must be freed using function D3D12MA::Allocator::FreeStatsString(). + +The format of this JSON string is not part of official documentation of the library, +but it will not change in backward-incompatible way without increasing library major version number +and appropriate mention in changelog. + +The JSON string contains all the data that can be obtained using D3D12MA::Allocator::CalculateStatistics(). +It can also contain detailed map of allocated memory blocks and their regions - +free and occupied by allocations. +This allows e.g. to visualize the memory or assess fragmentation. + + +\page resource_aliasing Resource aliasing (overlap) + +New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory +management, give an opportunity to alias (overlap) multiple resources in the +same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL). +It can be useful to save video memory, but it must be used with caution. + +For example, if you know the flow of your whole render frame in advance, you +are going to use some intermediate textures or buffers only during a small range of render passes, +and you know these ranges don't overlap in time, you can create these resources in +the same place in memory, even if they have completely different parameters (width, height, format etc.). + +![Resource aliasing (overlap)](../gfx/Aliasing.png) + +Such scenario is possible using D3D12MA, but you need to create your resources +using special function D3D12MA::Allocator::CreateAliasingResource. +Before that, you need to allocate memory with parameters calculated using formula: + +- allocation size = max(size of each resource) +- allocation alignment = max(alignment of each resource) + +Following example shows two different textures created in the same place in memory, +allocated to fit largest of them. + +\code +D3D12_RESOURCE_DESC resDesc1 = {}; +resDesc1.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; +resDesc1.Alignment = 0; +resDesc1.Width = 1920; +resDesc1.Height = 1080; +resDesc1.DepthOrArraySize = 1; +resDesc1.MipLevels = 1; +resDesc1.Format = DXGI_FORMAT_R8G8B8A8_UNORM; +resDesc1.SampleDesc.Count = 1; +resDesc1.SampleDesc.Quality = 0; +resDesc1.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; +resDesc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + +D3D12_RESOURCE_DESC resDesc2 = {}; +resDesc2.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; +resDesc2.Alignment = 0; +resDesc2.Width = 1024; +resDesc2.Height = 1024; +resDesc2.DepthOrArraySize = 1; +resDesc2.MipLevels = 0; +resDesc2.Format = DXGI_FORMAT_R8G8B8A8_UNORM; +resDesc2.SampleDesc.Count = 1; +resDesc2.SampleDesc.Quality = 0; +resDesc2.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; +resDesc2.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + +const D3D12_RESOURCE_ALLOCATION_INFO allocInfo1 = + device->GetResourceAllocationInfo(0, 1, &resDesc1); +const D3D12_RESOURCE_ALLOCATION_INFO allocInfo2 = + device->GetResourceAllocationInfo(0, 1, &resDesc2); + +D3D12_RESOURCE_ALLOCATION_INFO finalAllocInfo = {}; +finalAllocInfo.Alignment = std::max(allocInfo1.Alignment, allocInfo2.Alignment); +finalAllocInfo.SizeInBytes = std::max(allocInfo1.SizeInBytes, allocInfo2.SizeInBytes); + +D3D12MA::ALLOCATION_DESC allocDesc = {}; +allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; +allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES; + +D3D12MA::Allocation* alloc; +hr = allocator->AllocateMemory(&allocDesc, &finalAllocInfo, &alloc); +assert(alloc != NULL && alloc->GetHeap() != NULL); + +ID3D12Resource* res1; +hr = allocator->CreateAliasingResource( + alloc, + 0, // AllocationLocalOffset + &resDesc1, + D3D12_RESOURCE_STATE_COMMON, + NULL, // pOptimizedClearValue + IID_PPV_ARGS(&res1)); + +ID3D12Resource* res2; +hr = allocator->CreateAliasingResource( + alloc, + 0, // AllocationLocalOffset + &resDesc2, + D3D12_RESOURCE_STATE_COMMON, + NULL, // pOptimizedClearValue + IID_PPV_ARGS(&res2)); + +// You can use res1 and res2, but not at the same time! + +res2->Release(); +res1->Release(); +alloc->Release(); +\endcode + +Remember that using resouces that alias in memory requires proper synchronization. +You need to issue a special barrier of type `D3D12_RESOURCE_BARRIER_TYPE_ALIASING`. +You also need to treat a resource after aliasing as uninitialized - containing garbage data. +For example, if you use `res1` and then want to use `res2`, you need to first initialize `res2` +using either Clear, Discard, or Copy to the entire resource. + +Additional considerations: + +- D3D12 also allows to interpret contents of memory between aliasing resources consistently in some cases, + which is called "data inheritance". For details, see + Microsoft documentation chapter "Memory Aliasing and Data Inheritance". +- You can create more complex layout where different textures and buffers are bound + at different offsets inside one large allocation. For example, one can imagine + a big texture used in some render passes, aliasing with a set of many small buffers + used in some further passes. To bind a resource at non-zero offset of an allocation, + call D3D12MA::Allocator::CreateAliasingResource with appropriate value of `AllocationLocalOffset` parameter. +- Resources of the three categories: buffers, textures with `RENDER_TARGET` or `DEPTH_STENCIL` flags, and all other textures, + can be placed in the same memory only when `allocator->GetD3D12Options().ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2`. + Otherwise they must be placed in different memory heap types, and thus aliasing them is not possible. + + +\page linear_algorithm Linear allocation algorithm + +Each D3D12 memory block managed by this library has accompanying metadata that +keeps track of used and unused regions. By default, the metadata structure and +algorithm tries to find best place for new allocations among free regions to +optimize memory usage. This way you can allocate and free objects in any order. + +![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png) + +Sometimes there is a need to use simpler, linear allocation algorithm. You can +create custom pool that uses such algorithm by adding flag +D3D12MA::POOL_FLAG_ALGORITHM_LINEAR to D3D12MA::POOL_DESC::Flags while creating +D3D12MA::Pool object. Then an alternative metadata management is used. It always +creates new allocations after last one and doesn't reuse free regions after +allocations freed in the middle. It results in better allocation performance and +less memory consumed by metadata. + +![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png) + +With this one flag, you can create a custom pool that can be used in many ways: +free-at-once, stack, double stack, and ring buffer. See below for details. +You don't need to specify explicitly which of these options you are going to use - it is detected automatically. + +\section linear_algorithm_free_at_once Free-at-once + +In a pool that uses linear algorithm, you still need to free all the allocations +individually by calling `allocation->Release()`. You can free +them in any order. New allocations are always made after last one - free space +in the middle is not reused. However, when you release all the allocation and +the pool becomes empty, allocation starts from the beginning again. This way you +can use linear algorithm to speed up creation of allocations that you are going +to release all at once. + +![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png) + +This mode is also available for pools created with D3D12MA::POOL_DESC::MaxBlockCount +value that allows multiple memory blocks. + +\section linear_algorithm_stack Stack + +When you free an allocation that was created last, its space can be reused. +Thanks to this, if you always release allocations in the order opposite to their +creation (LIFO - Last In First Out), you can achieve behavior of a stack. + +![Stack](../gfx/Linear_allocator_4_stack.png) + +This mode is also available for pools created with D3D12MA::POOL_DESC::MaxBlockCount +value that allows multiple memory blocks. + +\section linear_algorithm_double_stack Double stack + +The space reserved by a custom pool with linear algorithm may be used by two +stacks: + +- First, default one, growing up from offset 0. +- Second, "upper" one, growing down from the end towards lower offsets. + +To make allocation from the upper stack, add flag D3D12MA::ALLOCATION_FLAG_UPPER_ADDRESS +to D3D12MA::ALLOCATION_DESC::Flags. + +![Double stack](../gfx/Linear_allocator_7_double_stack.png) + +Double stack is available only in pools with one memory block - +D3D12MA::POOL_DESC::MaxBlockCount must be 1. Otherwise behavior is undefined. + +When the two stacks' ends meet so there is not enough space between them for a +new allocation, such allocation fails with usual `E_OUTOFMEMORY` error. + +\section linear_algorithm_ring_buffer Ring buffer + +When you free some allocations from the beginning and there is not enough free space +for a new one at the end of a pool, allocator's "cursor" wraps around to the +beginning and starts allocation there. Thanks to this, if you always release +allocations in the same order as you created them (FIFO - First In First Out), +you can achieve behavior of a ring buffer / queue. + +![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png) + +Ring buffer is available only in pools with one memory block - +D3D12MA::POOL_DESC::MaxBlockCount must be 1. Otherwise behavior is undefined. + +\section linear_algorithm_additional_considerations Additional considerations + +Linear algorithm can also be used with \ref virtual_allocator. +See flag D3D12MA::VIRTUAL_BLOCK_FLAG_ALGORITHM_LINEAR. + + +\page virtual_allocator Virtual allocator + +As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator". +It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block". +You can use it to allocate your own memory or other objects, even completely unrelated to D3D12. +A common use case is sub-allocation of pieces of one large GPU buffer. + +\section virtual_allocator_creating_virtual_block Creating virtual block + +To use this functionality, there is no main "allocator" object. +You don't need to have D3D12MA::Allocator object created. +All you need to do is to create a separate D3D12MA::VirtualBlock object for each block of memory you want to be managed by the allocator: + +-# Fill in D3D12MA::ALLOCATOR_DESC structure. +-# Call D3D12MA::CreateVirtualBlock. Get new D3D12MA::VirtualBlock object. + +Example: + +\code +D3D12MA::VIRTUAL_BLOCK_DESC blockDesc = {}; +blockDesc.Size = 1048576; // 1 MB + +D3D12MA::VirtualBlock *block; +HRESULT hr = CreateVirtualBlock(&blockDesc, &block); +\endcode + +\section virtual_allocator_making_virtual_allocations Making virtual allocations + +D3D12MA::VirtualBlock object contains internal data structure that keeps track of free and occupied regions +using the same code as the main D3D12 memory allocator. +A single allocation is identified by a lightweight structure D3D12MA::VirtualAllocation. +You will also likely want to know the offset at which the allocation was made in the block. + +In order to make an allocation: + +-# Fill in D3D12MA::VIRTUAL_ALLOCATION_DESC structure. +-# Call D3D12MA::VirtualBlock::Allocate. Get new D3D12MA::VirtualAllocation value that identifies the allocation. + +Example: + +\code +D3D12MA::VIRTUAL_ALLOCATION_DESC allocDesc = {}; +allocDesc.Size = 4096; // 4 KB + +D3D12MA::VirtualAllocation alloc; +UINT64 allocOffset; +hr = block->Allocate(&allocDesc, &alloc, &allocOffset); +if(SUCCEEDED(hr)) +{ + // Use the 4 KB of your memory starting at allocOffset. +} +else +{ + // Allocation failed - no space for it could be found. Handle this error! +} +\endcode + +\section virtual_allocator_deallocation Deallocation + +When no longer needed, an allocation can be freed by calling D3D12MA::VirtualBlock::FreeAllocation. + +When whole block is no longer needed, the block object can be released by calling `block->Release()`. +All allocations must be freed before the block is destroyed, which is checked internally by an assert. +However, if you don't want to call `block->FreeAllocation` for each allocation, you can use D3D12MA::VirtualBlock::Clear to free them all at once - +a feature not available in normal D3D12 memory allocator. + +Example: + +\code +block->FreeAllocation(alloc); +block->Release(); +\endcode + +\section virtual_allocator_allocation_parameters Allocation parameters + +You can attach a custom pointer to each allocation by using D3D12MA::VirtualBlock::SetAllocationPrivateData. +Its default value is `NULL`. +It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some +larger data structure containing more information. Example: + +\code +struct CustomAllocData +{ + std::string m_AllocName; +}; +CustomAllocData* allocData = new CustomAllocData(); +allocData->m_AllocName = "My allocation 1"; +block->SetAllocationPrivateData(alloc, allocData); +\endcode + +The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function +D3D12MA::VirtualBlock::GetAllocationInfo and inspecting returned structure D3D12MA::VIRTUAL_ALLOCATION_INFO. +If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation! +Example: + +\code +VIRTUAL_ALLOCATION_INFO allocInfo; +block->GetAllocationInfo(alloc, &allocInfo); +delete (CustomAllocData*)allocInfo.pPrivateData; + +block->FreeAllocation(alloc); +\endcode + +\section virtual_allocator_alignment_and_units Alignment and units + +It feels natural to express sizes and offsets in bytes. +If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member +D3D12MA::VIRTUAL_ALLOCATION_DESC::Alignment to request it. Example: + +\code +D3D12MA::VIRTUAL_ALLOCATION_DESC allocDesc = {}; +allocDesc.Size = 4096; // 4 KB +allocDesc.Alignment = 4; // Returned offset must be a multiply of 4 B + +D3D12MA::VirtualAllocation alloc; +UINT64 allocOffset; +hr = block->Allocate(&allocDesc, &alloc, &allocOffset); +\endcode + +Alignments of different allocations made from one block may vary. +However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`, +you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes. +It might be more convenient, but you need to make sure to use this new unit consistently in all the places: + +- D3D12MA::VIRTUAL_BLOCK_DESC::Size +- D3D12MA::VIRTUAL_ALLOCATION_DESC::Size and D3D12MA::VIRTUAL_ALLOCATION_DESC::Alignment +- Using offset returned by D3D12MA::VirtualBlock::Allocate and D3D12MA::VIRTUAL_ALLOCATION_INFO::Offset + +\section virtual_allocator_statistics Statistics + +You can obtain brief statistics of a virtual block using D3D12MA::VirtualBlock::GetStatistics(). +The function fills structure D3D12MA::Statistics - same as used by the normal D3D12 memory allocator. +Example: + +\code +D3D12MA::Statistics stats; +block->GetStatistics(&stats); +printf("My virtual block has %llu bytes used by %u virtual allocations\n", + stats.AllocationBytes, stats.AllocationCount); +\endcode + +More detailed statistics can be obtained using function D3D12MA::VirtualBlock::CalculateStatistics(), +but they are slower to calculate. + +You can also request a full list of allocations and free regions as a string in JSON format by calling +D3D12MA::VirtualBlock::BuildStatsString. +Returned string must be later freed using D3D12MA::VirtualBlock::FreeStatsString. +The format of this string may differ from the one returned by the main D3D12 allocator, but it is similar. + +\section virtual_allocator_additional_considerations Additional considerations + +Alternative, linear algorithm can be used with virtual allocator - see flag +D3D12MA::VIRTUAL_BLOCK_FLAG_ALGORITHM_LINEAR and documentation: \ref linear_algorithm. + +Note that the "virtual allocator" functionality is implemented on a level of individual memory blocks. +Keeping track of a whole collection of blocks, allocating new ones when out of free space, +deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user. + + +\page configuration Configuration + +Please check file `D3D12MemAlloc.cpp` lines between "Configuration Begin" and +"Configuration End" to find macros that you can define to change the behavior of +the library, primarily for debugging purposes. + +\section custom_memory_allocator Custom CPU memory allocator + +If you use custom allocator for CPU memory rather than default C++ operator `new` +and `delete` or `malloc` and `free` functions, you can make this library using +your allocator as well by filling structure D3D12MA::ALLOCATION_CALLBACKS and +passing it as optional member D3D12MA::ALLOCATOR_DESC::pAllocationCallbacks. +Functions pointed there will be used by the library to make any CPU-side +allocations. Example: + +\code +#include + +void* CustomAllocate(size_t Size, size_t Alignment, void* pPrivateData) +{ + void* memory = _aligned_malloc(Size, Alignment); + // Your extra bookkeeping here... + return memory; +} + +void CustomFree(void* pMemory, void* pPrivateData) +{ + // Your extra bookkeeping here... + _aligned_free(pMemory); +} + +(...) + +D3D12MA::ALLOCATION_CALLBACKS allocationCallbacks = {}; +allocationCallbacks.pAllocate = &CustomAllocate; +allocationCallbacks.pFree = &CustomFree; + +D3D12MA::ALLOCATOR_DESC allocatorDesc = {}; +allocatorDesc.pDevice = device; +allocatorDesc.pAdapter = adapter; +allocatorDesc.pAllocationCallbacks = &allocationCallbacks; + +D3D12MA::Allocator* allocator; +HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator); +\endcode + + +\section debug_margins Debug margins + +By default, allocations are laid out in memory blocks next to each other if possible +(considering required alignment returned by `ID3D12Device::GetResourceAllocationInfo`). + +![Allocations without margin](../gfx/Margins_1.png) + +Define macro `D3D12MA_DEBUG_MARGIN` to some non-zero value (e.g. 16) inside "D3D12MemAlloc.cpp" +to enforce specified number of bytes as a margin after every allocation. + +![Allocations with margin](../gfx/Margins_2.png) + +If your bug goes away after enabling margins, it means it may be caused by memory +being overwritten outside of allocation boundaries. It is not 100% certain though. +Change in application behavior may also be caused by different order and distribution +of allocations across memory blocks after margins are applied. + +Margins work with all memory heap types. + +Margin is applied only to placed allocations made out of memory heaps and not to committed +allocations, which have their own, implicit memory heap of specific size. +It is thus not applied to allocations made using D3D12MA::ALLOCATION_FLAG_COMMITTED flag +or those automatically decided to put into committed allocations, e.g. due to its large size. + +Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space. + +Note that enabling margins increases memory usage and fragmentation. + +Margins do not apply to \ref virtual_allocator. + + +\page general_considerations General considerations + +\section general_considerations_thread_safety Thread safety + +- The library has no global state, so separate D3D12MA::Allocator objects can be used independently. + In typical applications there should be no need to create multiple such objects though - one per `ID3D12Device` is enough. +- All calls to methods of D3D12MA::Allocator class are safe to be made from multiple + threads simultaneously because they are synchronized internally when needed. +- When the allocator is created with D3D12MA::ALLOCATOR_FLAG_SINGLETHREADED, + calls to methods of D3D12MA::Allocator class must be made from a single thread or synchronized by the user. + Using this flag may improve performance. +- D3D12MA::VirtualBlock is not safe to be used from multiple threads simultaneously. + +\section general_considerations_versioning_and_compatibility Versioning and compatibility + +The library uses [**Semantic Versioning**](https://semver.org/), +which means version numbers follow convention: Major.Minor.Patch (e.g. 2.3.0), where: + +- Incremented Patch version means a release is backward- and forward-compatible, + introducing only some internal improvements, bug fixes, optimizations etc. + or changes that are out of scope of the official API described in this documentation. +- Incremented Minor version means a release is backward-compatible, + so existing code that uses the library should continue to work, while some new + symbols could have been added: new structures, functions, new values in existing + enums and bit flags, new structure members, but not new function parameters. +- Incrementing Major version means a release could break some backward compatibility. + +All changes between official releases are documented in file "CHANGELOG.md". + +\warning Backward compatiblity is considered on the level of C++ source code, not binary linkage. +Adding new members to existing structures is treated as backward compatible if initializing +the new members to binary zero results in the old behavior. +You should always fully initialize all library structures to zeros and not rely on their +exact binary size. + +\section general_considerations_features_not_supported Features not supported + +Features deliberately excluded from the scope of this library: + +- **Descriptor allocation.** Although also called "heaps", objects that represent + descriptors are separate part of the D3D12 API from buffers and textures. + You can still use \ref virtual_allocator to manage descriptors and their ranges inside a descriptor heap. +- **Support for reserved (tiled) resources.** We don't recommend using them. +- Support for `ID3D12Device::Evict` and `MakeResident`. We don't recommend using them. + You can call them on the D3D12 objects manually. + Plese keep in mind, however, that eviction happens on the level of entire `ID3D12Heap` memory blocks + and not individual buffers or textures which may be placed inside them. +- **Handling CPU memory allocation failures.** When dynamically creating small C++ + objects in CPU memory (not the GPU memory), allocation failures are not + handled gracefully, because that would complicate code significantly and + is usually not needed in desktop PC applications anyway. + Success of an allocation is just checked with an assert. +- **Code free of any compiler warnings.** + There are many preprocessor macros that make some variables unused, function parameters unreferenced, + or conditional expressions constant in some configurations. + The code of this library should not be bigger or more complicated just to silence these warnings. + It is recommended to disable such warnings instead. +- This is a C++ library. **Bindings or ports to any other programming languages** are welcome as external projects but + are not going to be included into this repository. +*/ diff --git a/vendor/rmlui_backend/RmlUi_DirectX/LICENSE.txt b/vendor/rmlui_backend/RmlUi_DirectX/LICENSE.txt new file mode 100644 index 0000000..5de5572 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_DirectX/LICENSE.txt @@ -0,0 +1,74 @@ +The RmlUi DirectX 12 renderer includes thirdparty code with the following licenses. + + +----- D3D12MemAlloc.cpp/.h ----- + +Copyright (c) 2019-2024 Advanced Micro Devices, Inc. All rights reserved. + +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. + + +----- offsetAllocator.cpp/.hpp ----- + +MIT License + +Copyright (c) 2023 Sebastian Aaltonen + +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. + + +----- Snippets from d3dx12.h header (included directly in RmlUi_Renderer_DX12.cpp) ----- + +The MIT License (MIT) + +Copyright (c) 2015 Microsoft + +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. diff --git a/vendor/rmlui_backend/RmlUi_DirectX/offsetAllocator.cpp b/vendor/rmlui_backend/RmlUi_DirectX/offsetAllocator.cpp new file mode 100644 index 0000000..b2808e6 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_DirectX/offsetAllocator.cpp @@ -0,0 +1,513 @@ +// (C) Sebastian Aaltonen 2023 +// MIT License (see file: LICENSE) + +#include "offsetAllocator.hpp" + +#ifdef DEBUG +#include +#define ASSERT(x) assert(x) +//#define DEBUG_VERBOSE +#else +#define ASSERT(x) +#endif + +#ifdef DEBUG_VERBOSE +#include +#endif + +#ifdef _MSC_VER +#include +#endif + +#include + +namespace OffsetAllocator +{ + inline uint32 lzcnt_nonzero(uint32 v) + { +#ifdef _MSC_VER + unsigned long retVal; + _BitScanReverse(&retVal, v); + return 31 - retVal; +#else + return __builtin_clz(v); +#endif + } + + inline uint32 tzcnt_nonzero(uint32 v) + { +#ifdef _MSC_VER + unsigned long retVal; + _BitScanForward(&retVal, v); + return retVal; +#else + return __builtin_ctz(v); +#endif + } + + namespace SmallFloat + { + static constexpr uint32 MANTISSA_BITS = 3; + static constexpr uint32 MANTISSA_VALUE = 1 << MANTISSA_BITS; + static constexpr uint32 MANTISSA_MASK = MANTISSA_VALUE - 1; + + // Bin sizes follow floating point (exponent + mantissa) distribution (piecewise linear log approx) + // This ensures that for each size class, the average overhead percentage stays the same + uint32 uintToFloatRoundUp(uint32 size) + { + uint32 exp = 0; + uint32 mantissa = 0; + + if (size < MANTISSA_VALUE) + { + // Denorm: 0..(MANTISSA_VALUE-1) + mantissa = size; + } + else + { + // Normalized: Hidden high bit always 1. Not stored. Just like float. + uint32 leadingZeros = lzcnt_nonzero(size); + uint32 highestSetBit = 31 - leadingZeros; + + uint32 mantissaStartBit = highestSetBit - MANTISSA_BITS; + exp = mantissaStartBit + 1; + mantissa = (size >> mantissaStartBit) & MANTISSA_MASK; + + uint32 lowBitsMask = (1 << mantissaStartBit) - 1; + + // Round up! + if ((size & lowBitsMask) != 0) + mantissa++; + } + + return (exp << MANTISSA_BITS) + mantissa; // + allows mantissa->exp overflow for round up + } + + uint32 uintToFloatRoundDown(uint32 size) + { + uint32 exp = 0; + uint32 mantissa = 0; + + if (size < MANTISSA_VALUE) + { + // Denorm: 0..(MANTISSA_VALUE-1) + mantissa = size; + } + else + { + // Normalized: Hidden high bit always 1. Not stored. Just like float. + uint32 leadingZeros = lzcnt_nonzero(size); + uint32 highestSetBit = 31 - leadingZeros; + + uint32 mantissaStartBit = highestSetBit - MANTISSA_BITS; + exp = mantissaStartBit + 1; + mantissa = (size >> mantissaStartBit) & MANTISSA_MASK; + } + + return (exp << MANTISSA_BITS) | mantissa; + } + + uint32 floatToUint(uint32 floatValue) + { + uint32 exponent = floatValue >> MANTISSA_BITS; + uint32 mantissa = floatValue & MANTISSA_MASK; + if (exponent == 0) + { + // Denorms + return mantissa; + } + else + { + return (mantissa | MANTISSA_VALUE) << (exponent - 1); + } + } + } + + // Utility functions + uint32 findLowestSetBitAfter(uint32 bitMask, uint32 startBitIndex) + { + uint32 maskBeforeStartIndex = (1 << startBitIndex) - 1; + uint32 maskAfterStartIndex = ~maskBeforeStartIndex; + uint32 bitsAfter = bitMask & maskAfterStartIndex; + if (bitsAfter == 0) return Allocation::NO_SPACE; + return tzcnt_nonzero(bitsAfter); + } + + // Allocator... + Allocator::Allocator(uint32 size, uint32 maxAllocs) : + m_size(size), + m_maxAllocs(maxAllocs), + m_nodes(nullptr), + m_freeNodes(nullptr) + { + if (sizeof(NodeIndex) == 2) + { + ASSERT(maxAllocs <= 65536); + } + reset(); + } + + Allocator::Allocator(Allocator &&other) : + m_size(other.m_size), + m_maxAllocs(other.m_maxAllocs), + m_freeStorage(other.m_freeStorage), + m_usedBinsTop(other.m_usedBinsTop), + m_nodes(other.m_nodes), + m_freeNodes(other.m_freeNodes), + m_freeOffset(other.m_freeOffset) + { + memcpy(m_usedBins, other.m_usedBins, sizeof(uint8) * NUM_TOP_BINS); + memcpy(m_binIndices, other.m_binIndices, sizeof(NodeIndex) * NUM_LEAF_BINS); + + other.m_nodes = nullptr; + other.m_freeNodes = nullptr; + other.m_freeOffset = 0; + other.m_maxAllocs = 0; + other.m_usedBinsTop = 0; + } + + void Allocator::reset() + { + m_freeStorage = 0; + m_usedBinsTop = 0; + m_freeOffset = m_maxAllocs - 1; + + for (uint32 i = 0 ; i < NUM_TOP_BINS; i++) + m_usedBins[i] = 0; + + for (uint32 i = 0 ; i < NUM_LEAF_BINS; i++) + m_binIndices[i] = Node::unused; + + if (m_nodes) delete[] m_nodes; + if (m_freeNodes) delete[] m_freeNodes; + + m_nodes = new Node[m_maxAllocs]; + m_freeNodes = new NodeIndex[m_maxAllocs]; + + // Freelist is a stack. Nodes in inverse order so that [0] pops first. + for (uint32 i = 0; i < m_maxAllocs; i++) + { + m_freeNodes[i] = m_maxAllocs - i - 1; + } + + // Start state: Whole storage as one big node + // Algorithm will split remainders and push them back as smaller nodes + insertNodeIntoBin(m_size, 0); + } + + Allocator::~Allocator() + { + delete[] m_nodes; + delete[] m_freeNodes; + } + + Allocation Allocator::allocate(uint32 size) + { + // Out of allocations? + if (m_freeOffset == 0) + { + Allocation result; + result.offset = Allocation::NO_SPACE; + result.metadata = Allocation::NO_SPACE; + //return {.offset = Allocation::NO_SPACE, .metadata = Allocation::NO_SPACE}; + return result; + } + + // Round up to bin index to ensure that alloc >= bin + // Gives us min bin index that fits the size + uint32 minBinIndex = SmallFloat::uintToFloatRoundUp(size); + + uint32 minTopBinIndex = minBinIndex >> TOP_BINS_INDEX_SHIFT; + uint32 minLeafBinIndex = minBinIndex & LEAF_BINS_INDEX_MASK; + + uint32 topBinIndex = minTopBinIndex; + uint32 leafBinIndex = Allocation::NO_SPACE; + + // If top bin exists, scan its leaf bin. This can fail (NO_SPACE). + if (m_usedBinsTop & (1 << topBinIndex)) + { + leafBinIndex = findLowestSetBitAfter(m_usedBins[topBinIndex], minLeafBinIndex); + } + + // If we didn't find space in top bin, we search top bin from +1 + if (leafBinIndex == Allocation::NO_SPACE) + { + topBinIndex = findLowestSetBitAfter(m_usedBinsTop, minTopBinIndex + 1); + + // Out of space? + if (topBinIndex == Allocation::NO_SPACE) + { + Allocation result; + result.offset = Allocation::NO_SPACE; + result.metadata = Allocation::NO_SPACE; + //return {.offset = Allocation::NO_SPACE, .metadata = Allocation::NO_SPACE}; + return result; + } + + // All leaf bins here fit the alloc, since the top bin was rounded up. Start leaf search from bit 0. + // NOTE: This search can't fail since at least one leaf bit was set because the top bit was set. + leafBinIndex = tzcnt_nonzero(m_usedBins[topBinIndex]); + } + + uint32 binIndex = (topBinIndex << TOP_BINS_INDEX_SHIFT) | leafBinIndex; + + // Pop the top node of the bin. Bin top = node.next. + uint32 nodeIndex = m_binIndices[binIndex]; + Node& node = m_nodes[nodeIndex]; + uint32 nodeTotalSize = node.dataSize; + node.dataSize = size; + node.used = true; + m_binIndices[binIndex] = node.binListNext; + if (node.binListNext != Node::unused) m_nodes[node.binListNext].binListPrev = Node::unused; + m_freeStorage -= nodeTotalSize; +#ifdef DEBUG_VERBOSE + printf("Free storage: %u (-%u) (allocate)\n", m_freeStorage, nodeTotalSize); +#endif + + // Bin empty? + if (m_binIndices[binIndex] == Node::unused) + { + // Remove a leaf bin mask bit + m_usedBins[topBinIndex] &= ~(1 << leafBinIndex); + + // All leaf bins empty? + if (m_usedBins[topBinIndex] == 0) + { + // Remove a top bin mask bit + m_usedBinsTop &= ~(1 << topBinIndex); + } + } + + // Push back reminder N elements to a lower bin + uint32 reminderSize = nodeTotalSize - size; + if (reminderSize > 0) + { + uint32 newNodeIndex = insertNodeIntoBin(reminderSize, node.dataOffset + size); + + // Link nodes next to each other so that we can merge them later if both are free + // And update the old next neighbor to point to the new node (in middle) + if (node.neighborNext != Node::unused) m_nodes[node.neighborNext].neighborPrev = newNodeIndex; + m_nodes[newNodeIndex].neighborPrev = nodeIndex; + m_nodes[newNodeIndex].neighborNext = node.neighborNext; + node.neighborNext = newNodeIndex; + } + + // return {.offset = node.dataOffset, .metadata = nodeIndex}; + Allocation result; + result.offset = node.dataOffset; + result.metadata = nodeIndex; + return result; + } + + void Allocator::free(Allocation allocation) + { + ASSERT(allocation.metadata != Allocation::NO_SPACE); + if (!m_nodes) return; + + uint32 nodeIndex = allocation.metadata; + Node& node = m_nodes[nodeIndex]; + + // Double delete check + ASSERT(node.used == true); + + // Merge with neighbors... + uint32 offset = node.dataOffset; + uint32 size = node.dataSize; + + if ((node.neighborPrev != Node::unused) && (m_nodes[node.neighborPrev].used == false)) + { + // Previous (contiguous) free node: Change offset to previous node offset. Sum sizes + Node& prevNode = m_nodes[node.neighborPrev]; + offset = prevNode.dataOffset; + size += prevNode.dataSize; + + // Remove node from the bin linked list and put it in the freelist + removeNodeFromBin(node.neighborPrev); + + ASSERT(prevNode.neighborNext == nodeIndex); + node.neighborPrev = prevNode.neighborPrev; + } + + if ((node.neighborNext != Node::unused) && (m_nodes[node.neighborNext].used == false)) + { + // Next (contiguous) free node: Offset remains the same. Sum sizes. + Node& nextNode = m_nodes[node.neighborNext]; + size += nextNode.dataSize; + + // Remove node from the bin linked list and put it in the freelist + removeNodeFromBin(node.neighborNext); + + ASSERT(nextNode.neighborPrev == nodeIndex); + node.neighborNext = nextNode.neighborNext; + } + + uint32 neighborNext = node.neighborNext; + uint32 neighborPrev = node.neighborPrev; + + // Insert the removed node to freelist +#ifdef DEBUG_VERBOSE + printf("Putting node %u into freelist[%u] (free)\n", nodeIndex, m_freeOffset + 1); +#endif + m_freeNodes[++m_freeOffset] = nodeIndex; + + // Insert the (combined) free node to bin + uint32 combinedNodeIndex = insertNodeIntoBin(size, offset); + + // Connect neighbors with the new combined node + if (neighborNext != Node::unused) + { + m_nodes[combinedNodeIndex].neighborNext = neighborNext; + m_nodes[neighborNext].neighborPrev = combinedNodeIndex; + } + if (neighborPrev != Node::unused) + { + m_nodes[combinedNodeIndex].neighborPrev = neighborPrev; + m_nodes[neighborPrev].neighborNext = combinedNodeIndex; + } + } + + uint32 Allocator::insertNodeIntoBin(uint32 size, uint32 dataOffset) + { + // Round down to bin index to ensure that bin >= alloc + uint32 binIndex = SmallFloat::uintToFloatRoundDown(size); + + uint32 topBinIndex = binIndex >> TOP_BINS_INDEX_SHIFT; + uint32 leafBinIndex = binIndex & LEAF_BINS_INDEX_MASK; + + // Bin was empty before? + if (m_binIndices[binIndex] == Node::unused) + { + // Set bin mask bits + m_usedBins[topBinIndex] |= 1 << leafBinIndex; + m_usedBinsTop |= 1 << topBinIndex; + } + + // Take a freelist node and insert on top of the bin linked list (next = old top) + uint32 topNodeIndex = m_binIndices[binIndex]; + uint32 nodeIndex = m_freeNodes[m_freeOffset--]; +#ifdef DEBUG_VERBOSE + printf("Getting node %u from freelist[%u]\n", nodeIndex, m_freeOffset + 1); +#endif + // m_nodes[nodeIndex] = {.dataOffset = dataOffset, .dataSize = size, .binListNext = topNodeIndex}; + Node node_instance; + node_instance.dataOffset = dataOffset; + node_instance.dataSize = size; + node_instance.binListNext = topNodeIndex; + m_nodes[nodeIndex] = node_instance; + + if (topNodeIndex != Node::unused) m_nodes[topNodeIndex].binListPrev = nodeIndex; + m_binIndices[binIndex] = nodeIndex; + + m_freeStorage += size; +#ifdef DEBUG_VERBOSE + printf("Free storage: %u (+%u) (insertNodeIntoBin)\n", m_freeStorage, size); +#endif + + return nodeIndex; + } + + void Allocator::removeNodeFromBin(uint32 nodeIndex) + { + Node &node = m_nodes[nodeIndex]; + + if (node.binListPrev != Node::unused) + { + // Easy case: We have previous node. Just remove this node from the middle of the list. + m_nodes[node.binListPrev].binListNext = node.binListNext; + if (node.binListNext != Node::unused) m_nodes[node.binListNext].binListPrev = node.binListPrev; + } + else + { + // Hard case: We are the first node in a bin. Find the bin. + + // Round down to bin index to ensure that bin >= alloc + uint32 binIndex = SmallFloat::uintToFloatRoundDown(node.dataSize); + + uint32 topBinIndex = binIndex >> TOP_BINS_INDEX_SHIFT; + uint32 leafBinIndex = binIndex & LEAF_BINS_INDEX_MASK; + + m_binIndices[binIndex] = node.binListNext; + if (node.binListNext != Node::unused) m_nodes[node.binListNext].binListPrev = Node::unused; + + // Bin empty? + if (m_binIndices[binIndex] == Node::unused) + { + // Remove a leaf bin mask bit + m_usedBins[topBinIndex] &= ~(1 << leafBinIndex); + + // All leaf bins empty? + if (m_usedBins[topBinIndex] == 0) + { + // Remove a top bin mask bit + m_usedBinsTop &= ~(1 << topBinIndex); + } + } + } + + // Insert the node to freelist +#ifdef DEBUG_VERBOSE + printf("Putting node %u into freelist[%u] (removeNodeFromBin)\n", nodeIndex, m_freeOffset + 1); +#endif + m_freeNodes[++m_freeOffset] = nodeIndex; + + m_freeStorage -= node.dataSize; +#ifdef DEBUG_VERBOSE + printf("Free storage: %u (-%u) (removeNodeFromBin)\n", m_freeStorage, node.dataSize); +#endif + } + + uint32 Allocator::allocationSize(Allocation allocation) const + { + if (allocation.metadata == Allocation::NO_SPACE) return 0; + if (!m_nodes) return 0; + + return m_nodes[allocation.metadata].dataSize; + } + + StorageReport Allocator::storageReport() const + { + uint32 largestFreeRegion = 0; + uint32 freeStorage = 0; + + // Out of allocations? -> Zero free space + if (m_freeOffset > 0) + { + freeStorage = m_freeStorage; + if (m_usedBinsTop) + { + uint32 topBinIndex = 31 - lzcnt_nonzero(m_usedBinsTop); + uint32 leafBinIndex = 31 - lzcnt_nonzero(m_usedBins[topBinIndex]); + largestFreeRegion = SmallFloat::floatToUint((topBinIndex << TOP_BINS_INDEX_SHIFT) | leafBinIndex); + ASSERT(freeStorage >= largestFreeRegion); + } + } + +// return {.totalFreeSpace = freeStorage, .largestFreeRegion = largestFreeRegion}; + StorageReport result; + result.totalFreeSpace = freeStorage; + result.largestFreeRegion = largestFreeRegion; + return result; + } + + StorageReportFull Allocator::storageReportFull() const + { + StorageReportFull report; + StorageReportFull::Region region; + for (uint32 i = 0; i < NUM_LEAF_BINS; i++) + { + uint32 count = 0; + uint32 nodeIndex = m_binIndices[i]; + while (nodeIndex != Node::unused) + { + nodeIndex = m_nodes[nodeIndex].binListNext; + count++; + } + + region.size = SmallFloat::floatToUint(i); + region.count = count; + // report.freeRegions[i] = { .size = SmallFloat::floatToUint(i), .count = count }; + report.freeRegions[i] = region; + } + return report; + } +} diff --git a/vendor/rmlui_backend/RmlUi_DirectX/offsetAllocator.hpp b/vendor/rmlui_backend/RmlUi_DirectX/offsetAllocator.hpp new file mode 100644 index 0000000..595145c --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_DirectX/offsetAllocator.hpp @@ -0,0 +1,97 @@ +// (C) Sebastian Aaltonen 2023 +// MIT License (see file: LICENSE) + +//#define USE_16_BIT_OFFSETS + +#pragma once + +namespace OffsetAllocator +{ + typedef unsigned char uint8; + typedef unsigned short uint16; + typedef unsigned int uint32; + + // 16 bit offsets mode will halve the metadata storage cost + // But it only supports up to 65536 maximum allocation count +#ifdef USE_16_BIT_NODE_INDICES + typedef uint16 NodeIndex; +#else + typedef uint32 NodeIndex; +#endif + + static constexpr uint32 NUM_TOP_BINS = 32; + static constexpr uint32 BINS_PER_LEAF = 8; + static constexpr uint32 TOP_BINS_INDEX_SHIFT = 3; + static constexpr uint32 LEAF_BINS_INDEX_MASK = 0x7; + static constexpr uint32 NUM_LEAF_BINS = NUM_TOP_BINS * BINS_PER_LEAF; + + struct Allocation + { + static constexpr uint32 NO_SPACE = 0xffffffff; + + uint32 offset = NO_SPACE; + NodeIndex metadata = NO_SPACE; // internal: node index + }; + + struct StorageReport + { + uint32 totalFreeSpace; + uint32 largestFreeRegion; + }; + + struct StorageReportFull + { + struct Region + { + uint32 size; + uint32 count; + }; + + Region freeRegions[NUM_LEAF_BINS]; + }; + + class Allocator + { + public: + Allocator(uint32 size, uint32 maxAllocs = 128 * 1024); + Allocator(Allocator &&other); + ~Allocator(); + void reset(); + + Allocation allocate(uint32 size); + void free(Allocation allocation); + + uint32 allocationSize(Allocation allocation) const; + StorageReport storageReport() const; + StorageReportFull storageReportFull() const; + + private: + uint32 insertNodeIntoBin(uint32 size, uint32 dataOffset); + void removeNodeFromBin(uint32 nodeIndex); + + struct Node + { + static constexpr NodeIndex unused = 0xffffffff; + + uint32 dataOffset = 0; + uint32 dataSize = 0; + NodeIndex binListPrev = unused; + NodeIndex binListNext = unused; + NodeIndex neighborPrev = unused; + NodeIndex neighborNext = unused; + bool used = false; // TODO: Merge as bit flag + }; + + uint32 m_size; + uint32 m_maxAllocs; + uint32 m_freeStorage; + + uint32 m_usedBinsTop; + uint8 m_usedBins[NUM_TOP_BINS]; + NodeIndex m_binIndices[NUM_LEAF_BINS]; + + Node* m_nodes; + NodeIndex* m_freeNodes; + uint32 m_freeOffset; + }; +} diff --git a/vendor/rmlui_backend/RmlUi_Include_DirectX_12.h b/vendor/rmlui_backend/RmlUi_Include_DirectX_12.h new file mode 100644 index 0000000..eed6aee --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Include_DirectX_12.h @@ -0,0 +1,284 @@ +#pragma once + +#if defined _MSC_VER + #pragma warning(push, 0) +#elif defined __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wall" + #pragma clang diagnostic ignored "-Wextra" + #pragma clang diagnostic ignored "-Wnullability-extension" + #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" + #pragma clang diagnostic ignored "-Wnullability-completeness" +#elif defined __GNUC__ + #pragma GCC system_header +#endif + +#include "RmlUi_DirectX/D3D12MemAlloc.h" +#include "RmlUi_DirectX/offsetAllocator.hpp" +#include +#include +#include +#include +#include + +namespace Rml { +inline constexpr const char* DXGIFormatToString(DXGI_FORMAT format) +{ + switch (format) + { + case DXGI_FORMAT::DXGI_FORMAT_UNKNOWN: return "DXGI_FORMAT_UNKNOWN"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32B32A32_TYPELESS: return "DXGI_FORMAT_R32G32B32A32_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32B32A32_FLOAT: return "DXGI_FORMAT_R32G32B32A32_FLOAT"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32B32A32_UINT: return "DXGI_FORMAT_R32G32B32A32_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32B32A32_SINT: return "DXGI_FORMAT_R32G32B32A32_SINT"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32B32_TYPELESS: return "DXGI_FORMAT_R32G32B32_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32B32_FLOAT: return "DXGI_FORMAT_R32G32B32_FLOAT"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32B32_UINT: return "DXGI_FORMAT_R32G32B32_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32B32_SINT: return "DXGI_FORMAT_R32G32B32_SINT"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_TYPELESS: return "DXGI_FORMAT_R16G16B16A16_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_FLOAT: return "DXGI_FORMAT_R16G16B16A16_FLOAT"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_UNORM: return "DXGI_FORMAT_R16G16B16A16_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_UINT: return "DXGI_FORMAT_R16G16B16A16_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_SNORM: return "DXGI_FORMAT_R16G16B16A16_SNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16B16A16_SINT: return "DXGI_FORMAT_R16G16B16A16_SINT"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32_TYPELESS: return "DXGI_FORMAT_R32G32_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32_FLOAT: return "DXGI_FORMAT_R32G32_FLOAT"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32_UINT: return "DXGI_FORMAT_R32G32_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R32G32_SINT: return "DXGI_FORMAT_R32G32_SINT"; + case DXGI_FORMAT::DXGI_FORMAT_R32G8X24_TYPELESS: return "DXGI_FORMAT_R32G8X24_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_D32_FLOAT_S8X24_UINT: return "DXGI_FORMAT_D32_FLOAT_S8X24_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: return "DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: return "DXGI_FORMAT_X32_TYPELESS_G8X24_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R10G10B10A2_TYPELESS: return "DXGI_FORMAT_R10G10B10A2_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_R10G10B10A2_UNORM: return "DXGI_FORMAT_R10G10B10A2_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R10G10B10A2_UINT: return "DXGI_FORMAT_R10G10B10A2_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R11G11B10_FLOAT: return "DXGI_FORMAT_R11G11B10_FLOAT"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_TYPELESS: return "DXGI_FORMAT_R8G8B8A8_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_UNORM: return "DXGI_FORMAT_R8G8B8A8_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_UINT: return "DXGI_FORMAT_R8G8B8A8_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_SNORM: return "DXGI_FORMAT_R8G8B8A8_SNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_SINT: return "DXGI_FORMAT_R8G8B8A8_SINT"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16_TYPELESS: return "DXGI_FORMAT_R16G16_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16_FLOAT: return "DXGI_FORMAT_R16G16_FLOAT"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16_UNORM: return "DXGI_FORMAT_R16G16_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16_UINT: return "DXGI_FORMAT_R16G16_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16_SNORM: return "DXGI_FORMAT_R16G16_SNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R16G16_SINT: return "DXGI_FORMAT_R16G16_SINT"; + case DXGI_FORMAT::DXGI_FORMAT_R32_TYPELESS: return "DXGI_FORMAT_R32_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_D32_FLOAT: return "DXGI_FORMAT_D32_FLOAT"; + case DXGI_FORMAT::DXGI_FORMAT_R32_FLOAT: return "DXGI_FORMAT_R32_FLOAT"; + case DXGI_FORMAT::DXGI_FORMAT_R32_UINT: return "DXGI_FORMAT_R32_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R32_SINT: return "DXGI_FORMAT_R32_SINT"; + case DXGI_FORMAT::DXGI_FORMAT_R24G8_TYPELESS: return "DXGI_FORMAT_R24G8_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_D24_UNORM_S8_UINT: return "DXGI_FORMAT_D24_UNORM_S8_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return "DXGI_FORMAT_R24_UNORM_X8_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_X24_TYPELESS_G8_UINT: return "DXGI_FORMAT_X24_TYPELESS_G8_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8_TYPELESS: return "DXGI_FORMAT_R8G8_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8_UNORM: return "DXGI_FORMAT_R8G8_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8_UINT: return "DXGI_FORMAT_R8G8_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8_SNORM: return "DXGI_FORMAT_R8G8_SNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8_SINT: return "DXGI_FORMAT_R8G8_SINT"; + case DXGI_FORMAT::DXGI_FORMAT_R16_TYPELESS: return "DXGI_FORMAT_R16_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_R16_FLOAT: return "DXGI_FORMAT_R16_FLOAT"; + case DXGI_FORMAT::DXGI_FORMAT_D16_UNORM: return "DXGI_FORMAT_D16_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R16_UNORM: return "DXGI_FORMAT_R16_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R16_UINT: return "DXGI_FORMAT_R16_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R16_SNORM: return "DXGI_FORMAT_R16_SNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R16_SINT: return "DXGI_FORMAT_R16_SINT"; + case DXGI_FORMAT::DXGI_FORMAT_R8_TYPELESS: return "DXGI_FORMAT_R8_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_R8_UNORM: return "DXGI_FORMAT_R8_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R8_UINT: return "DXGI_FORMAT_R8_UINT"; + case DXGI_FORMAT::DXGI_FORMAT_R8_SNORM: return "DXGI_FORMAT_R8_SNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R8_SINT: return "DXGI_FORMAT_R8_SINT"; + case DXGI_FORMAT::DXGI_FORMAT_A8_UNORM: return "DXGI_FORMAT_A8_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R1_UNORM: return "DXGI_FORMAT_R1_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R9G9B9E5_SHAREDEXP: return "DXGI_FORMAT_R9G9B9E5_SHAREDEXP"; + case DXGI_FORMAT::DXGI_FORMAT_R8G8_B8G8_UNORM: return "DXGI_FORMAT_R8G8_B8G8_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_G8R8_G8B8_UNORM: return "DXGI_FORMAT_G8R8_G8B8_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_BC1_TYPELESS: return "DXGI_FORMAT_BC1_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_BC1_UNORM: return "DXGI_FORMAT_BC1_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_BC1_UNORM_SRGB: return "DXGI_FORMAT_BC1_UNORM_SRGB"; + case DXGI_FORMAT::DXGI_FORMAT_BC2_TYPELESS: return "DXGI_FORMAT_BC2_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_BC2_UNORM: return "DXGI_FORMAT_BC2_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_BC2_UNORM_SRGB: return "DXGI_FORMAT_BC2_UNORM_SRGB"; + case DXGI_FORMAT::DXGI_FORMAT_BC3_TYPELESS: return "DXGI_FORMAT_BC3_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_BC3_UNORM: return "DXGI_FORMAT_BC3_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_BC3_UNORM_SRGB: return "DXGI_FORMAT_BC3_UNORM_SRGB"; + case DXGI_FORMAT::DXGI_FORMAT_BC4_TYPELESS: return "DXGI_FORMAT_BC4_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_BC4_UNORM: return "DXGI_FORMAT_BC4_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_BC4_SNORM: return "DXGI_FORMAT_BC4_SNORM"; + case DXGI_FORMAT::DXGI_FORMAT_BC5_TYPELESS: return "DXGI_FORMAT_BC5_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_BC5_UNORM: return "DXGI_FORMAT_BC5_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_BC5_SNORM: return "DXGI_FORMAT_BC5_SNORM"; + case DXGI_FORMAT::DXGI_FORMAT_B5G6R5_UNORM: return "DXGI_FORMAT_B5G6R5_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_B5G5R5A1_UNORM: return "DXGI_FORMAT_B5G5R5A1_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM: return "DXGI_FORMAT_B8G8R8A8_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_B8G8R8X8_UNORM: return "DXGI_FORMAT_B8G8R8X8_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: return "DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_TYPELESS: return "DXGI_FORMAT_B8G8R8A8_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return "DXGI_FORMAT_B8G8R8A8_UNORM_SRGB"; + case DXGI_FORMAT::DXGI_FORMAT_B8G8R8X8_TYPELESS: return "DXGI_FORMAT_B8G8R8X8_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return "DXGI_FORMAT_B8G8R8X8_UNORM_SRGB"; + case DXGI_FORMAT::DXGI_FORMAT_BC6H_TYPELESS: return "DXGI_FORMAT_BC6H_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_BC6H_UF16: return "DXGI_FORMAT_BC6H_UF16"; + case DXGI_FORMAT::DXGI_FORMAT_BC6H_SF16: return "DXGI_FORMAT_BC6H_SF16"; + case DXGI_FORMAT::DXGI_FORMAT_BC7_TYPELESS: return "DXGI_FORMAT_BC7_TYPELESS"; + case DXGI_FORMAT::DXGI_FORMAT_BC7_UNORM: return "DXGI_FORMAT_BC7_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_BC7_UNORM_SRGB: return "DXGI_FORMAT_BC7_UNORM_SRGB"; + case DXGI_FORMAT::DXGI_FORMAT_AYUV: return "DXGI_FORMAT_AYUV"; + case DXGI_FORMAT::DXGI_FORMAT_Y410: return "DXGI_FORMAT_Y410"; + case DXGI_FORMAT::DXGI_FORMAT_Y416: return "DXGI_FORMAT_Y416"; + case DXGI_FORMAT::DXGI_FORMAT_NV12: return "DXGI_FORMAT_NV12"; + case DXGI_FORMAT::DXGI_FORMAT_P010: return "DXGI_FORMAT_P010"; + case DXGI_FORMAT::DXGI_FORMAT_P016: return "DXGI_FORMAT_P016"; + case DXGI_FORMAT::DXGI_FORMAT_420_OPAQUE: return "DXGI_FORMAT_420_OPAQUE"; + case DXGI_FORMAT::DXGI_FORMAT_YUY2: return "DXGI_FORMAT_YUY2"; + case DXGI_FORMAT::DXGI_FORMAT_Y210: return "DXGI_FORMAT_Y210"; + case DXGI_FORMAT::DXGI_FORMAT_Y216: return "DXGI_FORMAT_Y216"; + case DXGI_FORMAT::DXGI_FORMAT_NV11: return "DXGI_FORMAT_NV11"; + case DXGI_FORMAT::DXGI_FORMAT_AI44: return "DXGI_FORMAT_AI44"; + case DXGI_FORMAT::DXGI_FORMAT_IA44: return "DXGI_FORMAT_IA44"; + case DXGI_FORMAT::DXGI_FORMAT_P8: return "DXGI_FORMAT_P8"; + case DXGI_FORMAT::DXGI_FORMAT_A8P8: return "DXGI_FORMAT_A8P8"; + case DXGI_FORMAT::DXGI_FORMAT_B4G4R4A4_UNORM: return "DXGI_FORMAT_B4G4R4A4_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_P208: return "DXGI_FORMAT_P208"; + case DXGI_FORMAT::DXGI_FORMAT_V208: return "DXGI_FORMAT_V208"; + case DXGI_FORMAT::DXGI_FORMAT_V408: return "DXGI_FORMAT_V408"; + case DXGI_FORMAT::DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE: return "DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE"; + case DXGI_FORMAT::DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE: return "DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE"; + case DXGI_FORMAT::DXGI_FORMAT_A4B4G4R4_UNORM: return "DXGI_FORMAT_A4B4G4R4_UNORM"; + case DXGI_FORMAT::DXGI_FORMAT_FORCE_UINT: return "DXGI_FORMAT_FORCE_UINT"; + default: return "UNKNOWN_DXGI_FORMAT"; + } +} +} // namespace Rml + +#ifdef RMLUI_DEBUG + #define RMLUI_DX_VERIFY_MSG(statement, msg) RMLUI_ASSERTMSG(SUCCEEDED(statement), msg) + +// Uncomment the following line to enable additional DirectX debugging. +// #define RMLUI_DX_DEBUG +#else + #define RMLUI_DX_VERIFY_MSG(statement, msg) static_cast(statement) +#endif + +#if defined _MSC_VER + #pragma warning(pop) +#elif defined __clang__ + #pragma clang diagnostic pop +#endif + +// user's preprocessor overrides + +// system field (it is not supposed to specify by initialization structure) +// on some render backend implementations (Vulkan/DirectX-12) we have to check memory leaks but +// if we don't reserve memory for a field that contains layers (it is vector) +// at runtime we will get a called dtor of move-to-copy object (because of reallocation) +// and for that matter we will get a false-positive trigger of assert and it is not right generally +#ifndef RMLUI_RENDER_BACKEND_FIELD_RESERVECOUNT_OF_RENDERSTACK_LAYERS + #define RMLUI_RENDER_BACKEND_FIELD_RESERVECOUNT_OF_RENDERSTACK_LAYERS 6 +#endif + +// this field specifies the default amount of swapchain buffer that will be created +// but it is used only when user provided invalid input in initialization structure of backend +#ifndef RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT + #define RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT 3 +#endif +static_assert(RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT > 0); + +// this field specifies the default amount of videomemory that will be used on creation +// this field is used when user provided invalid input in initialization structure of backend +#ifndef RMLUI_RENDER_BACKEND_FIELD_VIDEOMEMORY_FOR_BUFFER_ALLOCATION + #define RMLUI_RENDER_BACKEND_FIELD_VIDEOMEMORY_FOR_BUFFER_ALLOCATION (1024 * 1024 * 2) +#endif +static_assert(RMLUI_RENDER_BACKEND_FIELD_VIDEOMEMORY_FOR_BUFFER_ALLOCATION > 0); + +// this field specifies the default amount of videomemory that will be used for texture creation +// on some backends it determines the size of a temp buffer that might be used not trivial (just for uploading texture by copying data in it) +// like on DirectX-12 it has a placement resources feature and making this value lower (4 Mb) the placement resource feature becomes pointless +// and doesn't gain any performance related boosting +#ifndef RMLUI_RENDER_BACKEND_FIELD_VIDEOMEMORY_FOR_TEXTURE_ALLOCATION + #define RMLUI_RENDER_BACKEND_FIELD_VIDEOMEMORY_FOR_TEXTURE_ALLOCATION (4 * 1024 * 1024) +#endif +static_assert(RMLUI_RENDER_BACKEND_FIELD_VIDEOMEMORY_FOR_TEXTURE_ALLOCATION > 0); + +// system field that is not supposed to be in initialization structure and used as default (input user doesn't affect it at all like we always +// use it, not just because we handling invalid input from user) +#ifndef RMLUI_RENDER_BACKEND_FIELD_ALIGNMENT_FOR_BUFFER + #define RMLUI_RENDER_BACKEND_FIELD_ALIGNMENT_FOR_BUFFER D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT +#endif + +// system field that is not supposed to be in initialization structure and used as default (input user doesn't affect it at all like we always +// use it, not just because we handling invalid input from user) +#ifndef RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT + #define RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_UNORM +#endif + +// system field that is not supposed to be in initialization structure and used as default (input user doesn't affect it at all like we always +// use it, not just because we handling invalid input from user) +#ifndef RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT + #define RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT DXGI_FORMAT::DXGI_FORMAT_D32_FLOAT_S8X24_UINT +#endif + +// system field that is not supposed to be in initialization structure and used as default (input user doesn't affect it at all like we always +// use it, not just because we handling invalid input from user) +// notice: this field is shared for all srv and cbv and uav it doesn't mean that it specifically allocates for srv 128, cbv 128 and uav 128, +// no! it allocates only on descriptor for all of such types and total amount is 128 not 3 * 128 = 384 !!! +#ifndef RMLUI_RENDER_BACKEND_FIELD_DESCRIPTORAMOUNT_FOR_SRV_CBV_UAV + #define RMLUI_RENDER_BACKEND_FIELD_DESCRIPTORAMOUNT_FOR_SRV_CBV_UAV 128 +#endif + +#ifndef RMLUI_RENDER_BACKEND_FIELD_MAXNUMPROGRAMS + #define RMLUI_RENDER_BACKEND_FIELD_MAXNUMPROGRAMS 32 +#endif + +// for getting total size of constant buffers you should multiply kPreAllocatedConstantBuffers * kSwapchainBackBufferCount e.g. 512 * +// RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT = 1536 (if RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT=3) but keep in mind that +// allocation per constant buffer is 512 bytes (due to fact of the max presented CB in shaders) so 1536 * 512 will be 786'432 bytes and knowing +// that default buffer budget is just 2Mb (2'097'152) you will have 1'310'720 bytes (~37% of budget took by CBs) but some information will go to +// vertices and some information will go to indices and then you will have some free space (maybe small depends on your input) for handling other +// constant buffers that weren't sufficient for your current frame aka reallocation happens at some point it is fine just because the memory +// management of this backend handles a reallocation situations and you will just get a new allocated buffer and all stuff goes to that buffer but +// I give you this reasoning for being a developer that cares about optimizations and cares about pipeline development for end user and for that +// reason I gave you understanding that generally constant buffers even 512 bytes but uses so much in frame will take a big chunk from your VB/IB +// budget just because GAPI needs to handle many frames from swapchain, just because we expect indeed hard scene for rendering, but for average +// mid or low pages that's enough for sure (in terms of preallocated memory for VB,IB,CB) +#ifndef RMLUI_RENDER_BACKEND_FIELD_PREALLOCATED_CONSTANTBUFFERS + #define RMLUI_RENDER_BACKEND_FIELD_PREALLOCATED_CONSTANTBUFFERS 512 +#endif + +#ifndef RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT + #define RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT 2 +#endif + +#ifndef RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_RTV + #define RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_RTV 12 +#endif + +#ifndef RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_DSV + #define RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_DSV 8 +#endif + +// specifies general (for all depth stencil textures that might be allocated by backend) depth value on clear operation (::ClearDepthStencilView) +#ifndef RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_DEPTH_VALUE + #define RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_DEPTH_VALUE 1.0f +#endif + +// specifies general (for all depth stencil textures that might be allocated by backend) stencil value on clear operation +// (::ClearDepthStencilView) +#ifndef RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_STENCIL_VALUE + #define RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_STENCIL_VALUE 0 +#endif + +#ifndef RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_RENDERTARGET_COLOR_VAlUE + #define RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_RENDERTARGET_COLOR_VAlUE 0.0f, 0.0f, 0.0f, 1.0f +#endif + +#ifndef RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_SIZE + #define RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_SIZE 1024 * 1024 * 8 +#endif + +#ifndef RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED + #define RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED 1 +#endif diff --git a/vendor/rmlui_backend/RmlUi_Include_GL3.h b/vendor/rmlui_backend/RmlUi_Include_GL3.h new file mode 100644 index 0000000..0b270f5 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Include_GL3.h @@ -0,0 +1,3582 @@ +/** + * Loader generated by glad 2.0.0-beta on Fri Mar 11 10:53:06 2022 + * + * Generator: C/C++ + * Specification: gl + * Extensions: 0 + * + * APIs: + * - gl:core=3.3 + * + * Options: + * - ALIAS = False + * - DEBUG = False + * - HEADER_ONLY = True + * - LOADER = True + * - MX = False + * - MX_GLOBAL = False + * - ON_DEMAND = False + * + * Commandline: + * --api='gl:core=3.3' --extensions='' c --header-only --loader + * + * Online: + * http://glad.sh/#api=gl%3Acore%3D3.3&extensions=&generator=c&options=HEADER_ONLY%2CLOADER + * + */ + +#ifndef GLAD_GL_H_ +#define GLAD_GL_H_ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#ifdef __gl_h_ + #error OpenGL (gl.h) header already included (API: gl), remove previous include! +#endif +#define __gl_h_ 1 +#ifdef __gl3_h_ + #error OpenGL (gl3.h) header already included (API: gl), remove previous include! +#endif +#define __gl3_h_ 1 +#ifdef __glext_h_ + #error OpenGL (glext.h) header already included (API: gl), remove previous include! +#endif +#define __glext_h_ 1 +#ifdef __gl3ext_h_ + #error OpenGL (gl3ext.h) header already included (API: gl), remove previous include! +#endif +#define __gl3ext_h_ 1 +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#define GLAD_GL +#define GLAD_OPTION_GL_HEADER_ONLY +#define GLAD_OPTION_GL_LOADER + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef GLAD_PLATFORM_H_ +#define GLAD_PLATFORM_H_ + +#ifndef GLAD_PLATFORM_WIN32 + #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__) + #define GLAD_PLATFORM_WIN32 1 + #else + #define GLAD_PLATFORM_WIN32 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_APPLE + #ifdef __APPLE__ + #define GLAD_PLATFORM_APPLE 1 + #else + #define GLAD_PLATFORM_APPLE 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_EMSCRIPTEN + #ifdef __EMSCRIPTEN__ + #define GLAD_PLATFORM_EMSCRIPTEN 1 + #else + #define GLAD_PLATFORM_EMSCRIPTEN 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_UWP + #if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY) + #ifdef __has_include + #if __has_include() + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #endif + + #ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY + #include + #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + #define GLAD_PLATFORM_UWP 1 + #endif + #endif + + #ifndef GLAD_PLATFORM_UWP + #define GLAD_PLATFORM_UWP 0 + #endif +#endif + +#ifdef __GNUC__ + #define GLAD_GNUC_EXTENSION __extension__ +#else + #define GLAD_GNUC_EXTENSION +#endif + +#ifndef GLAD_API_CALL + #if defined(GLAD_API_CALL_EXPORT) + #if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) + #if defined(GLAD_API_CALL_EXPORT_BUILD) + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllexport)) extern + #else + #define GLAD_API_CALL __declspec(dllexport) extern + #endif + #else + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllimport)) extern + #else + #define GLAD_API_CALL __declspec(dllimport) extern + #endif + #endif + #elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD) + #define GLAD_API_CALL __attribute__ ((visibility ("default"))) extern + #else + #define GLAD_API_CALL extern + #endif + #else + #define GLAD_API_CALL extern + #endif +#endif + +#ifdef APIENTRY + #define GLAD_API_PTR APIENTRY +#elif GLAD_PLATFORM_WIN32 + #define GLAD_API_PTR __stdcall +#else + #define GLAD_API_PTR +#endif + +#ifndef GLAPI +#define GLAPI GLAD_API_CALL +#endif + +#ifndef GLAPIENTRY +#define GLAPIENTRY GLAD_API_PTR +#endif + +#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) +#define GLAD_VERSION_MAJOR(version) (version / 10000) +#define GLAD_VERSION_MINOR(version) (version % 10000) + +#define GLAD_GENERATOR_VERSION "2.0.0-beta" + +typedef void (*GLADapiproc)(void); + +typedef GLADapiproc (*GLADloadfunc)(const char *name); +typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); + +typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); +typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); + +#endif /* GLAD_PLATFORM_H_ */ + +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_ALPHA 0x1906 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_ALWAYS 0x0207 +#define GL_AND 0x1501 +#define GL_AND_INVERTED 0x1504 +#define GL_AND_REVERSE 0x1502 +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_BACK 0x0405 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_BGRA_INTEGER 0x8D9B +#define GL_BGR_INTEGER 0x8D9A +#define GL_BLEND 0x0BE2 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_EQUATION 0x8009 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLUE 0x1905 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_BYTE 0x1400 +#define GL_CCW 0x0901 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_CLEAR 0x1500 +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_COLOR 0x1800 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_COMPILE_STATUS 0x8B81 +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_RG 0x8226 +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CONDITION_SATISFIED 0x911C +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_FLAGS 0x821E +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_COPY 0x1503 +#define GL_COPY_INVERTED 0x150C +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_CURRENT_QUERY 0x8865 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_CW 0x0900 +#define GL_DECR 0x1E03 +#define GL_DECR_WRAP 0x8508 +#define GL_DELETE_STATUS 0x8B80 +#define GL_DEPTH 0x1801 +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_DEPTH_CLAMP 0x864F +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH_FUNC 0x0B74 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DITHER 0x0BD0 +#define GL_DONT_CARE 0x1100 +#define GL_DOUBLE 0x140A +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_DRAW_BUFFER 0x0C01 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DST_ALPHA 0x0304 +#define GL_DST_COLOR 0x0306 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_EQUAL 0x0202 +#define GL_EQUIV 0x1509 +#define GL_EXTENSIONS 0x1F03 +#define GL_FALSE 0 +#define GL_FASTEST 0x1101 +#define GL_FILL 0x1B02 +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_FIXED_ONLY 0x891D +#define GL_FLOAT 0x1406 +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_FRAMEBUFFER 0x8D40 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_FRONT 0x0404 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_FRONT_FACE 0x0B46 +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEQUAL 0x0206 +#define GL_GREATER 0x0204 +#define GL_GREEN 0x1904 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_HALF_FLOAT 0x140B +#define GL_INCR 0x1E02 +#define GL_INCR_WRAP 0x8507 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_INT 0x1404 +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_INT_2_10_10_10_REV 0x8D9F +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_INVALID_INDEX 0xFFFFFFFF +#define GL_INVALID_OPERATION 0x0502 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVERT 0x150A +#define GL_KEEP 0x1E00 +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_LEFT 0x0406 +#define GL_LEQUAL 0x0203 +#define GL_LESS 0x0201 +#define GL_LINE 0x1B01 +#define GL_LINEAR 0x2601 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINES 0x0001 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_LINE_STRIP 0x0003 +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_LINE_WIDTH_RANGE 0x0B22 +#define GL_LINK_STATUS 0x8B82 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_MAJOR_VERSION 0x821B +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAX 0x8008 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_INTEGER_SAMPLES 0x9110 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MIN 0x8007 +#define GL_MINOR_VERSION 0x821C +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MULTISAMPLE 0x809D +#define GL_NAND 0x150E +#define GL_NEAREST 0x2600 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEVER 0x0200 +#define GL_NICEST 0x1102 +#define GL_NONE 0 +#define GL_NOOP 0x1505 +#define GL_NOR 0x1508 +#define GL_NOTEQUAL 0x0205 +#define GL_NO_ERROR 0 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_NUM_EXTENSIONS 0x821D +#define GL_OBJECT_TYPE 0x9112 +#define GL_ONE 1 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_OR 0x1507 +#define GL_OR_INVERTED 0x150D +#define GL_OR_REVERSE 0x150B +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_POINT 0x1B00 +#define GL_POINTS 0x0000 +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_POINT_SIZE_RANGE 0x0B12 +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_QUERY_WAIT 0x8E13 +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_R16 0x822A +#define GL_R16F 0x822D +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R16_SNORM 0x8F98 +#define GL_R32F 0x822E +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_R3_G3_B2 0x2A10 +#define GL_R8 0x8229 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R8_SNORM 0x8F94 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_READ_BUFFER 0x0C02 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_READ_ONLY 0x88B8 +#define GL_READ_WRITE 0x88BA +#define GL_RED 0x1903 +#define GL_RED_INTEGER 0x8D94 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERER 0x1F01 +#define GL_REPEAT 0x2901 +#define GL_REPLACE 0x1E01 +#define GL_RG 0x8227 +#define GL_RG16 0x822C +#define GL_RG16F 0x822F +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG16_SNORM 0x8F99 +#define GL_RG32F 0x8230 +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_RG8 0x822B +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB 0x1907 +#define GL_RGB10 0x8052 +#define GL_RGB10_A2 0x8059 +#define GL_RGB10_A2UI 0x906F +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGB16F 0x881B +#define GL_RGB16I 0x8D89 +#define GL_RGB16UI 0x8D77 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGB32F 0x8815 +#define GL_RGB32I 0x8D83 +#define GL_RGB32UI 0x8D71 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB5_A1 0x8057 +#define GL_RGB8 0x8051 +#define GL_RGB8I 0x8D8F +#define GL_RGB8UI 0x8D7D +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGB9_E5 0x8C3D +#define GL_RGBA 0x1908 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_RGBA16F 0x881A +#define GL_RGBA16I 0x8D88 +#define GL_RGBA16UI 0x8D76 +#define GL_RGBA16_SNORM 0x8F9B +#define GL_RGBA2 0x8055 +#define GL_RGBA32F 0x8814 +#define GL_RGBA32I 0x8D82 +#define GL_RGBA32UI 0x8D70 +#define GL_RGBA4 0x8056 +#define GL_RGBA8 0x8058 +#define GL_RGBA8I 0x8D8E +#define GL_RGBA8UI 0x8D7C +#define GL_RGBA8_SNORM 0x8F97 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RG_INTEGER 0x8228 +#define GL_RIGHT 0x0407 +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_SET 0x150F +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_SHADER_TYPE 0x8B4F +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_SHORT 0x1402 +#define GL_SIGNALED 0x9119 +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SRC1_ALPHA 0x8589 +#define GL_SRC1_COLOR 0x88F9 +#define GL_SRC_ALPHA 0x0302 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_SRC_COLOR 0x0300 +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_STATIC_COPY 0x88E6 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STENCIL 0x1802 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_INDEX 0x1901 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STEREO 0x0C33 +#define GL_STREAM_COPY 0x88E2 +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_SYNC_STATUS 0x9114 +#define GL_TEXTURE 0x1702 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_TEXTURE_3D 0x806F +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF +#define GL_TIMESTAMP 0x8E28 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_FAN 0x0006 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_TRUE 1 +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_UNSIGNALED 0x9118 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_VENDOR 0x1F00 +#define GL_VERSION 0x1F02 +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_VIEWPORT 0x0BA2 +#define GL_WAIT_FAILED 0x911D +#define GL_WRITE_ONLY 0x88B9 +#define GL_XOR 0x1506 +#define GL_ZERO 0 + + +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are 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 Materials. +** +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_GLAD_API_PTR + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_GLAD_API_PTR funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) + /* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_GLAD_API_PTR + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_GLAD_API_PTR __stdcall +#else +# define KHRONOS_GLAD_API_PTR +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef khronos_int8_t GLbyte; +typedef khronos_uint8_t GLubyte; +typedef khronos_int16_t GLshort; +typedef khronos_uint16_t GLushort; +typedef int GLint; +typedef unsigned int GLuint; +typedef khronos_int32_t GLclampx; +typedef int GLsizei; +typedef khronos_float_t GLfloat; +typedef khronos_float_t GLclampf; +typedef double GLdouble; +typedef double GLclampd; +typedef void *GLeglClientBufferEXT; +typedef void *GLeglImageOES; +typedef char GLchar; +typedef char GLcharARB; +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif +typedef khronos_uint16_t GLhalf; +typedef khronos_uint16_t GLhalfARB; +typedef khronos_int32_t GLfixed; +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptr; +#else +typedef khronos_intptr_t GLintptr; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptrARB; +#else +typedef khronos_intptr_t GLintptrARB; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptr; +#else +typedef khronos_ssize_t GLsizeiptr; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptrARB; +#else +typedef khronos_ssize_t GLsizeiptrARB; +#endif +typedef khronos_int64_t GLint64; +typedef khronos_int64_t GLint64EXT; +typedef khronos_uint64_t GLuint64; +typedef khronos_uint64_t GLuint64EXT; +typedef struct __GLsync *GLsync; +struct _cl_context; +struct _cl_event; +typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +typedef unsigned short GLhalfNV; +typedef GLintptr GLvdpauSurfaceNV; +typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); + + +#define GL_VERSION_1_0 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_0; +#define GL_VERSION_1_1 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_1; +#define GL_VERSION_1_2 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_2; +#define GL_VERSION_1_3 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_3; +#define GL_VERSION_1_4 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_4; +#define GL_VERSION_1_5 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_5; +#define GL_VERSION_2_0 1 +GLAD_API_CALL int GLAD_GL_VERSION_2_0; +#define GL_VERSION_2_1 1 +GLAD_API_CALL int GLAD_GL_VERSION_2_1; +#define GL_VERSION_3_0 1 +GLAD_API_CALL int GLAD_GL_VERSION_3_0; +#define GL_VERSION_3_1 1 +GLAD_API_CALL int GLAD_GL_VERSION_3_1; +#define GL_VERSION_3_2 1 +GLAD_API_CALL int GLAD_GL_VERSION_3_2; +#define GL_VERSION_3_3 1 +GLAD_API_CALL int GLAD_GL_VERSION_3_3; + + +typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture); +typedef void (GLAD_API_PTR *PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); +typedef void (GLAD_API_PTR *PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); +typedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); +typedef void (GLAD_API_PTR *PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler); +typedef void (GLAD_API_PTR *PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); +typedef void (GLAD_API_PTR *PFNGLBINDVERTEXARRAYPROC)(GLuint array); +typedef void (GLAD_API_PTR *PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (GLAD_API_PTR *PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); +typedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); +typedef GLenum (GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); +typedef void (GLAD_API_PTR *PFNGLCLEARPROC)(GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHPROC)(GLdouble depth); +typedef void (GLAD_API_PTR *PFNGLCLEARSTENCILPROC)(GLint s); +typedef GLenum (GLAD_API_PTR *PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef GLuint (GLAD_API_PTR *PFNGLCREATEPROGRAMPROC)(void); +typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROC)(GLenum type); +typedef void (GLAD_API_PTR *PFNGLCULLFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint * samplers); +typedef void (GLAD_API_PTR *PFNGLDELETESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDELETESYNCPROC)(GLsync sync); +typedef void (GLAD_API_PTR *PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLDEPTHFUNCPROC)(GLenum func); +typedef void (GLAD_API_PTR *PFNGLDEPTHMASKPROC)(GLboolean flag); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f); +typedef void (GLAD_API_PTR *PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDISABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISABLEIPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERPROC)(GLenum buf); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum * bufs); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices); +typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLENABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLENABLEIPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLENDCONDITIONALRENDERPROC)(void); +typedef void (GLAD_API_PTR *PFNGLENDQUERYPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLENDTRANSFORMFEEDBACKPROC)(void); +typedef GLsync (GLAD_API_PTR *PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLFINISHPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFLUSHPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (GLAD_API_PTR *PFNGLFRONTFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLGENBUFFERSPROC)(GLsizei n, GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLGENQUERIESPROC)(GLsizei n, GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint * samplers); +typedef void (GLAD_API_PTR *PFNGLGENTEXTURESPROC)(GLsizei n, GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLGENERATEMIPMAPPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformBlockName); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformName); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint * uniformIndices, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders); +typedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean * data); +typedef void (GLAD_API_PTR *PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean * data); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void ** params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void * data); +typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void * img); +typedef void (GLAD_API_PTR *PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble * data); +typedef GLenum (GLAD_API_PTR *PFNGLGETERRORPROC)(void); +typedef void (GLAD_API_PTR *PFNGLGETFLOATVPROC)(GLenum pname, GLfloat * data); +typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar * name); +typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERVPROC)(GLenum pname, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat * val); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source); +typedef void (GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params); +typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name); +typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); +typedef void (GLAD_API_PTR *PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei count, GLsizei * length, GLint * values); +typedef void (GLAD_API_PTR *PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void * pixels); +typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLsizei * size, GLenum * type, GLchar * name); +typedef GLuint (GLAD_API_PTR *PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar * uniformBlockName); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const* uniformNames, GLuint * uniformIndices); +typedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void ** pointer); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLHINTPROC)(GLenum target, GLenum mode); +typedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERPROC)(GLuint buffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDPROC)(GLenum cap); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDIPROC)(GLenum target, GLuint index); +typedef GLboolean (GLAD_API_PTR *PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPROC)(GLuint program); +typedef GLboolean (GLAD_API_PTR *PFNGLISQUERYPROC)(GLuint id); +typedef GLboolean (GLAD_API_PTR *PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISSAMPLERPROC)(GLuint sampler); +typedef GLboolean (GLAD_API_PTR *PFNGLISSHADERPROC)(GLuint shader); +typedef GLboolean (GLAD_API_PTR *PFNGLISSYNCPROC)(GLsync sync); +typedef GLboolean (GLAD_API_PTR *PFNGLISTEXTUREPROC)(GLuint texture); +typedef GLboolean (GLAD_API_PTR *PFNGLISVERTEXARRAYPROC)(GLuint array); +typedef void (GLAD_API_PTR *PFNGLLINEWIDTHPROC)(GLfloat width); +typedef void (GLAD_API_PTR *PFNGLLINKPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLLOGICOPPROC)(GLenum opcode); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei drawcount); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount, const GLint * basevertex); +typedef void (GLAD_API_PTR *PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLPOINTSIZEPROC)(GLfloat size); +typedef void (GLAD_API_PTR *PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); +typedef void (GLAD_API_PTR *PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLPROVOKINGVERTEXPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target); +typedef void (GLAD_API_PTR *PFNGLREADBUFFERPROC)(GLenum src); +typedef void (GLAD_API_PTR *PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); +typedef void (GLAD_API_PTR *PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKPROC)(GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const* varyings, GLenum bufferMode); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IPROC)(GLint location, GLint v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPBUFFERPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); + +GLAD_API_CALL PFNGLACTIVETEXTUREPROC glad_glActiveTexture; +#define glActiveTexture glad_glActiveTexture +GLAD_API_CALL PFNGLATTACHSHADERPROC glad_glAttachShader; +#define glAttachShader glad_glAttachShader +GLAD_API_CALL PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender; +#define glBeginConditionalRender glad_glBeginConditionalRender +GLAD_API_CALL PFNGLBEGINQUERYPROC glad_glBeginQuery; +#define glBeginQuery glad_glBeginQuery +GLAD_API_CALL PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback; +#define glBeginTransformFeedback glad_glBeginTransformFeedback +GLAD_API_CALL PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; +#define glBindAttribLocation glad_glBindAttribLocation +GLAD_API_CALL PFNGLBINDBUFFERPROC glad_glBindBuffer; +#define glBindBuffer glad_glBindBuffer +GLAD_API_CALL PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase; +#define glBindBufferBase glad_glBindBufferBase +GLAD_API_CALL PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange; +#define glBindBufferRange glad_glBindBufferRange +GLAD_API_CALL PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation; +#define glBindFragDataLocation glad_glBindFragDataLocation +GLAD_API_CALL PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed; +#define glBindFragDataLocationIndexed glad_glBindFragDataLocationIndexed +GLAD_API_CALL PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; +#define glBindFramebuffer glad_glBindFramebuffer +GLAD_API_CALL PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; +#define glBindRenderbuffer glad_glBindRenderbuffer +GLAD_API_CALL PFNGLBINDSAMPLERPROC glad_glBindSampler; +#define glBindSampler glad_glBindSampler +GLAD_API_CALL PFNGLBINDTEXTUREPROC glad_glBindTexture; +#define glBindTexture glad_glBindTexture +GLAD_API_CALL PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray; +#define glBindVertexArray glad_glBindVertexArray +GLAD_API_CALL PFNGLBLENDCOLORPROC glad_glBlendColor; +#define glBlendColor glad_glBlendColor +GLAD_API_CALL PFNGLBLENDEQUATIONPROC glad_glBlendEquation; +#define glBlendEquation glad_glBlendEquation +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; +#define glBlendEquationSeparate glad_glBlendEquationSeparate +GLAD_API_CALL PFNGLBLENDFUNCPROC glad_glBlendFunc; +#define glBlendFunc glad_glBlendFunc +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; +#define glBlendFuncSeparate glad_glBlendFuncSeparate +GLAD_API_CALL PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer; +#define glBlitFramebuffer glad_glBlitFramebuffer +GLAD_API_CALL PFNGLBUFFERDATAPROC glad_glBufferData; +#define glBufferData glad_glBufferData +GLAD_API_CALL PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; +#define glBufferSubData glad_glBufferSubData +GLAD_API_CALL PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; +#define glCheckFramebufferStatus glad_glCheckFramebufferStatus +GLAD_API_CALL PFNGLCLAMPCOLORPROC glad_glClampColor; +#define glClampColor glad_glClampColor +GLAD_API_CALL PFNGLCLEARPROC glad_glClear; +#define glClear glad_glClear +GLAD_API_CALL PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi; +#define glClearBufferfi glad_glClearBufferfi +GLAD_API_CALL PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv; +#define glClearBufferfv glad_glClearBufferfv +GLAD_API_CALL PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv; +#define glClearBufferiv glad_glClearBufferiv +GLAD_API_CALL PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv; +#define glClearBufferuiv glad_glClearBufferuiv +GLAD_API_CALL PFNGLCLEARCOLORPROC glad_glClearColor; +#define glClearColor glad_glClearColor +GLAD_API_CALL PFNGLCLEARDEPTHPROC glad_glClearDepth; +#define glClearDepth glad_glClearDepth +GLAD_API_CALL PFNGLCLEARSTENCILPROC glad_glClearStencil; +#define glClearStencil glad_glClearStencil +GLAD_API_CALL PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync; +#define glClientWaitSync glad_glClientWaitSync +GLAD_API_CALL PFNGLCOLORMASKPROC glad_glColorMask; +#define glColorMask glad_glColorMask +GLAD_API_CALL PFNGLCOLORMASKIPROC glad_glColorMaski; +#define glColorMaski glad_glColorMaski +GLAD_API_CALL PFNGLCOMPILESHADERPROC glad_glCompileShader; +#define glCompileShader glad_glCompileShader +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D; +#define glCompressedTexImage1D glad_glCompressedTexImage1D +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; +#define glCompressedTexImage2D glad_glCompressedTexImage2D +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D; +#define glCompressedTexImage3D glad_glCompressedTexImage3D +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D; +#define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; +#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D; +#define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D +GLAD_API_CALL PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData; +#define glCopyBufferSubData glad_glCopyBufferSubData +GLAD_API_CALL PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D; +#define glCopyTexImage1D glad_glCopyTexImage1D +GLAD_API_CALL PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D; +#define glCopyTexImage2D glad_glCopyTexImage2D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D; +#define glCopyTexSubImage1D glad_glCopyTexSubImage1D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; +#define glCopyTexSubImage2D glad_glCopyTexSubImage2D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D; +#define glCopyTexSubImage3D glad_glCopyTexSubImage3D +GLAD_API_CALL PFNGLCREATEPROGRAMPROC glad_glCreateProgram; +#define glCreateProgram glad_glCreateProgram +GLAD_API_CALL PFNGLCREATESHADERPROC glad_glCreateShader; +#define glCreateShader glad_glCreateShader +GLAD_API_CALL PFNGLCULLFACEPROC glad_glCullFace; +#define glCullFace glad_glCullFace +GLAD_API_CALL PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; +#define glDeleteBuffers glad_glDeleteBuffers +GLAD_API_CALL PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; +#define glDeleteFramebuffers glad_glDeleteFramebuffers +GLAD_API_CALL PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; +#define glDeleteProgram glad_glDeleteProgram +GLAD_API_CALL PFNGLDELETEQUERIESPROC glad_glDeleteQueries; +#define glDeleteQueries glad_glDeleteQueries +GLAD_API_CALL PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; +#define glDeleteRenderbuffers glad_glDeleteRenderbuffers +GLAD_API_CALL PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers; +#define glDeleteSamplers glad_glDeleteSamplers +GLAD_API_CALL PFNGLDELETESHADERPROC glad_glDeleteShader; +#define glDeleteShader glad_glDeleteShader +GLAD_API_CALL PFNGLDELETESYNCPROC glad_glDeleteSync; +#define glDeleteSync glad_glDeleteSync +GLAD_API_CALL PFNGLDELETETEXTURESPROC glad_glDeleteTextures; +#define glDeleteTextures glad_glDeleteTextures +GLAD_API_CALL PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays; +#define glDeleteVertexArrays glad_glDeleteVertexArrays +GLAD_API_CALL PFNGLDEPTHFUNCPROC glad_glDepthFunc; +#define glDepthFunc glad_glDepthFunc +GLAD_API_CALL PFNGLDEPTHMASKPROC glad_glDepthMask; +#define glDepthMask glad_glDepthMask +GLAD_API_CALL PFNGLDEPTHRANGEPROC glad_glDepthRange; +#define glDepthRange glad_glDepthRange +GLAD_API_CALL PFNGLDETACHSHADERPROC glad_glDetachShader; +#define glDetachShader glad_glDetachShader +GLAD_API_CALL PFNGLDISABLEPROC glad_glDisable; +#define glDisable glad_glDisable +GLAD_API_CALL PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; +#define glDisableVertexAttribArray glad_glDisableVertexAttribArray +GLAD_API_CALL PFNGLDISABLEIPROC glad_glDisablei; +#define glDisablei glad_glDisablei +GLAD_API_CALL PFNGLDRAWARRAYSPROC glad_glDrawArrays; +#define glDrawArrays glad_glDrawArrays +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced; +#define glDrawArraysInstanced glad_glDrawArraysInstanced +GLAD_API_CALL PFNGLDRAWBUFFERPROC glad_glDrawBuffer; +#define glDrawBuffer glad_glDrawBuffer +GLAD_API_CALL PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; +#define glDrawBuffers glad_glDrawBuffers +GLAD_API_CALL PFNGLDRAWELEMENTSPROC glad_glDrawElements; +#define glDrawElements glad_glDrawElements +GLAD_API_CALL PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex; +#define glDrawElementsBaseVertex glad_glDrawElementsBaseVertex +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced; +#define glDrawElementsInstanced glad_glDrawElementsInstanced +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex; +#define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex +GLAD_API_CALL PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements; +#define glDrawRangeElements glad_glDrawRangeElements +GLAD_API_CALL PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex; +#define glDrawRangeElementsBaseVertex glad_glDrawRangeElementsBaseVertex +GLAD_API_CALL PFNGLENABLEPROC glad_glEnable; +#define glEnable glad_glEnable +GLAD_API_CALL PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; +#define glEnableVertexAttribArray glad_glEnableVertexAttribArray +GLAD_API_CALL PFNGLENABLEIPROC glad_glEnablei; +#define glEnablei glad_glEnablei +GLAD_API_CALL PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender; +#define glEndConditionalRender glad_glEndConditionalRender +GLAD_API_CALL PFNGLENDQUERYPROC glad_glEndQuery; +#define glEndQuery glad_glEndQuery +GLAD_API_CALL PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback; +#define glEndTransformFeedback glad_glEndTransformFeedback +GLAD_API_CALL PFNGLFENCESYNCPROC glad_glFenceSync; +#define glFenceSync glad_glFenceSync +GLAD_API_CALL PFNGLFINISHPROC glad_glFinish; +#define glFinish glad_glFinish +GLAD_API_CALL PFNGLFLUSHPROC glad_glFlush; +#define glFlush glad_glFlush +GLAD_API_CALL PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange; +#define glFlushMappedBufferRange glad_glFlushMappedBufferRange +GLAD_API_CALL PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; +#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture; +#define glFramebufferTexture glad_glFramebufferTexture +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D; +#define glFramebufferTexture1D glad_glFramebufferTexture1D +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D; +#define glFramebufferTexture2D glad_glFramebufferTexture2D +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D; +#define glFramebufferTexture3D glad_glFramebufferTexture3D +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer; +#define glFramebufferTextureLayer glad_glFramebufferTextureLayer +GLAD_API_CALL PFNGLFRONTFACEPROC glad_glFrontFace; +#define glFrontFace glad_glFrontFace +GLAD_API_CALL PFNGLGENBUFFERSPROC glad_glGenBuffers; +#define glGenBuffers glad_glGenBuffers +GLAD_API_CALL PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; +#define glGenFramebuffers glad_glGenFramebuffers +GLAD_API_CALL PFNGLGENQUERIESPROC glad_glGenQueries; +#define glGenQueries glad_glGenQueries +GLAD_API_CALL PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; +#define glGenRenderbuffers glad_glGenRenderbuffers +GLAD_API_CALL PFNGLGENSAMPLERSPROC glad_glGenSamplers; +#define glGenSamplers glad_glGenSamplers +GLAD_API_CALL PFNGLGENTEXTURESPROC glad_glGenTextures; +#define glGenTextures glad_glGenTextures +GLAD_API_CALL PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays; +#define glGenVertexArrays glad_glGenVertexArrays +GLAD_API_CALL PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; +#define glGenerateMipmap glad_glGenerateMipmap +GLAD_API_CALL PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; +#define glGetActiveAttrib glad_glGetActiveAttrib +GLAD_API_CALL PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; +#define glGetActiveUniform glad_glGetActiveUniform +GLAD_API_CALL PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName; +#define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName +GLAD_API_CALL PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv; +#define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv +GLAD_API_CALL PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName; +#define glGetActiveUniformName glad_glGetActiveUniformName +GLAD_API_CALL PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv; +#define glGetActiveUniformsiv glad_glGetActiveUniformsiv +GLAD_API_CALL PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; +#define glGetAttachedShaders glad_glGetAttachedShaders +GLAD_API_CALL PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; +#define glGetAttribLocation glad_glGetAttribLocation +GLAD_API_CALL PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v; +#define glGetBooleani_v glad_glGetBooleani_v +GLAD_API_CALL PFNGLGETBOOLEANVPROC glad_glGetBooleanv; +#define glGetBooleanv glad_glGetBooleanv +GLAD_API_CALL PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v; +#define glGetBufferParameteri64v glad_glGetBufferParameteri64v +GLAD_API_CALL PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; +#define glGetBufferParameteriv glad_glGetBufferParameteriv +GLAD_API_CALL PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; +#define glGetBufferPointerv glad_glGetBufferPointerv +GLAD_API_CALL PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData; +#define glGetBufferSubData glad_glGetBufferSubData +GLAD_API_CALL PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage; +#define glGetCompressedTexImage glad_glGetCompressedTexImage +GLAD_API_CALL PFNGLGETDOUBLEVPROC glad_glGetDoublev; +#define glGetDoublev glad_glGetDoublev +GLAD_API_CALL PFNGLGETERRORPROC glad_glGetError; +#define glGetError glad_glGetError +GLAD_API_CALL PFNGLGETFLOATVPROC glad_glGetFloatv; +#define glGetFloatv glad_glGetFloatv +GLAD_API_CALL PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex; +#define glGetFragDataIndex glad_glGetFragDataIndex +GLAD_API_CALL PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation; +#define glGetFragDataLocation glad_glGetFragDataLocation +GLAD_API_CALL PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; +#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv +GLAD_API_CALL PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v; +#define glGetInteger64i_v glad_glGetInteger64i_v +GLAD_API_CALL PFNGLGETINTEGER64VPROC glad_glGetInteger64v; +#define glGetInteger64v glad_glGetInteger64v +GLAD_API_CALL PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v; +#define glGetIntegeri_v glad_glGetIntegeri_v +GLAD_API_CALL PFNGLGETINTEGERVPROC glad_glGetIntegerv; +#define glGetIntegerv glad_glGetIntegerv +GLAD_API_CALL PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv; +#define glGetMultisamplefv glad_glGetMultisamplefv +GLAD_API_CALL PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; +#define glGetProgramInfoLog glad_glGetProgramInfoLog +GLAD_API_CALL PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; +#define glGetProgramiv glad_glGetProgramiv +GLAD_API_CALL PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v; +#define glGetQueryObjecti64v glad_glGetQueryObjecti64v +GLAD_API_CALL PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv; +#define glGetQueryObjectiv glad_glGetQueryObjectiv +GLAD_API_CALL PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v; +#define glGetQueryObjectui64v glad_glGetQueryObjectui64v +GLAD_API_CALL PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv; +#define glGetQueryObjectuiv glad_glGetQueryObjectuiv +GLAD_API_CALL PFNGLGETQUERYIVPROC glad_glGetQueryiv; +#define glGetQueryiv glad_glGetQueryiv +GLAD_API_CALL PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; +#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv; +#define glGetSamplerParameterIiv glad_glGetSamplerParameterIiv +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv; +#define glGetSamplerParameterIuiv glad_glGetSamplerParameterIuiv +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv; +#define glGetSamplerParameterfv glad_glGetSamplerParameterfv +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv; +#define glGetSamplerParameteriv glad_glGetSamplerParameteriv +GLAD_API_CALL PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; +#define glGetShaderInfoLog glad_glGetShaderInfoLog +GLAD_API_CALL PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; +#define glGetShaderSource glad_glGetShaderSource +GLAD_API_CALL PFNGLGETSHADERIVPROC glad_glGetShaderiv; +#define glGetShaderiv glad_glGetShaderiv +GLAD_API_CALL PFNGLGETSTRINGPROC glad_glGetString; +#define glGetString glad_glGetString +GLAD_API_CALL PFNGLGETSTRINGIPROC glad_glGetStringi; +#define glGetStringi glad_glGetStringi +GLAD_API_CALL PFNGLGETSYNCIVPROC glad_glGetSynciv; +#define glGetSynciv glad_glGetSynciv +GLAD_API_CALL PFNGLGETTEXIMAGEPROC glad_glGetTexImage; +#define glGetTexImage glad_glGetTexImage +GLAD_API_CALL PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv; +#define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv +GLAD_API_CALL PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv; +#define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv +GLAD_API_CALL PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv; +#define glGetTexParameterIiv glad_glGetTexParameterIiv +GLAD_API_CALL PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv; +#define glGetTexParameterIuiv glad_glGetTexParameterIuiv +GLAD_API_CALL PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; +#define glGetTexParameterfv glad_glGetTexParameterfv +GLAD_API_CALL PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; +#define glGetTexParameteriv glad_glGetTexParameteriv +GLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying; +#define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying +GLAD_API_CALL PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex; +#define glGetUniformBlockIndex glad_glGetUniformBlockIndex +GLAD_API_CALL PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices; +#define glGetUniformIndices glad_glGetUniformIndices +GLAD_API_CALL PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; +#define glGetUniformLocation glad_glGetUniformLocation +GLAD_API_CALL PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; +#define glGetUniformfv glad_glGetUniformfv +GLAD_API_CALL PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; +#define glGetUniformiv glad_glGetUniformiv +GLAD_API_CALL PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv; +#define glGetUniformuiv glad_glGetUniformuiv +GLAD_API_CALL PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv; +#define glGetVertexAttribIiv glad_glGetVertexAttribIiv +GLAD_API_CALL PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv; +#define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv +GLAD_API_CALL PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; +#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv +GLAD_API_CALL PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv; +#define glGetVertexAttribdv glad_glGetVertexAttribdv +GLAD_API_CALL PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; +#define glGetVertexAttribfv glad_glGetVertexAttribfv +GLAD_API_CALL PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; +#define glGetVertexAttribiv glad_glGetVertexAttribiv +GLAD_API_CALL PFNGLHINTPROC glad_glHint; +#define glHint glad_glHint +GLAD_API_CALL PFNGLISBUFFERPROC glad_glIsBuffer; +#define glIsBuffer glad_glIsBuffer +GLAD_API_CALL PFNGLISENABLEDPROC glad_glIsEnabled; +#define glIsEnabled glad_glIsEnabled +GLAD_API_CALL PFNGLISENABLEDIPROC glad_glIsEnabledi; +#define glIsEnabledi glad_glIsEnabledi +GLAD_API_CALL PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; +#define glIsFramebuffer glad_glIsFramebuffer +GLAD_API_CALL PFNGLISPROGRAMPROC glad_glIsProgram; +#define glIsProgram glad_glIsProgram +GLAD_API_CALL PFNGLISQUERYPROC glad_glIsQuery; +#define glIsQuery glad_glIsQuery +GLAD_API_CALL PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; +#define glIsRenderbuffer glad_glIsRenderbuffer +GLAD_API_CALL PFNGLISSAMPLERPROC glad_glIsSampler; +#define glIsSampler glad_glIsSampler +GLAD_API_CALL PFNGLISSHADERPROC glad_glIsShader; +#define glIsShader glad_glIsShader +GLAD_API_CALL PFNGLISSYNCPROC glad_glIsSync; +#define glIsSync glad_glIsSync +GLAD_API_CALL PFNGLISTEXTUREPROC glad_glIsTexture; +#define glIsTexture glad_glIsTexture +GLAD_API_CALL PFNGLISVERTEXARRAYPROC glad_glIsVertexArray; +#define glIsVertexArray glad_glIsVertexArray +GLAD_API_CALL PFNGLLINEWIDTHPROC glad_glLineWidth; +#define glLineWidth glad_glLineWidth +GLAD_API_CALL PFNGLLINKPROGRAMPROC glad_glLinkProgram; +#define glLinkProgram glad_glLinkProgram +GLAD_API_CALL PFNGLLOGICOPPROC glad_glLogicOp; +#define glLogicOp glad_glLogicOp +GLAD_API_CALL PFNGLMAPBUFFERPROC glad_glMapBuffer; +#define glMapBuffer glad_glMapBuffer +GLAD_API_CALL PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange; +#define glMapBufferRange glad_glMapBufferRange +GLAD_API_CALL PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays; +#define glMultiDrawArrays glad_glMultiDrawArrays +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements; +#define glMultiDrawElements glad_glMultiDrawElements +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex; +#define glMultiDrawElementsBaseVertex glad_glMultiDrawElementsBaseVertex +GLAD_API_CALL PFNGLPIXELSTOREFPROC glad_glPixelStoref; +#define glPixelStoref glad_glPixelStoref +GLAD_API_CALL PFNGLPIXELSTOREIPROC glad_glPixelStorei; +#define glPixelStorei glad_glPixelStorei +GLAD_API_CALL PFNGLPOINTPARAMETERFPROC glad_glPointParameterf; +#define glPointParameterf glad_glPointParameterf +GLAD_API_CALL PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv; +#define glPointParameterfv glad_glPointParameterfv +GLAD_API_CALL PFNGLPOINTPARAMETERIPROC glad_glPointParameteri; +#define glPointParameteri glad_glPointParameteri +GLAD_API_CALL PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv; +#define glPointParameteriv glad_glPointParameteriv +GLAD_API_CALL PFNGLPOINTSIZEPROC glad_glPointSize; +#define glPointSize glad_glPointSize +GLAD_API_CALL PFNGLPOLYGONMODEPROC glad_glPolygonMode; +#define glPolygonMode glad_glPolygonMode +GLAD_API_CALL PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; +#define glPolygonOffset glad_glPolygonOffset +GLAD_API_CALL PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex; +#define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex +GLAD_API_CALL PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex; +#define glProvokingVertex glad_glProvokingVertex +GLAD_API_CALL PFNGLQUERYCOUNTERPROC glad_glQueryCounter; +#define glQueryCounter glad_glQueryCounter +GLAD_API_CALL PFNGLREADBUFFERPROC glad_glReadBuffer; +#define glReadBuffer glad_glReadBuffer +GLAD_API_CALL PFNGLREADPIXELSPROC glad_glReadPixels; +#define glReadPixels glad_glReadPixels +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; +#define glRenderbufferStorage glad_glRenderbufferStorage +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample; +#define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample +GLAD_API_CALL PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; +#define glSampleCoverage glad_glSampleCoverage +GLAD_API_CALL PFNGLSAMPLEMASKIPROC glad_glSampleMaski; +#define glSampleMaski glad_glSampleMaski +GLAD_API_CALL PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv; +#define glSamplerParameterIiv glad_glSamplerParameterIiv +GLAD_API_CALL PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv; +#define glSamplerParameterIuiv glad_glSamplerParameterIuiv +GLAD_API_CALL PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf; +#define glSamplerParameterf glad_glSamplerParameterf +GLAD_API_CALL PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv; +#define glSamplerParameterfv glad_glSamplerParameterfv +GLAD_API_CALL PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri; +#define glSamplerParameteri glad_glSamplerParameteri +GLAD_API_CALL PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv; +#define glSamplerParameteriv glad_glSamplerParameteriv +GLAD_API_CALL PFNGLSCISSORPROC glad_glScissor; +#define glScissor glad_glScissor +GLAD_API_CALL PFNGLSHADERSOURCEPROC glad_glShaderSource; +#define glShaderSource glad_glShaderSource +GLAD_API_CALL PFNGLSTENCILFUNCPROC glad_glStencilFunc; +#define glStencilFunc glad_glStencilFunc +GLAD_API_CALL PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; +#define glStencilFuncSeparate glad_glStencilFuncSeparate +GLAD_API_CALL PFNGLSTENCILMASKPROC glad_glStencilMask; +#define glStencilMask glad_glStencilMask +GLAD_API_CALL PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; +#define glStencilMaskSeparate glad_glStencilMaskSeparate +GLAD_API_CALL PFNGLSTENCILOPPROC glad_glStencilOp; +#define glStencilOp glad_glStencilOp +GLAD_API_CALL PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; +#define glStencilOpSeparate glad_glStencilOpSeparate +GLAD_API_CALL PFNGLTEXBUFFERPROC glad_glTexBuffer; +#define glTexBuffer glad_glTexBuffer +GLAD_API_CALL PFNGLTEXIMAGE1DPROC glad_glTexImage1D; +#define glTexImage1D glad_glTexImage1D +GLAD_API_CALL PFNGLTEXIMAGE2DPROC glad_glTexImage2D; +#define glTexImage2D glad_glTexImage2D +GLAD_API_CALL PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample; +#define glTexImage2DMultisample glad_glTexImage2DMultisample +GLAD_API_CALL PFNGLTEXIMAGE3DPROC glad_glTexImage3D; +#define glTexImage3D glad_glTexImage3D +GLAD_API_CALL PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample; +#define glTexImage3DMultisample glad_glTexImage3DMultisample +GLAD_API_CALL PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv; +#define glTexParameterIiv glad_glTexParameterIiv +GLAD_API_CALL PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv; +#define glTexParameterIuiv glad_glTexParameterIuiv +GLAD_API_CALL PFNGLTEXPARAMETERFPROC glad_glTexParameterf; +#define glTexParameterf glad_glTexParameterf +GLAD_API_CALL PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; +#define glTexParameterfv glad_glTexParameterfv +GLAD_API_CALL PFNGLTEXPARAMETERIPROC glad_glTexParameteri; +#define glTexParameteri glad_glTexParameteri +GLAD_API_CALL PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; +#define glTexParameteriv glad_glTexParameteriv +GLAD_API_CALL PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D; +#define glTexSubImage1D glad_glTexSubImage1D +GLAD_API_CALL PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; +#define glTexSubImage2D glad_glTexSubImage2D +GLAD_API_CALL PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D; +#define glTexSubImage3D glad_glTexSubImage3D +GLAD_API_CALL PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings; +#define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings +GLAD_API_CALL PFNGLUNIFORM1FPROC glad_glUniform1f; +#define glUniform1f glad_glUniform1f +GLAD_API_CALL PFNGLUNIFORM1FVPROC glad_glUniform1fv; +#define glUniform1fv glad_glUniform1fv +GLAD_API_CALL PFNGLUNIFORM1IPROC glad_glUniform1i; +#define glUniform1i glad_glUniform1i +GLAD_API_CALL PFNGLUNIFORM1IVPROC glad_glUniform1iv; +#define glUniform1iv glad_glUniform1iv +GLAD_API_CALL PFNGLUNIFORM1UIPROC glad_glUniform1ui; +#define glUniform1ui glad_glUniform1ui +GLAD_API_CALL PFNGLUNIFORM1UIVPROC glad_glUniform1uiv; +#define glUniform1uiv glad_glUniform1uiv +GLAD_API_CALL PFNGLUNIFORM2FPROC glad_glUniform2f; +#define glUniform2f glad_glUniform2f +GLAD_API_CALL PFNGLUNIFORM2FVPROC glad_glUniform2fv; +#define glUniform2fv glad_glUniform2fv +GLAD_API_CALL PFNGLUNIFORM2IPROC glad_glUniform2i; +#define glUniform2i glad_glUniform2i +GLAD_API_CALL PFNGLUNIFORM2IVPROC glad_glUniform2iv; +#define glUniform2iv glad_glUniform2iv +GLAD_API_CALL PFNGLUNIFORM2UIPROC glad_glUniform2ui; +#define glUniform2ui glad_glUniform2ui +GLAD_API_CALL PFNGLUNIFORM2UIVPROC glad_glUniform2uiv; +#define glUniform2uiv glad_glUniform2uiv +GLAD_API_CALL PFNGLUNIFORM3FPROC glad_glUniform3f; +#define glUniform3f glad_glUniform3f +GLAD_API_CALL PFNGLUNIFORM3FVPROC glad_glUniform3fv; +#define glUniform3fv glad_glUniform3fv +GLAD_API_CALL PFNGLUNIFORM3IPROC glad_glUniform3i; +#define glUniform3i glad_glUniform3i +GLAD_API_CALL PFNGLUNIFORM3IVPROC glad_glUniform3iv; +#define glUniform3iv glad_glUniform3iv +GLAD_API_CALL PFNGLUNIFORM3UIPROC glad_glUniform3ui; +#define glUniform3ui glad_glUniform3ui +GLAD_API_CALL PFNGLUNIFORM3UIVPROC glad_glUniform3uiv; +#define glUniform3uiv glad_glUniform3uiv +GLAD_API_CALL PFNGLUNIFORM4FPROC glad_glUniform4f; +#define glUniform4f glad_glUniform4f +GLAD_API_CALL PFNGLUNIFORM4FVPROC glad_glUniform4fv; +#define glUniform4fv glad_glUniform4fv +GLAD_API_CALL PFNGLUNIFORM4IPROC glad_glUniform4i; +#define glUniform4i glad_glUniform4i +GLAD_API_CALL PFNGLUNIFORM4IVPROC glad_glUniform4iv; +#define glUniform4iv glad_glUniform4iv +GLAD_API_CALL PFNGLUNIFORM4UIPROC glad_glUniform4ui; +#define glUniform4ui glad_glUniform4ui +GLAD_API_CALL PFNGLUNIFORM4UIVPROC glad_glUniform4uiv; +#define glUniform4uiv glad_glUniform4uiv +GLAD_API_CALL PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding; +#define glUniformBlockBinding glad_glUniformBlockBinding +GLAD_API_CALL PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; +#define glUniformMatrix2fv glad_glUniformMatrix2fv +GLAD_API_CALL PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv; +#define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv +GLAD_API_CALL PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv; +#define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv +GLAD_API_CALL PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; +#define glUniformMatrix3fv glad_glUniformMatrix3fv +GLAD_API_CALL PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv; +#define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv +GLAD_API_CALL PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv; +#define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv +GLAD_API_CALL PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; +#define glUniformMatrix4fv glad_glUniformMatrix4fv +GLAD_API_CALL PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv; +#define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv +GLAD_API_CALL PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv; +#define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv +GLAD_API_CALL PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer; +#define glUnmapBuffer glad_glUnmapBuffer +GLAD_API_CALL PFNGLUSEPROGRAMPROC glad_glUseProgram; +#define glUseProgram glad_glUseProgram +GLAD_API_CALL PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; +#define glValidateProgram glad_glValidateProgram +GLAD_API_CALL PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d; +#define glVertexAttrib1d glad_glVertexAttrib1d +GLAD_API_CALL PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv; +#define glVertexAttrib1dv glad_glVertexAttrib1dv +GLAD_API_CALL PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; +#define glVertexAttrib1f glad_glVertexAttrib1f +GLAD_API_CALL PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; +#define glVertexAttrib1fv glad_glVertexAttrib1fv +GLAD_API_CALL PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s; +#define glVertexAttrib1s glad_glVertexAttrib1s +GLAD_API_CALL PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv; +#define glVertexAttrib1sv glad_glVertexAttrib1sv +GLAD_API_CALL PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d; +#define glVertexAttrib2d glad_glVertexAttrib2d +GLAD_API_CALL PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv; +#define glVertexAttrib2dv glad_glVertexAttrib2dv +GLAD_API_CALL PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; +#define glVertexAttrib2f glad_glVertexAttrib2f +GLAD_API_CALL PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; +#define glVertexAttrib2fv glad_glVertexAttrib2fv +GLAD_API_CALL PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s; +#define glVertexAttrib2s glad_glVertexAttrib2s +GLAD_API_CALL PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv; +#define glVertexAttrib2sv glad_glVertexAttrib2sv +GLAD_API_CALL PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d; +#define glVertexAttrib3d glad_glVertexAttrib3d +GLAD_API_CALL PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv; +#define glVertexAttrib3dv glad_glVertexAttrib3dv +GLAD_API_CALL PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; +#define glVertexAttrib3f glad_glVertexAttrib3f +GLAD_API_CALL PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; +#define glVertexAttrib3fv glad_glVertexAttrib3fv +GLAD_API_CALL PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s; +#define glVertexAttrib3s glad_glVertexAttrib3s +GLAD_API_CALL PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv; +#define glVertexAttrib3sv glad_glVertexAttrib3sv +GLAD_API_CALL PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv; +#define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv +GLAD_API_CALL PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv; +#define glVertexAttrib4Niv glad_glVertexAttrib4Niv +GLAD_API_CALL PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv; +#define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv +GLAD_API_CALL PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub; +#define glVertexAttrib4Nub glad_glVertexAttrib4Nub +GLAD_API_CALL PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv; +#define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv +GLAD_API_CALL PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv; +#define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv +GLAD_API_CALL PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv; +#define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv +GLAD_API_CALL PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv; +#define glVertexAttrib4bv glad_glVertexAttrib4bv +GLAD_API_CALL PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d; +#define glVertexAttrib4d glad_glVertexAttrib4d +GLAD_API_CALL PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv; +#define glVertexAttrib4dv glad_glVertexAttrib4dv +GLAD_API_CALL PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; +#define glVertexAttrib4f glad_glVertexAttrib4f +GLAD_API_CALL PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; +#define glVertexAttrib4fv glad_glVertexAttrib4fv +GLAD_API_CALL PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv; +#define glVertexAttrib4iv glad_glVertexAttrib4iv +GLAD_API_CALL PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s; +#define glVertexAttrib4s glad_glVertexAttrib4s +GLAD_API_CALL PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv; +#define glVertexAttrib4sv glad_glVertexAttrib4sv +GLAD_API_CALL PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv; +#define glVertexAttrib4ubv glad_glVertexAttrib4ubv +GLAD_API_CALL PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv; +#define glVertexAttrib4uiv glad_glVertexAttrib4uiv +GLAD_API_CALL PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv; +#define glVertexAttrib4usv glad_glVertexAttrib4usv +GLAD_API_CALL PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor; +#define glVertexAttribDivisor glad_glVertexAttribDivisor +GLAD_API_CALL PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i; +#define glVertexAttribI1i glad_glVertexAttribI1i +GLAD_API_CALL PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv; +#define glVertexAttribI1iv glad_glVertexAttribI1iv +GLAD_API_CALL PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui; +#define glVertexAttribI1ui glad_glVertexAttribI1ui +GLAD_API_CALL PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv; +#define glVertexAttribI1uiv glad_glVertexAttribI1uiv +GLAD_API_CALL PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i; +#define glVertexAttribI2i glad_glVertexAttribI2i +GLAD_API_CALL PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv; +#define glVertexAttribI2iv glad_glVertexAttribI2iv +GLAD_API_CALL PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui; +#define glVertexAttribI2ui glad_glVertexAttribI2ui +GLAD_API_CALL PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv; +#define glVertexAttribI2uiv glad_glVertexAttribI2uiv +GLAD_API_CALL PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i; +#define glVertexAttribI3i glad_glVertexAttribI3i +GLAD_API_CALL PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv; +#define glVertexAttribI3iv glad_glVertexAttribI3iv +GLAD_API_CALL PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui; +#define glVertexAttribI3ui glad_glVertexAttribI3ui +GLAD_API_CALL PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv; +#define glVertexAttribI3uiv glad_glVertexAttribI3uiv +GLAD_API_CALL PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv; +#define glVertexAttribI4bv glad_glVertexAttribI4bv +GLAD_API_CALL PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i; +#define glVertexAttribI4i glad_glVertexAttribI4i +GLAD_API_CALL PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv; +#define glVertexAttribI4iv glad_glVertexAttribI4iv +GLAD_API_CALL PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv; +#define glVertexAttribI4sv glad_glVertexAttribI4sv +GLAD_API_CALL PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv; +#define glVertexAttribI4ubv glad_glVertexAttribI4ubv +GLAD_API_CALL PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui; +#define glVertexAttribI4ui glad_glVertexAttribI4ui +GLAD_API_CALL PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv; +#define glVertexAttribI4uiv glad_glVertexAttribI4uiv +GLAD_API_CALL PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv; +#define glVertexAttribI4usv glad_glVertexAttribI4usv +GLAD_API_CALL PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer; +#define glVertexAttribIPointer glad_glVertexAttribIPointer +GLAD_API_CALL PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui; +#define glVertexAttribP1ui glad_glVertexAttribP1ui +GLAD_API_CALL PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv; +#define glVertexAttribP1uiv glad_glVertexAttribP1uiv +GLAD_API_CALL PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui; +#define glVertexAttribP2ui glad_glVertexAttribP2ui +GLAD_API_CALL PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv; +#define glVertexAttribP2uiv glad_glVertexAttribP2uiv +GLAD_API_CALL PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui; +#define glVertexAttribP3ui glad_glVertexAttribP3ui +GLAD_API_CALL PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv; +#define glVertexAttribP3uiv glad_glVertexAttribP3uiv +GLAD_API_CALL PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui; +#define glVertexAttribP4ui glad_glVertexAttribP4ui +GLAD_API_CALL PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv; +#define glVertexAttribP4uiv glad_glVertexAttribP4uiv +GLAD_API_CALL PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; +#define glVertexAttribPointer glad_glVertexAttribPointer +GLAD_API_CALL PFNGLVIEWPORTPROC glad_glViewport; +#define glViewport glad_glViewport +GLAD_API_CALL PFNGLWAITSYNCPROC glad_glWaitSync; +#define glWaitSync glad_glWaitSync + + + + + +GLAD_API_CALL int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr); +GLAD_API_CALL int gladLoadGL( GLADloadfunc load); + + +#ifdef GLAD_GL + +GLAD_API_CALL int gladLoaderLoadGL(void); +GLAD_API_CALL void gladLoaderUnloadGL(void); + +#endif + +#ifdef __cplusplus +} +#endif +#endif + +/* Source */ +#ifdef GLAD_GL_IMPLEMENTATION +#include +#include +#include + +#ifndef GLAD_IMPL_UTIL_C_ +#define GLAD_IMPL_UTIL_C_ + +#ifdef _MSC_VER +#define GLAD_IMPL_UTIL_SSCANF sscanf_s +#else +#define GLAD_IMPL_UTIL_SSCANF sscanf +#endif + +#endif /* GLAD_IMPL_UTIL_C_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +int GLAD_GL_VERSION_1_0 = 0; +int GLAD_GL_VERSION_1_1 = 0; +int GLAD_GL_VERSION_1_2 = 0; +int GLAD_GL_VERSION_1_3 = 0; +int GLAD_GL_VERSION_1_4 = 0; +int GLAD_GL_VERSION_1_5 = 0; +int GLAD_GL_VERSION_2_0 = 0; +int GLAD_GL_VERSION_2_1 = 0; +int GLAD_GL_VERSION_3_0 = 0; +int GLAD_GL_VERSION_3_1 = 0; +int GLAD_GL_VERSION_3_2 = 0; +int GLAD_GL_VERSION_3_3 = 0; + + + +PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; +PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; +PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL; +PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL; +PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL; +PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; +PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; +PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL; +PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL; +PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL; +PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL; +PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; +PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; +PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL; +PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; +PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL; +PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; +PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; +PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; +PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; +PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; +PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL; +PFNGLBUFFERDATAPROC glad_glBufferData = NULL; +PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; +PFNGLCLAMPCOLORPROC glad_glClampColor = NULL; +PFNGLCLEARPROC glad_glClear = NULL; +PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL; +PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL; +PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL; +PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL; +PFNGLCLEARCOLORPROC glad_glClearColor = NULL; +PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL; +PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; +PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL; +PFNGLCOLORMASKPROC glad_glColorMask = NULL; +PFNGLCOLORMASKIPROC glad_glColorMaski = NULL; +PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; +PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL; +PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; +PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL; +PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL; +PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL; +PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL; +PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL; +PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; +PFNGLCREATESHADERPROC glad_glCreateShader = NULL; +PFNGLCULLFACEPROC glad_glCullFace = NULL; +PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; +PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; +PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; +PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL; +PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; +PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL; +PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; +PFNGLDELETESYNCPROC glad_glDeleteSync = NULL; +PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; +PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL; +PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; +PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; +PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL; +PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; +PFNGLDISABLEPROC glad_glDisable = NULL; +PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; +PFNGLDISABLEIPROC glad_glDisablei = NULL; +PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; +PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; +PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL; +PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; +PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; +PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex = NULL; +PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL; +PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; +PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL; +PFNGLENABLEPROC glad_glEnable = NULL; +PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; +PFNGLENABLEIPROC glad_glEnablei = NULL; +PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL; +PFNGLENDQUERYPROC glad_glEndQuery = NULL; +PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL; +PFNGLFENCESYNCPROC glad_glFenceSync = NULL; +PFNGLFINISHPROC glad_glFinish = NULL; +PFNGLFLUSHPROC glad_glFlush = NULL; +PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; +PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL; +PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL; +PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; +PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL; +PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL; +PFNGLFRONTFACEPROC glad_glFrontFace = NULL; +PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; +PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; +PFNGLGENQUERIESPROC glad_glGenQueries = NULL; +PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; +PFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL; +PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; +PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL; +PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; +PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; +PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; +PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL; +PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL; +PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL; +PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL; +PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; +PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; +PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL; +PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; +PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL; +PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; +PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL; +PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL; +PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL; +PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL; +PFNGLGETERRORPROC glad_glGetError = NULL; +PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; +PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex = NULL; +PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; +PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL; +PFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL; +PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL; +PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; +PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL; +PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; +PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; +PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL; +PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL; +PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v = NULL; +PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL; +PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL; +PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; +PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv = NULL; +PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv = NULL; +PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL; +PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL; +PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; +PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; +PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; +PFNGLGETSTRINGPROC glad_glGetString = NULL; +PFNGLGETSTRINGIPROC glad_glGetStringi = NULL; +PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL; +PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL; +PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL; +PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL; +PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL; +PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL; +PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; +PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; +PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL; +PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL; +PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL; +PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; +PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; +PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; +PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL; +PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL; +PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL; +PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; +PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL; +PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; +PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; +PFNGLHINTPROC glad_glHint = NULL; +PFNGLISBUFFERPROC glad_glIsBuffer = NULL; +PFNGLISENABLEDPROC glad_glIsEnabled = NULL; +PFNGLISENABLEDIPROC glad_glIsEnabledi = NULL; +PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; +PFNGLISPROGRAMPROC glad_glIsProgram = NULL; +PFNGLISQUERYPROC glad_glIsQuery = NULL; +PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; +PFNGLISSAMPLERPROC glad_glIsSampler = NULL; +PFNGLISSHADERPROC glad_glIsShader = NULL; +PFNGLISSYNCPROC glad_glIsSync = NULL; +PFNGLISTEXTUREPROC glad_glIsTexture = NULL; +PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL; +PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; +PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; +PFNGLLOGICOPPROC glad_glLogicOp = NULL; +PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL; +PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL; +PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL; +PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL; +PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL; +PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL; +PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; +PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL; +PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL; +PFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL; +PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL; +PFNGLPOINTSIZEPROC glad_glPointSize = NULL; +PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL; +PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; +PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL; +PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL; +PFNGLQUERYCOUNTERPROC glad_glQueryCounter = NULL; +PFNGLREADBUFFERPROC glad_glReadBuffer = NULL; +PFNGLREADPIXELSPROC glad_glReadPixels = NULL; +PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL; +PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; +PFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL; +PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv = NULL; +PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv = NULL; +PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL; +PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL; +PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL; +PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL; +PFNGLSCISSORPROC glad_glScissor = NULL; +PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; +PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; +PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; +PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; +PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; +PFNGLSTENCILOPPROC glad_glStencilOp = NULL; +PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; +PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL; +PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL; +PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; +PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL; +PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL; +PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample = NULL; +PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL; +PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL; +PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; +PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; +PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; +PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; +PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL; +PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; +PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL; +PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL; +PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; +PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; +PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; +PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; +PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL; +PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL; +PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; +PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; +PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; +PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; +PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL; +PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL; +PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; +PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; +PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; +PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; +PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL; +PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL; +PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; +PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; +PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; +PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; +PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL; +PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL; +PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL; +PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; +PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL; +PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL; +PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; +PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL; +PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL; +PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; +PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL; +PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL; +PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL; +PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; +PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; +PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL; +PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL; +PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; +PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; +PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL; +PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL; +PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL; +PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL; +PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; +PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; +PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL; +PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL; +PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL; +PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL; +PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; +PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; +PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL; +PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL; +PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL; +PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL; +PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL; +PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL; +PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL; +PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL; +PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL; +PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL; +PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL; +PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL; +PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; +PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; +PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL; +PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL; +PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL; +PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL; +PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL; +PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL; +PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL; +PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL; +PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL; +PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL; +PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL; +PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL; +PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL; +PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL; +PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL; +PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL; +PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL; +PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL; +PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL; +PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL; +PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL; +PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL; +PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL; +PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL; +PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL; +PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL; +PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL; +PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL; +PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui = NULL; +PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv = NULL; +PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui = NULL; +PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv = NULL; +PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui = NULL; +PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv = NULL; +PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui = NULL; +PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv = NULL; +PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; +PFNGLVIEWPORTPROC glad_glViewport = NULL; +PFNGLWAITSYNCPROC glad_glWaitSync = NULL; + + +static void glad_gl_load_GL_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_0) return; + glad_glBlendFunc = (PFNGLBLENDFUNCPROC) load(userptr, "glBlendFunc"); + glad_glClear = (PFNGLCLEARPROC) load(userptr, "glClear"); + glad_glClearColor = (PFNGLCLEARCOLORPROC) load(userptr, "glClearColor"); + glad_glClearDepth = (PFNGLCLEARDEPTHPROC) load(userptr, "glClearDepth"); + glad_glClearStencil = (PFNGLCLEARSTENCILPROC) load(userptr, "glClearStencil"); + glad_glColorMask = (PFNGLCOLORMASKPROC) load(userptr, "glColorMask"); + glad_glCullFace = (PFNGLCULLFACEPROC) load(userptr, "glCullFace"); + glad_glDepthFunc = (PFNGLDEPTHFUNCPROC) load(userptr, "glDepthFunc"); + glad_glDepthMask = (PFNGLDEPTHMASKPROC) load(userptr, "glDepthMask"); + glad_glDepthRange = (PFNGLDEPTHRANGEPROC) load(userptr, "glDepthRange"); + glad_glDisable = (PFNGLDISABLEPROC) load(userptr, "glDisable"); + glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC) load(userptr, "glDrawBuffer"); + glad_glEnable = (PFNGLENABLEPROC) load(userptr, "glEnable"); + glad_glFinish = (PFNGLFINISHPROC) load(userptr, "glFinish"); + glad_glFlush = (PFNGLFLUSHPROC) load(userptr, "glFlush"); + glad_glFrontFace = (PFNGLFRONTFACEPROC) load(userptr, "glFrontFace"); + glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC) load(userptr, "glGetBooleanv"); + glad_glGetDoublev = (PFNGLGETDOUBLEVPROC) load(userptr, "glGetDoublev"); + glad_glGetError = (PFNGLGETERRORPROC) load(userptr, "glGetError"); + glad_glGetFloatv = (PFNGLGETFLOATVPROC) load(userptr, "glGetFloatv"); + glad_glGetIntegerv = (PFNGLGETINTEGERVPROC) load(userptr, "glGetIntegerv"); + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + glad_glGetTexImage = (PFNGLGETTEXIMAGEPROC) load(userptr, "glGetTexImage"); + glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC) load(userptr, "glGetTexLevelParameterfv"); + glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC) load(userptr, "glGetTexLevelParameteriv"); + glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) load(userptr, "glGetTexParameterfv"); + glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) load(userptr, "glGetTexParameteriv"); + glad_glHint = (PFNGLHINTPROC) load(userptr, "glHint"); + glad_glIsEnabled = (PFNGLISENABLEDPROC) load(userptr, "glIsEnabled"); + glad_glLineWidth = (PFNGLLINEWIDTHPROC) load(userptr, "glLineWidth"); + glad_glLogicOp = (PFNGLLOGICOPPROC) load(userptr, "glLogicOp"); + glad_glPixelStoref = (PFNGLPIXELSTOREFPROC) load(userptr, "glPixelStoref"); + glad_glPixelStorei = (PFNGLPIXELSTOREIPROC) load(userptr, "glPixelStorei"); + glad_glPointSize = (PFNGLPOINTSIZEPROC) load(userptr, "glPointSize"); + glad_glPolygonMode = (PFNGLPOLYGONMODEPROC) load(userptr, "glPolygonMode"); + glad_glReadBuffer = (PFNGLREADBUFFERPROC) load(userptr, "glReadBuffer"); + glad_glReadPixels = (PFNGLREADPIXELSPROC) load(userptr, "glReadPixels"); + glad_glScissor = (PFNGLSCISSORPROC) load(userptr, "glScissor"); + glad_glStencilFunc = (PFNGLSTENCILFUNCPROC) load(userptr, "glStencilFunc"); + glad_glStencilMask = (PFNGLSTENCILMASKPROC) load(userptr, "glStencilMask"); + glad_glStencilOp = (PFNGLSTENCILOPPROC) load(userptr, "glStencilOp"); + glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC) load(userptr, "glTexImage1D"); + glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC) load(userptr, "glTexImage2D"); + glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC) load(userptr, "glTexParameterf"); + glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC) load(userptr, "glTexParameterfv"); + glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC) load(userptr, "glTexParameteri"); + glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC) load(userptr, "glTexParameteriv"); + glad_glViewport = (PFNGLVIEWPORTPROC) load(userptr, "glViewport"); +} +static void glad_gl_load_GL_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_1) return; + glad_glBindTexture = (PFNGLBINDTEXTUREPROC) load(userptr, "glBindTexture"); + glad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC) load(userptr, "glCopyTexImage1D"); + glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) load(userptr, "glCopyTexImage2D"); + glad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC) load(userptr, "glCopyTexSubImage1D"); + glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) load(userptr, "glCopyTexSubImage2D"); + glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC) load(userptr, "glDeleteTextures"); + glad_glDrawArrays = (PFNGLDRAWARRAYSPROC) load(userptr, "glDrawArrays"); + glad_glDrawElements = (PFNGLDRAWELEMENTSPROC) load(userptr, "glDrawElements"); + glad_glGenTextures = (PFNGLGENTEXTURESPROC) load(userptr, "glGenTextures"); + glad_glIsTexture = (PFNGLISTEXTUREPROC) load(userptr, "glIsTexture"); + glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC) load(userptr, "glPolygonOffset"); + glad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC) load(userptr, "glTexSubImage1D"); + glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) load(userptr, "glTexSubImage2D"); +} +static void glad_gl_load_GL_VERSION_1_2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_2) return; + glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC) load(userptr, "glCopyTexSubImage3D"); + glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC) load(userptr, "glDrawRangeElements"); + glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC) load(userptr, "glTexImage3D"); + glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC) load(userptr, "glTexSubImage3D"); +} +static void glad_gl_load_GL_VERSION_1_3( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_3) return; + glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC) load(userptr, "glActiveTexture"); + glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC) load(userptr, "glCompressedTexImage1D"); + glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load(userptr, "glCompressedTexImage2D"); + glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC) load(userptr, "glCompressedTexImage3D"); + glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) load(userptr, "glCompressedTexSubImage1D"); + glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load(userptr, "glCompressedTexSubImage2D"); + glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) load(userptr, "glCompressedTexSubImage3D"); + glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC) load(userptr, "glGetCompressedTexImage"); + glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load(userptr, "glSampleCoverage"); +} +static void glad_gl_load_GL_VERSION_1_4( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_4) return; + glad_glBlendColor = (PFNGLBLENDCOLORPROC) load(userptr, "glBlendColor"); + glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC) load(userptr, "glBlendEquation"); + glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) load(userptr, "glBlendFuncSeparate"); + glad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC) load(userptr, "glMultiDrawArrays"); + glad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC) load(userptr, "glMultiDrawElements"); + glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC) load(userptr, "glPointParameterf"); + glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC) load(userptr, "glPointParameterfv"); + glad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC) load(userptr, "glPointParameteri"); + glad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC) load(userptr, "glPointParameteriv"); +} +static void glad_gl_load_GL_VERSION_1_5( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_5) return; + glad_glBeginQuery = (PFNGLBEGINQUERYPROC) load(userptr, "glBeginQuery"); + glad_glBindBuffer = (PFNGLBINDBUFFERPROC) load(userptr, "glBindBuffer"); + glad_glBufferData = (PFNGLBUFFERDATAPROC) load(userptr, "glBufferData"); + glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) load(userptr, "glBufferSubData"); + glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) load(userptr, "glDeleteBuffers"); + glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC) load(userptr, "glDeleteQueries"); + glad_glEndQuery = (PFNGLENDQUERYPROC) load(userptr, "glEndQuery"); + glad_glGenBuffers = (PFNGLGENBUFFERSPROC) load(userptr, "glGenBuffers"); + glad_glGenQueries = (PFNGLGENQUERIESPROC) load(userptr, "glGenQueries"); + glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load(userptr, "glGetBufferParameteriv"); + glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC) load(userptr, "glGetBufferPointerv"); + glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC) load(userptr, "glGetBufferSubData"); + glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC) load(userptr, "glGetQueryObjectiv"); + glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC) load(userptr, "glGetQueryObjectuiv"); + glad_glGetQueryiv = (PFNGLGETQUERYIVPROC) load(userptr, "glGetQueryiv"); + glad_glIsBuffer = (PFNGLISBUFFERPROC) load(userptr, "glIsBuffer"); + glad_glIsQuery = (PFNGLISQUERYPROC) load(userptr, "glIsQuery"); + glad_glMapBuffer = (PFNGLMAPBUFFERPROC) load(userptr, "glMapBuffer"); + glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) load(userptr, "glUnmapBuffer"); +} +static void glad_gl_load_GL_VERSION_2_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_2_0) return; + glad_glAttachShader = (PFNGLATTACHSHADERPROC) load(userptr, "glAttachShader"); + glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load(userptr, "glBindAttribLocation"); + glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) load(userptr, "glBlendEquationSeparate"); + glad_glCompileShader = (PFNGLCOMPILESHADERPROC) load(userptr, "glCompileShader"); + glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC) load(userptr, "glCreateProgram"); + glad_glCreateShader = (PFNGLCREATESHADERPROC) load(userptr, "glCreateShader"); + glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC) load(userptr, "glDeleteProgram"); + glad_glDeleteShader = (PFNGLDELETESHADERPROC) load(userptr, "glDeleteShader"); + glad_glDetachShader = (PFNGLDETACHSHADERPROC) load(userptr, "glDetachShader"); + glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, "glDisableVertexAttribArray"); + glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC) load(userptr, "glDrawBuffers"); + glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, "glEnableVertexAttribArray"); + glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load(userptr, "glGetActiveAttrib"); + glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load(userptr, "glGetActiveUniform"); + glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) load(userptr, "glGetAttachedShaders"); + glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load(userptr, "glGetAttribLocation"); + glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) load(userptr, "glGetProgramInfoLog"); + glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC) load(userptr, "glGetProgramiv"); + glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) load(userptr, "glGetShaderInfoLog"); + glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC) load(userptr, "glGetShaderSource"); + glad_glGetShaderiv = (PFNGLGETSHADERIVPROC) load(userptr, "glGetShaderiv"); + glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load(userptr, "glGetUniformLocation"); + glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC) load(userptr, "glGetUniformfv"); + glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC) load(userptr, "glGetUniformiv"); + glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, "glGetVertexAttribPointerv"); + glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC) load(userptr, "glGetVertexAttribdv"); + glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, "glGetVertexAttribfv"); + glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, "glGetVertexAttribiv"); + glad_glIsProgram = (PFNGLISPROGRAMPROC) load(userptr, "glIsProgram"); + glad_glIsShader = (PFNGLISSHADERPROC) load(userptr, "glIsShader"); + glad_glLinkProgram = (PFNGLLINKPROGRAMPROC) load(userptr, "glLinkProgram"); + glad_glShaderSource = (PFNGLSHADERSOURCEPROC) load(userptr, "glShaderSource"); + glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) load(userptr, "glStencilFuncSeparate"); + glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) load(userptr, "glStencilMaskSeparate"); + glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) load(userptr, "glStencilOpSeparate"); + glad_glUniform1f = (PFNGLUNIFORM1FPROC) load(userptr, "glUniform1f"); + glad_glUniform1fv = (PFNGLUNIFORM1FVPROC) load(userptr, "glUniform1fv"); + glad_glUniform1i = (PFNGLUNIFORM1IPROC) load(userptr, "glUniform1i"); + glad_glUniform1iv = (PFNGLUNIFORM1IVPROC) load(userptr, "glUniform1iv"); + glad_glUniform2f = (PFNGLUNIFORM2FPROC) load(userptr, "glUniform2f"); + glad_glUniform2fv = (PFNGLUNIFORM2FVPROC) load(userptr, "glUniform2fv"); + glad_glUniform2i = (PFNGLUNIFORM2IPROC) load(userptr, "glUniform2i"); + glad_glUniform2iv = (PFNGLUNIFORM2IVPROC) load(userptr, "glUniform2iv"); + glad_glUniform3f = (PFNGLUNIFORM3FPROC) load(userptr, "glUniform3f"); + glad_glUniform3fv = (PFNGLUNIFORM3FVPROC) load(userptr, "glUniform3fv"); + glad_glUniform3i = (PFNGLUNIFORM3IPROC) load(userptr, "glUniform3i"); + glad_glUniform3iv = (PFNGLUNIFORM3IVPROC) load(userptr, "glUniform3iv"); + glad_glUniform4f = (PFNGLUNIFORM4FPROC) load(userptr, "glUniform4f"); + glad_glUniform4fv = (PFNGLUNIFORM4FVPROC) load(userptr, "glUniform4fv"); + glad_glUniform4i = (PFNGLUNIFORM4IPROC) load(userptr, "glUniform4i"); + glad_glUniform4iv = (PFNGLUNIFORM4IVPROC) load(userptr, "glUniform4iv"); + glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load(userptr, "glUniformMatrix2fv"); + glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load(userptr, "glUniformMatrix3fv"); + glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load(userptr, "glUniformMatrix4fv"); + glad_glUseProgram = (PFNGLUSEPROGRAMPROC) load(userptr, "glUseProgram"); + glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load(userptr, "glValidateProgram"); + glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC) load(userptr, "glVertexAttrib1d"); + glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC) load(userptr, "glVertexAttrib1dv"); + glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, "glVertexAttrib1f"); + glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, "glVertexAttrib1fv"); + glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC) load(userptr, "glVertexAttrib1s"); + glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC) load(userptr, "glVertexAttrib1sv"); + glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC) load(userptr, "glVertexAttrib2d"); + glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC) load(userptr, "glVertexAttrib2dv"); + glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, "glVertexAttrib2f"); + glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, "glVertexAttrib2fv"); + glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC) load(userptr, "glVertexAttrib2s"); + glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC) load(userptr, "glVertexAttrib2sv"); + glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC) load(userptr, "glVertexAttrib3d"); + glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC) load(userptr, "glVertexAttrib3dv"); + glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, "glVertexAttrib3f"); + glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, "glVertexAttrib3fv"); + glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC) load(userptr, "glVertexAttrib3s"); + glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC) load(userptr, "glVertexAttrib3sv"); + glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC) load(userptr, "glVertexAttrib4Nbv"); + glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC) load(userptr, "glVertexAttrib4Niv"); + glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC) load(userptr, "glVertexAttrib4Nsv"); + glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC) load(userptr, "glVertexAttrib4Nub"); + glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC) load(userptr, "glVertexAttrib4Nubv"); + glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC) load(userptr, "glVertexAttrib4Nuiv"); + glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC) load(userptr, "glVertexAttrib4Nusv"); + glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC) load(userptr, "glVertexAttrib4bv"); + glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC) load(userptr, "glVertexAttrib4d"); + glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC) load(userptr, "glVertexAttrib4dv"); + glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, "glVertexAttrib4f"); + glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, "glVertexAttrib4fv"); + glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC) load(userptr, "glVertexAttrib4iv"); + glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC) load(userptr, "glVertexAttrib4s"); + glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC) load(userptr, "glVertexAttrib4sv"); + glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC) load(userptr, "glVertexAttrib4ubv"); + glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC) load(userptr, "glVertexAttrib4uiv"); + glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC) load(userptr, "glVertexAttrib4usv"); + glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, "glVertexAttribPointer"); +} +static void glad_gl_load_GL_VERSION_2_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_2_1) return; + glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC) load(userptr, "glUniformMatrix2x3fv"); + glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC) load(userptr, "glUniformMatrix2x4fv"); + glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC) load(userptr, "glUniformMatrix3x2fv"); + glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) load(userptr, "glUniformMatrix3x4fv"); + glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC) load(userptr, "glUniformMatrix4x2fv"); + glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC) load(userptr, "glUniformMatrix4x3fv"); +} +static void glad_gl_load_GL_VERSION_3_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_0) return; + glad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC) load(userptr, "glBeginConditionalRender"); + glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC) load(userptr, "glBeginTransformFeedback"); + glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); + glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); + glad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC) load(userptr, "glBindFragDataLocation"); + glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, "glBindFramebuffer"); + glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, "glBindRenderbuffer"); + glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) load(userptr, "glBindVertexArray"); + glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) load(userptr, "glBlitFramebuffer"); + glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckFramebufferStatus"); + glad_glClampColor = (PFNGLCLAMPCOLORPROC) load(userptr, "glClampColor"); + glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC) load(userptr, "glClearBufferfi"); + glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC) load(userptr, "glClearBufferfv"); + glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC) load(userptr, "glClearBufferiv"); + glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC) load(userptr, "glClearBufferuiv"); + glad_glColorMaski = (PFNGLCOLORMASKIPROC) load(userptr, "glColorMaski"); + glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, "glDeleteFramebuffers"); + glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, "glDeleteRenderbuffers"); + glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) load(userptr, "glDeleteVertexArrays"); + glad_glDisablei = (PFNGLDISABLEIPROC) load(userptr, "glDisablei"); + glad_glEnablei = (PFNGLENABLEIPROC) load(userptr, "glEnablei"); + glad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC) load(userptr, "glEndConditionalRender"); + glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC) load(userptr, "glEndTransformFeedback"); + glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) load(userptr, "glFlushMappedBufferRange"); + glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glFramebufferRenderbuffer"); + glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) load(userptr, "glFramebufferTexture1D"); + glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, "glFramebufferTexture2D"); + glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) load(userptr, "glFramebufferTexture3D"); + glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glFramebufferTextureLayer"); + glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, "glGenFramebuffers"); + glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, "glGenRenderbuffers"); + glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) load(userptr, "glGenVertexArrays"); + glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, "glGenerateMipmap"); + glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC) load(userptr, "glGetBooleani_v"); + glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC) load(userptr, "glGetFragDataLocation"); + glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetFramebufferAttachmentParameteriv"); + glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); + glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetRenderbufferParameteriv"); + glad_glGetStringi = (PFNGLGETSTRINGIPROC) load(userptr, "glGetStringi"); + glad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC) load(userptr, "glGetTexParameterIiv"); + glad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC) load(userptr, "glGetTexParameterIuiv"); + glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) load(userptr, "glGetTransformFeedbackVarying"); + glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC) load(userptr, "glGetUniformuiv"); + glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC) load(userptr, "glGetVertexAttribIiv"); + glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC) load(userptr, "glGetVertexAttribIuiv"); + glad_glIsEnabledi = (PFNGLISENABLEDIPROC) load(userptr, "glIsEnabledi"); + glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, "glIsFramebuffer"); + glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, "glIsRenderbuffer"); + glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC) load(userptr, "glIsVertexArray"); + glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) load(userptr, "glMapBufferRange"); + glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage"); + glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glRenderbufferStorageMultisample"); + glad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC) load(userptr, "glTexParameterIiv"); + glad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC) load(userptr, "glTexParameterIuiv"); + glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC) load(userptr, "glTransformFeedbackVaryings"); + glad_glUniform1ui = (PFNGLUNIFORM1UIPROC) load(userptr, "glUniform1ui"); + glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC) load(userptr, "glUniform1uiv"); + glad_glUniform2ui = (PFNGLUNIFORM2UIPROC) load(userptr, "glUniform2ui"); + glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC) load(userptr, "glUniform2uiv"); + glad_glUniform3ui = (PFNGLUNIFORM3UIPROC) load(userptr, "glUniform3ui"); + glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC) load(userptr, "glUniform3uiv"); + glad_glUniform4ui = (PFNGLUNIFORM4UIPROC) load(userptr, "glUniform4ui"); + glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC) load(userptr, "glUniform4uiv"); + glad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC) load(userptr, "glVertexAttribI1i"); + glad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC) load(userptr, "glVertexAttribI1iv"); + glad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC) load(userptr, "glVertexAttribI1ui"); + glad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC) load(userptr, "glVertexAttribI1uiv"); + glad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC) load(userptr, "glVertexAttribI2i"); + glad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC) load(userptr, "glVertexAttribI2iv"); + glad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC) load(userptr, "glVertexAttribI2ui"); + glad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC) load(userptr, "glVertexAttribI2uiv"); + glad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC) load(userptr, "glVertexAttribI3i"); + glad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC) load(userptr, "glVertexAttribI3iv"); + glad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC) load(userptr, "glVertexAttribI3ui"); + glad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC) load(userptr, "glVertexAttribI3uiv"); + glad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC) load(userptr, "glVertexAttribI4bv"); + glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC) load(userptr, "glVertexAttribI4i"); + glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC) load(userptr, "glVertexAttribI4iv"); + glad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC) load(userptr, "glVertexAttribI4sv"); + glad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC) load(userptr, "glVertexAttribI4ubv"); + glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC) load(userptr, "glVertexAttribI4ui"); + glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC) load(userptr, "glVertexAttribI4uiv"); + glad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC) load(userptr, "glVertexAttribI4usv"); + glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC) load(userptr, "glVertexAttribIPointer"); +} +static void glad_gl_load_GL_VERSION_3_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_1) return; + glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); + glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); + glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC) load(userptr, "glCopyBufferSubData"); + glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC) load(userptr, "glDrawArraysInstanced"); + glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC) load(userptr, "glDrawElementsInstanced"); + glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) load(userptr, "glGetActiveUniformBlockName"); + glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) load(userptr, "glGetActiveUniformBlockiv"); + glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC) load(userptr, "glGetActiveUniformName"); + glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC) load(userptr, "glGetActiveUniformsiv"); + glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); + glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) load(userptr, "glGetUniformBlockIndex"); + glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC) load(userptr, "glGetUniformIndices"); + glad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC) load(userptr, "glPrimitiveRestartIndex"); + glad_glTexBuffer = (PFNGLTEXBUFFERPROC) load(userptr, "glTexBuffer"); + glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) load(userptr, "glUniformBlockBinding"); +} +static void glad_gl_load_GL_VERSION_3_2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_2) return; + glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC) load(userptr, "glClientWaitSync"); + glad_glDeleteSync = (PFNGLDELETESYNCPROC) load(userptr, "glDeleteSync"); + glad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glDrawElementsBaseVertex"); + glad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) load(userptr, "glDrawElementsInstancedBaseVertex"); + glad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) load(userptr, "glDrawRangeElementsBaseVertex"); + glad_glFenceSync = (PFNGLFENCESYNCPROC) load(userptr, "glFenceSync"); + glad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC) load(userptr, "glFramebufferTexture"); + glad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC) load(userptr, "glGetBufferParameteri64v"); + glad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC) load(userptr, "glGetInteger64i_v"); + glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC) load(userptr, "glGetInteger64v"); + glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) load(userptr, "glGetMultisamplefv"); + glad_glGetSynciv = (PFNGLGETSYNCIVPROC) load(userptr, "glGetSynciv"); + glad_glIsSync = (PFNGLISSYNCPROC) load(userptr, "glIsSync"); + glad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glMultiDrawElementsBaseVertex"); + glad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC) load(userptr, "glProvokingVertex"); + glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC) load(userptr, "glSampleMaski"); + glad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) load(userptr, "glTexImage2DMultisample"); + glad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) load(userptr, "glTexImage3DMultisample"); + glad_glWaitSync = (PFNGLWAITSYNCPROC) load(userptr, "glWaitSync"); +} +static void glad_gl_load_GL_VERSION_3_3( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_3) return; + glad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) load(userptr, "glBindFragDataLocationIndexed"); + glad_glBindSampler = (PFNGLBINDSAMPLERPROC) load(userptr, "glBindSampler"); + glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC) load(userptr, "glDeleteSamplers"); + glad_glGenSamplers = (PFNGLGENSAMPLERSPROC) load(userptr, "glGenSamplers"); + glad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC) load(userptr, "glGetFragDataIndex"); + glad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC) load(userptr, "glGetQueryObjecti64v"); + glad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC) load(userptr, "glGetQueryObjectui64v"); + glad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC) load(userptr, "glGetSamplerParameterIiv"); + glad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC) load(userptr, "glGetSamplerParameterIuiv"); + glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC) load(userptr, "glGetSamplerParameterfv"); + glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC) load(userptr, "glGetSamplerParameteriv"); + glad_glIsSampler = (PFNGLISSAMPLERPROC) load(userptr, "glIsSampler"); + glad_glQueryCounter = (PFNGLQUERYCOUNTERPROC) load(userptr, "glQueryCounter"); + glad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC) load(userptr, "glSamplerParameterIiv"); + glad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC) load(userptr, "glSamplerParameterIuiv"); + glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC) load(userptr, "glSamplerParameterf"); + glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC) load(userptr, "glSamplerParameterfv"); + glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC) load(userptr, "glSamplerParameteri"); + glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC) load(userptr, "glSamplerParameteriv"); + glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC) load(userptr, "glVertexAttribDivisor"); + glad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC) load(userptr, "glVertexAttribP1ui"); + glad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC) load(userptr, "glVertexAttribP1uiv"); + glad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC) load(userptr, "glVertexAttribP2ui"); + glad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC) load(userptr, "glVertexAttribP2uiv"); + glad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC) load(userptr, "glVertexAttribP3ui"); + glad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC) load(userptr, "glVertexAttribP3uiv"); + glad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC) load(userptr, "glVertexAttribP4ui"); + glad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC) load(userptr, "glVertexAttribP4uiv"); +} + + + +#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) +#define GLAD_GL_IS_SOME_NEW_VERSION 1 +#else +#define GLAD_GL_IS_SOME_NEW_VERSION 0 +#endif + +static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) { +#if GLAD_GL_IS_SOME_NEW_VERSION + if(GLAD_VERSION_MAJOR(version) < 3) { +#else + (void) version; + (void) out_num_exts_i; + (void) out_exts_i; +#endif + if (glad_glGetString == NULL) { + return 0; + } + *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS); +#if GLAD_GL_IS_SOME_NEW_VERSION + } else { + unsigned int index = 0; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (glad_glGetStringi == NULL || glad_glGetIntegerv == NULL) { + return 0; + } + glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i); + if (num_exts_i > 0) { + exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i)); + } + if (exts_i == NULL) { + return 0; + } + for(index = 0; index < num_exts_i; index++) { + const char *gl_str_tmp = (const char*) glad_glGetStringi(GL_EXTENSIONS, index); + size_t len = strlen(gl_str_tmp) + 1; + + char *local_str = (char*) malloc(len * sizeof(char)); + if(local_str != NULL) { + memcpy(local_str, gl_str_tmp, len * sizeof(char)); + } + + exts_i[index] = local_str; + } + + *out_num_exts_i = num_exts_i; + *out_exts_i = exts_i; + } +#endif + return 1; +} +static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) { + if (exts_i != NULL) { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + free((void *) (exts_i[index])); + } + free((void *)exts_i); + exts_i = NULL; + } +} +static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) { + if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) { + const char *extensions; + const char *loc; + const char *terminator; + extensions = exts; + if(extensions == NULL || ext == NULL) { + return 0; + } + while(1) { + loc = strstr(extensions, ext); + if(loc == NULL) { + return 0; + } + terminator = loc + strlen(ext); + if((loc == extensions || *(loc - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return 1; + } + extensions = terminator; + } + } else { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + const char *e = exts_i[index]; + if(strcmp(e, ext) == 0) { + return 1; + } + } + } + return 0; +} + +static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) { + return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); +} + +static int glad_gl_find_extensions_gl( int version) { + const char *exts = NULL; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; + + (void) glad_gl_has_extension; + + glad_gl_free_extensions(exts_i, num_exts_i); + + return 1; +} + +static int glad_gl_find_core_gl(void) { + int i; + const char* version; + const char* prefixes[] = { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + "OpenGL SC ", + NULL + }; + int major = 0; + int minor = 0; + version = (const char*) glad_glGetString(GL_VERSION); + if (!version) return 0; + for (i = 0; prefixes[i]; i++) { + const size_t length = strlen(prefixes[i]); + if (strncmp(version, prefixes[i], length) == 0) { + version += length; + break; + } + } + + GLAD_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor); + + GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; + GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; + GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; + GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; + GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1; + GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1; + GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; + GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2; + GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; + GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3; + GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3; + GLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3; + + return GLAD_MAKE_VERSION(major, minor); +} + +int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) { + int version; + + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + if(glad_glGetString == NULL) return 0; + if(glad_glGetString(GL_VERSION) == NULL) return 0; + version = glad_gl_find_core_gl(); + + glad_gl_load_GL_VERSION_1_0(load, userptr); + glad_gl_load_GL_VERSION_1_1(load, userptr); + glad_gl_load_GL_VERSION_1_2(load, userptr); + glad_gl_load_GL_VERSION_1_3(load, userptr); + glad_gl_load_GL_VERSION_1_4(load, userptr); + glad_gl_load_GL_VERSION_1_5(load, userptr); + glad_gl_load_GL_VERSION_2_0(load, userptr); + glad_gl_load_GL_VERSION_2_1(load, userptr); + glad_gl_load_GL_VERSION_3_0(load, userptr); + glad_gl_load_GL_VERSION_3_1(load, userptr); + glad_gl_load_GL_VERSION_3_2(load, userptr); + glad_gl_load_GL_VERSION_3_3(load, userptr); + + if (!glad_gl_find_extensions_gl(version)) return 0; + + + + return version; +} + + +int gladLoadGL( GLADloadfunc load) { + return gladLoadGLUserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); +} + + + + + +#ifdef GLAD_GL + +#ifndef GLAD_LOADER_LIBRARY_C_ +#define GLAD_LOADER_LIBRARY_C_ + +#include +#include + +#if GLAD_PLATFORM_WIN32 +#include +#else +#include +#endif + + +static void* glad_get_dlopen_handle(const char *lib_names[], int length) { + void *handle = NULL; + int i; + + for (i = 0; i < length; ++i) { +#if GLAD_PLATFORM_WIN32 + #if GLAD_PLATFORM_UWP + size_t buffer_size = (strlen(lib_names[i]) + 1) * sizeof(WCHAR); + LPWSTR buffer = (LPWSTR) malloc(buffer_size); + if (buffer != NULL) { + int ret = MultiByteToWideChar(CP_ACP, 0, lib_names[i], -1, buffer, buffer_size); + if (ret != 0) { + handle = (void*) LoadPackagedLibrary(buffer, 0); + } + free((void*) buffer); + } + #else + handle = (void*) LoadLibraryA(lib_names[i]); + #endif +#else + handle = dlopen(lib_names[i], RTLD_LAZY | RTLD_LOCAL); +#endif + if (handle != NULL) { + return handle; + } + } + + return NULL; +} + +static void glad_close_dlopen_handle(void* handle) { + if (handle != NULL) { +#if GLAD_PLATFORM_WIN32 + FreeLibrary((HMODULE) handle); +#else + dlclose(handle); +#endif + } +} + +static GLADapiproc glad_dlsym_handle(void* handle, const char *name) { + if (handle == NULL) { + return NULL; + } + +#if GLAD_PLATFORM_WIN32 + return (GLADapiproc) GetProcAddress((HMODULE) handle, name); +#else + return GLAD_GNUC_EXTENSION (GLADapiproc) dlsym(handle, name); +#endif +} + +#endif /* GLAD_LOADER_LIBRARY_C_ */ + +typedef void* (GLAD_API_PTR *GLADglprocaddrfunc)(const char*); +struct _glad_gl_userptr { + void *handle; + GLADglprocaddrfunc gl_get_proc_address_ptr; +}; + +static GLADapiproc glad_gl_get_proc(void *vuserptr, const char *name) { + struct _glad_gl_userptr userptr = *(struct _glad_gl_userptr*) vuserptr; + GLADapiproc result = NULL; + + if(userptr.gl_get_proc_address_ptr != NULL) { + result = GLAD_GNUC_EXTENSION (GLADapiproc) userptr.gl_get_proc_address_ptr(name); + } + if(result == NULL) { + result = glad_dlsym_handle(userptr.handle, name); + } + + return result; +} + +static void* _gl_handle = NULL; + +static void* glad_gl_dlopen_handle(void) { +#if GLAD_PLATFORM_APPLE + static const char *NAMES[] = { + "../Frameworks/OpenGL.framework/OpenGL", + "/Library/Frameworks/OpenGL.framework/OpenGL", + "/System/Library/Frameworks/OpenGL.framework/OpenGL", + "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL" + }; +#elif GLAD_PLATFORM_WIN32 + static const char *NAMES[] = {"opengl32.dll"}; +#else + static const char *NAMES[] = { + #if defined(__CYGWIN__) + "libGL-1.so", + #endif + "libGL.so.1", + "libGL.so" + }; +#endif + + if (_gl_handle == NULL) { + _gl_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0])); + } + + return _gl_handle; +} + +static struct _glad_gl_userptr glad_gl_build_userptr(void *handle) { + struct _glad_gl_userptr userptr; + + userptr.handle = handle; +#if GLAD_PLATFORM_APPLE || defined(__HAIKU__) + userptr.gl_get_proc_address_ptr = NULL; +#elif GLAD_PLATFORM_WIN32 + userptr.gl_get_proc_address_ptr = + (GLADglprocaddrfunc) glad_dlsym_handle(handle, "wglGetProcAddress"); +#else + userptr.gl_get_proc_address_ptr = + (GLADglprocaddrfunc) glad_dlsym_handle(handle, "glXGetProcAddressARB"); +#endif + + return userptr; +} + +int gladLoaderLoadGL(void) { + int version = 0; + void *handle; + int did_load = 0; + struct _glad_gl_userptr userptr; + + did_load = _gl_handle == NULL; + handle = glad_gl_dlopen_handle(); + if (handle) { + userptr = glad_gl_build_userptr(handle); + + version = gladLoadGLUserPtr(glad_gl_get_proc, &userptr); + + if (did_load) { + gladLoaderUnloadGL(); + } + } + + return version; +} + + + +void gladLoaderUnloadGL(void) { + if (_gl_handle != NULL) { + glad_close_dlopen_handle(_gl_handle); + _gl_handle = NULL; + } +} + +#endif /* GLAD_GL */ + +#ifdef __cplusplus +} +#endif + +#endif /* GLAD_GL_IMPLEMENTATION */ + diff --git a/vendor/rmlui_backend/RmlUi_Include_Vulkan.h b/vendor/rmlui_backend/RmlUi_Include_Vulkan.h new file mode 100644 index 0000000..bc59afc --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Include_Vulkan.h @@ -0,0 +1,32 @@ +#ifndef RMLUI_BACKENDS_INCLUDE_VULKAN_H + #define RMLUI_BACKENDS_INCLUDE_VULKAN_H + + #if defined RMLUI_PLATFORM_UNIX + #define VK_USE_PLATFORM_XCB_KHR 1 + #endif + #define VMA_STATIC_VULKAN_FUNCTIONS 0 + #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 +#endif + +#if defined _MSC_VER + #pragma warning(push, 0) +#elif defined __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wall" + #pragma clang diagnostic ignored "-Wextra" + #pragma clang diagnostic ignored "-Wnullability-extension" + #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" + #pragma clang diagnostic ignored "-Wnullability-completeness" +#elif defined __GNUC__ + #pragma GCC system_header +#endif + +#include "RmlUi_Vulkan/vulkan.h" +// Always include "vulkan.h" first, this comment prevents clang-format from reordering the includes. +#include "RmlUi_Vulkan/vk_mem_alloc.h" + +#if defined _MSC_VER + #pragma warning(pop) +#elif defined __clang__ + #pragma clang diagnostic pop +#endif diff --git a/vendor/rmlui_backend/RmlUi_Include_Windows.h b/vendor/rmlui_backend/RmlUi_Include_Windows.h new file mode 100644 index 0000000..fb09874 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Include_Windows.h @@ -0,0 +1,20 @@ +#pragma once + +#ifndef RMLUI_DISABLE_INCLUDE_WINDOWS + + #if !defined _WIN32_WINNT || _WIN32_WINNT < 0x0601 + #undef _WIN32_WINNT + // Target Windows 7 + #define _WIN32_WINNT 0x0601 + #endif + + #define UNICODE + #define _UNICODE + #define WIN32_LEAN_AND_MEAN + #ifndef NOMINMAX + #define NOMINMAX + #endif + + #include + +#endif diff --git a/vendor/rmlui_backend/RmlUi_Include_Xlib.h b/vendor/rmlui_backend/RmlUi_Include_Xlib.h new file mode 100644 index 0000000..3546403 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Include_Xlib.h @@ -0,0 +1,18 @@ +#pragma once + +#ifndef RMLUI_DISABLE_INCLUDE_XLIB + + #include + + // The None and Always defines from X.h conflicts with RmlUi code base, + // use their underlying constants where necessary. + #ifdef None + // Value 0L + #undef None + #endif + #ifdef Always + // Value 2 + #undef Always + #endif + +#endif diff --git a/vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp b/vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp new file mode 100644 index 0000000..3635510 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp @@ -0,0 +1,330 @@ +#include "RmlUi_Platform_GLFW.h" +#include +#include +#include +#include +#include + +#define GLFW_HAS_EXTRA_CURSORS (GLFW_VERSION_MAJOR >= 3 && GLFW_VERSION_MINOR >= 4) + +SystemInterface_GLFW::SystemInterface_GLFW(GLFWwindow* window) : window(window) +{ + RMLUI_ASSERTMSG(window, "Please provide a valid SDL window to the SDL system interface"); + + cursor_pointer = glfwCreateStandardCursor(GLFW_HAND_CURSOR); + cursor_cross = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR); + cursor_text = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); +#if GLFW_HAS_EXTRA_CURSORS + cursor_move = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); + cursor_resize = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); + cursor_unavailable = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); +#else + cursor_move = cursor_pointer; + cursor_resize = cursor_pointer; + cursor_unavailable = nullptr; +#endif +} + +SystemInterface_GLFW::~SystemInterface_GLFW() +{ + glfwDestroyCursor(cursor_pointer); + glfwDestroyCursor(cursor_cross); + glfwDestroyCursor(cursor_text); +#if GLFW_HAS_EXTRA_CURSORS + glfwDestroyCursor(cursor_move); + glfwDestroyCursor(cursor_resize); + glfwDestroyCursor(cursor_unavailable); +#endif +} + +double SystemInterface_GLFW::GetElapsedTime() +{ + return glfwGetTime(); +} + +void SystemInterface_GLFW::SetMouseCursor(const Rml::String& cursor_name) +{ + GLFWcursor* cursor = nullptr; + + if (cursor_name.empty() || cursor_name == "arrow") + cursor = nullptr; + else if (cursor_name == "move") + cursor = cursor_move; + else if (cursor_name == "pointer") + cursor = cursor_pointer; + else if (cursor_name == "resize") + cursor = cursor_resize; + else if (cursor_name == "cross") + cursor = cursor_cross; + else if (cursor_name == "text") + cursor = cursor_text; + else if (cursor_name == "unavailable") + cursor = cursor_unavailable; + else if (Rml::StringUtilities::StartsWith(cursor_name, "rmlui-scroll")) + cursor = cursor_move; + + glfwSetCursor(window, cursor); +} + +void SystemInterface_GLFW::SetClipboardText(const Rml::String& text_utf8) +{ + glfwSetClipboardString(window, text_utf8.c_str()); +} + +void SystemInterface_GLFW::GetClipboardText(Rml::String& text) +{ + if (const char* clipboard = glfwGetClipboardString(window)) + { + text = Rml::String(clipboard); + } + else + { + text.clear(); + } +} + +bool RmlGLFW::ProcessKeyCallback(Rml::Context* context, int key, int action, int mods) +{ + if (!context) + return true; + + bool result = true; + + switch (action) + { + case GLFW_PRESS: + case GLFW_REPEAT: + result = context->ProcessKeyDown(RmlGLFW::ConvertKey(key), RmlGLFW::ConvertKeyModifiers(mods)); + if (key == GLFW_KEY_ENTER || key == GLFW_KEY_KP_ENTER) + result &= context->ProcessTextInput('\n'); + break; + case GLFW_RELEASE: result = context->ProcessKeyUp(RmlGLFW::ConvertKey(key), RmlGLFW::ConvertKeyModifiers(mods)); break; + } + + return result; +} +bool RmlGLFW::ProcessCharCallback(Rml::Context* context, unsigned int codepoint) +{ + if (!context) + return true; + + bool result = context->ProcessTextInput((Rml::Character)codepoint); + return result; +} + +bool RmlGLFW::ProcessCursorEnterCallback(Rml::Context* context, int entered) +{ + if (!context) + return true; + + bool result = true; + if (!entered) + result = context->ProcessMouseLeave(); + return result; +} + +bool RmlGLFW::ProcessCursorPosCallback(Rml::Context* context, GLFWwindow* window, double xpos, double ypos, int mods) +{ + if (!context) + return true; + + using Rml::Vector2i; + using Vector2d = Rml::Vector2; + + Vector2i window_size, framebuffer_size; + glfwGetWindowSize(window, &window_size.x, &window_size.y); + glfwGetFramebufferSize(window, &framebuffer_size.x, &framebuffer_size.y); + + // Convert from mouse position in GLFW screen coordinates to framebuffer coordinates (pixels) used by RmlUi. + const Vector2d mouse_pos = Vector2d(xpos, ypos) * (Vector2d(framebuffer_size) / Vector2d(window_size)); + const Vector2i mouse_pos_round = {int(Rml::Math::Round(mouse_pos.x)), int(Rml::Math::Round(mouse_pos.y))}; + + bool result = context->ProcessMouseMove(mouse_pos_round.x, mouse_pos_round.y, RmlGLFW::ConvertKeyModifiers(mods)); + return result; +} + +bool RmlGLFW::ProcessMouseButtonCallback(Rml::Context* context, int button, int action, int mods) +{ + if (!context) + return true; + + bool result = true; + + switch (action) + { + case GLFW_PRESS: result = context->ProcessMouseButtonDown(button, RmlGLFW::ConvertKeyModifiers(mods)); break; + case GLFW_RELEASE: result = context->ProcessMouseButtonUp(button, RmlGLFW::ConvertKeyModifiers(mods)); break; + } + return result; +} + +bool RmlGLFW::ProcessScrollCallback(Rml::Context* context, double yoffset, int mods) +{ + if (!context) + return true; + + bool result = context->ProcessMouseWheel(-float(yoffset), RmlGLFW::ConvertKeyModifiers(mods)); + return result; +} + +void RmlGLFW::ProcessFramebufferSizeCallback(Rml::Context* context, int width, int height) +{ + if (context) + context->SetDimensions(Rml::Vector2i(width, height)); +} + +void RmlGLFW::ProcessContentScaleCallback(Rml::Context* context, float xscale) +{ + if (context) + context->SetDensityIndependentPixelRatio(xscale); +} + +int RmlGLFW::ConvertKeyModifiers(int glfw_mods) +{ + int key_modifier_state = 0; + + if (GLFW_MOD_SHIFT & glfw_mods) + key_modifier_state |= Rml::Input::KM_SHIFT; + + if (GLFW_MOD_CONTROL & glfw_mods) + key_modifier_state |= Rml::Input::KM_CTRL; + + if (GLFW_MOD_ALT & glfw_mods) + key_modifier_state |= Rml::Input::KM_ALT; + + if (GLFW_MOD_CAPS_LOCK & glfw_mods) + key_modifier_state |= Rml::Input::KM_SCROLLLOCK; + + if (GLFW_MOD_NUM_LOCK & glfw_mods) + key_modifier_state |= Rml::Input::KM_NUMLOCK; + + return key_modifier_state; +} + +Rml::Input::KeyIdentifier RmlGLFW::ConvertKey(int glfw_key) +{ + // clang-format off + switch (glfw_key) + { + case GLFW_KEY_A: return Rml::Input::KI_A; + case GLFW_KEY_B: return Rml::Input::KI_B; + case GLFW_KEY_C: return Rml::Input::KI_C; + case GLFW_KEY_D: return Rml::Input::KI_D; + case GLFW_KEY_E: return Rml::Input::KI_E; + case GLFW_KEY_F: return Rml::Input::KI_F; + case GLFW_KEY_G: return Rml::Input::KI_G; + case GLFW_KEY_H: return Rml::Input::KI_H; + case GLFW_KEY_I: return Rml::Input::KI_I; + case GLFW_KEY_J: return Rml::Input::KI_J; + case GLFW_KEY_K: return Rml::Input::KI_K; + case GLFW_KEY_L: return Rml::Input::KI_L; + case GLFW_KEY_M: return Rml::Input::KI_M; + case GLFW_KEY_N: return Rml::Input::KI_N; + case GLFW_KEY_O: return Rml::Input::KI_O; + case GLFW_KEY_P: return Rml::Input::KI_P; + case GLFW_KEY_Q: return Rml::Input::KI_Q; + case GLFW_KEY_R: return Rml::Input::KI_R; + case GLFW_KEY_S: return Rml::Input::KI_S; + case GLFW_KEY_T: return Rml::Input::KI_T; + case GLFW_KEY_U: return Rml::Input::KI_U; + case GLFW_KEY_V: return Rml::Input::KI_V; + case GLFW_KEY_W: return Rml::Input::KI_W; + case GLFW_KEY_X: return Rml::Input::KI_X; + case GLFW_KEY_Y: return Rml::Input::KI_Y; + case GLFW_KEY_Z: return Rml::Input::KI_Z; + + case GLFW_KEY_0: return Rml::Input::KI_0; + case GLFW_KEY_1: return Rml::Input::KI_1; + case GLFW_KEY_2: return Rml::Input::KI_2; + case GLFW_KEY_3: return Rml::Input::KI_3; + case GLFW_KEY_4: return Rml::Input::KI_4; + case GLFW_KEY_5: return Rml::Input::KI_5; + case GLFW_KEY_6: return Rml::Input::KI_6; + case GLFW_KEY_7: return Rml::Input::KI_7; + case GLFW_KEY_8: return Rml::Input::KI_8; + case GLFW_KEY_9: return Rml::Input::KI_9; + + case GLFW_KEY_BACKSPACE: return Rml::Input::KI_BACK; + case GLFW_KEY_TAB: return Rml::Input::KI_TAB; + + case GLFW_KEY_ENTER: return Rml::Input::KI_RETURN; + + case GLFW_KEY_PAUSE: return Rml::Input::KI_PAUSE; + case GLFW_KEY_CAPS_LOCK: return Rml::Input::KI_CAPITAL; + + case GLFW_KEY_ESCAPE: return Rml::Input::KI_ESCAPE; + + case GLFW_KEY_SPACE: return Rml::Input::KI_SPACE; + case GLFW_KEY_PAGE_UP: return Rml::Input::KI_PRIOR; + case GLFW_KEY_PAGE_DOWN: return Rml::Input::KI_NEXT; + case GLFW_KEY_END: return Rml::Input::KI_END; + case GLFW_KEY_HOME: return Rml::Input::KI_HOME; + case GLFW_KEY_LEFT: return Rml::Input::KI_LEFT; + case GLFW_KEY_UP: return Rml::Input::KI_UP; + case GLFW_KEY_RIGHT: return Rml::Input::KI_RIGHT; + case GLFW_KEY_DOWN: return Rml::Input::KI_DOWN; + case GLFW_KEY_PRINT_SCREEN: return Rml::Input::KI_SNAPSHOT; + case GLFW_KEY_INSERT: return Rml::Input::KI_INSERT; + case GLFW_KEY_DELETE: return Rml::Input::KI_DELETE; + + case GLFW_KEY_LEFT_SUPER: return Rml::Input::KI_LWIN; + case GLFW_KEY_RIGHT_SUPER: return Rml::Input::KI_RWIN; + + case GLFW_KEY_KP_0: return Rml::Input::KI_NUMPAD0; + case GLFW_KEY_KP_1: return Rml::Input::KI_NUMPAD1; + case GLFW_KEY_KP_2: return Rml::Input::KI_NUMPAD2; + case GLFW_KEY_KP_3: return Rml::Input::KI_NUMPAD3; + case GLFW_KEY_KP_4: return Rml::Input::KI_NUMPAD4; + case GLFW_KEY_KP_5: return Rml::Input::KI_NUMPAD5; + case GLFW_KEY_KP_6: return Rml::Input::KI_NUMPAD6; + case GLFW_KEY_KP_7: return Rml::Input::KI_NUMPAD7; + case GLFW_KEY_KP_8: return Rml::Input::KI_NUMPAD8; + case GLFW_KEY_KP_9: return Rml::Input::KI_NUMPAD9; + case GLFW_KEY_KP_ENTER: return Rml::Input::KI_NUMPADENTER; + case GLFW_KEY_KP_MULTIPLY: return Rml::Input::KI_MULTIPLY; + case GLFW_KEY_KP_ADD: return Rml::Input::KI_ADD; + case GLFW_KEY_KP_SUBTRACT: return Rml::Input::KI_SUBTRACT; + case GLFW_KEY_KP_DECIMAL: return Rml::Input::KI_DECIMAL; + case GLFW_KEY_KP_DIVIDE: return Rml::Input::KI_DIVIDE; + + case GLFW_KEY_F1: return Rml::Input::KI_F1; + case GLFW_KEY_F2: return Rml::Input::KI_F2; + case GLFW_KEY_F3: return Rml::Input::KI_F3; + case GLFW_KEY_F4: return Rml::Input::KI_F4; + case GLFW_KEY_F5: return Rml::Input::KI_F5; + case GLFW_KEY_F6: return Rml::Input::KI_F6; + case GLFW_KEY_F7: return Rml::Input::KI_F7; + case GLFW_KEY_F8: return Rml::Input::KI_F8; + case GLFW_KEY_F9: return Rml::Input::KI_F9; + case GLFW_KEY_F10: return Rml::Input::KI_F10; + case GLFW_KEY_F11: return Rml::Input::KI_F11; + case GLFW_KEY_F12: return Rml::Input::KI_F12; + case GLFW_KEY_F13: return Rml::Input::KI_F13; + case GLFW_KEY_F14: return Rml::Input::KI_F14; + case GLFW_KEY_F15: return Rml::Input::KI_F15; + case GLFW_KEY_F16: return Rml::Input::KI_F16; + case GLFW_KEY_F17: return Rml::Input::KI_F17; + case GLFW_KEY_F18: return Rml::Input::KI_F18; + case GLFW_KEY_F19: return Rml::Input::KI_F19; + case GLFW_KEY_F20: return Rml::Input::KI_F20; + case GLFW_KEY_F21: return Rml::Input::KI_F21; + case GLFW_KEY_F22: return Rml::Input::KI_F22; + case GLFW_KEY_F23: return Rml::Input::KI_F23; + case GLFW_KEY_F24: return Rml::Input::KI_F24; + + case GLFW_KEY_NUM_LOCK: return Rml::Input::KI_NUMLOCK; + case GLFW_KEY_SCROLL_LOCK: return Rml::Input::KI_SCROLL; + + case GLFW_KEY_LEFT_SHIFT: return Rml::Input::KI_LSHIFT; + case GLFW_KEY_LEFT_CONTROL: return Rml::Input::KI_LCONTROL; + case GLFW_KEY_RIGHT_SHIFT: return Rml::Input::KI_RSHIFT; + case GLFW_KEY_RIGHT_CONTROL: return Rml::Input::KI_RCONTROL; + case GLFW_KEY_MENU: return Rml::Input::KI_LMENU; + + case GLFW_KEY_KP_EQUAL: return Rml::Input::KI_OEM_NEC_EQUAL; + default: break; + } + // clang-format on + + return Rml::Input::KI_UNKNOWN; +} diff --git a/vendor/rmlui_backend/RmlUi_Platform_GLFW.h b/vendor/rmlui_backend/RmlUi_Platform_GLFW.h new file mode 100644 index 0000000..700fcc8 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Platform_GLFW.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include + +class SystemInterface_GLFW : public Rml::SystemInterface { +public: + SystemInterface_GLFW(GLFWwindow* window); + ~SystemInterface_GLFW(); + + // -- Inherited from Rml::SystemInterface -- + + double GetElapsedTime() override; + + void SetMouseCursor(const Rml::String& cursor_name) override; + + void SetClipboardText(const Rml::String& text) override; + void GetClipboardText(Rml::String& text) override; + +private: + GLFWwindow* window = nullptr; + + GLFWcursor* cursor_pointer = nullptr; + GLFWcursor* cursor_cross = nullptr; + GLFWcursor* cursor_text = nullptr; + GLFWcursor* cursor_move = nullptr; + GLFWcursor* cursor_resize = nullptr; + GLFWcursor* cursor_unavailable = nullptr; +}; + +/** + Optional helper functions for the GLFW plaform. + */ +namespace RmlGLFW { + +// The following optional functions are intended to be called from their respective GLFW callback functions. The functions expect arguments passed +// directly from GLFW, in addition to the RmlUi context to apply the input or sizing event on. The input callbacks return true if the event is +// propagating, i.e. was not handled by the context. +bool ProcessKeyCallback(Rml::Context* context, int key, int action, int mods); +bool ProcessCharCallback(Rml::Context* context, unsigned int codepoint); +bool ProcessCursorEnterCallback(Rml::Context* context, int entered); +bool ProcessCursorPosCallback(Rml::Context* context, GLFWwindow* window, double xpos, double ypos, int mods); +bool ProcessMouseButtonCallback(Rml::Context* context, int button, int action, int mods); +bool ProcessScrollCallback(Rml::Context* context, double yoffset, int mods); +void ProcessFramebufferSizeCallback(Rml::Context* context, int width, int height); +void ProcessContentScaleCallback(Rml::Context* context, float xscale); + +// Converts the GLFW key to RmlUi key. +Rml::Input::KeyIdentifier ConvertKey(int glfw_key); + +// Converts the GLFW key modifiers to RmlUi key modifiers. +int ConvertKeyModifiers(int glfw_mods); + +} // namespace RmlGLFW diff --git a/vendor/rmlui_backend/RmlUi_Platform_SDL.cpp b/vendor/rmlui_backend/RmlUi_Platform_SDL.cpp new file mode 100644 index 0000000..811cb84 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Platform_SDL.cpp @@ -0,0 +1,569 @@ +#include "RmlUi_Platform_SDL.h" +#include +#include +#include +#include +#include + +static Rml::TouchList TouchEventToTouchList(SDL_Event& ev, Rml::Context* context, SDL_FingerID finger_id) +{ + const Rml::Vector2f position = Rml::Vector2f{ev.tfinger.x, ev.tfinger.y} * Rml::Vector2f{context->GetDimensions()}; + return {Rml::Touch{static_cast(finger_id), position}}; +} + +SystemInterface_SDL::SystemInterface_SDL(SDL_Window* in_window) : window(in_window) +{ + RMLUI_ASSERTMSG(window, "Please provide a valid SDL window to the SDL system interface"); + +#if SDL_MAJOR_VERSION >= 3 + cursor_default = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); + cursor_move = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE); + cursor_pointer = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER); + cursor_resize = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE); + cursor_cross = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR); + cursor_text = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT); + cursor_unavailable = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED); +#else + cursor_default = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + cursor_move = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); + cursor_pointer = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); + cursor_resize = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); + cursor_cross = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR); + cursor_text = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); + cursor_unavailable = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); + (void)window; // Window is unused on SDL 2. +#endif +} + +SystemInterface_SDL::~SystemInterface_SDL() +{ +#if SDL_MAJOR_VERSION >= 3 + auto DestroyCursor = [](SDL_Cursor* cursor) { SDL_DestroyCursor(cursor); }; +#else + auto DestroyCursor = [](SDL_Cursor* cursor) { SDL_FreeCursor(cursor); }; +#endif + + DestroyCursor(cursor_default); + DestroyCursor(cursor_move); + DestroyCursor(cursor_pointer); + DestroyCursor(cursor_resize); + DestroyCursor(cursor_cross); + DestroyCursor(cursor_text); + DestroyCursor(cursor_unavailable); +} + +double SystemInterface_SDL::GetElapsedTime() +{ + static const Uint64 start = SDL_GetPerformanceCounter(); + static const double frequency = double(SDL_GetPerformanceFrequency()); + return double(SDL_GetPerformanceCounter() - start) / frequency; +} + +void SystemInterface_SDL::SetMouseCursor(const Rml::String& cursor_name) +{ + SDL_Cursor* cursor = nullptr; + + if (cursor_name.empty() || cursor_name == "arrow") + cursor = cursor_default; + else if (cursor_name == "move") + cursor = cursor_move; + else if (cursor_name == "pointer") + cursor = cursor_pointer; + else if (cursor_name == "resize") + cursor = cursor_resize; + else if (cursor_name == "cross") + cursor = cursor_cross; + else if (cursor_name == "text") + cursor = cursor_text; + else if (cursor_name == "unavailable") + cursor = cursor_unavailable; + else if (Rml::StringUtilities::StartsWith(cursor_name, "rmlui-scroll")) + cursor = cursor_move; + + if (cursor) + SDL_SetCursor(cursor); +} + +void SystemInterface_SDL::SetClipboardText(const Rml::String& text) +{ + SDL_SetClipboardText(text.c_str()); +} + +void SystemInterface_SDL::GetClipboardText(Rml::String& text) +{ + char* raw_text = SDL_GetClipboardText(); + text = Rml::String(raw_text); + SDL_free(raw_text); +} + +void SystemInterface_SDL::ActivateKeyboard(Rml::Vector2f caret_position, float line_height) +{ +#if SDL_MAJOR_VERSION >= 3 + const SDL_Rect rect = {int(caret_position.x), int(caret_position.y), 1, int(line_height)}; + SDL_SetTextInputArea(window, &rect, 0); + SDL_StartTextInput(window); +#else + (void)caret_position; + (void)line_height; + SDL_StartTextInput(); +#endif +} + +void SystemInterface_SDL::DeactivateKeyboard() +{ +#if SDL_MAJOR_VERSION >= 3 + SDL_StopTextInput(window); +#else + SDL_StopTextInput(); +#endif +} + +bool RmlSDL::InputEventHandler(Rml::Context* context, SDL_Window* window, SDL_Event& ev) +{ +#if SDL_MAJOR_VERSION >= 3 + #define RMLSDL_WINDOW_EVENTS_BEGIN + #define RMLSDL_WINDOW_EVENTS_END + auto GetKey = [](const SDL_Event& event) { return event.key.key; }; + auto GetPixelDensity = [](SDL_Window* target_window) { return SDL_GetWindowPixelDensity(target_window); }; + auto GetFingerId = [](const SDL_Event& event) { return event.tfinger.fingerID; }; + constexpr auto event_mouse_motion = SDL_EVENT_MOUSE_MOTION; + constexpr auto event_mouse_down = SDL_EVENT_MOUSE_BUTTON_DOWN; + constexpr auto event_mouse_up = SDL_EVENT_MOUSE_BUTTON_UP; + constexpr auto event_mouse_wheel = SDL_EVENT_MOUSE_WHEEL; + constexpr auto event_key_down = SDL_EVENT_KEY_DOWN; + constexpr auto event_key_up = SDL_EVENT_KEY_UP; + constexpr auto event_text_input = SDL_EVENT_TEXT_INPUT; + constexpr auto event_window_size_changed = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED; + constexpr auto event_window_leave = SDL_EVENT_WINDOW_MOUSE_LEAVE; + constexpr auto event_finger_down = SDL_EVENT_FINGER_DOWN; + constexpr auto event_finger_up = SDL_EVENT_FINGER_UP; + constexpr auto event_finger_motion = SDL_EVENT_FINGER_MOTION; + + constexpr auto rmlsdl_true = true; + constexpr auto rmlsdl_false = false; +#else + (void)window; + #define RMLSDL_WINDOW_EVENTS_BEGIN \ + case SDL_WINDOWEVENT: \ + { \ + switch (ev.window.event) \ + { + #define RMLSDL_WINDOW_EVENTS_END \ + } \ + } \ + break; + auto GetKey = [](const SDL_Event& event) { return event.key.keysym.sym; }; + auto GetPixelDensity = [](SDL_Window* /*target_window*/) { return 1.f; }; + auto GetFingerId = [](const SDL_Event& event) { return event.tfinger.fingerId; }; + constexpr auto event_mouse_motion = SDL_MOUSEMOTION; + constexpr auto event_mouse_down = SDL_MOUSEBUTTONDOWN; + constexpr auto event_mouse_up = SDL_MOUSEBUTTONUP; + constexpr auto event_mouse_wheel = SDL_MOUSEWHEEL; + constexpr auto event_key_down = SDL_KEYDOWN; + constexpr auto event_key_up = SDL_KEYUP; + constexpr auto event_text_input = SDL_TEXTINPUT; + constexpr auto event_window_size_changed = SDL_WINDOWEVENT_SIZE_CHANGED; + constexpr auto event_window_leave = SDL_WINDOWEVENT_LEAVE; + constexpr auto event_finger_down = SDL_FINGERDOWN; + constexpr auto event_finger_up = SDL_FINGERUP; + constexpr auto event_finger_motion = SDL_FINGERMOTION; + + constexpr auto rmlsdl_true = SDL_TRUE; + constexpr auto rmlsdl_false = SDL_FALSE; +#endif + + bool result = true; + + switch (ev.type) + { +#ifndef RMLUI_BACKEND_SIMULATE_TOUCH + case event_mouse_motion: + { + const float pixel_density = GetPixelDensity(window); + result = context->ProcessMouseMove(int(ev.motion.x * pixel_density), int(ev.motion.y * pixel_density), GetKeyModifierState()); + } + break; + case event_mouse_down: + { + result = context->ProcessMouseButtonDown(ConvertMouseButton(ev.button.button), GetKeyModifierState()); + SDL_CaptureMouse(rmlsdl_true); + } + break; + case event_mouse_up: + { + SDL_CaptureMouse(rmlsdl_false); + result = context->ProcessMouseButtonUp(ConvertMouseButton(ev.button.button), GetKeyModifierState()); + } + break; +#endif + + case event_mouse_wheel: + { + result = context->ProcessMouseWheel(float(-ev.wheel.y), GetKeyModifierState()); + } + break; + case event_key_down: + { + result = context->ProcessKeyDown(ConvertKey(GetKey(ev)), GetKeyModifierState()); + if (GetKey(ev) == SDLK_RETURN || GetKey(ev) == SDLK_KP_ENTER) + result &= context->ProcessTextInput('\n'); + } + break; + case event_key_up: + { + result = context->ProcessKeyUp(ConvertKey(GetKey(ev)), GetKeyModifierState()); + } + break; + case event_text_input: + { + result = context->ProcessTextInput(Rml::String(&ev.text.text[0])); + } + break; + case event_finger_down: + { + const Rml::TouchList touches = TouchEventToTouchList(ev, context, GetFingerId(ev)); + result = context->ProcessTouchStart(touches, GetKeyModifierState()); + } + break; + case event_finger_motion: + { + const Rml::TouchList touches = TouchEventToTouchList(ev, context, GetFingerId(ev)); + result = context->ProcessTouchMove(touches, GetKeyModifierState()); + } + break; + case event_finger_up: + { + const Rml::TouchList touches = TouchEventToTouchList(ev, context, GetFingerId(ev)); + result = context->ProcessTouchEnd(touches, GetKeyModifierState()); + } + break; + + RMLSDL_WINDOW_EVENTS_BEGIN + + case event_window_size_changed: + { + Rml::Vector2i dimensions(ev.window.data1, ev.window.data2); + + #if SDL_MAJOR_VERSION >= 3 + // SDL_Renderer backend (SDL3): if SDL_SetRenderLogicalPresentation() is enabled, the renderer uses a fixed logical + // output size (render coordinates) and scales it to the window; use that logical size for the RmlUi context. + // Input events should be converted to render coordinates first (e.g. SDL_ConvertEventToRenderCoordinates()). + SDL_Renderer* renderer = SDL_GetRenderer(window); + if (renderer) + { + int logical_w = 0; + int logical_h = 0; + SDL_RendererLogicalPresentation mode{}; + if (SDL_GetRenderLogicalPresentation(renderer, &logical_w, &logical_h, &mode) + && mode != SDL_LOGICAL_PRESENTATION_DISABLED + && logical_w > 0 && logical_h > 0) + dimensions = Rml::Vector2i(logical_w, logical_h); + } + #endif + + context->SetDimensions(dimensions); + } + break; + case event_window_leave: + { + context->ProcessMouseLeave(); + } + break; + +#if SDL_MAJOR_VERSION >= 3 + case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: + { + const float display_scale = SDL_GetWindowDisplayScale(window); + context->SetDensityIndependentPixelRatio(display_scale); + } + break; +#endif + + RMLSDL_WINDOW_EVENTS_END + + default: break; + } + + return result; +} + +Rml::Input::KeyIdentifier RmlSDL::ConvertKey(int sdlkey) +{ +#if SDL_MAJOR_VERSION >= 3 + constexpr auto key_a = SDLK_A; + constexpr auto key_b = SDLK_B; + constexpr auto key_c = SDLK_C; + constexpr auto key_d = SDLK_D; + constexpr auto key_e = SDLK_E; + constexpr auto key_f = SDLK_F; + constexpr auto key_g = SDLK_G; + constexpr auto key_h = SDLK_H; + constexpr auto key_i = SDLK_I; + constexpr auto key_j = SDLK_J; + constexpr auto key_k = SDLK_K; + constexpr auto key_l = SDLK_L; + constexpr auto key_m = SDLK_M; + constexpr auto key_n = SDLK_N; + constexpr auto key_o = SDLK_O; + constexpr auto key_p = SDLK_P; + constexpr auto key_q = SDLK_Q; + constexpr auto key_r = SDLK_R; + constexpr auto key_s = SDLK_S; + constexpr auto key_t = SDLK_T; + constexpr auto key_u = SDLK_U; + constexpr auto key_v = SDLK_V; + constexpr auto key_w = SDLK_W; + constexpr auto key_x = SDLK_X; + constexpr auto key_y = SDLK_Y; + constexpr auto key_z = SDLK_Z; + constexpr auto key_grave = SDLK_GRAVE; + constexpr auto key_dblapostrophe = SDLK_DBLAPOSTROPHE; +#else + constexpr auto key_a = SDLK_a; + constexpr auto key_b = SDLK_b; + constexpr auto key_c = SDLK_c; + constexpr auto key_d = SDLK_d; + constexpr auto key_e = SDLK_e; + constexpr auto key_f = SDLK_f; + constexpr auto key_g = SDLK_g; + constexpr auto key_h = SDLK_h; + constexpr auto key_i = SDLK_i; + constexpr auto key_j = SDLK_j; + constexpr auto key_k = SDLK_k; + constexpr auto key_l = SDLK_l; + constexpr auto key_m = SDLK_m; + constexpr auto key_n = SDLK_n; + constexpr auto key_o = SDLK_o; + constexpr auto key_p = SDLK_p; + constexpr auto key_q = SDLK_q; + constexpr auto key_r = SDLK_r; + constexpr auto key_s = SDLK_s; + constexpr auto key_t = SDLK_t; + constexpr auto key_u = SDLK_u; + constexpr auto key_v = SDLK_v; + constexpr auto key_w = SDLK_w; + constexpr auto key_x = SDLK_x; + constexpr auto key_y = SDLK_y; + constexpr auto key_z = SDLK_z; + constexpr auto key_grave = SDLK_BACKQUOTE; + constexpr auto key_dblapostrophe = SDLK_QUOTEDBL; +#endif + + // clang-format off + switch (sdlkey) + { + case SDLK_UNKNOWN: return Rml::Input::KI_UNKNOWN; + case SDLK_ESCAPE: return Rml::Input::KI_ESCAPE; + case SDLK_SPACE: return Rml::Input::KI_SPACE; + case SDLK_0: return Rml::Input::KI_0; + case SDLK_1: return Rml::Input::KI_1; + case SDLK_2: return Rml::Input::KI_2; + case SDLK_3: return Rml::Input::KI_3; + case SDLK_4: return Rml::Input::KI_4; + case SDLK_5: return Rml::Input::KI_5; + case SDLK_6: return Rml::Input::KI_6; + case SDLK_7: return Rml::Input::KI_7; + case SDLK_8: return Rml::Input::KI_8; + case SDLK_9: return Rml::Input::KI_9; + case key_a: return Rml::Input::KI_A; + case key_b: return Rml::Input::KI_B; + case key_c: return Rml::Input::KI_C; + case key_d: return Rml::Input::KI_D; + case key_e: return Rml::Input::KI_E; + case key_f: return Rml::Input::KI_F; + case key_g: return Rml::Input::KI_G; + case key_h: return Rml::Input::KI_H; + case key_i: return Rml::Input::KI_I; + case key_j: return Rml::Input::KI_J; + case key_k: return Rml::Input::KI_K; + case key_l: return Rml::Input::KI_L; + case key_m: return Rml::Input::KI_M; + case key_n: return Rml::Input::KI_N; + case key_o: return Rml::Input::KI_O; + case key_p: return Rml::Input::KI_P; + case key_q: return Rml::Input::KI_Q; + case key_r: return Rml::Input::KI_R; + case key_s: return Rml::Input::KI_S; + case key_t: return Rml::Input::KI_T; + case key_u: return Rml::Input::KI_U; + case key_v: return Rml::Input::KI_V; + case key_w: return Rml::Input::KI_W; + case key_x: return Rml::Input::KI_X; + case key_y: return Rml::Input::KI_Y; + case key_z: return Rml::Input::KI_Z; + case SDLK_SEMICOLON: return Rml::Input::KI_OEM_1; + case SDLK_PLUS: return Rml::Input::KI_OEM_PLUS; + case SDLK_COMMA: return Rml::Input::KI_OEM_COMMA; + case SDLK_MINUS: return Rml::Input::KI_OEM_MINUS; + case SDLK_PERIOD: return Rml::Input::KI_OEM_PERIOD; + case SDLK_SLASH: return Rml::Input::KI_OEM_2; + case key_grave: return Rml::Input::KI_OEM_3; + case SDLK_LEFTBRACKET: return Rml::Input::KI_OEM_4; + case SDLK_BACKSLASH: return Rml::Input::KI_OEM_5; + case SDLK_RIGHTBRACKET: return Rml::Input::KI_OEM_6; + case key_dblapostrophe: return Rml::Input::KI_OEM_7; + case SDLK_KP_0: return Rml::Input::KI_NUMPAD0; + case SDLK_KP_1: return Rml::Input::KI_NUMPAD1; + case SDLK_KP_2: return Rml::Input::KI_NUMPAD2; + case SDLK_KP_3: return Rml::Input::KI_NUMPAD3; + case SDLK_KP_4: return Rml::Input::KI_NUMPAD4; + case SDLK_KP_5: return Rml::Input::KI_NUMPAD5; + case SDLK_KP_6: return Rml::Input::KI_NUMPAD6; + case SDLK_KP_7: return Rml::Input::KI_NUMPAD7; + case SDLK_KP_8: return Rml::Input::KI_NUMPAD8; + case SDLK_KP_9: return Rml::Input::KI_NUMPAD9; + case SDLK_KP_ENTER: return Rml::Input::KI_NUMPADENTER; + case SDLK_KP_MULTIPLY: return Rml::Input::KI_MULTIPLY; + case SDLK_KP_PLUS: return Rml::Input::KI_ADD; + case SDLK_KP_MINUS: return Rml::Input::KI_SUBTRACT; + case SDLK_KP_PERIOD: return Rml::Input::KI_DECIMAL; + case SDLK_KP_DIVIDE: return Rml::Input::KI_DIVIDE; + case SDLK_KP_EQUALS: return Rml::Input::KI_OEM_NEC_EQUAL; + case SDLK_BACKSPACE: return Rml::Input::KI_BACK; + case SDLK_TAB: return Rml::Input::KI_TAB; + case SDLK_CLEAR: return Rml::Input::KI_CLEAR; + case SDLK_RETURN: return Rml::Input::KI_RETURN; + case SDLK_PAUSE: return Rml::Input::KI_PAUSE; + case SDLK_CAPSLOCK: return Rml::Input::KI_CAPITAL; + case SDLK_PAGEUP: return Rml::Input::KI_PRIOR; + case SDLK_PAGEDOWN: return Rml::Input::KI_NEXT; + case SDLK_END: return Rml::Input::KI_END; + case SDLK_HOME: return Rml::Input::KI_HOME; + case SDLK_LEFT: return Rml::Input::KI_LEFT; + case SDLK_UP: return Rml::Input::KI_UP; + case SDLK_RIGHT: return Rml::Input::KI_RIGHT; + case SDLK_DOWN: return Rml::Input::KI_DOWN; + case SDLK_INSERT: return Rml::Input::KI_INSERT; + case SDLK_DELETE: return Rml::Input::KI_DELETE; + case SDLK_HELP: return Rml::Input::KI_HELP; + case SDLK_F1: return Rml::Input::KI_F1; + case SDLK_F2: return Rml::Input::KI_F2; + case SDLK_F3: return Rml::Input::KI_F3; + case SDLK_F4: return Rml::Input::KI_F4; + case SDLK_F5: return Rml::Input::KI_F5; + case SDLK_F6: return Rml::Input::KI_F6; + case SDLK_F7: return Rml::Input::KI_F7; + case SDLK_F8: return Rml::Input::KI_F8; + case SDLK_F9: return Rml::Input::KI_F9; + case SDLK_F10: return Rml::Input::KI_F10; + case SDLK_F11: return Rml::Input::KI_F11; + case SDLK_F12: return Rml::Input::KI_F12; + case SDLK_F13: return Rml::Input::KI_F13; + case SDLK_F14: return Rml::Input::KI_F14; + case SDLK_F15: return Rml::Input::KI_F15; + case SDLK_NUMLOCKCLEAR: return Rml::Input::KI_NUMLOCK; + case SDLK_SCROLLLOCK: return Rml::Input::KI_SCROLL; + case SDLK_LSHIFT: return Rml::Input::KI_LSHIFT; + case SDLK_RSHIFT: return Rml::Input::KI_RSHIFT; + case SDLK_LCTRL: return Rml::Input::KI_LCONTROL; + case SDLK_RCTRL: return Rml::Input::KI_RCONTROL; + case SDLK_LALT: return Rml::Input::KI_LMENU; + case SDLK_RALT: return Rml::Input::KI_RMENU; + case SDLK_LGUI: return Rml::Input::KI_LMETA; + case SDLK_RGUI: return Rml::Input::KI_RMETA; + /* + case SDLK_LSUPER: return Rml::Input::KI_LWIN; + case SDLK_RSUPER: return Rml::Input::KI_RWIN; + */ + default: break; + } + // clang-format on + + return Rml::Input::KI_UNKNOWN; +} + +int RmlSDL::ConvertMouseButton(int button) +{ + switch (button) + { + case SDL_BUTTON_LEFT: return 0; + case SDL_BUTTON_RIGHT: return 1; + case SDL_BUTTON_MIDDLE: return 2; + default: return 3; + } +} + +int RmlSDL::GetKeyModifierState() +{ + SDL_Keymod sdl_mods = SDL_GetModState(); + +#if SDL_MAJOR_VERSION >= 3 + constexpr auto mod_ctrl = SDL_KMOD_CTRL; + constexpr auto mod_shift = SDL_KMOD_SHIFT; + constexpr auto mod_alt = SDL_KMOD_ALT; + constexpr auto mod_num = SDL_KMOD_NUM; + constexpr auto mod_caps = SDL_KMOD_CAPS; +#else + constexpr auto mod_ctrl = KMOD_CTRL; + constexpr auto mod_shift = KMOD_SHIFT; + constexpr auto mod_alt = KMOD_ALT; + constexpr auto mod_num = KMOD_NUM; + constexpr auto mod_caps = KMOD_CAPS; +#endif + + int retval = 0; + + if (sdl_mods & mod_ctrl) + retval |= Rml::Input::KM_CTRL; + + if (sdl_mods & mod_shift) + retval |= Rml::Input::KM_SHIFT; + + if (sdl_mods & mod_alt) + retval |= Rml::Input::KM_ALT; + + if (sdl_mods & mod_num) + retval |= Rml::Input::KM_NUMLOCK; + + if (sdl_mods & mod_caps) + retval |= Rml::Input::KM_CAPSLOCK; + + return retval; +} + +void TextInputMethodEditor_SDL::OnActivate(Rml::TextInputContext* input_context) +{ + context = input_context; +} + +void TextInputMethodEditor_SDL::OnDeactivate(Rml::TextInputContext* input_context) +{ + if (context == input_context) + context = nullptr; +} + +void TextInputMethodEditor_SDL::OnDestroy(Rml::TextInputContext* input_context) +{ + if (context == input_context) + context = nullptr; +} + +void TextInputMethodEditor_SDL::HandleEdit(const SDL_TextEditingEvent& ev) +{ + if (context == nullptr) + return; + + auto string = Rml::String(ev.text); + auto length = static_cast(Rml::StringUtilities::LengthUTF8(string)); + + auto composing = start != end; + + if (!composing) + context->GetSelectionRange(start, end); + + if (composing || length > 0) + context->SetText(string, start, end); + + end = start + length; + context->SetCompositionRange(start, end); + + if (length > 0 && ev.start >= 0 && ev.length >= 0) + context->SetSelectionRange(start + ev.start, start + ev.start + ev.length); + else if (composing) + context->SetCursorPosition(end); + + // When committing, SDL sends a text editing event with an empty string and a + // separate text input event with the committed text. + if (composing && length == 0) + context->CommitComposition(Rml::StringView()); +} diff --git a/vendor/rmlui_backend/RmlUi_Platform_SDL.h b/vendor/rmlui_backend/RmlUi_Platform_SDL.h new file mode 100644 index 0000000..896d438 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Platform_SDL.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include + +#if RMLUI_SDL_VERSION_MAJOR == 3 + #include +#elif RMLUI_SDL_VERSION_MAJOR == 2 + #include +#else + #error "Unspecified RMLUI_SDL_VERSION_MAJOR. Please set this definition to the major version of the SDL library being linked to." +#endif + +class SystemInterface_SDL : public Rml::SystemInterface { +public: + SystemInterface_SDL(SDL_Window* window); + ~SystemInterface_SDL(); + + // -- Inherited from Rml::SystemInterface -- + + double GetElapsedTime() override; + + void SetMouseCursor(const Rml::String& cursor_name) override; + + void SetClipboardText(const Rml::String& text) override; + void GetClipboardText(Rml::String& text) override; + + void ActivateKeyboard(Rml::Vector2f caret_position, float line_height) override; + void DeactivateKeyboard() override; + +private: + SDL_Window* window = nullptr; + + SDL_Cursor* cursor_default = nullptr; + SDL_Cursor* cursor_move = nullptr; + SDL_Cursor* cursor_pointer = nullptr; + SDL_Cursor* cursor_resize = nullptr; + SDL_Cursor* cursor_cross = nullptr; + SDL_Cursor* cursor_text = nullptr; + SDL_Cursor* cursor_unavailable = nullptr; +}; + +namespace RmlSDL { + +// Applies input on the context based on the given SDL event. +// +// Note (SDL3 + SDL_Renderer): When using SDL_SetRenderLogicalPresentation(), SDL_Renderer operates in render +// coordinates (logical coordinates). Therefore, before passing an SDL_Event to InputEventHandler, input event +// coordinates (mouse/touch/etc.) should be converted to render coordinates, e.g. +// SDL_ConvertEventToRenderCoordinates(renderer, &ev). +// @return True if the event is still propagating, false if it was handled by the context. +bool InputEventHandler(Rml::Context* context, SDL_Window* window, SDL_Event& ev); + +// Converts the SDL key to RmlUi key. +Rml::Input::KeyIdentifier ConvertKey(int sdl_key); + +// Converts the SDL mouse button to RmlUi mouse button. +int ConvertMouseButton(int sdl_mouse_button); + +// Returns the active RmlUi key modifier state. +int GetKeyModifierState(); + +} // namespace RmlSDL + +class TextInputMethodEditor_SDL final : public Rml::TextInputHandler { +public: + void OnActivate(Rml::TextInputContext* input_context) override; + void OnDeactivate(Rml::TextInputContext* input_context) override; + void OnDestroy(Rml::TextInputContext* input_context) override; + + void HandleEdit(const SDL_TextEditingEvent& ev); + +private: + Rml::TextInputContext* context = nullptr; + int start = 0, end = 0; +}; diff --git a/vendor/rmlui_backend/RmlUi_Platform_SFML.cpp b/vendor/rmlui_backend/RmlUi_Platform_SFML.cpp new file mode 100644 index 0000000..f05b7e0 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Platform_SFML.cpp @@ -0,0 +1,287 @@ +#include "RmlUi_Platform_SFML.h" +#include +#include +#include +#include +#include +#include +#include + +#if SFML_VERSION_MAJOR >= 3 +SystemInterface_SFML::Cursors::Cursors() : + cursor_default(sf::Cursor::Type::Arrow), cursor_move(sf::Cursor::Type::SizeAll), cursor_pointer(sf::Cursor::Type::Hand), + cursor_resize(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeTopLeftBottomRight).value_or(sf::Cursor(sf::Cursor::Type::SizeAll))), + cursor_cross(sf::Cursor::Type::Cross), cursor_text(sf::Cursor::Type::Text), cursor_unavailable(sf::Cursor::Type::NotAllowed) +{} +SystemInterface_SFML::SystemInterface_SFML() +{ + try + { + cursors = std::make_unique(); + } catch (const sf::Exception& /*exception*/) + { + cursors.reset(); + } +} + +#else + +SystemInterface_SFML::Cursors::Cursors() {} +SystemInterface_SFML::SystemInterface_SFML() +{ + cursors = std::make_unique(); + bool cursors_valid = true; + cursors_valid &= cursors->cursor_default.loadFromSystem(sf::Cursor::Arrow); + cursors_valid &= cursors->cursor_move.loadFromSystem(sf::Cursor::SizeAll); + cursors_valid &= cursors->cursor_pointer.loadFromSystem(sf::Cursor::Hand); + cursors_valid &= + cursors->cursor_resize.loadFromSystem(sf::Cursor::SizeTopLeftBottomRight) || cursors->cursor_resize.loadFromSystem(sf::Cursor::SizeAll); + cursors_valid &= cursors->cursor_cross.loadFromSystem(sf::Cursor::Cross); + cursors_valid &= cursors->cursor_text.loadFromSystem(sf::Cursor::Text); + cursors_valid &= cursors->cursor_unavailable.loadFromSystem(sf::Cursor::NotAllowed); + if (!cursors_valid) + cursors.reset(); +} +#endif + +void SystemInterface_SFML::SetWindow(sf::RenderWindow* in_window) +{ + window = in_window; +} + +double SystemInterface_SFML::GetElapsedTime() +{ + return static_cast(timer.getElapsedTime().asMicroseconds()) / 1'000'000.0; +} + +void SystemInterface_SFML::SetMouseCursor(const Rml::String& cursor_name) +{ + if (cursors && window) + { + sf::Cursor* cursor = nullptr; + if (cursor_name.empty() || cursor_name == "arrow") + cursor = &cursors->cursor_default; + else if (cursor_name == "move") + cursor = &cursors->cursor_move; + else if (cursor_name == "pointer") + cursor = &cursors->cursor_pointer; + else if (cursor_name == "resize") + cursor = &cursors->cursor_resize; + else if (cursor_name == "cross") + cursor = &cursors->cursor_cross; + else if (cursor_name == "text") + cursor = &cursors->cursor_text; + else if (cursor_name == "unavailable") + cursor = &cursors->cursor_unavailable; + else if (Rml::StringUtilities::StartsWith(cursor_name, "rmlui-scroll")) + cursor = &cursors->cursor_move; + + if (cursor) + window->setMouseCursor(*cursor); + } +} + +void SystemInterface_SFML::SetClipboardText(const Rml::String& text_utf8) +{ + sf::Clipboard::setString(text_utf8); +} + +void SystemInterface_SFML::GetClipboardText(Rml::String& text) +{ + text = sf::Clipboard::getString(); +} + +bool RmlSFML::InputHandler(Rml::Context* context, const sf::Event& ev) +{ + bool result = true; + +#if SFML_VERSION_MAJOR >= 3 + + if (auto mouse_moved = ev.getIf()) + { + result = context->ProcessMouseMove(mouse_moved->position.x, mouse_moved->position.y, RmlSFML::GetKeyModifierState()); + } + else if (auto mouse_pressed = ev.getIf()) + { + result = context->ProcessMouseButtonDown(int(mouse_pressed->button), RmlSFML::GetKeyModifierState()); + } + else if (auto mouse_released = ev.getIf()) + { + result = context->ProcessMouseButtonUp(int(mouse_released->button), RmlSFML::GetKeyModifierState()); + } + else if (auto wheel_scrolled = ev.getIf()) + { + const Rml::Vector2f delta = { + wheel_scrolled->wheel == sf::Mouse::Wheel::Horizontal ? -wheel_scrolled->delta : 0.f, + wheel_scrolled->wheel == sf::Mouse::Wheel::Vertical ? -wheel_scrolled->delta : 0.f, + }; + result = context->ProcessMouseWheel(delta, RmlSFML::GetKeyModifierState()); + } + else if (ev.is()) + { + result = context->ProcessMouseLeave(); + } + else if (auto text_entered = ev.getIf()) + { + Rml::Character character = Rml::Character(text_entered->unicode); + if (character == Rml::Character('\r')) + character = Rml::Character('\n'); + + if (text_entered->unicode >= 32 || character == Rml::Character('\n')) + result = context->ProcessTextInput(character); + } + else if (auto key_pressed = ev.getIf()) + { + result = context->ProcessKeyDown(RmlSFML::ConvertKey(key_pressed->code), RmlSFML::GetKeyModifierState()); + } + else if (auto key_released = ev.getIf()) + { + result = context->ProcessKeyUp(RmlSFML::ConvertKey(key_released->code), RmlSFML::GetKeyModifierState()); + } + +#else + + switch (ev.type) + { + case sf::Event::MouseMoved: result = context->ProcessMouseMove(ev.mouseMove.x, ev.mouseMove.y, RmlSFML::GetKeyModifierState()); break; + case sf::Event::MouseButtonPressed: result = context->ProcessMouseButtonDown(ev.mouseButton.button, RmlSFML::GetKeyModifierState()); break; + case sf::Event::MouseButtonReleased: result = context->ProcessMouseButtonUp(ev.mouseButton.button, RmlSFML::GetKeyModifierState()); break; + case sf::Event::MouseWheelMoved: result = context->ProcessMouseWheel(float(-ev.mouseWheel.delta), RmlSFML::GetKeyModifierState()); break; + case sf::Event::MouseLeft: result = context->ProcessMouseLeave(); break; + case sf::Event::TextEntered: + { + Rml::Character character = Rml::Character(ev.text.unicode); + if (character == Rml::Character('\r')) + character = Rml::Character('\n'); + + if (ev.text.unicode >= 32 || character == Rml::Character('\n')) + result = context->ProcessTextInput(character); + } + break; + case sf::Event::KeyPressed: result = context->ProcessKeyDown(RmlSFML::ConvertKey(ev.key.code), RmlSFML::GetKeyModifierState()); break; + case sf::Event::KeyReleased: result = context->ProcessKeyUp(RmlSFML::ConvertKey(ev.key.code), RmlSFML::GetKeyModifierState()); break; + default: break; + } +#endif + + return result; +} + +Rml::Input::KeyIdentifier RmlSFML::ConvertKey(sf::Keyboard::Key sfml_key) +{ +#if SFML_VERSION_MAJOR >= 3 + constexpr auto sfBackspace = sf::Keyboard::Key::Backspace; + constexpr auto sfEnter = sf::Keyboard::Key::Enter; +#else + constexpr auto sfBackspace = sf::Keyboard::Key::BackSpace; + constexpr auto sfEnter = sf::Keyboard::Key::Return; +#endif + + // clang-format off + switch (sfml_key) + { + case sf::Keyboard::Key::A: return Rml::Input::KI_A; + case sf::Keyboard::Key::B: return Rml::Input::KI_B; + case sf::Keyboard::Key::C: return Rml::Input::KI_C; + case sf::Keyboard::Key::D: return Rml::Input::KI_D; + case sf::Keyboard::Key::E: return Rml::Input::KI_E; + case sf::Keyboard::Key::F: return Rml::Input::KI_F; + case sf::Keyboard::Key::G: return Rml::Input::KI_G; + case sf::Keyboard::Key::H: return Rml::Input::KI_H; + case sf::Keyboard::Key::I: return Rml::Input::KI_I; + case sf::Keyboard::Key::J: return Rml::Input::KI_J; + case sf::Keyboard::Key::K: return Rml::Input::KI_K; + case sf::Keyboard::Key::L: return Rml::Input::KI_L; + case sf::Keyboard::Key::M: return Rml::Input::KI_M; + case sf::Keyboard::Key::N: return Rml::Input::KI_N; + case sf::Keyboard::Key::O: return Rml::Input::KI_O; + case sf::Keyboard::Key::P: return Rml::Input::KI_P; + case sf::Keyboard::Key::Q: return Rml::Input::KI_Q; + case sf::Keyboard::Key::R: return Rml::Input::KI_R; + case sf::Keyboard::Key::S: return Rml::Input::KI_S; + case sf::Keyboard::Key::T: return Rml::Input::KI_T; + case sf::Keyboard::Key::U: return Rml::Input::KI_U; + case sf::Keyboard::Key::V: return Rml::Input::KI_V; + case sf::Keyboard::Key::W: return Rml::Input::KI_W; + case sf::Keyboard::Key::X: return Rml::Input::KI_X; + case sf::Keyboard::Key::Y: return Rml::Input::KI_Y; + case sf::Keyboard::Key::Z: return Rml::Input::KI_Z; + case sf::Keyboard::Key::Num0: return Rml::Input::KI_0; + case sf::Keyboard::Key::Num1: return Rml::Input::KI_1; + case sf::Keyboard::Key::Num2: return Rml::Input::KI_2; + case sf::Keyboard::Key::Num3: return Rml::Input::KI_3; + case sf::Keyboard::Key::Num4: return Rml::Input::KI_4; + case sf::Keyboard::Key::Num5: return Rml::Input::KI_5; + case sf::Keyboard::Key::Num6: return Rml::Input::KI_6; + case sf::Keyboard::Key::Num7: return Rml::Input::KI_7; + case sf::Keyboard::Key::Num8: return Rml::Input::KI_8; + case sf::Keyboard::Key::Num9: return Rml::Input::KI_9; + case sf::Keyboard::Key::Numpad0: return Rml::Input::KI_NUMPAD0; + case sf::Keyboard::Key::Numpad1: return Rml::Input::KI_NUMPAD1; + case sf::Keyboard::Key::Numpad2: return Rml::Input::KI_NUMPAD2; + case sf::Keyboard::Key::Numpad3: return Rml::Input::KI_NUMPAD3; + case sf::Keyboard::Key::Numpad4: return Rml::Input::KI_NUMPAD4; + case sf::Keyboard::Key::Numpad5: return Rml::Input::KI_NUMPAD5; + case sf::Keyboard::Key::Numpad6: return Rml::Input::KI_NUMPAD6; + case sf::Keyboard::Key::Numpad7: return Rml::Input::KI_NUMPAD7; + case sf::Keyboard::Key::Numpad8: return Rml::Input::KI_NUMPAD8; + case sf::Keyboard::Key::Numpad9: return Rml::Input::KI_NUMPAD9; + case sf::Keyboard::Key::Left: return Rml::Input::KI_LEFT; + case sf::Keyboard::Key::Right: return Rml::Input::KI_RIGHT; + case sf::Keyboard::Key::Up: return Rml::Input::KI_UP; + case sf::Keyboard::Key::Down: return Rml::Input::KI_DOWN; + case sf::Keyboard::Key::Add: return Rml::Input::KI_ADD; + case sfBackspace: return Rml::Input::KI_BACK; + case sf::Keyboard::Key::Delete: return Rml::Input::KI_DELETE; + case sf::Keyboard::Key::Divide: return Rml::Input::KI_DIVIDE; + case sf::Keyboard::Key::End: return Rml::Input::KI_END; + case sf::Keyboard::Key::Escape: return Rml::Input::KI_ESCAPE; + case sf::Keyboard::Key::F1: return Rml::Input::KI_F1; + case sf::Keyboard::Key::F2: return Rml::Input::KI_F2; + case sf::Keyboard::Key::F3: return Rml::Input::KI_F3; + case sf::Keyboard::Key::F4: return Rml::Input::KI_F4; + case sf::Keyboard::Key::F5: return Rml::Input::KI_F5; + case sf::Keyboard::Key::F6: return Rml::Input::KI_F6; + case sf::Keyboard::Key::F7: return Rml::Input::KI_F7; + case sf::Keyboard::Key::F8: return Rml::Input::KI_F8; + case sf::Keyboard::Key::F9: return Rml::Input::KI_F9; + case sf::Keyboard::Key::F10: return Rml::Input::KI_F10; + case sf::Keyboard::Key::F11: return Rml::Input::KI_F11; + case sf::Keyboard::Key::F12: return Rml::Input::KI_F12; + case sf::Keyboard::Key::F13: return Rml::Input::KI_F13; + case sf::Keyboard::Key::F14: return Rml::Input::KI_F14; + case sf::Keyboard::Key::F15: return Rml::Input::KI_F15; + case sf::Keyboard::Key::Home: return Rml::Input::KI_HOME; + case sf::Keyboard::Key::Insert: return Rml::Input::KI_INSERT; + case sf::Keyboard::Key::LControl: return Rml::Input::KI_LCONTROL; + case sf::Keyboard::Key::LShift: return Rml::Input::KI_LSHIFT; + case sf::Keyboard::Key::Multiply: return Rml::Input::KI_MULTIPLY; + case sf::Keyboard::Key::Pause: return Rml::Input::KI_PAUSE; + case sf::Keyboard::Key::RControl: return Rml::Input::KI_RCONTROL; + case sfEnter: return Rml::Input::KI_RETURN; + case sf::Keyboard::Key::RShift: return Rml::Input::KI_RSHIFT; + case sf::Keyboard::Key::Space: return Rml::Input::KI_SPACE; + case sf::Keyboard::Key::Subtract: return Rml::Input::KI_SUBTRACT; + case sf::Keyboard::Key::Tab: return Rml::Input::KI_TAB; + default: break; + } + // clang-format on + + return Rml::Input::KI_UNKNOWN; +} + +int RmlSFML::GetKeyModifierState() +{ + int modifiers = 0; + + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LShift) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RShift)) + modifiers |= Rml::Input::KM_SHIFT; + + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RControl)) + modifiers |= Rml::Input::KM_CTRL; + + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LAlt) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RAlt)) + modifiers |= Rml::Input::KM_ALT; + + return modifiers; +} diff --git a/vendor/rmlui_backend/RmlUi_Platform_SFML.h b/vendor/rmlui_backend/RmlUi_Platform_SFML.h new file mode 100644 index 0000000..9391e9b --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Platform_SFML.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class SystemInterface_SFML : public Rml::SystemInterface { +public: + SystemInterface_SFML(); + + // Optionally, provide or change the window to be used for setting the mouse cursors. + // @lifetime Any window provided here must be destroyed before the system interface. + // @lifetime The currently active window must stay alive until after the call to Rml::Shutdown. + void SetWindow(sf::RenderWindow* window); + + // -- Inherited from Rml::SystemInterface -- + + double GetElapsedTime() override; + + void SetMouseCursor(const Rml::String& cursor_name) override; + + void SetClipboardText(const Rml::String& text) override; + void GetClipboardText(Rml::String& text) override; + +private: + sf::Clock timer; + sf::RenderWindow* window = nullptr; + + struct Cursors { + Cursors(); + sf::Cursor cursor_default; + sf::Cursor cursor_move; + sf::Cursor cursor_pointer; + sf::Cursor cursor_resize; + sf::Cursor cursor_cross; + sf::Cursor cursor_text; + sf::Cursor cursor_unavailable; + }; + Rml::UniquePtr cursors; +}; + +/** + Optional helper functions for the SFML platform. + */ +namespace RmlSFML { + +// Applies input on the context based on the given SFML event. +// @return True if the event is still propagating, false if it was handled by the context. +bool InputHandler(Rml::Context* context, const sf::Event& ev); + +// Converts the SFML key to RmlUi key. +Rml::Input::KeyIdentifier ConvertKey(sf::Keyboard::Key sfml_key); + +// Returns the active RmlUi key modifier state. +int GetKeyModifierState(); + +} // namespace RmlSFML diff --git a/vendor/rmlui_backend/RmlUi_Platform_Win32.cpp b/vendor/rmlui_backend/RmlUi_Platform_Win32.cpp new file mode 100644 index 0000000..2b0b121 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Platform_Win32.cpp @@ -0,0 +1,730 @@ +#include "RmlUi_Platform_Win32.h" +#include "RmlUi_Include_Windows.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// Used to interact with the input method editor (IME). Users of MinGW should manually link to this. +#ifdef _MSC_VER + #pragma comment(lib, "imm32") +#endif + +SystemInterface_Win32::SystemInterface_Win32() +{ + LARGE_INTEGER time_ticks_per_second; + QueryPerformanceFrequency(&time_ticks_per_second); + QueryPerformanceCounter(&time_startup); + + time_frequency = 1.0 / (double)time_ticks_per_second.QuadPart; + + // Load cursors + cursor_default = LoadCursor(nullptr, IDC_ARROW); + cursor_move = LoadCursor(nullptr, IDC_SIZEALL); + cursor_pointer = LoadCursor(nullptr, IDC_HAND); + cursor_resize = LoadCursor(nullptr, IDC_SIZENWSE); + cursor_cross = LoadCursor(nullptr, IDC_CROSS); + cursor_text = LoadCursor(nullptr, IDC_IBEAM); + cursor_unavailable = LoadCursor(nullptr, IDC_NO); +} + +SystemInterface_Win32::~SystemInterface_Win32() = default; + +void SystemInterface_Win32::SetWindow(HWND in_window_handle) +{ + window_handle = in_window_handle; +} + +double SystemInterface_Win32::GetElapsedTime() +{ + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + + return double(counter.QuadPart - time_startup.QuadPart) * time_frequency; +} + +void SystemInterface_Win32::SetMouseCursor(const Rml::String& cursor_name) +{ + if (window_handle) + { + HCURSOR cursor_handle = nullptr; + if (cursor_name.empty() || cursor_name == "arrow") + cursor_handle = cursor_default; + else if (cursor_name == "move") + cursor_handle = cursor_move; + else if (cursor_name == "pointer") + cursor_handle = cursor_pointer; + else if (cursor_name == "resize") + cursor_handle = cursor_resize; + else if (cursor_name == "cross") + cursor_handle = cursor_cross; + else if (cursor_name == "text") + cursor_handle = cursor_text; + else if (cursor_name == "unavailable") + cursor_handle = cursor_unavailable; + else if (Rml::StringUtilities::StartsWith(cursor_name, "rmlui-scroll")) + cursor_handle = cursor_move; + + if (cursor_handle) + { + SetCursor(cursor_handle); + SetClassLongPtrA(window_handle, GCLP_HCURSOR, (LONG_PTR)cursor_handle); + } + } +} + +void SystemInterface_Win32::SetClipboardText(const Rml::String& text_utf8) +{ + if (window_handle) + { + if (!OpenClipboard(window_handle)) + return; + + EmptyClipboard(); + + const std::wstring text = RmlWin32::ConvertToUTF16(text_utf8); + const size_t size = sizeof(wchar_t) * (text.size() + 1); + + HGLOBAL clipboard_data = GlobalAlloc(GMEM_FIXED, size); + memcpy(clipboard_data, text.data(), size); + + if (SetClipboardData(CF_UNICODETEXT, clipboard_data) == nullptr) + { + CloseClipboard(); + GlobalFree(clipboard_data); + } + else + CloseClipboard(); + } +} + +void SystemInterface_Win32::GetClipboardText(Rml::String& text) +{ + if (window_handle) + { + if (!OpenClipboard(window_handle)) + return; + + HANDLE clipboard_data = GetClipboardData(CF_UNICODETEXT); + if (clipboard_data == nullptr) + { + CloseClipboard(); + return; + } + + const wchar_t* clipboard_text = (const wchar_t*)GlobalLock(clipboard_data); + if (clipboard_text) + text = RmlWin32::ConvertToUTF8(clipboard_text); + GlobalUnlock(clipboard_data); + + CloseClipboard(); + } +} + +void SystemInterface_Win32::ActivateKeyboard(Rml::Vector2f caret_position, float line_height) +{ + HIMC himc = ImmGetContext(window_handle); + if (himc == NULL) + return; + + constexpr LONG BottomMargin = 2; + + // Adjust the position of the input method editor (IME) to the caret. + const LONG x = static_cast(caret_position.x); + const LONG y = static_cast(caret_position.y); + const LONG w = 1; + const LONG h = static_cast(line_height) + BottomMargin; + + COMPOSITIONFORM comp = {}; + comp.dwStyle = CFS_FORCE_POSITION; + comp.ptCurrentPos = {x, y}; + ImmSetCompositionWindow(himc, &comp); + + CANDIDATEFORM cand = {}; + cand.dwStyle = CFS_EXCLUDE; + cand.ptCurrentPos = {x, y}; + cand.rcArea = {x, y, x + w, y + h}; + ImmSetCandidateWindow(himc, &cand); + + ImmReleaseContext(window_handle, himc); +} + +Rml::String RmlWin32::ConvertToUTF8(const std::wstring& wstr) +{ + const int count = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL); + Rml::String str(count, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &str[0], count, NULL, NULL); + return str; +} + +std::wstring RmlWin32::ConvertToUTF16(const Rml::String& str) +{ + const int count = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), NULL, 0); + std::wstring wstr(count, 0); + MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), &wstr[0], count); + return wstr; +} + +static int IMEGetCursorPosition(HIMC context) +{ + return ImmGetCompositionString(context, GCS_CURSORPOS, nullptr, 0); +} + +static std::wstring IMEGetCompositionString(HIMC context, bool finalize) +{ + DWORD type = finalize ? GCS_RESULTSTR : GCS_COMPSTR; + int len_bytes = ImmGetCompositionString(context, type, nullptr, 0); + + if (len_bytes <= 0) + return {}; + + int len_chars = len_bytes / sizeof(TCHAR); + Rml::UniquePtr buffer(new TCHAR[len_chars + 1]); + ImmGetCompositionString(context, type, buffer.get(), len_bytes); + +#ifdef UNICODE + return std::wstring(buffer.get(), len_chars); +#else + return RmlWin32::ConvertToUTF16(Rml::String(buffer.get(), len_chars)); +#endif +} + +static void IMECompleteComposition(HWND window_handle) +{ + if (HIMC context = ImmGetContext(window_handle)) + { + ImmNotifyIME(context, NI_COMPOSITIONSTR, CPS_COMPLETE, NULL); + ImmReleaseContext(window_handle, context); + } +} + +bool RmlWin32::WindowProcedure(Rml::Context* context, TextInputMethodEditor_Win32& text_input_method_editor, HWND window_handle, UINT message, + WPARAM w_param, LPARAM l_param) +{ + if (!context) + return true; + + static bool tracking_mouse_leave = false; + + // If the user tries to interact with the window by using the mouse in any way, end the + // composition by committing the current string. This behavior is identical to other + // browsers and is expected, yet, Windows does not send any IME messages in such a case. + if (text_input_method_editor.IsComposing() && message >= WM_LBUTTONDOWN && message <= WM_MBUTTONDBLCLK) + IMECompleteComposition(window_handle); + + bool result = true; + + switch (message) + { + case WM_LBUTTONDOWN: + result = context->ProcessMouseButtonDown(0, RmlWin32::GetKeyModifierState()); + SetCapture(window_handle); + break; + case WM_LBUTTONUP: + ReleaseCapture(); + result = context->ProcessMouseButtonUp(0, RmlWin32::GetKeyModifierState()); + break; + case WM_RBUTTONDOWN: result = context->ProcessMouseButtonDown(1, RmlWin32::GetKeyModifierState()); break; + case WM_RBUTTONUP: result = context->ProcessMouseButtonUp(1, RmlWin32::GetKeyModifierState()); break; + case WM_MBUTTONDOWN: result = context->ProcessMouseButtonDown(2, RmlWin32::GetKeyModifierState()); break; + case WM_MBUTTONUP: result = context->ProcessMouseButtonUp(2, RmlWin32::GetKeyModifierState()); break; + case WM_MOUSEMOVE: + result = context->ProcessMouseMove(static_cast((short)LOWORD(l_param)), static_cast((short)HIWORD(l_param)), + RmlWin32::GetKeyModifierState()); + + if (!tracking_mouse_leave) + { + TRACKMOUSEEVENT tme = {}; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = window_handle; + tracking_mouse_leave = TrackMouseEvent(&tme); + } + break; + case WM_MOUSEWHEEL: + result = context->ProcessMouseWheel(static_cast((short)HIWORD(w_param)) / static_cast(-WHEEL_DELTA), + RmlWin32::GetKeyModifierState()); + break; + case WM_MOUSELEAVE: + result = context->ProcessMouseLeave(); + tracking_mouse_leave = false; + break; + case WM_KEYDOWN: result = context->ProcessKeyDown(RmlWin32::ConvertKey((int)w_param), RmlWin32::GetKeyModifierState()); break; + case WM_KEYUP: result = context->ProcessKeyUp(RmlWin32::ConvertKey((int)w_param), RmlWin32::GetKeyModifierState()); break; + case WM_CHAR: + { + static wchar_t first_u16_code_unit = 0; + + const wchar_t c = (wchar_t)w_param; + Rml::Character character = (Rml::Character)c; + + // Windows sends two-wide characters as two messages. + if (c >= 0xD800 && c < 0xDC00) + { + // First 16-bit code unit of a two-wide character. + first_u16_code_unit = c; + } + else + { + if (c >= 0xDC00 && c < 0xE000 && first_u16_code_unit != 0) + { + // Second 16-bit code unit of a two-wide character. + Rml::String utf8 = ConvertToUTF8(std::wstring{first_u16_code_unit, c}); + character = Rml::StringUtilities::ToCharacter(utf8.data(), utf8.data() + utf8.size()); + } + else if (c == '\r') + { + // Windows sends new-lines as carriage returns, convert to endlines. + character = (Rml::Character)'\n'; + } + + first_u16_code_unit = 0; + + // Only send through printable characters. + if (((char32_t)character >= 32 || character == (Rml::Character)'\n') && character != (Rml::Character)127) + result = context->ProcessTextInput(character); + } + } + break; + case WM_IME_STARTCOMPOSITION: + text_input_method_editor.StartComposition(); + // Prevent the native composition window from appearing by capturing the message. + result = false; + break; + case WM_IME_ENDCOMPOSITION: + if (text_input_method_editor.IsComposing()) + text_input_method_editor.ConfirmComposition(Rml::StringView()); + break; + case WM_IME_COMPOSITION: + { + HIMC imm_context = ImmGetContext(window_handle); + + // Not every IME starts a composition. + if (!text_input_method_editor.IsComposing()) + text_input_method_editor.StartComposition(); + + if (!!(l_param & GCS_CURSORPOS)) + { + // The cursor position is the wchar_t offset in the composition string. Because we + // work with UTF-8 and not UTF-16, we will have to convert the character offset. + int cursor_pos = IMEGetCursorPosition(imm_context); + + std::wstring composition = IMEGetCompositionString(imm_context, false); + Rml::String converted = RmlWin32::ConvertToUTF8(composition.substr(0, cursor_pos)); + cursor_pos = (int)Rml::StringUtilities::LengthUTF8(converted); + + text_input_method_editor.SetCursorPosition(cursor_pos, true); + } + + if (!!(l_param & CS_NOMOVECARET)) + { + // Suppress the cursor position update. CS_NOMOVECARET is always a part of a more + // complex message which means that the cursor is updated from a different event. + text_input_method_editor.SetCursorPosition(-1, false); + } + + if (!!(l_param & GCS_RESULTSTR)) + { + std::wstring composition = IMEGetCompositionString(imm_context, true); + text_input_method_editor.ConfirmComposition(RmlWin32::ConvertToUTF8(composition)); + } + + if (!!(l_param & GCS_COMPSTR)) + { + std::wstring composition = IMEGetCompositionString(imm_context, false); + text_input_method_editor.SetComposition(RmlWin32::ConvertToUTF8(composition)); + } + + // The composition has been canceled. + if (!l_param) + text_input_method_editor.CancelComposition(); + + ImmReleaseContext(window_handle, imm_context); + } + break; + case WM_IME_CHAR: + case WM_IME_REQUEST: + // Ignore WM_IME_CHAR and WM_IME_REQUEST to block the system from appending the composition string. + result = false; + break; + default: break; + } + + return result; +} + +int RmlWin32::GetKeyModifierState() +{ + int key_modifier_state = 0; + + if (GetKeyState(VK_CAPITAL) & 1) + key_modifier_state |= Rml::Input::KM_CAPSLOCK; + + if (GetKeyState(VK_NUMLOCK) & 1) + key_modifier_state |= Rml::Input::KM_NUMLOCK; + + if (HIWORD(GetKeyState(VK_SHIFT)) & 1) + key_modifier_state |= Rml::Input::KM_SHIFT; + + if (HIWORD(GetKeyState(VK_CONTROL)) & 1) + key_modifier_state |= Rml::Input::KM_CTRL; + + if (HIWORD(GetKeyState(VK_MENU)) & 1) + key_modifier_state |= Rml::Input::KM_ALT; + + return key_modifier_state; +} + +// These are defined in winuser.h of MinGW 64 but are missing from MinGW 32 +// Visual Studio has them by default +#if defined(__MINGW32__) && !defined(__MINGW64__) + #define VK_OEM_NEC_EQUAL 0x92 + #define VK_OEM_FJ_JISHO 0x92 + #define VK_OEM_FJ_MASSHOU 0x93 + #define VK_OEM_FJ_TOUROKU 0x94 + #define VK_OEM_FJ_LOYA 0x95 + #define VK_OEM_FJ_ROYA 0x96 + #define VK_OEM_AX 0xE1 + #define VK_ICO_HELP 0xE3 + #define VK_ICO_00 0xE4 + #define VK_ICO_CLEAR 0xE6 +#endif // !defined(__MINGW32__) || defined(__MINGW64__) + +Rml::Input::KeyIdentifier RmlWin32::ConvertKey(int win32_key_code) +{ + // clang-format off + switch (win32_key_code) + { + case 'A': return Rml::Input::KI_A; + case 'B': return Rml::Input::KI_B; + case 'C': return Rml::Input::KI_C; + case 'D': return Rml::Input::KI_D; + case 'E': return Rml::Input::KI_E; + case 'F': return Rml::Input::KI_F; + case 'G': return Rml::Input::KI_G; + case 'H': return Rml::Input::KI_H; + case 'I': return Rml::Input::KI_I; + case 'J': return Rml::Input::KI_J; + case 'K': return Rml::Input::KI_K; + case 'L': return Rml::Input::KI_L; + case 'M': return Rml::Input::KI_M; + case 'N': return Rml::Input::KI_N; + case 'O': return Rml::Input::KI_O; + case 'P': return Rml::Input::KI_P; + case 'Q': return Rml::Input::KI_Q; + case 'R': return Rml::Input::KI_R; + case 'S': return Rml::Input::KI_S; + case 'T': return Rml::Input::KI_T; + case 'U': return Rml::Input::KI_U; + case 'V': return Rml::Input::KI_V; + case 'W': return Rml::Input::KI_W; + case 'X': return Rml::Input::KI_X; + case 'Y': return Rml::Input::KI_Y; + case 'Z': return Rml::Input::KI_Z; + + case '0': return Rml::Input::KI_0; + case '1': return Rml::Input::KI_1; + case '2': return Rml::Input::KI_2; + case '3': return Rml::Input::KI_3; + case '4': return Rml::Input::KI_4; + case '5': return Rml::Input::KI_5; + case '6': return Rml::Input::KI_6; + case '7': return Rml::Input::KI_7; + case '8': return Rml::Input::KI_8; + case '9': return Rml::Input::KI_9; + + case VK_BACK: return Rml::Input::KI_BACK; + case VK_TAB: return Rml::Input::KI_TAB; + + case VK_CLEAR: return Rml::Input::KI_CLEAR; + case VK_RETURN: return Rml::Input::KI_RETURN; + + case VK_PAUSE: return Rml::Input::KI_PAUSE; + case VK_CAPITAL: return Rml::Input::KI_CAPITAL; + + case VK_KANA: return Rml::Input::KI_KANA; + //case VK_HANGUL: return Rml::Input::KI_HANGUL; /* overlaps with VK_KANA */ + case VK_JUNJA: return Rml::Input::KI_JUNJA; + case VK_FINAL: return Rml::Input::KI_FINAL; + case VK_HANJA: return Rml::Input::KI_HANJA; + //case VK_KANJI: return Rml::Input::KI_KANJI; /* overlaps with VK_HANJA */ + + case VK_ESCAPE: return Rml::Input::KI_ESCAPE; + + case VK_CONVERT: return Rml::Input::KI_CONVERT; + case VK_NONCONVERT: return Rml::Input::KI_NONCONVERT; + case VK_ACCEPT: return Rml::Input::KI_ACCEPT; + case VK_MODECHANGE: return Rml::Input::KI_MODECHANGE; + + case VK_SPACE: return Rml::Input::KI_SPACE; + case VK_PRIOR: return Rml::Input::KI_PRIOR; + case VK_NEXT: return Rml::Input::KI_NEXT; + case VK_END: return Rml::Input::KI_END; + case VK_HOME: return Rml::Input::KI_HOME; + case VK_LEFT: return Rml::Input::KI_LEFT; + case VK_UP: return Rml::Input::KI_UP; + case VK_RIGHT: return Rml::Input::KI_RIGHT; + case VK_DOWN: return Rml::Input::KI_DOWN; + case VK_SELECT: return Rml::Input::KI_SELECT; + case VK_PRINT: return Rml::Input::KI_PRINT; + case VK_EXECUTE: return Rml::Input::KI_EXECUTE; + case VK_SNAPSHOT: return Rml::Input::KI_SNAPSHOT; + case VK_INSERT: return Rml::Input::KI_INSERT; + case VK_DELETE: return Rml::Input::KI_DELETE; + case VK_HELP: return Rml::Input::KI_HELP; + + case VK_LWIN: return Rml::Input::KI_LWIN; + case VK_RWIN: return Rml::Input::KI_RWIN; + case VK_APPS: return Rml::Input::KI_APPS; + + case VK_SLEEP: return Rml::Input::KI_SLEEP; + + case VK_NUMPAD0: return Rml::Input::KI_NUMPAD0; + case VK_NUMPAD1: return Rml::Input::KI_NUMPAD1; + case VK_NUMPAD2: return Rml::Input::KI_NUMPAD2; + case VK_NUMPAD3: return Rml::Input::KI_NUMPAD3; + case VK_NUMPAD4: return Rml::Input::KI_NUMPAD4; + case VK_NUMPAD5: return Rml::Input::KI_NUMPAD5; + case VK_NUMPAD6: return Rml::Input::KI_NUMPAD6; + case VK_NUMPAD7: return Rml::Input::KI_NUMPAD7; + case VK_NUMPAD8: return Rml::Input::KI_NUMPAD8; + case VK_NUMPAD9: return Rml::Input::KI_NUMPAD9; + case VK_MULTIPLY: return Rml::Input::KI_MULTIPLY; + case VK_ADD: return Rml::Input::KI_ADD; + case VK_SEPARATOR: return Rml::Input::KI_SEPARATOR; + case VK_SUBTRACT: return Rml::Input::KI_SUBTRACT; + case VK_DECIMAL: return Rml::Input::KI_DECIMAL; + case VK_DIVIDE: return Rml::Input::KI_DIVIDE; + case VK_F1: return Rml::Input::KI_F1; + case VK_F2: return Rml::Input::KI_F2; + case VK_F3: return Rml::Input::KI_F3; + case VK_F4: return Rml::Input::KI_F4; + case VK_F5: return Rml::Input::KI_F5; + case VK_F6: return Rml::Input::KI_F6; + case VK_F7: return Rml::Input::KI_F7; + case VK_F8: return Rml::Input::KI_F8; + case VK_F9: return Rml::Input::KI_F9; + case VK_F10: return Rml::Input::KI_F10; + case VK_F11: return Rml::Input::KI_F11; + case VK_F12: return Rml::Input::KI_F12; + case VK_F13: return Rml::Input::KI_F13; + case VK_F14: return Rml::Input::KI_F14; + case VK_F15: return Rml::Input::KI_F15; + case VK_F16: return Rml::Input::KI_F16; + case VK_F17: return Rml::Input::KI_F17; + case VK_F18: return Rml::Input::KI_F18; + case VK_F19: return Rml::Input::KI_F19; + case VK_F20: return Rml::Input::KI_F20; + case VK_F21: return Rml::Input::KI_F21; + case VK_F22: return Rml::Input::KI_F22; + case VK_F23: return Rml::Input::KI_F23; + case VK_F24: return Rml::Input::KI_F24; + + case VK_NUMLOCK: return Rml::Input::KI_NUMLOCK; + case VK_SCROLL: return Rml::Input::KI_SCROLL; + + case VK_OEM_NEC_EQUAL: return Rml::Input::KI_OEM_NEC_EQUAL; + + //case VK_OEM_FJ_JISHO: return Rml::Input::KI_OEM_FJ_JISHO; /* overlaps with VK_OEM_NEC_EQUAL */ + case VK_OEM_FJ_MASSHOU: return Rml::Input::KI_OEM_FJ_MASSHOU; + case VK_OEM_FJ_TOUROKU: return Rml::Input::KI_OEM_FJ_TOUROKU; + case VK_OEM_FJ_LOYA: return Rml::Input::KI_OEM_FJ_LOYA; + case VK_OEM_FJ_ROYA: return Rml::Input::KI_OEM_FJ_ROYA; + + case VK_SHIFT: return Rml::Input::KI_LSHIFT; + case VK_CONTROL: return Rml::Input::KI_LCONTROL; + case VK_MENU: return Rml::Input::KI_LMENU; + + case VK_BROWSER_BACK: return Rml::Input::KI_BROWSER_BACK; + case VK_BROWSER_FORWARD: return Rml::Input::KI_BROWSER_FORWARD; + case VK_BROWSER_REFRESH: return Rml::Input::KI_BROWSER_REFRESH; + case VK_BROWSER_STOP: return Rml::Input::KI_BROWSER_STOP; + case VK_BROWSER_SEARCH: return Rml::Input::KI_BROWSER_SEARCH; + case VK_BROWSER_FAVORITES: return Rml::Input::KI_BROWSER_FAVORITES; + case VK_BROWSER_HOME: return Rml::Input::KI_BROWSER_HOME; + + case VK_VOLUME_MUTE: return Rml::Input::KI_VOLUME_MUTE; + case VK_VOLUME_DOWN: return Rml::Input::KI_VOLUME_DOWN; + case VK_VOLUME_UP: return Rml::Input::KI_VOLUME_UP; + case VK_MEDIA_NEXT_TRACK: return Rml::Input::KI_MEDIA_NEXT_TRACK; + case VK_MEDIA_PREV_TRACK: return Rml::Input::KI_MEDIA_PREV_TRACK; + case VK_MEDIA_STOP: return Rml::Input::KI_MEDIA_STOP; + case VK_MEDIA_PLAY_PAUSE: return Rml::Input::KI_MEDIA_PLAY_PAUSE; + case VK_LAUNCH_MAIL: return Rml::Input::KI_LAUNCH_MAIL; + case VK_LAUNCH_MEDIA_SELECT: return Rml::Input::KI_LAUNCH_MEDIA_SELECT; + case VK_LAUNCH_APP1: return Rml::Input::KI_LAUNCH_APP1; + case VK_LAUNCH_APP2: return Rml::Input::KI_LAUNCH_APP2; + + case VK_OEM_1: return Rml::Input::KI_OEM_1; + case VK_OEM_PLUS: return Rml::Input::KI_OEM_PLUS; + case VK_OEM_COMMA: return Rml::Input::KI_OEM_COMMA; + case VK_OEM_MINUS: return Rml::Input::KI_OEM_MINUS; + case VK_OEM_PERIOD: return Rml::Input::KI_OEM_PERIOD; + case VK_OEM_2: return Rml::Input::KI_OEM_2; + case VK_OEM_3: return Rml::Input::KI_OEM_3; + + case VK_OEM_4: return Rml::Input::KI_OEM_4; + case VK_OEM_5: return Rml::Input::KI_OEM_5; + case VK_OEM_6: return Rml::Input::KI_OEM_6; + case VK_OEM_7: return Rml::Input::KI_OEM_7; + case VK_OEM_8: return Rml::Input::KI_OEM_8; + + case VK_OEM_AX: return Rml::Input::KI_OEM_AX; + case VK_OEM_102: return Rml::Input::KI_OEM_102; + case VK_ICO_HELP: return Rml::Input::KI_ICO_HELP; + case VK_ICO_00: return Rml::Input::KI_ICO_00; + + case VK_PROCESSKEY: return Rml::Input::KI_PROCESSKEY; + + case VK_ICO_CLEAR: return Rml::Input::KI_ICO_CLEAR; + + case VK_ATTN: return Rml::Input::KI_ATTN; + case VK_CRSEL: return Rml::Input::KI_CRSEL; + case VK_EXSEL: return Rml::Input::KI_EXSEL; + case VK_EREOF: return Rml::Input::KI_EREOF; + case VK_PLAY: return Rml::Input::KI_PLAY; + case VK_ZOOM: return Rml::Input::KI_ZOOM; + case VK_PA1: return Rml::Input::KI_PA1; + case VK_OEM_CLEAR: return Rml::Input::KI_OEM_CLEAR; + } + // clang-format on + + return Rml::Input::KI_UNKNOWN; +} + +TextInputMethodEditor_Win32::TextInputMethodEditor_Win32() : + input_context(nullptr), composing(false), cursor_pos(-1), composition_range_start(0), composition_range_end(0) +{} + +void TextInputMethodEditor_Win32::OnActivate(Rml::TextInputContext* _input_context) +{ + input_context = _input_context; +} + +void TextInputMethodEditor_Win32::OnDeactivate(Rml::TextInputContext* _input_context) +{ + if (input_context == _input_context) + input_context = nullptr; +} + +void TextInputMethodEditor_Win32::OnDestroy(Rml::TextInputContext* _input_context) +{ + if (input_context == _input_context) + input_context = nullptr; +} + +bool TextInputMethodEditor_Win32::IsComposing() const +{ + return composing; +} + +void TextInputMethodEditor_Win32::StartComposition() +{ + RMLUI_ASSERT(!composing); + composing = true; +} + +void TextInputMethodEditor_Win32::EndComposition() +{ + if (input_context != nullptr) + input_context->SetCompositionRange(0, 0); + + RMLUI_ASSERT(composing); + composing = false; + + composition_range_start = 0; + composition_range_end = 0; +} + +void TextInputMethodEditor_Win32::CancelComposition() +{ + RMLUI_ASSERT(IsComposing()); + + if (input_context != nullptr) + { + // Purge the current composition string. + input_context->SetText(Rml::StringView(), composition_range_start, composition_range_end); + // Move the cursor back to where the composition began. + input_context->SetCursorPosition(composition_range_start); + } + + EndComposition(); +} + +void TextInputMethodEditor_Win32::SetComposition(Rml::StringView composition) +{ + RMLUI_ASSERT(IsComposing()); + + SetCompositionString(composition); + UpdateCursorPosition(); + + // Update the composition range only if the cursor can be moved around. Editors working with a single + // character (e.g., Hangul IME) should have no visual feedback; they use a selection range instead. + if (cursor_pos != -1 && input_context != nullptr) + input_context->SetCompositionRange(composition_range_start, composition_range_end); +} + +void TextInputMethodEditor_Win32::ConfirmComposition(Rml::StringView composition) +{ + RMLUI_ASSERT(IsComposing()); + + SetCompositionString(composition); + + if (input_context != nullptr) + { + input_context->SetCompositionRange(composition_range_start, composition_range_end); + input_context->CommitComposition(composition); + } + + // Move the cursor to the end of the string. + SetCursorPosition(composition_range_end - composition_range_start, true); + + EndComposition(); +} + +void TextInputMethodEditor_Win32::SetCursorPosition(int _cursor_pos, bool update) +{ + RMLUI_ASSERT(IsComposing()); + + cursor_pos = _cursor_pos; + + if (update) + UpdateCursorPosition(); +} + +void TextInputMethodEditor_Win32::SetCompositionString(Rml::StringView composition) +{ + if (input_context == nullptr) + return; + + // Retrieve the composition range if it is missing. + if (composition_range_start == 0 && composition_range_end == 0) + input_context->GetSelectionRange(composition_range_start, composition_range_end); + + input_context->SetText(composition, composition_range_start, composition_range_end); + + size_t length = Rml::StringUtilities::LengthUTF8(composition); + composition_range_end = composition_range_start + (int)length; +} + +void TextInputMethodEditor_Win32::UpdateCursorPosition() +{ + // Cursor position update happens before a composition is set; ignore this event. + if (input_context == nullptr || (composition_range_start == 0 && composition_range_end == 0)) + return; + + if (cursor_pos != -1) + { + int position = composition_range_start + cursor_pos; + input_context->SetCursorPosition(position); + } + else + { + // If the API reports no cursor position, select the entire composition string for a better UX. + input_context->SetSelectionRange(composition_range_start, composition_range_end); + } +} diff --git a/vendor/rmlui_backend/RmlUi_Platform_Win32.h b/vendor/rmlui_backend/RmlUi_Platform_Win32.h new file mode 100644 index 0000000..b516e2e --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Platform_Win32.h @@ -0,0 +1,119 @@ +#pragma once + +#include "RmlUi_Include_Windows.h" +#include +#include +#include +#include +#include +#include + +class SystemInterface_Win32 : public Rml::SystemInterface { +public: + SystemInterface_Win32(); + ~SystemInterface_Win32(); + + // Optionally, provide or change the window to be used for setting the mouse cursor, clipboard text and IME position. + void SetWindow(HWND window_handle); + + // -- Inherited from Rml::SystemInterface -- + + double GetElapsedTime() override; + + void SetMouseCursor(const Rml::String& cursor_name) override; + + void SetClipboardText(const Rml::String& text) override; + void GetClipboardText(Rml::String& text) override; + + void ActivateKeyboard(Rml::Vector2f caret_position, float line_height) override; + +private: + HWND window_handle = nullptr; + + double time_frequency = {}; + LARGE_INTEGER time_startup = {}; + + HCURSOR cursor_default = nullptr; + HCURSOR cursor_move = nullptr; + HCURSOR cursor_pointer = nullptr; + HCURSOR cursor_resize = nullptr; + HCURSOR cursor_cross = nullptr; + HCURSOR cursor_text = nullptr; + HCURSOR cursor_unavailable = nullptr; +}; + +class TextInputMethodEditor_Win32; + +/** + Optional helper functions for the Win32 platform. + */ +namespace RmlWin32 { + +// Convenience helpers for converting between RmlUi strings (UTF-8) and Windows strings (UTF-16). +Rml::String ConvertToUTF8(const std::wstring& wstr); +std::wstring ConvertToUTF16(const Rml::String& str); + +// Window event handler to submit default input behavior to the context. +// @return True if the event is still propagating, false if it was handled by the context. +bool WindowProcedure(Rml::Context* context, TextInputMethodEditor_Win32& text_input_method_editor, HWND window_handle, UINT message, WPARAM w_param, + LPARAM l_param); + +// Converts the key from Win32 key code to RmlUi key. +Rml::Input::KeyIdentifier ConvertKey(int win32_key_code); + +// Returns the active RmlUi key modifier state. +int GetKeyModifierState(); + +} // namespace RmlWin32 + +/** + Custom backend implementation of TextInputHandler to handle the system's Input Method Editor (IME). + This version supports only one active text input context. + */ +class TextInputMethodEditor_Win32 final : public Rml::TextInputHandler { +public: + TextInputMethodEditor_Win32(); + + void OnActivate(Rml::TextInputContext* input_context) override; + void OnDeactivate(Rml::TextInputContext* input_context) override; + void OnDestroy(Rml::TextInputContext* input_context) override; + + /// Check that a composition is currently active. + /// @return True if we are composing, false otherwise. + bool IsComposing() const; + + void StartComposition(); + void CancelComposition(); + + /// Set the composition string. + /// @param[in] composition A string to be set. + void SetComposition(Rml::StringView composition); + + /// End the current composition by confirming the composition string. + /// @param[in] composition A string to confirm. + void ConfirmComposition(Rml::StringView composition); + + /// Set the cursor position within the composition. + /// @param[in] cursor_pos A character position of the cursor within the composition string. + /// @param[in] update Update the cursor position within active input contexts. + void SetCursorPosition(int cursor_pos, bool update); + +private: + void EndComposition(); + void SetCompositionString(Rml::StringView composition); + + void UpdateCursorPosition(); + +private: + // An actively used text input method context. + Rml::TextInputContext* input_context; + + // A flag to mark a composition is currently active. + bool composing; + // Character position of the cursor in the composition string. + int cursor_pos; + + // Composition range (character position) relative to the text input value. + int composition_range_start; + int composition_range_end; +}; diff --git a/vendor/rmlui_backend/RmlUi_Platform_X11.cpp b/vendor/rmlui_backend/RmlUi_Platform_X11.cpp new file mode 100644 index 0000000..4d4061f --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Platform_X11.cpp @@ -0,0 +1,610 @@ +#include "RmlUi_Platform_X11.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAS_X11XKBLIB + #include +#endif // HAS_X11XKBLIB +#include + +static Rml::Character GetCharacterCode(Rml::Input::KeyIdentifier key_identifier, int key_modifier_state); + +SystemInterface_X11::SystemInterface_X11(Display* display) : display(display) +{ + // Create cursors + cursor_default = XCreateFontCursor(display, XC_left_ptr); + cursor_move = XCreateFontCursor(display, XC_fleur); + cursor_pointer = XCreateFontCursor(display, XC_hand1); + cursor_resize = XCreateFontCursor(display, XC_sizing); + cursor_cross = XCreateFontCursor(display, XC_crosshair); + cursor_text = XCreateFontCursor(display, XC_xterm); + cursor_unavailable = XCreateFontCursor(display, XC_X_cursor); + + // For copy & paste functions + UTF8_atom = XInternAtom(display, "UTF8_STRING", 1); + XSEL_DATA_atom = XInternAtom(display, "XSEL_DATA", 0); + CLIPBOARD_atom = XInternAtom(display, "CLIPBOARD", 0); + TARGETS_atom = XInternAtom(display, "TARGETS", 0); + TEXT_atom = XInternAtom(display, "TEXT", 0); + + gettimeofday(&start_time, nullptr); +} + +void SystemInterface_X11::SetWindow(Window in_window) +{ + window = in_window; +} + +bool SystemInterface_X11::HandleSelectionRequest(const XEvent& ev) +{ + if (ev.type == SelectionRequest && XGetSelectionOwner(display, CLIPBOARD_atom) == window && ev.xselectionrequest.selection == CLIPBOARD_atom) + { + XCopy(clipboard_text, ev); + return false; + } + return true; +} + +double SystemInterface_X11::GetElapsedTime() +{ + struct timeval now; + + gettimeofday(&now, nullptr); + + double sec = now.tv_sec - start_time.tv_sec; + double usec = now.tv_usec - start_time.tv_usec; + double result = sec + (usec / 1000000.0); + + return result; +} + +void SystemInterface_X11::SetMouseCursor(const Rml::String& cursor_name) +{ + if (display && window) + { + Cursor cursor_handle = 0; + if (cursor_name.empty() || cursor_name == "arrow") + cursor_handle = cursor_default; + else if (cursor_name == "move") + cursor_handle = cursor_move; + else if (cursor_name == "pointer") + cursor_handle = cursor_pointer; + else if (cursor_name == "resize") + cursor_handle = cursor_resize; + else if (cursor_name == "cross") + cursor_handle = cursor_cross; + else if (cursor_name == "text") + cursor_handle = cursor_text; + else if (cursor_name == "rmlui-scroll-idle") + cursor_handle = cursor_move; + else if (cursor_name == "rmlui-scroll-up") + cursor_handle = cursor_move; + else if (cursor_name == "rmlui-scroll-down") + cursor_handle = cursor_move; + else if (cursor_name == "unavailable") + cursor_handle = cursor_unavailable; + + if (cursor_handle) + { + XDefineCursor(display, window, cursor_handle); + } + } +} + +void SystemInterface_X11::SetClipboardText(const Rml::String& text) +{ + clipboard_text = text; + XSetSelectionOwner(display, CLIPBOARD_atom, window, 0); +} + +void SystemInterface_X11::GetClipboardText(Rml::String& text) +{ + if (XGetSelectionOwner(display, CLIPBOARD_atom) != window) + { + if (!UTF8_atom || !XPaste(UTF8_atom, text)) + { + // fallback + XPaste(XA_STRING_atom, text); + } + } + else + { + text = clipboard_text; + } +} + +void SystemInterface_X11::XCopy(const Rml::String& clipboard_data, const XEvent& event) +{ + Atom format = (UTF8_atom ? UTF8_atom : XA_STRING_atom); + + XSelectionEvent ev = { + SelectionNotify, // the event type that will be sent to the requestor + 0, // serial + 0, // send_event + event.xselectionrequest.display, event.xselectionrequest.requestor, event.xselectionrequest.selection, event.xselectionrequest.target, + event.xselectionrequest.property, + 0 // time + }; + + int retval = 0; + if (ev.target == TARGETS_atom) + { + retval = XChangeProperty(ev.display, ev.requestor, ev.property, XA_atom, 32, PropModeReplace, (unsigned char*)&format, 1); + } + else if (ev.target == XA_STRING_atom || ev.target == TEXT_atom) + { + retval = XChangeProperty(ev.display, ev.requestor, ev.property, XA_STRING_atom, 8, PropModeReplace, (unsigned char*)clipboard_data.c_str(), + clipboard_data.size()); + } + else if (ev.target == UTF8_atom) + { + retval = XChangeProperty(ev.display, ev.requestor, ev.property, UTF8_atom, 8, PropModeReplace, (unsigned char*)clipboard_data.c_str(), + clipboard_data.size()); + } + else + { + ev.property = 0; + } + + if ((retval & 2) == 0) + { + // Notify the requestor that clipboard data is available + XSendEvent(display, ev.requestor, 0, 0, (XEvent*)&ev); + } +} + +bool SystemInterface_X11::XPaste(Atom target_atom, Rml::String& clipboard_data) +{ + XEvent ev; + + // A SelectionRequest event will be sent to the clipboard owner, which should respond with SelectionNotify + XConvertSelection(display, CLIPBOARD_atom, target_atom, XSEL_DATA_atom, window, CurrentTime); + XSync(display, 0); + XNextEvent(display, &ev); + + if (ev.type == SelectionNotify) + { + if (ev.xselection.property == 0) + { + // If no owner for the specified selection exists, the X server generates + // a SelectionNotify event with property None (0). + return false; + } + if (ev.xselection.selection == CLIPBOARD_atom) + { + int actual_format; + unsigned long bytes_after, nitems; + char* prop = nullptr; + Atom actual_type; + XGetWindowProperty(ev.xselection.display, ev.xselection.requestor, ev.xselection.property, + 0L, // offset + (~0L), // length + 0, // delete? + AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, (unsigned char**)&prop); + + if (actual_type == UTF8_atom || actual_type == XA_STRING_atom) + { + clipboard_data = Rml::String(prop, prop + nitems); + XFree(prop); + } + + XDeleteProperty(ev.xselection.display, ev.xselection.requestor, ev.xselection.property); + return true; + } + } + + return false; +} + +bool RmlX11::HandleInputEvent(Rml::Context* context, Display* display, const XEvent& ev) +{ + switch (ev.type) + { + case ButtonPress: + { + switch (ev.xbutton.button) + { + case Button1: + case Button2: + case Button3: return context->ProcessMouseButtonDown(ConvertMouseButton(ev.xbutton.button), RmlX11::GetKeyModifierState(ev.xbutton.state)); + case Button4: return context->ProcessMouseWheel(-1, RmlX11::GetKeyModifierState(ev.xbutton.state)); + case Button5: return context->ProcessMouseWheel(1, RmlX11::GetKeyModifierState(ev.xbutton.state)); + default: return true; + } + } + break; + case ButtonRelease: + { + switch (ev.xbutton.button) + { + case Button1: + case Button2: + case Button3: return context->ProcessMouseButtonUp(ConvertMouseButton(ev.xbutton.button), RmlX11::GetKeyModifierState(ev.xbutton.state)); + default: return true; + } + } + break; + case MotionNotify: + { + return context->ProcessMouseMove(ev.xmotion.x, ev.xmotion.y, RmlX11::GetKeyModifierState(ev.xmotion.state)); + } + break; + case LeaveNotify: + { + return context->ProcessMouseLeave(); + } + break; + case KeyPress: + { + Rml::Input::KeyIdentifier key_identifier = RmlX11::ConvertKey(display, ev.xkey.keycode); + const int key_modifier_state = RmlX11::GetKeyModifierState(ev.xkey.state); + + bool propagates = true; + + if (key_identifier != Rml::Input::KI_UNKNOWN) + propagates = context->ProcessKeyDown(key_identifier, key_modifier_state); + + Rml::Character character = GetCharacterCode(key_identifier, key_modifier_state); + if (character != Rml::Character::Null && !(key_modifier_state & Rml::Input::KM_CTRL)) + propagates &= context->ProcessTextInput(character); + + return propagates; + } + break; + case KeyRelease: + { + Rml::Input::KeyIdentifier key_identifier = RmlX11::ConvertKey(display, ev.xkey.keycode); + const int key_modifier_state = RmlX11::GetKeyModifierState(ev.xkey.state); + + bool propagates = true; + + if (key_identifier != Rml::Input::KI_UNKNOWN) + propagates = context->ProcessKeyUp(key_identifier, key_modifier_state); + + return propagates; + } + break; + default: break; + } + + return true; +} + +int RmlX11::GetKeyModifierState(int x11_state) +{ + int key_modifier_state = 0; + + if (x11_state & ShiftMask) + key_modifier_state |= Rml::Input::KM_SHIFT; + + if (x11_state & LockMask) + key_modifier_state |= Rml::Input::KM_CAPSLOCK; + + if (x11_state & ControlMask) + key_modifier_state |= Rml::Input::KM_CTRL; + + if (x11_state & Mod5Mask) + key_modifier_state |= Rml::Input::KM_ALT; + + if (x11_state & Mod2Mask) + key_modifier_state |= Rml::Input::KM_NUMLOCK; + + return key_modifier_state; +} + +/** + X11 Key data used for key code conversion. + */ +struct XKeyData { +#ifdef HAS_X11XKBLIB + bool has_xkblib = false; +#endif // HAS_X11XKBLIB + + bool initialized = false; + int min_keycode = 0, max_keycode = 0, keysyms_per_keycode = 0; + KeySym* x11_key_mapping = nullptr; +}; + +// Get the key data, which is initialized and filled on the first fetch. +static const XKeyData& GetXKeyData(Display* display) +{ + RMLUI_ASSERT(display); + static XKeyData data; + + if (!data.initialized) + { + data.initialized = true; + +#ifdef HAS_X11XKBLIB + int opcode_rtrn = -1; + int event_rtrn = -1; + int error_rtrn = -1; + int major_in_out = -1; + int minor_in_out = -1; + + // Xkb extension may not exist in the server. This checks for its existence and initializes the extension if available. + data.has_xkblib = XkbQueryExtension(display, &opcode_rtrn, &event_rtrn, &error_rtrn, &major_in_out, &minor_in_out); + + // if Xkb isn't available, fall back to using XGetKeyboardMapping, which may occur if RmlUi is compiled with Xkb support but the server + // doesn't support it. This occurs with older X11 servers or virtual framebuffers such as x11vnc server. + if (!data.has_xkblib) +#endif // HAS_X11XKBLIB + { + XDisplayKeycodes(display, &data.min_keycode, &data.max_keycode); + + data.x11_key_mapping = XGetKeyboardMapping(display, data.min_keycode, data.max_keycode + 1 - data.min_keycode, &data.keysyms_per_keycode); + } + } + + return data; +} + +Rml::Input::KeyIdentifier RmlX11::ConvertKey(Display* display, unsigned int x11_key_code) +{ + RMLUI_ASSERT(display); + const XKeyData& key_data = GetXKeyData(display); + + const int group_index = 0; // this is always 0 for our limited example + KeySym sym = {}; + +#ifdef HAS_X11XKBLIB + if (key_data.has_xkblib) + { + sym = XkbKeycodeToKeysym(display, x11_key_code, 0, group_index); + } + else +#endif // HAS_X11XKBLIB + { + KeySym sym_full = key_data.x11_key_mapping[(x11_key_code - key_data.min_keycode) * key_data.keysyms_per_keycode + group_index]; + + KeySym lower_sym, upper_sym; + XConvertCase(sym_full, &lower_sym, &upper_sym); + sym = lower_sym; + } + + // clang-format off + switch (sym & 0xFF) + { + case XK_BackSpace & 0xFF: return Rml::Input::KI_BACK; + case XK_Tab & 0xFF: return Rml::Input::KI_TAB; + case XK_Clear & 0xFF: return Rml::Input::KI_CLEAR; + case XK_Return & 0xFF: return Rml::Input::KI_RETURN; + case XK_Pause & 0xFF: return Rml::Input::KI_PAUSE; + case XK_Scroll_Lock & 0xFF: return Rml::Input::KI_SCROLL; + case XK_Escape & 0xFF: return Rml::Input::KI_ESCAPE; + case XK_Delete & 0xFF: return Rml::Input::KI_DELETE; + + case XK_Kanji & 0xFF: return Rml::Input::KI_KANJI; + // case XK_Muhenkan & 0xFF: return Rml::Input::; /* Cancel Conversion */ + // case XK_Henkan_Mode & 0xFF: return Rml::Input::; /* Start/Stop Conversion */ + // case XK_Henkan & 0xFF: return Rml::Input::; /* Alias for Henkan_Mode */ + // case XK_Romaji & 0xFF: return Rml::Input::; /* to Romaji */ + // case XK_Hiragana & 0xFF: return Rml::Input::; /* to Hiragana */ + // case XK_Katakana & 0xFF: return Rml::Input::; /* to Katakana */ + // case XK_Hiragana_Katakana & 0xFF: return Rml::Input::; /* Hiragana/Katakana toggle */ + // case XK_Zenkaku & 0xFF: return Rml::Input::; /* to Zenkaku */ + // case XK_Hankaku & 0xFF: return Rml::Input::; /* to Hankaku */ + // case XK_Zenkaku_Hankaku & 0xFF: return Rml::Input::; /* Zenkaku/Hankaku toggle */ + case XK_Touroku & 0xFF: return Rml::Input::KI_OEM_FJ_TOUROKU; + // case XK_Massyo & 0xFF: return Rml::Input::KI_OEM_FJ_MASSHOU; + // case XK_Kana_Lock & 0xFF: return Rml::Input::; /* Kana Lock */ + // case XK_Kana_Shift & 0xFF: return Rml::Input::; /* Kana Shift */ + // case XK_Eisu_Shift & 0xFF: return Rml::Input::; /* Alphanumeric Shift */ + // case XK_Eisu_toggle & 0xFF: return Rml::Input::; /* Alphanumeric toggle */ + + case XK_Home & 0xFF: return Rml::Input::KI_HOME; + case XK_Left & 0xFF: return Rml::Input::KI_LEFT; + case XK_Up & 0xFF: return Rml::Input::KI_UP; + case XK_Right & 0xFF: return Rml::Input::KI_RIGHT; + case XK_Down & 0xFF: return Rml::Input::KI_DOWN; + case XK_Prior & 0xFF: return Rml::Input::KI_PRIOR; + case XK_Next & 0xFF: return Rml::Input::KI_NEXT; + case XK_End & 0xFF: return Rml::Input::KI_END; + case XK_Begin & 0xFF: return Rml::Input::KI_HOME; + + // case XK_Print & 0xFF: return Rml::Input::KI_SNAPSHOT; + // case XK_Insert & 0xFF: return Rml::Input::KI_INSERT; + case XK_Num_Lock & 0xFF: return Rml::Input::KI_NUMLOCK; + + case XK_KP_Space & 0xFF: return Rml::Input::KI_SPACE; + case XK_KP_Tab & 0xFF: return Rml::Input::KI_TAB; + case XK_KP_Enter & 0xFF: return Rml::Input::KI_NUMPADENTER; + case XK_KP_F1 & 0xFF: return Rml::Input::KI_F1; + case XK_KP_F2 & 0xFF: return Rml::Input::KI_F2; + case XK_KP_F3 & 0xFF: return Rml::Input::KI_F3; + case XK_KP_F4 & 0xFF: return Rml::Input::KI_F4; + case XK_KP_Home & 0xFF: return Rml::Input::KI_NUMPAD7; + case XK_KP_Left & 0xFF: return Rml::Input::KI_NUMPAD4; + case XK_KP_Up & 0xFF: return Rml::Input::KI_NUMPAD8; + case XK_KP_Right & 0xFF: return Rml::Input::KI_NUMPAD6; + case XK_KP_Down & 0xFF: return Rml::Input::KI_NUMPAD2; + case XK_KP_Prior & 0xFF: return Rml::Input::KI_NUMPAD9; + case XK_KP_Next & 0xFF: return Rml::Input::KI_NUMPAD3; + case XK_KP_End & 0xFF: return Rml::Input::KI_NUMPAD1; + case XK_KP_Begin & 0xFF: return Rml::Input::KI_NUMPAD5; + case XK_KP_Insert & 0xFF: return Rml::Input::KI_NUMPAD0; + case XK_KP_Delete & 0xFF: return Rml::Input::KI_DECIMAL; + case XK_KP_Equal & 0xFF: return Rml::Input::KI_OEM_NEC_EQUAL; + case XK_KP_Multiply & 0xFF: return Rml::Input::KI_MULTIPLY; + case XK_KP_Add & 0xFF: return Rml::Input::KI_ADD; + case XK_KP_Separator & 0xFF: return Rml::Input::KI_SEPARATOR; + case XK_KP_Subtract & 0xFF: return Rml::Input::KI_SUBTRACT; + case XK_KP_Decimal & 0xFF: return Rml::Input::KI_DECIMAL; + case XK_KP_Divide & 0xFF: return Rml::Input::KI_DIVIDE; + + case XK_F1 & 0xFF: return Rml::Input::KI_F1; + case XK_F2 & 0xFF: return Rml::Input::KI_F2; + case XK_F3 & 0xFF: return Rml::Input::KI_F3; + case XK_F4 & 0xFF: return Rml::Input::KI_F4; + case XK_F5 & 0xFF: return Rml::Input::KI_F5; + case XK_F6 & 0xFF: return Rml::Input::KI_F6; + case XK_F7 & 0xFF: return Rml::Input::KI_F7; + case XK_F8 & 0xFF: return Rml::Input::KI_F8; + case XK_F9 & 0xFF: return Rml::Input::KI_F9; + case XK_F10 & 0xFF: return Rml::Input::KI_F10; + case XK_F11 & 0xFF: return Rml::Input::KI_F11; + case XK_F12 & 0xFF: return Rml::Input::KI_F12; + case XK_F13 & 0xFF: return Rml::Input::KI_F13; + case XK_F14 & 0xFF: return Rml::Input::KI_F14; + case XK_F15 & 0xFF: return Rml::Input::KI_F15; + case XK_F16 & 0xFF: return Rml::Input::KI_F16; + case XK_F17 & 0xFF: return Rml::Input::KI_F17; + case XK_F18 & 0xFF: return Rml::Input::KI_F18; + case XK_F19 & 0xFF: return Rml::Input::KI_F19; + case XK_F20 & 0xFF: return Rml::Input::KI_F20; + case XK_F21 & 0xFF: return Rml::Input::KI_F21; + case XK_F22 & 0xFF: return Rml::Input::KI_F22; + case XK_F23 & 0xFF: return Rml::Input::KI_F23; + case XK_F24 & 0xFF: return Rml::Input::KI_F24; + + case XK_Shift_L & 0xFF: return Rml::Input::KI_LSHIFT; + case XK_Shift_R & 0xFF: return Rml::Input::KI_RSHIFT; + case XK_Control_L & 0xFF: return Rml::Input::KI_LCONTROL; + case XK_Control_R & 0xFF: return Rml::Input::KI_RCONTROL; + case XK_Caps_Lock & 0xFF: return Rml::Input::KI_CAPITAL; + + case XK_Alt_L & 0xFF: return Rml::Input::KI_LMENU; + case XK_Alt_R & 0xFF: return Rml::Input::KI_RMENU; + + case XK_space & 0xFF: return Rml::Input::KI_SPACE; + case XK_apostrophe & 0xFF: return Rml::Input::KI_OEM_7; + case XK_comma & 0xFF: return Rml::Input::KI_OEM_COMMA; + case XK_minus & 0xFF: return Rml::Input::KI_OEM_MINUS; + case XK_period & 0xFF: return Rml::Input::KI_OEM_PERIOD; + case XK_slash & 0xFF: return Rml::Input::KI_OEM_2; + case XK_0 & 0xFF: return Rml::Input::KI_0; + case XK_1 & 0xFF: return Rml::Input::KI_1; + case XK_2 & 0xFF: return Rml::Input::KI_2; + case XK_3 & 0xFF: return Rml::Input::KI_3; + case XK_4 & 0xFF: return Rml::Input::KI_4; + case XK_5 & 0xFF: return Rml::Input::KI_5; + case XK_6 & 0xFF: return Rml::Input::KI_6; + case XK_7 & 0xFF: return Rml::Input::KI_7; + case XK_8 & 0xFF: return Rml::Input::KI_8; + case XK_9 & 0xFF: return Rml::Input::KI_9; + case XK_semicolon & 0xFF: return Rml::Input::KI_OEM_1; + case XK_equal & 0xFF: return Rml::Input::KI_OEM_PLUS; + case XK_bracketleft & 0xFF: return Rml::Input::KI_OEM_4; + case XK_backslash & 0xFF: return Rml::Input::KI_OEM_5; + case XK_bracketright & 0xFF: return Rml::Input::KI_OEM_6; + case XK_grave & 0xFF: return Rml::Input::KI_OEM_3; + case XK_a & 0xFF: return Rml::Input::KI_A; + case XK_b & 0xFF: return Rml::Input::KI_B; + case XK_c & 0xFF: return Rml::Input::KI_C; + case XK_d & 0xFF: return Rml::Input::KI_D; + case XK_e & 0xFF: return Rml::Input::KI_E; + case XK_f & 0xFF: return Rml::Input::KI_F; + case XK_g & 0xFF: return Rml::Input::KI_G; + case XK_h & 0xFF: return Rml::Input::KI_H; + case XK_i & 0xFF: return Rml::Input::KI_I; + case XK_j & 0xFF: return Rml::Input::KI_J; + case XK_k & 0xFF: return Rml::Input::KI_K; + case XK_l & 0xFF: return Rml::Input::KI_L; + case XK_m & 0xFF: return Rml::Input::KI_M; + case XK_n & 0xFF: return Rml::Input::KI_N; + case XK_o & 0xFF: return Rml::Input::KI_O; + case XK_p & 0xFF: return Rml::Input::KI_P; + case XK_q & 0xFF: return Rml::Input::KI_Q; + case XK_r & 0xFF: return Rml::Input::KI_R; + case XK_s & 0xFF: return Rml::Input::KI_S; + case XK_t & 0xFF: return Rml::Input::KI_T; + case XK_u & 0xFF: return Rml::Input::KI_U; + case XK_v & 0xFF: return Rml::Input::KI_V; + case XK_w & 0xFF: return Rml::Input::KI_W; + case XK_x & 0xFF: return Rml::Input::KI_X; + case XK_y & 0xFF: return Rml::Input::KI_Y; + case XK_z & 0xFF: return Rml::Input::KI_Z; + default: break; + } + // clang-format on + + return Rml::Input::KI_UNKNOWN; +} + +int RmlX11::ConvertMouseButton(unsigned int x11_mouse_button) +{ + switch (x11_mouse_button) + { + case Button1: return 0; + case Button2: return 2; + case Button3: return 1; + default: break; + } + return 0; +} + +/** + This map contains 4 different mappings from key identifiers to character codes. Each entry represents a different + combination of shift and capslock state. + */ + +static char ascii_map[4][51] = { + // shift off and capslock off + {0, ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', + 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ';', '=', ',', '-', '.', '/', '`', '[', '\\', ']', '\'', 0, 0}, + // shift on and capslock off + {0, ' ', ')', '!', '@', '#', '$', '%', '^', '&', '*', '(', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ':', '+', '<', '_', '>', '?', '~', '{', '|', '}', '"', 0, 0}, + // shift on and capslock on + {0, ' ', ')', '!', '@', '#', '$', '%', '^', '&', '*', '(', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', + 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ':', '+', '<', '_', '>', '?', '~', '{', '|', '}', '"', 0, 0}, + // shift off and capslock on + {0, ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ';', '=', ',', '-', '.', '/', '`', '[', '\\', ']', '\'', 0, 0}}; + +static char keypad_map[2][18] = {{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '\n', '*', '+', 0, '-', '.', '/', '='}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', '*', '+', 0, '-', 0, '/', '='}}; + +// Returns the character code for a key identifer / key modifier combination. +static Rml::Character GetCharacterCode(Rml::Input::KeyIdentifier key_identifier, int key_modifier_state) +{ + using Rml::Character; + + // Check if we have a keycode capable of generating characters on the main keyboard (ie, not on the numeric + // keypad; that is dealt with below). + if (key_identifier <= Rml::Input::KI_OEM_102) + { + // Get modifier states + bool shift = (key_modifier_state & Rml::Input::KM_SHIFT) > 0; + bool capslock = (key_modifier_state & Rml::Input::KM_CAPSLOCK) > 0; + + // Return character code based on identifier and modifiers + if (shift && !capslock) + return (Character)ascii_map[1][key_identifier]; + + if (shift && capslock) + return (Character)ascii_map[2][key_identifier]; + + if (!shift && capslock) + return (Character)ascii_map[3][key_identifier]; + + return (Character)ascii_map[0][key_identifier]; + } + + // Check if we have a keycode from the numeric keypad. + else if (key_identifier <= Rml::Input::KI_OEM_NEC_EQUAL) + { + if (key_modifier_state & Rml::Input::KM_NUMLOCK) + return (Character)keypad_map[0][key_identifier - Rml::Input::KI_NUMPAD0]; + else + return (Character)keypad_map[1][key_identifier - Rml::Input::KI_NUMPAD0]; + } + + else if (key_identifier == Rml::Input::KI_RETURN) + return (Character)'\n'; + + return Character::Null; +} diff --git a/vendor/rmlui_backend/RmlUi_Platform_X11.h b/vendor/rmlui_backend/RmlUi_Platform_X11.h new file mode 100644 index 0000000..2639be8 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Platform_X11.h @@ -0,0 +1,75 @@ +#pragma once + +#include "RmlUi_Include_Xlib.h" +#include +#include +#include +#include + +class SystemInterface_X11 : public Rml::SystemInterface { +public: + SystemInterface_X11(Display* display); + + // Optionally, provide or change the window to be used for setting the mouse cursors and clipboard text. + void SetWindow(Window window); + + // Handle selection request events. + // @return True if the event is still propagating, false if it was handled here. + bool HandleSelectionRequest(const XEvent& ev); + + // -- Inherited from Rml::SystemInterface -- + + double GetElapsedTime() override; + + void SetMouseCursor(const Rml::String& cursor_name) override; + + void SetClipboardText(const Rml::String& text) override; + void GetClipboardText(Rml::String& text) override; + +private: + void XCopy(const Rml::String& clipboard_data, const XEvent& event); + bool XPaste(Atom target_atom, Rml::String& clipboard_data); + + Display* display = nullptr; + Window window = 0; + timeval start_time; + + Cursor cursor_default = 0; + Cursor cursor_move = 0; + Cursor cursor_pointer = 0; + Cursor cursor_resize = 0; + Cursor cursor_cross = 0; + Cursor cursor_text = 0; + Cursor cursor_unavailable = 0; + + // -- Clipboard + Rml::String clipboard_text; + Atom XA_atom = 4; + Atom XA_STRING_atom = 31; + Atom UTF8_atom; + Atom CLIPBOARD_atom; + Atom XSEL_DATA_atom; + Atom TARGETS_atom; + Atom TEXT_atom; +}; + +/** + Optional helper functions for the X11 plaform. + */ +namespace RmlX11 { + +// Applies input on the context based on the given SFML event. +// @return True if the event is still propagating, false if it was handled by the context. +bool HandleInputEvent(Rml::Context* context, Display* display, const XEvent& ev); + +// Converts the X11 key code to RmlUi key. +// @note The display must be passed here as it needs to query the connected X server display for information about its install keymap abilities. +Rml::Input::KeyIdentifier ConvertKey(Display* display, unsigned int x11_key_code); + +// Converts the X11 mouse button to RmlUi mouse button. +int ConvertMouseButton(unsigned int x11_mouse_button); + +// Converts the X11 key modifier to RmlUi key modifier. +int GetKeyModifierState(int x11_state); + +} // namespace RmlX11 diff --git a/vendor/rmlui_backend/RmlUi_Renderer_DX12.cpp b/vendor/rmlui_backend/RmlUi_Renderer_DX12.cpp new file mode 100644 index 0000000..f8b2c64 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_DX12.cpp @@ -0,0 +1,9995 @@ +#include "RmlUi_Renderer_DX12.h" +#include "RmlUi_Backend.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef RMLUI_PLATFORM_WIN32 + #error unable to compile platform specific renderer required Windows OS that support DirectX-12 +#endif + +#ifdef RMLUI_DEBUG + #define RMLUI_ATTR_ASSERT_VARIABLE +#else + #define RMLUI_ATTR_ASSERT_VARIABLE [[maybe_unused]] +#endif + +#ifdef RMLUI_DX_DEBUG + #include + #include +#endif + +/// @brief in bytes see pShaderSourceText_Vertex - > cbuffer ConstantBuffer : register(b0) +static constexpr uint32_t kAllocationSize_ConstantBuffer_Vertex_Main = 72; + +/// @brief in bytes see pShaderSourceText_Vertex_Blur - > cbuffer SharedConstantBuffer : register(b0) +static constexpr uint32_t kAllocationSize_ConstantBuffer_Vertex_Blur = 96; + +static constexpr uint32_t kAllocationSize_ConstantBuffer_Pixel_Gradient = 416; + +/// @brief generally saying it is not universal approach but for keeping things not so much complex better to specify max amount of constantbuffer +/// that's enough to satisfy all shaders otherwise better to use only those which size is required for allocation +static constexpr uint32_t kAllocationSizeMax_ConstantBuffer = + std::max({kAllocationSize_ConstantBuffer_Vertex_Main, kAllocationSize_ConstantBuffer_Vertex_Blur, kAllocationSize_ConstantBuffer_Pixel_Gradient}); + +#define MAX_NUM_STOPS 16 +#define BLUR_SIZE 7 +#define BLUR_NUM_WEIGHTS ((BLUR_SIZE + 1) / 2) + +#define RMLUI_STRINGIFY_IMPL(x) #x +#define RMLUI_STRINGIFY(x) RMLUI_STRINGIFY_IMPL(x) + +static constexpr const char pShaderSourceText_Color[] = R"( +struct VS_INPUT +{ + float4 pos : SV_Position; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +float4 main(const VS_INPUT IN) : SV_TARGET +{ + return IN.color; +} +)"; + +// main +static constexpr const char pShaderSourceText_Vertex[] = R"( +struct VS_INPUT +{ + float2 position : POSITION; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +struct PS_OUTPUT +{ + float4 position : SV_Position; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +cbuffer ConstantBuffer : register(b0) +{ + float4x4 m_transform; + float2 m_translate; +}; + +PS_OUTPUT main(const VS_INPUT IN) +{ + PS_OUTPUT OUT; + + float2 translatedPos = IN.position + m_translate; + float4 resPos = mul(m_transform, float4(translatedPos.x, translatedPos.y, 0.0, 1.0)); + + OUT.position = resPos; + OUT.color = IN.color; + OUT.uv = IN.uv; + + return OUT; +}; +)"; + +static constexpr const char pShaderSourceText_Texture[] = R"( +struct VS_INPUT +{ + float4 pos : SV_Position; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +Texture2D g_InputTexture : register(t0); + +SamplerState g_SamplerLinear : register(s0); + + +float4 main(const VS_INPUT IN) : SV_TARGET +{ + return IN.color * g_InputTexture.Sample(g_SamplerLinear, IN.uv); +} +)"; + +static constexpr const char pShaderSourceText_Vertex_PassThrough[] = R"( +struct VS_INPUT +{ + float2 position : POSITION; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +struct PS_OUTPUT +{ + float4 position : SV_Position; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +PS_OUTPUT main(const VS_INPUT IN) +{ + PS_OUTPUT OUT; + OUT.position = float4(IN.position.x, IN.position.y, 0.0f, 1.0f); + OUT.color = IN.color; + // need to flip here since Rml::MeshUtilities::GenerateQuad makes valid data for GL APIs but on DirectX + // it is not valid UV (otherwise image will be flipped) + OUT.uv = float2(IN.uv.x, 1.0f - IN.uv.y); + + return OUT; +} +)"; + +static constexpr const char pShaderSourceText_Pixel_Passthrough[] = R"( +struct PS_INPUT +{ + float4 pos : SV_Position; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +Texture2D g_InputTexture : register(t0); + +SamplerState g_SamplerLinear : register(s0); + + +float4 main(const PS_INPUT inputArgs) : SV_TARGET +{ + return g_InputTexture.Sample(g_SamplerLinear, inputArgs.uv); +} +)"; + +#define RMLUI_SHADER_HEADER "#define MAX_NUM_STOPS " RMLUI_STRINGIFY(MAX_NUM_STOPS) "\n#line " RMLUI_STRINGIFY(__LINE__) "\n" + +#define RMLUI_SHADER_BLUR_HEADER \ + RMLUI_SHADER_HEADER "\n#define BLUR_SIZE " RMLUI_STRINGIFY(BLUR_SIZE) "\n#define BLUR_NUM_WEIGHTS " RMLUI_STRINGIFY(BLUR_NUM_WEIGHTS) + +static constexpr const char pShaderSourceText_Vertex_Blur[] = RMLUI_SHADER_BLUR_HEADER R"( +struct VS_INPUT +{ + float2 position : POSITION; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +struct PS_INPUT +{ + float4 position : SV_Position; + float2 uv[BLUR_SIZE] : TEXCOORD; +}; + +cbuffer SharedConstantBuffer : register(b0) +{ + float4x4 m_transform; + float2 m_translate; + float2 m_texelOffset; + float4 m_weights; + float2 m_texCoordMin; + float2 m_texCoordMax; +}; + +PS_INPUT main(const VS_INPUT IN) +{ + PS_INPUT result = (PS_INPUT)0; + + for (int i = 0; i < BLUR_SIZE; i++) { + result.uv[i] = IN.uv - float(i - BLUR_NUM_WEIGHTS + 1) * m_texelOffset; + } + result.position = float4(IN.position.xy, 1.0, 1.0); + + return result; +}; +)"; + +static constexpr const char pShaderSourceText_Pixel_Blur[] = RMLUI_SHADER_BLUR_HEADER R"( +Texture2D g_InputTexture : register(t0); +SamplerState g_SamplerLinear : register(s0); + +cbuffer SharedConstantBuffer : register(b0) +{ + float4x4 m_transform; + float2 m_translate; + float2 m_texelOffset; + float4 m_weights; + float2 m_texCoordMin; + float2 m_texCoordMax; +}; + +struct PS_INPUT +{ + float4 position : SV_Position; + float2 uv[BLUR_SIZE] : TEXCOORD; +}; + +float4 main(const PS_INPUT IN) : SV_TARGET +{ + float4 color = float4(0.0, 0.0, 0.0, 0.0); + for(int i = 0; i < BLUR_SIZE; i++) + { + float2 in_region = step(m_texCoordMin, IN.uv[i]) * step(IN.uv[i], m_texCoordMax); + color += g_InputTexture.Sample(g_SamplerLinear, float2(IN.uv[i].x, 1.0f - IN.uv[i].y)) * in_region.x * in_region.y * m_weights[abs(i - BLUR_NUM_WEIGHTS + 1)]; + } + return color; +}; +)"; + +static constexpr const char pShaderSourceText_Pixel_DropShadow[] = R"( +Texture2D g_InputTexture : register(t0); +SamplerState g_SamplerLinear : register(s0); + +cbuffer DropShadowBuffer : register(b0) +{ + float2 m_texCoordMin; + float2 m_texCoordMax; + float4 m_color; +}; + +struct PS_INPUT +{ + float4 position : SV_Position; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +float4 main(const PS_INPUT IN) : SV_TARGET +{ + float2 in_region = step(m_texCoordMin, IN.uv) * step(IN.uv, m_texCoordMax); + return g_InputTexture.Sample(g_SamplerLinear, IN.uv).a * in_region.x * in_region.y * m_color; +}; +)"; + +static constexpr const char pShaderSourceTet_Pixel_ColorMatrix[] = RMLUI_SHADER_HEADER R"( + +Texture2D g_InputTexture : register(t0); +SamplerState g_SamplerLinear : register(s0); + +cbuffer ConstantBuffer : register(b0) +{ + float4x4 m_color_matrix; +}; + +struct PS_Input +{ + float4 position : SV_Position; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +float4 main(const PS_Input IN) : SV_TARGET +{ + // The general case uses a 4x5 color matrix for full rgba transformation, plus a constant term with the last column. + // However, we only consider the case of rgb transformations. Thus, we could in principle use a 3x4 matrix, but we + // keep the alpha row for simplicity. + // In the general case we should do the matrix transformation in non-premultiplied space. However, without alpha + // transformations, we can do it directly in premultiplied space to avoid the extra division and multiplication + // steps. In this space, the constant term needs to be multiplied by the alpha value, instead of unity. + float4 texColor = g_InputTexture.Sample(g_SamplerLinear, IN.uv); + float3 transformedColor = mul(m_color_matrix, texColor).rgb; + return float4(transformedColor, texColor.a); +}; +)"; + +static constexpr const char pShaderSourceText_Pixel_BlendMask[] = RMLUI_SHADER_HEADER R"( +Texture2D g_InputTexture : register(t0); + +Texture2D g_MaskTexture : register(t1); + +SamplerState g_SamplerLinear : register(s0); + +struct PS_INPUT +{ + float4 position : SV_Position; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +float4 main(const PS_INPUT IN) : SV_TARGET +{ + float4 texColor = g_InputTexture.Sample(g_SamplerLinear, IN.uv); + float maskAlpha = g_MaskTexture.Sample(g_SamplerLinear, IN.uv).a; + return texColor * maskAlpha; +}; +)"; + +// We need to round up at compile-time so that we can address each element of the float4's +#define CEILING(x, y) (((x) + (y) - 1) / (y)) +static const char pShaderSourceText_Pixel_Gradient[] = + RMLUI_SHADER_HEADER "#define MAX_NUM_STOPS_PACKED (uint)" RMLUI_STRINGIFY(CEILING(MAX_NUM_STOPS, 4)) R"( +#define LINEAR 0 +#define RADIAL 1 +#define CONIC 2 +#define REPEATING_LINEAR 3 +#define REPEATING_RADIAL 4 +#define REPEATING_CONIC 5 +#define PI 3.14159265 + +cbuffer SharedConstantBuffer : register(b0) +{ + float4x4 m_transform; + float2 m_translate; + + // One to one translation of the OpenGL uniforms results in a LOT of wasted space due to CBuffer alignment rules. + // Changes from GL3: + // - Moved m_num_stops below m_func (saved 4 bytes of padding). + // - Packed m_stop_positions into a float4[MAX_NUM_STOPS / 4] array, as each array element starts a new 16-byte row. + // The below layout has 0 bytes of padding. + + int m_func; // one of the above definitions + int m_num_stops; + float2 m_p; // linear: starting point, radial: center, conic: center + float2 m_v; // linear: vector to ending point, radial: 2d curvature (inverse radius), conic: angled unit vector + float4 m_stop_colors[MAX_NUM_STOPS]; + float4 m_stop_positions[MAX_NUM_STOPS_PACKED]; // normalized, 0 -> starting point, 1 -> ending point +}; + +// Hide the way the data is packed in the cbuffer through a macro +// @NOTE: Hardcoded for MAX_NUM_STOPS 16. +// i >> 2 => i >> sqrt(MAX_NUM_STOPS) +#define GET_STOP_POS(i) (m_stop_positions[i >> 2][i & 3]) + +struct PS_INPUT +{ + float4 position : SV_Position; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +#define glsl_mod(x,y) (((x)-(y)*floor((x)/(y)))) + +float4 lerp_stop_colors(float t) { + float4 color = m_stop_colors[0]; + + for (int i = 1; i < m_num_stops; i++) + color = lerp(color, m_stop_colors[i], smoothstep(GET_STOP_POS(i-1), GET_STOP_POS(i), t)); + + return color; +}; + +float4 main(const PS_INPUT IN) : SV_TARGET +{ + float t = 0.0; + + if (m_func == LINEAR || m_func == REPEATING_LINEAR) { + float dist_square = dot(m_v, m_v); + float2 V = IN.uv.xy - m_p; + t = dot(m_v, V) / dist_square; + } + else if (m_func == RADIAL || m_func == REPEATING_RADIAL) { + float2 V = IN.uv.xy - m_p; + t = length(m_v * V); + } + else if (m_func == CONIC || m_func == REPEATING_CONIC) { + float2x2 R = float2x2(m_v.x, -m_v.y, m_v.y, m_v.x); + float2 V = mul((IN.uv.xy - m_p), R); + t = 0.5 + atan2(-V.x, V.y) / (2.0 * PI); + } + + if (m_func == REPEATING_LINEAR || m_func == REPEATING_RADIAL || m_func == REPEATING_CONIC) { + float t0 = GET_STOP_POS(0); + float t1 = GET_STOP_POS(m_num_stops - 1); + t = t0 + glsl_mod(t - t0, t1 - t0); + } + + return IN.color * lerp_stop_colors(t); +}; +)"; + +static constexpr const char pShaderSourceText_Pixel_Creation[] = RMLUI_SHADER_HEADER R"( +struct PS_Input +{ + float4 position : SV_Position; + float4 color : COLOR; + float2 uv : TEXCOORD; +}; + +cbuffer SharedConstantBuffer : register(b0) +{ + float4x4 m_transform; + float2 m_translate; + float2 m_dimensions; + float m_value; +}; + +#define glsl_mod(x,y) (((x)-(y)*floor((x)/(y)))) + +float4 main(const PS_Input IN) : SV_TARGET +{ + float t = m_value; + float3 c; + float l; + for (int i = 0; i < 3; i++) { + float2 p = IN.uv; + float2 uv = p; + p -= .5; + p.x *= m_dimensions.x / m_dimensions.y; + float z = t + ((float)i) * .07; + l = length(p); + uv += p / l * (sin(z) + 1.) * abs(sin(l * 9. - z - z)); + c[i] = .01 / length(glsl_mod(uv, 1.) - .5); + } + return float4(c / l, IN.color.a); +}; +)"; + +// AlignUp(314, 256) = 512 +template +static T AlignUp(T val, T alignment) +{ + RMLUI_ZoneScopedN("DirectX 12 - AlignUp"); + return (val + alignment - (T)1) & ~(alignment - (T)1); +} + +static Rml::Colourf ConvertToColorf(Rml::ColourbPremultiplied c0) +{ + RMLUI_ZoneScopedN("DirectX 12 - ConvertToColorf"); + Rml::Colourf result; + for (int i = 0; i < 4; i++) + result[i] = (1.f / 255.f) * float(c0[i]); + return result; +} + +/// Flip vertical axis of the rectangle, and move its origin to the vertically opposite side of the viewport. +/// @note Changes coordinate system from RmlUi to OpenGL, or equivalently in reverse. +/// @note The Rectangle::Top and Rectangle::Bottom members will have reverse meaning in the returned rectangle. +static Rml::Rectanglei VerticallyFlipped(Rml::Rectanglei rect, int viewport_height) +{ + RMLUI_ZoneScopedN("DirectX 12 - VerticallyFlipped"); + RMLUI_ASSERT(rect.Valid()); + Rml::Rectanglei flipped_rect = rect; + flipped_rect.p0.y = viewport_height - rect.p1.y; + flipped_rect.p1.y = viewport_height - rect.p0.y; + return flipped_rect; +} + +static void SetBlurWeights(Rml::Vector4f& p_weights, float sigma) +{ + float weights[BLUR_NUM_WEIGHTS]; + float normalization = 0.0f; + for (int i = 0; i < BLUR_NUM_WEIGHTS; i++) + { + if (Rml::Math::Absolute(sigma) < 0.1f) + weights[i] = float(i == 0); + else + weights[i] = Rml::Math::Exp(-float(i * i) / (2.0f * sigma * sigma)) / (Rml::Math::SquareRoot(2.f * Rml::Math::RMLUI_PI) * sigma); + + normalization += (i == 0 ? 1.f : 2.0f) * weights[i]; + } + for (int i = 0; i < BLUR_NUM_WEIGHTS; i++) + weights[i] /= normalization; + + p_weights.x = weights[0]; + p_weights.y = weights[1]; + p_weights.z = weights[2]; + p_weights.w = weights[3]; +} + +static void SetTexCoordLimits(Rml::Vector2f& p_tex_coord_min, Rml::Vector2f& p_tex_coord_max, Rml::Rectanglei rectangle_flipped, + Rml::Vector2i framebuffer_size) +{ + // Offset by half-texel values so that texture lookups are clamped to fragment centers, thereby avoiding color + // bleeding from neighboring texels due to bilinear interpolation. + const Rml::Vector2f min = (Rml::Vector2f(rectangle_flipped.p0) + Rml::Vector2f(0.5f)) / Rml::Vector2f(framebuffer_size); + const Rml::Vector2f max = (Rml::Vector2f(rectangle_flipped.p1) - Rml::Vector2f(0.5f)) / Rml::Vector2f(framebuffer_size); + + p_tex_coord_min.x = min.x; + p_tex_coord_min.y = min.y; + p_tex_coord_max.x = max.x; + p_tex_coord_max.y = max.y; +} + +/// Helper function from the d3dx12 header (MIT License - Microsoft). We avoid the full header for lightness. +/// https://github.com/microsoft/DirectX-Graphics-Samples/blob/71f3c57648b6cecde532f67dccd07265485e2313/TechniqueDemos/D3D12MemoryManagement/src/d3dx12.h#L1761C5-L1762C43) +inline UINT64 GetRequiredIntermediateSize(_In_ ID3D12Resource* pDestinationResource, _In_range_(0, D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0, D3D12_REQ_SUBRESOURCES - FirstSubresource) UINT NumSubresources) +{ + D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc(); + UINT64 RequiredSize = 0; + + ID3D12Device* pDevice = nullptr; + pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, 0, nullptr, nullptr, nullptr, &RequiredSize); + pDevice->Release(); + + return RequiredSize; +} + +/// Helper function from the d3dx12 header (MIT License, Microsoft). We avoid the full header for lightness. +/// https://github.com/microsoft/DirectX-Graphics-Samples/blob/71f3c57648b6cecde532f67dccd07265485e2313/TechniqueDemos/D3D12MemoryManagement/src/d3dx12.h#L1740 +inline void MemcpySubresource(const _In_ D3D12_MEMCPY_DEST* pDest, const _In_ D3D12_SUBRESOURCE_DATA* pSrc, SIZE_T RowSizeInBytes, UINT NumRows, + UINT NumSlices) +{ + if (!pSrc) + return; + + if (!pSrc->pData) + return; + + if (!pDest) + return; + + for (UINT z = 0; z < NumSlices; ++z) + { + BYTE* pDestSlice = reinterpret_cast(pDest->pData) + pDest->SlicePitch * z; + const BYTE* pSrcSlice = reinterpret_cast(pSrc->pData) + pSrc->SlicePitch * z; + for (UINT y = 0; y < NumRows; ++y) + { + memcpy(pDestSlice + pDest->RowPitch * y, pSrcSlice + pSrc->RowPitch * y, RowSizeInBytes); + } + } +} + +/// Helper function from the d3dx12 header (MIT License, Microsoft). We avoid the full header for lightness. +/// https://github.com/microsoft/DirectX-Graphics-Samples/blob/71f3c57648b6cecde532f67dccd07265485e2313/TechniqueDemos/D3D12MemoryManagement/src/d3dx12.h#L1780C15-L1780C33 +inline UINT64 UpdateSubresources(_In_ ID3D12GraphicsCommandList* pCmdList, _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, _In_range_(0, D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0, D3D12_REQ_SUBRESOURCES - FirstSubresource) UINT NumSubresources, UINT64 RequiredSize, + _In_reads_(NumSubresources) const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts, _In_reads_(NumSubresources) const UINT* pNumRows, + _In_reads_(NumSubresources) const UINT64* pRowSizesInBytes, _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData) +{ + // Minor validation + D3D12_RESOURCE_DESC IntermediateDesc = pIntermediate->GetDesc(); + D3D12_RESOURCE_DESC DestinationDesc = pDestinationResource->GetDesc(); + if (IntermediateDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER || IntermediateDesc.Width < RequiredSize + pLayouts[0].Offset || + RequiredSize > (SIZE_T)-1 || + (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && (FirstSubresource != 0 || NumSubresources != 1))) + { + return 0; + } + + BYTE* pData; + HRESULT hr = pIntermediate->Map(0, NULL, reinterpret_cast(&pData)); + if (FAILED(hr)) + { + return 0; + } + + for (UINT i = 0; i < NumSubresources; ++i) + { + if (pRowSizesInBytes[i] > (SIZE_T)-1) + return 0; + D3D12_MEMCPY_DEST DestData = {pData + pLayouts[i].Offset, pLayouts[i].Footprint.RowPitch, pLayouts[i].Footprint.RowPitch * pNumRows[i]}; + MemcpySubresource(&DestData, &pSrcData[i], (SIZE_T)pRowSizesInBytes[i], pNumRows[i], pLayouts[i].Footprint.Depth); + } + pIntermediate->Unmap(0, NULL); + + if (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) + { + D3D12_BOX SrcBox; + SrcBox.left = UINT(pLayouts[0].Offset); + SrcBox.right = UINT(pLayouts[0].Offset + pLayouts[0].Footprint.Width); + SrcBox.top = 0; + SrcBox.front = 0; + SrcBox.bottom = 1; + SrcBox.back = 1; + + pCmdList->CopyBufferRegion(pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width); + } + else + { + for (UINT i = 0; i < NumSubresources; ++i) + { + D3D12_TEXTURE_COPY_LOCATION Dst; + Dst.pResource = pDestinationResource; + Dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + Dst.SubresourceIndex = i + FirstSubresource; + + D3D12_TEXTURE_COPY_LOCATION Src; + Src.pResource = pIntermediate; + Src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + Src.PlacedFootprint = pLayouts[i]; + + pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr); + } + } + + return RequiredSize; +} + +/// stack-allocating +/// Helper function from the d3dx12 header (MIT License, Microsoft). We avoid the full header for lightness. +/// https://github.com/microsoft/DirectX-Graphics-Samples/blob/71f3c57648b6cecde532f67dccd07265485e2313/TechniqueDemos/D3D12MemoryManagement/src/d3dx12.h#L1876 +template +inline UINT64 UpdateSubresources(_In_ ID3D12GraphicsCommandList* pCmdList, _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, UINT64 IntermediateOffset, _In_range_(0, MaxSubresources) UINT FirstSubresource, + _In_range_(1, MaxSubresources - FirstSubresource) UINT NumSubresources, _In_reads_(NumSubresources) D3D12_SUBRESOURCE_DATA* pSrcData) +{ + UINT64 RequiredSize = 0; + D3D12_PLACED_SUBRESOURCE_FOOTPRINT Layouts[MaxSubresources]; + UINT NumRows[MaxSubresources]; + UINT64 RowSizesInBytes[MaxSubresources]; + + D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc(); + ID3D12Device* pDevice = nullptr; + pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, Layouts, NumRows, RowSizesInBytes, &RequiredSize); + pDevice->Release(); + + return UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, Layouts, NumRows, + RowSizesInBytes, pSrcData); +} + +enum class ShaderGradientFunction { Linear, Radial, Conic, RepeatingLinear, RepeatingRadial, RepeatingConic }; // Must match shader definitions below. + +enum class FilterType { Invalid = 0, Passthrough, Blur, DropShadow, ColorMatrix, MaskImage }; +struct CompiledFilter { + FilterType type; + + // Passthrough + float blend_factor; + + // Blur + float sigma; + + // Drop shadow + Rml::Vector2f offset; + Rml::ColourbPremultiplied color; + + // ColorMatrix + Rml::Matrix4f color_matrix; +}; + +enum class CompiledShaderType { Invalid = 0, Gradient, Creation }; +struct CompiledShader { + CompiledShaderType type; + + // Gradient + ShaderGradientFunction gradient_function; + Rml::Vector2f p; + Rml::Vector2f v; + Rml::Vector stop_positions; + Rml::Vector stop_colors; + + // Shader + Rml::Vector2f dimensions; +}; + +// those programs that have postfix as _MSAA it means that it accepts render target with sample count >= 2 (thus it is called MSAA) +enum class ProgramId : int { + None, + Color_Stencil_Always, + Color_Stencil_Equal, + Color_Stencil_Set, + Color_Stencil_SetInverse, + Color_Stencil_Intersect, + Color_Stencil_Disabled, + Texture_Stencil_Always, + Texture_Stencil_Equal, + Texture_Stencil_Disabled, + Gradient, + Creation, + // this is for presenting our msaa render target texture for NO MSAA RT + // if you do not correctly stuff DX12 validation will say about different + // sample count like it is expected 1 (because no MSAA) but your RT target texture was created with + // sample count = 2, so it is not a correct way of using it + Passthrough, + Passthrough_NoDepthStencil, + Passthrough_Opacity, + Passthrough_MSAA, + Passthrough_MSAA_Equal, + Passthrough_NoBlend, // for MSAA RT + Passthrough_NoBlendAndNoMSAA, // for RT that's not MSAA + ColorMatrix, + BlendMask, + Blur, + DropShadow, + Count +}; + +#define MAX_NUM_STOPS 16 +#define BLUR_SIZE 7 +#define BLUR_NUM_WEIGHTS ((BLUR_SIZE + 1) / 2) + +#define RMLUI_STRINGIFY_IMPL(x) #x +#define RMLUI_STRINGIFY(x) RMLUI_STRINGIFY_IMPL(x) + +#pragma comment(lib, "d3dcompiler.lib") +#pragma comment(lib, "d3d12.lib") +#pragma comment(lib, "dxgi.lib") + +#ifdef RMLUI_DX_DEBUG + #pragma comment(lib, "dxguid.lib") +#endif + +#ifdef RMLUI_DX_DEBUG + // first argument specifies that input string is char if it is needed to use other languages than English then use u8 prefix + // example: RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "::RenderGeometry"); + // example UTF8: RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, u8"::RenderGeometry"); + #define RMLUI_DX_MARKER_BEGIN(list, name) \ + if (list) \ + list->BeginEvent(1, reinterpret_cast(name), static_cast(strlen(name) + 1)); + #define RMLUI_DX_MARKER_END(list) \ + if (list) \ + list->EndEvent(); +#else + #define RMLUI_DX_MARKER_BEGIN(list, name) + #define RMLUI_DX_MARKER_END(list) +#endif + +namespace Gfx { +struct FramebufferData { +public: + FramebufferData() : + m_is_render_target{true}, m_width{}, m_height{}, m_id{-1}, m_p_texture{}, m_p_texture_depth_stencil{}, m_texture_descriptor_resource_view{}, + m_allocation_descriptor_offset{} + {} + + FramebufferData(const FramebufferData&) = delete; + FramebufferData& operator=(const FramebufferData&) = delete; + + // overload because we don't need to copy information about how our object was allocated (from storage or on stack, see m_is_allocated_on_stack) + FramebufferData(FramebufferData&& data) noexcept : + m_is_render_target{std::exchange(data.m_is_render_target, true)}, m_width{std::exchange(data.m_width, 0)}, + m_height{std::exchange(data.m_height, 0)}, m_id{std::exchange(data.m_id, -1)}, m_p_texture{std::exchange(data.m_p_texture, nullptr)}, + m_p_texture_depth_stencil{std::exchange(data.m_p_texture_depth_stencil, nullptr)}, + m_texture_descriptor_resource_view{std::exchange(data.m_texture_descriptor_resource_view, {})}, + m_allocation_descriptor_offset{std::exchange(data.m_allocation_descriptor_offset, {})} + {} + + // overload because we don't need to copy information about how our object was allocated (from storage or on stack, see m_is_allocated_on_stack) + FramebufferData& operator=(FramebufferData&& data) noexcept + { + m_is_render_target = std::exchange(data.m_is_render_target, true); + m_width = std::exchange(data.m_width, 0); + m_height = std::exchange(data.m_height, 0); + m_id = std::exchange(data.m_id, -1); + m_p_texture = std::exchange(data.m_p_texture, nullptr); + m_p_texture_depth_stencil = std::exchange(data.m_p_texture_depth_stencil, nullptr); + m_texture_descriptor_resource_view = std::exchange(data.m_texture_descriptor_resource_view, {}); + m_allocation_descriptor_offset = std::exchange(data.m_allocation_descriptor_offset, {}); + return *this; + } + + ~FramebufferData() + { + m_id = -1; + +#ifdef RMLUI_DX_DEBUG + if (m_is_allocated_on_stack == false) + RMLUI_ASSERTMSG(m_p_texture == nullptr, "you must manually deallocate texture and set this field as nullptr!"); +#endif + } + + int Get_Width() const { return m_width; } + void Set_Width(int value) { m_width = value; } + + int Get_Height() const { return m_height; } + void Set_Height(int value) { m_height = value; } + + void Set_ID(int layer_current_size_index) { m_id = layer_current_size_index; } + int Get_ID() const { return m_id; } + + void Set_Texture(RenderInterface_DX12::TextureHandleType* p_texture) { m_p_texture = p_texture; } + RenderInterface_DX12::TextureHandleType* Get_Texture() const { return m_p_texture; } + + // ResourceView means RTV or DSV + void Set_DescriptorResourceView(const D3D12_CPU_DESCRIPTOR_HANDLE& handle) { m_texture_descriptor_resource_view = handle; } + const D3D12_CPU_DESCRIPTOR_HANDLE& Get_DescriptorResourceView() const { return m_texture_descriptor_resource_view; } + + void Set_SharedDepthStencilTexture(FramebufferData* p_data) { m_p_texture_depth_stencil = p_data; } + FramebufferData* Get_SharedDepthStencilTexture() const { return m_p_texture_depth_stencil; } + + D3D12MA::VirtualAllocation* Get_VirtualAllocation_Descriptor() { return &m_allocation_descriptor_offset; } + + bool Is_RenderTarget() const { return m_is_render_target; } + void Set_RenderTarget(bool value) { m_is_render_target = value; } + +#ifdef RMLUI_DX_DEBUG + // in order to prevent false triggering assert in destructor we determine if object was created on stack or not + // if not we manually set this field to false + bool m_is_allocated_on_stack = true; +#endif + +private: + bool m_is_render_target; + int m_width; + int m_height; + int m_id; + RenderInterface_DX12::TextureHandleType* m_p_texture; + // this is shared texture and original pointer stored and managed at renderlayerstack class + FramebufferData* m_p_texture_depth_stencil; + D3D12_CPU_DESCRIPTOR_HANDLE m_texture_descriptor_resource_view; + // from dsv or rtv heap!! + D3D12MA::VirtualAllocation m_allocation_descriptor_offset; +}; +} // namespace Gfx + +RenderInterface_DX12::RenderInterface_DX12(void* p_window_handle, const Backend::RmlRendererSettings& settings) : + m_is_use_vsync{settings.vsync}, m_is_use_tearing{}, m_is_scissor_was_set{}, m_is_stencil_enabled{}, m_is_stencil_equal{}, m_is_use_msaa{true}, + m_msaa_sample_count{settings.msaa_sample_count}, m_user_framebuffer_index{}, m_width{}, m_height{}, m_current_clip_operation{-1}, + m_active_program_id{}, m_size_descriptor_heap_render_target_view{}, m_size_descriptor_heap_shaders{}, m_current_back_buffer_index{}, + m_stencil_ref_value{}, m_p_device{}, m_p_command_queue{}, m_p_copy_queue{}, m_p_swapchain{}, m_p_command_graphics_list{}, + m_p_command_graphics_list_screenshot{}, m_p_command_allocator_screenshot{}, m_p_descriptor_heap_render_target_view{}, + m_p_descriptor_heap_render_target_view_for_texture_manager{}, m_p_descriptor_heap_depth_stencil_view_for_texture_manager{}, + m_p_descriptor_heap_shaders{}, m_p_descriptor_heap_depthstencil{}, m_p_depthstencil_resource{}, m_p_backbuffer_fence{}, m_p_fence_screenshot{}, + m_p_adapter{}, m_p_copy_allocator{}, m_p_copy_command_list{}, m_p_allocator{}, m_p_offset_allocator_for_descriptor_heap_shaders{}, + m_p_user_rtv_present{}, m_p_user_dsv_present{}, m_p_window_handle{}, m_p_fence_event{}, m_p_fence_event_screenshot{}, m_fence_value{}, + m_fence_screenshot_value{}, m_precompiled_fullscreen_quad_geometry{} +{ + static_assert(RenderInterface_DX12::NumPrograms == static_cast(ProgramId::Count), + "Please adjust the NumPrograms constant for consistency with the number of program IDs."); + + RMLUI_ZoneScopedN("DirectX 12 - Constructor (non user)"); + RMLUI_ASSERTMSG(p_window_handle, "you can't pass an empty window handle! (also it must be castable to HWND)"); + + m_p_window_handle = static_cast(p_window_handle); + + std::memset(m_pipelines, 0, sizeof(m_pipelines)); + std::memset(m_root_signatures, 0, sizeof(m_root_signatures)); + std::memset(m_constant_buffer_count_per_frame.data(), 0, sizeof(m_constant_buffer_count_per_frame)); + +#ifdef RMLUI_DX_DEBUG + m_default_shader_flags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; +#else + m_default_shader_flags = 0; +#endif + + RMLUI_ZoneScopedN("DirectX 12 - Initialize"); + + Initialize_DebugLayer(); + Initialize_Adapter(); + Initialize_Device(); + + unsigned char max_msaa_supported_sample_count = GetMSAASupportedSampleCount(64); + + m_is_use_msaa = m_msaa_sample_count <= max_msaa_supported_sample_count; + + // requested count is 1 so we force it to false because in case if GPU doesn't support multisampling and returns 1 we get 1 == 1 situation + // and m_is_use_msaa will be true but it is not right and validation layers will get assertions about this situation like we want to resolve + // resource that as source with sample count equal to 1 but it expects to be >= 2 + if (m_msaa_sample_count == 1) + m_is_use_msaa = false; + +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::LT_INFO, "[DirectX-12] Max supported MSAA sample count (Quality:0): %d", max_msaa_supported_sample_count); + Rml::Log::Message(Rml::Log::LT_INFO, "[DirectX-12] Requested MSAA level: %d (compile-time: %d | supported: %d)", m_msaa_sample_count, + RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT, max_msaa_supported_sample_count); + Rml::Log::Message(Rml::Log::LT_INFO, "[DirectX-12] MSAA: %s", m_is_use_msaa ? "supported and enabled" : "Not supported by hardware and disabled"); +#endif + + m_p_command_queue = Create_CommandQueue(D3D12_COMMAND_LIST_TYPE_DIRECT); + m_p_copy_queue = Create_CommandQueue(D3D12_COMMAND_LIST_TYPE_COPY); + + RMLUI_ASSERTMSG(m_p_command_queue, "must create command queue!"); + RMLUI_ASSERTMSG(m_p_copy_queue, "must create copy queue!"); + +#ifdef RMLUI_DX_DEBUG + m_p_copy_queue->SetName(TEXT("Copy Queue (for texture manager)")); +#endif + + Initialize_Swapchain(0, 0); + + m_p_descriptor_heap_render_target_view = Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, + D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_NONE, RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT); + + m_p_descriptor_heap_render_target_view_for_texture_manager = Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, + D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_NONE, RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_RTV); + + m_p_descriptor_heap_shaders = Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, RMLUI_RENDER_BACKEND_FIELD_DESCRIPTORAMOUNT_FOR_SRV_CBV_UAV); + + m_p_descriptor_heap_depthstencil = + Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); + + m_p_descriptor_heap_depth_stencil_view_for_texture_manager = Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, + D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_NONE, RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_DSV); + + m_handle_shaders = D3D12_CPU_DESCRIPTOR_HANDLE(m_p_descriptor_heap_shaders->GetCPUDescriptorHandleForHeapStart()); + + m_size_descriptor_heap_render_target_view = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + m_size_descriptor_heap_shaders = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + Create_Resources_DependentOnSize(); + + Initialize_CommandAllocators(); + m_p_command_graphics_list = + Create_CommandList(m_backbuffers_allocators.at(m_current_back_buffer_index), D3D12_COMMAND_LIST_TYPE::D3D12_COMMAND_LIST_TYPE_DIRECT); + Initialize_Allocator(); + + m_p_command_allocator_screenshot = Create_CommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT); + m_p_command_graphics_list_screenshot = Create_CommandList(m_p_command_allocator_screenshot, D3D12_COMMAND_LIST_TYPE_DIRECT); + + Initialize_SyncPrimitives(); + + Initialize_SyncPrimitives_Screenshot(); + + m_p_copy_allocator = Create_CommandAllocator(D3D12_COMMAND_LIST_TYPE::D3D12_COMMAND_LIST_TYPE_COPY); + m_p_copy_command_list = Create_CommandList(m_p_copy_allocator, D3D12_COMMAND_LIST_TYPE::D3D12_COMMAND_LIST_TYPE_COPY); + + m_p_offset_allocator_for_descriptor_heap_shaders = + new OffsetAllocator::Allocator(RMLUI_RENDER_BACKEND_FIELD_DESCRIPTORAMOUNT_FOR_SRV_CBV_UAV * m_size_descriptor_heap_shaders); + +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "amount of srv_cbv_uav: %d size of increment: %d", + RMLUI_RENDER_BACKEND_FIELD_DESCRIPTORAMOUNT_FOR_SRV_CBV_UAV, m_size_descriptor_heap_shaders); +#endif + + m_manager_buffer.Initialize(m_p_device, m_p_allocator, m_p_offset_allocator_for_descriptor_heap_shaders, &m_handle_shaders, + m_size_descriptor_heap_shaders); + m_manager_texture.Initialize(m_p_allocator, m_p_offset_allocator_for_descriptor_heap_shaders, m_p_device, m_p_copy_command_list, + m_p_command_graphics_list, m_p_copy_allocator, m_p_descriptor_heap_shaders, m_p_descriptor_heap_render_target_view_for_texture_manager, + m_p_descriptor_heap_depth_stencil_view_for_texture_manager, m_p_copy_queue, &m_handle_shaders, this); + m_manager_render_layer.Initialize(this); + + Create_Resource_Pipelines(); + + Rml::Mesh mesh; + Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(-1.f), Rml::Vector2f(2.f), {}); + + m_precompiled_fullscreen_quad_geometry = RenderInterface_DX12::CompileGeometry(mesh.vertices, mesh.indices); + +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "DirectX 12 Initialize type: full"); +#endif +} + +RenderInterface_DX12::~RenderInterface_DX12() +{ + RMLUI_ZoneScopedN("DirectX 12 - Destructor"); + + Flush(); + + m_manager_render_layer.Shutdown(); + + Destroy_Resource_Pipelines(); + + if (m_precompiled_fullscreen_quad_geometry) + { + Free_Geometry(reinterpret_cast(m_precompiled_fullscreen_quad_geometry)); + m_precompiled_fullscreen_quad_geometry = {}; + } + + m_manager_buffer.Shutdown(); + m_manager_texture.Shutdown(); + + if (m_p_offset_allocator_for_descriptor_heap_shaders) + { + delete m_p_offset_allocator_for_descriptor_heap_shaders; + m_p_offset_allocator_for_descriptor_heap_shaders = nullptr; + } + + if (m_p_command_graphics_list_screenshot) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_command_graphics_list_screenshot->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_command_allocator_screenshot) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_command_allocator_screenshot->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + Destroy_SyncPrimitives_Screenshot(); + + { + Destroy_Allocator(); + Destroy_SyncPrimitives(); + Destroy_Resource_RenderTagetViews(); + Destroy_Swapchain(); + Destroy_CommandAllocators(); + + if (m_p_command_graphics_list) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_command_graphics_list->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_command_queue) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_command_queue->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_descriptor_heap_render_target_view) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_descriptor_heap_render_target_view->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_descriptor_heap_render_target_view_for_texture_manager) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_descriptor_heap_render_target_view_for_texture_manager->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_descriptor_heap_depth_stencil_view_for_texture_manager) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_descriptor_heap_depth_stencil_view_for_texture_manager->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_descriptor_heap_shaders) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_descriptor_heap_shaders->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_descriptor_heap_depthstencil) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_descriptor_heap_depthstencil->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_copy_command_list) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_copy_command_list->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_copy_allocator) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_copy_allocator->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_copy_queue) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_copy_queue->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_adapter) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_adapter->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_device) + { + // if here you got assert on debug that's 100% leak and not just showing some connections that will be later released like other instances + // might show (but not true due to correct order of deallocations you get the correct report of unhanleded aka not released resources in + // dx12), so in good scenario every instance must return ref_count == 0 + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_device->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + } + + m_p_command_allocator_screenshot = nullptr; + m_p_command_graphics_list_screenshot = nullptr; + m_p_device = nullptr; + m_p_adapter = nullptr; + m_p_window_handle = nullptr; + m_p_swapchain = nullptr; + m_p_command_graphics_list = nullptr; + m_p_command_queue = nullptr; + m_p_descriptor_heap_render_target_view = nullptr; + m_p_allocator = nullptr; + m_p_descriptor_heap_shaders = nullptr; + m_p_descriptor_heap_depthstencil = nullptr; + m_p_copy_command_list = nullptr; + m_p_copy_allocator = nullptr; + +#ifdef RMLUI_DX_DEBUG + { + auto dll_dxgidebug = LoadLibraryEx(TEXT("dxgidebug.dll"), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + + if (dll_dxgidebug) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "loaded dxgidebug.dll for detecting memory leaks on DirectX's side!"); + + typedef HRESULT(WINAPI * LPDXGIGETDEBUGINTERFACE)(REFIID, void**); + auto callback_DXGIGetDebugInterface = + reinterpret_cast(reinterpret_cast(GetProcAddress(dll_dxgidebug, "DXGIGetDebugInterface"))); + + if (callback_DXGIGetDebugInterface) + { + IDXGIDebug* p_debug_references{}; + + auto status = callback_DXGIGetDebugInterface(IID_PPV_ARGS(&p_debug_references)); + + RMLUI_DX_VERIFY_MSG(status, "failed to DXGIGetDebugInterface"); + + if (SUCCEEDED(status)) + { + if (p_debug_references) + { + p_debug_references->ReportLiveObjects(DXGI_DEBUG_ALL, + DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_IGNORE_INTERNAL)); + p_debug_references->Release(); + } + } + else + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, + "Failed to initialize IDXGIDebug interface by DXGIGetDebugInterface function from dxgidebug.dll!"); + } + } + } + else + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, + "your Windows version is too old for loading dxgidebug.dll! (your OS's SDK doesn't provide a such dll)"); + } + } +#endif + + Rml::Log::Message(Rml::Log::Type::LT_INFO, "Backend DirectX 12 is destroyed!"); +} + +RenderInterface_DX12::operator bool() const +{ + RMLUI_ZoneScopedN("DirectX 12 - operator bool()"); + return !!(m_p_device); +} + +void RenderInterface_DX12::BeginFrame() +{ + RMLUI_ZoneScopedN("DirectX 12 - BeginFrame"); + + auto* p_command_allocator = m_backbuffers_allocators.at(m_current_back_buffer_index); + + RMLUI_ASSERTMSG(p_command_allocator, "should be allocated and initialized! Probably early calling"); + RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be allocated and initialized! Probably early calling"); + + if (p_command_allocator) + { + RMLUI_DX_VERIFY_MSG(p_command_allocator->Reset(), "failed to reset command allocator"); + } + + if (m_p_command_graphics_list) + { + RMLUI_DX_VERIFY_MSG(m_p_command_graphics_list->Reset(p_command_allocator, nullptr), "failed to reset command graphics list"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "BeginFrame"); + + m_stencil_ref_value = 0; + m_is_scissor_was_set = false; + + SetTransform(nullptr); + + m_manager_render_layer.BeginFrame(m_width, m_height); + BindRenderTarget(m_manager_render_layer.GetTopLayer()); + + D3D12_VIEWPORT viewport{}; + viewport.Height = static_cast(m_height); + viewport.Width = static_cast(m_width); + viewport.MaxDepth = 1.0f; + m_p_command_graphics_list->RSSetViewports(1, &viewport); + + UseProgram(ProgramId::None); + m_is_stencil_equal = false; + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + } +} + +void RenderInterface_DX12::EndFrame() +{ + RMLUI_ZoneScopedN("DirectX 12 - EndFrame"); + auto* p_resource_backbuffer = m_backbuffers_resources.at(m_current_back_buffer_index); + + RMLUI_ASSERTMSG(p_resource_backbuffer, "should be allocated and initialized! Probably early calling"); + RMLUI_ASSERTMSG(m_p_command_graphics_list, "Must be allocated and initialzied. Probably early calling!"); + RMLUI_ASSERTMSG(m_p_command_queue, "Must be allocated and initialzied. Probably early calling!"); + + if (m_p_command_graphics_list) + { + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "EndFrame"); + D3D12_RESOURCE_BARRIER backbuffer_barrier_from_rt_to_present; + + backbuffer_barrier_from_rt_to_present.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + backbuffer_barrier_from_rt_to_present.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + backbuffer_barrier_from_rt_to_present.Transition.pResource = p_resource_backbuffer; + backbuffer_barrier_from_rt_to_present.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + backbuffer_barrier_from_rt_to_present.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + backbuffer_barrier_from_rt_to_present.Transition.Subresource = 0; + + const Gfx::FramebufferData& fb_active = m_manager_render_layer.GetTopLayer(); + const Gfx::FramebufferData& fb_postprocess = m_manager_render_layer.GetPostprocessPrimary(); + + ID3D12Resource* p_msaa_texture{}; + ID3D12Resource* p_postprocess_texture{}; + + TextureHandleType* p_handle_postprocess_texture = fb_postprocess.Get_Texture(); + + if (fb_active.Get_Texture()) + { + TextureHandleType* p_resource = fb_active.Get_Texture(); + RMLUI_ASSERTMSG(p_resource->Get_Info().buffer_index == -1, "can't be allocated as placed resource no sense!"); + + D3D12MA::Allocation* p_allocation = static_cast(p_resource->Get_Resource()); + p_msaa_texture = p_allocation->GetResource(); + } + + if (fb_postprocess.Get_Texture()) + { + TextureHandleType* p_resource = fb_postprocess.Get_Texture(); + RMLUI_ASSERTMSG(p_resource->Get_Info().buffer_index == -1, "can't be allocated as place resource no sense!"); + + D3D12MA::Allocation* p_allocation = static_cast(p_resource->Get_Resource()); + p_postprocess_texture = p_allocation->GetResource(); + } + + RMLUI_ASSERTMSG(p_msaa_texture, "can't be, must be a valid texture!"); + RMLUI_ASSERTMSG(p_postprocess_texture, "can't be, must be a valid texture!"); + RMLUI_ASSERTMSG(p_handle_postprocess_texture, "must be valid!"); + + RMLUI_ASSERTMSG(p_msaa_texture->GetDesc().Width == p_postprocess_texture->GetDesc().Width, "must be same otherwise use blitframebuffer!"); + RMLUI_ASSERTMSG(p_msaa_texture->GetDesc().Height == p_postprocess_texture->GetDesc().Height, "must be same otherwise use blitframebuffer!"); + +#if RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT > 1 + D3D12_RESOURCE_BARRIER barriers[2]{}; + + barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[0].Transition.Subresource = 0; + barriers[0].Transition.pResource = p_msaa_texture; + barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_SOURCE; + barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[1].Transition.Subresource = 0; + barriers[1].Transition.pResource = p_postprocess_texture; + barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_DEST; + barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + D3D12_RESOURCE_BARRIER barrier_transition_from_msaa_resolve_source_to_rt; + barrier_transition_from_msaa_resolve_source_to_rt.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier_transition_from_msaa_resolve_source_to_rt.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier_transition_from_msaa_resolve_source_to_rt.Transition.Subresource = 0; + barrier_transition_from_msaa_resolve_source_to_rt.Transition.pResource = p_msaa_texture; + barrier_transition_from_msaa_resolve_source_to_rt.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier_transition_from_msaa_resolve_source_to_rt.Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_SOURCE; + + m_p_command_graphics_list->ResourceBarrier(2, barriers); + + m_p_command_graphics_list->ResolveSubresource(p_postprocess_texture, 0, p_msaa_texture, 0, RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT); + + m_p_command_graphics_list->ResourceBarrier(1, &barrier_transition_from_msaa_resolve_source_to_rt); + + D3D12_RESOURCE_BARRIER offscreen_texture_barrier_for_shader; + offscreen_texture_barrier_for_shader.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + offscreen_texture_barrier_for_shader.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + offscreen_texture_barrier_for_shader.Transition.pResource = p_postprocess_texture; + offscreen_texture_barrier_for_shader.Transition.Subresource = 0; + offscreen_texture_barrier_for_shader.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + offscreen_texture_barrier_for_shader.Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_DEST; + + m_p_command_graphics_list->ResourceBarrier(1, &offscreen_texture_barrier_for_shader); +#else + D3D12_RESOURCE_BARRIER barriers[2]{}; + + barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[0].Transition.Subresource = 0; + barriers[0].Transition.pResource = p_msaa_texture; + barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[1].Transition.Subresource = 0; + barriers[1].Transition.pResource = p_postprocess_texture; + barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + D3D12_RESOURCE_BARRIER barrier_transition_from_msaa_resolve_source_to_rt; + barrier_transition_from_msaa_resolve_source_to_rt.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier_transition_from_msaa_resolve_source_to_rt.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier_transition_from_msaa_resolve_source_to_rt.Transition.Subresource = 0; + barrier_transition_from_msaa_resolve_source_to_rt.Transition.pResource = p_msaa_texture; + barrier_transition_from_msaa_resolve_source_to_rt.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier_transition_from_msaa_resolve_source_to_rt.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; + + m_p_command_graphics_list->ResourceBarrier(2, barriers); + + m_p_command_graphics_list->CopyResource(p_postprocess_texture, p_msaa_texture); + + m_p_command_graphics_list->ResourceBarrier(1, &barrier_transition_from_msaa_resolve_source_to_rt); + + D3D12_RESOURCE_BARRIER offscreen_texture_barrier_for_shader; + offscreen_texture_barrier_for_shader.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + offscreen_texture_barrier_for_shader.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + offscreen_texture_barrier_for_shader.Transition.pResource = p_postprocess_texture; + offscreen_texture_barrier_for_shader.Transition.Subresource = 0; + offscreen_texture_barrier_for_shader.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + offscreen_texture_barrier_for_shader.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + + m_p_command_graphics_list->ResourceBarrier(1, &offscreen_texture_barrier_for_shader); +#endif + + D3D12_CPU_DESCRIPTOR_HANDLE handle_rtv(m_p_descriptor_heap_render_target_view->GetCPUDescriptorHandleForHeapStart()); + handle_rtv.ptr += m_current_back_buffer_index * (m_size_descriptor_heap_render_target_view); + D3D12_CPU_DESCRIPTOR_HANDLE handle_dsv(m_p_descriptor_heap_depthstencil->GetCPUDescriptorHandleForHeapStart()); + + m_p_command_graphics_list->OMSetRenderTargets(1, &handle_rtv, FALSE, &handle_dsv); + + UseProgram(ProgramId::Passthrough); + + BindTexture(p_handle_postprocess_texture); + + DrawFullscreenQuad(); + + m_manager_render_layer.EndFrame(); + + m_p_command_graphics_list->ResourceBarrier(1, &backbuffer_barrier_from_rt_to_present); + + D3D12_RESOURCE_BARRIER restore_state_of_postprocess_texture_return_to_rt; + restore_state_of_postprocess_texture_return_to_rt.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + restore_state_of_postprocess_texture_return_to_rt.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + restore_state_of_postprocess_texture_return_to_rt.Transition.Subresource = 0; + restore_state_of_postprocess_texture_return_to_rt.Transition.pResource = p_postprocess_texture; + restore_state_of_postprocess_texture_return_to_rt.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + restore_state_of_postprocess_texture_return_to_rt.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + m_p_command_graphics_list->ResourceBarrier(1, &restore_state_of_postprocess_texture_return_to_rt); + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + + RMLUI_DX_VERIFY_MSG(m_p_command_graphics_list->Close(), "failed to Close"); + + ID3D12CommandList* const lists[] = {m_p_command_graphics_list}; + + if (m_p_command_queue) + { + m_p_command_queue->ExecuteCommandLists(_countof(lists), lists); + } + + UINT sync_interval = m_is_use_vsync ? 1 : 0; + UINT present_flags = (m_is_use_tearing && !m_is_use_vsync) ? DXGI_PRESENT_ALLOW_TEARING : 0; + + RMLUI_DX_VERIFY_MSG(m_p_swapchain->Present(sync_interval, present_flags), "failed to Present"); + + auto fence_value = Signal(m_current_back_buffer_index); + +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] current allocated constant buffers per draw (for frame[%d]): %zu", + m_current_back_buffer_index, m_constant_buffer_count_per_frame[m_current_back_buffer_index]); +#endif + + m_constant_buffer_count_per_frame[m_current_back_buffer_index] = 0; + + m_current_back_buffer_index = (uint32_t)m_p_swapchain->GetCurrentBackBufferIndex(); + + WaitForFenceValue(m_current_back_buffer_index); + Update_PendingForDeletion_Geometry(); + m_backbuffers_fence_values[m_current_back_buffer_index] = fence_value + 1; + } +} + +void RenderInterface_DX12::Clear() +{ + RMLUI_ZoneScopedN("DirectX 12 - Clear"); + RMLUI_ASSERTMSG(m_p_command_graphics_list, "early calling prob!"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "Clear"); + + auto* p_backbuffer = m_backbuffers_resources.at(m_current_back_buffer_index); + + D3D12_RESOURCE_BARRIER barrier; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition.pResource = p_backbuffer; + barrier.Transition.Subresource = 0; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + + m_p_command_graphics_list->ResourceBarrier(1, &barrier); + + constexpr FLOAT clear_color[] = {RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_RENDERTARGET_COLOR_VAlUE}; + + D3D12_CPU_DESCRIPTOR_HANDLE rtv(m_p_descriptor_heap_render_target_view->GetCPUDescriptorHandleForHeapStart()); + rtv.ptr += m_current_back_buffer_index * (m_size_descriptor_heap_render_target_view); + + m_p_command_graphics_list->ClearDepthStencilView(m_p_descriptor_heap_depthstencil->GetCPUDescriptorHandleForHeapStart(), + D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr); + + m_p_command_graphics_list->ClearRenderTargetView(rtv, clear_color, 0, nullptr); + + auto& p_current_rtv = m_manager_render_layer.GetTopLayer().Get_DescriptorResourceView(); + // auto& p_current_dsv = m_manager_render_layer.GetTopLayer().Get_SharedDepthStencilTexture()->Get_DescriptorResourceView(); + + constexpr FLOAT clear_color_framebuffer[] = {0.0f, 0.0f, 0.0f, 0.0f}; + m_p_command_graphics_list->ClearRenderTargetView(p_current_rtv, clear_color_framebuffer, 0, nullptr); + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +void RenderInterface_DX12::SetViewport(int viewport_width, int viewport_height) +{ + RMLUI_ZoneScopedN("DirectX 12 - SetViewport"); + if (viewport_width <= 0 || viewport_height <= 0) + return; + + if (m_width != viewport_width || m_height != viewport_height) + { + Flush(); + + if (m_p_depthstencil_resource) + { + Destroy_Resource_DepthStencil(); + } + + m_width = viewport_width; + m_height = viewport_height; + + m_projection = Rml::Matrix4f::ProjectOrtho(0, static_cast(viewport_width), static_cast(viewport_height), 0, -10000, 10000); + + if (m_p_swapchain) + { + Destroy_Resources_DependentOnSize(); + + for (int i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i) + { + m_backbuffers_fence_values[i] = m_backbuffers_fence_values[m_current_back_buffer_index]; + } + + DXGI_SWAP_CHAIN_DESC desc; + RMLUI_DX_VERIFY_MSG(m_p_swapchain->GetDesc(&desc), "failed to GetDesc"); + RMLUI_DX_VERIFY_MSG(m_p_swapchain->ResizeBuffers(static_cast(RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT), + static_cast(m_width), static_cast(m_height), desc.BufferDesc.Format, desc.Flags), + "failed to ResizeBuffers"); + + m_current_back_buffer_index = m_p_swapchain->GetCurrentBackBufferIndex(); + + Create_Resources_DependentOnSize(); + Create_Resource_DepthStencil(); + } + } +} + +void RenderInterface_DX12::RenderGeometry(Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation, Rml::TextureHandle texture) +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderGeometry"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "RenderGeometry"); + + GeometryHandleType* p_handle_geometry = reinterpret_cast(geometry); + TextureHandleType* p_handle_texture{}; + + RMLUI_ASSERTMSG(p_handle_geometry, "expected valid pointer!"); + + ConstantBufferType* p_constant_buffer{}; + uint32_t default_index_for_one_cbv_that_will_be_used_in_vertex_shader{}; + const uint32_t* p_constant_buffer_root_parameter_indicies{&default_index_for_one_cbv_that_will_be_used_in_vertex_shader}; + uint8_t amount_of_constant_buffer_root_parameter_indicies{1}; + + if (texture == TexturePostprocess) + { + if (p_handle_geometry->Get_ConstantBuffer()) + { + p_constant_buffer = p_handle_geometry->Get_ConstantBuffer(); + p_constant_buffer_root_parameter_indicies = + p_handle_geometry->Get_ConstantBufferRootParameterIndices(amount_of_constant_buffer_root_parameter_indicies); + } + } + else if (texture) + { + p_handle_texture = reinterpret_cast(texture); + RMLUI_ASSERTMSG(p_handle_texture, "expected valid pointer!"); + + if (m_is_stencil_enabled) + { + if (m_is_stencil_equal) + { + UseProgram(ProgramId::Texture_Stencil_Equal); + } + else + { + UseProgram(ProgramId::Texture_Stencil_Always); + } + } + else + { + UseProgram(ProgramId::Texture_Stencil_Disabled); + } + + if (p_handle_geometry->Get_ConstantBuffer() == nullptr) + { + p_constant_buffer = Get_ConstantBuffer(m_current_back_buffer_index); + } + else + { + p_constant_buffer = p_handle_geometry->Get_ConstantBuffer(); + p_constant_buffer_root_parameter_indicies = + p_handle_geometry->Get_ConstantBufferRootParameterIndices(amount_of_constant_buffer_root_parameter_indicies); + } + + SubmitTransformUniform(*p_constant_buffer, translation); + + if (texture != TextureEnableWithoutBinding) + { + D3D12_GPU_DESCRIPTOR_HANDLE srv_handle; + srv_handle.ptr = + m_p_descriptor_heap_shaders->GetGPUDescriptorHandleForHeapStart().ptr + p_handle_texture->Get_Allocation_DescriptorHeap().offset; + + m_p_command_graphics_list->SetGraphicsRootDescriptorTable(1, srv_handle); + } + } + else + { + if (m_current_clip_operation == -1) + { + if (m_is_stencil_enabled) + { + if (m_is_stencil_equal) + { + UseProgram(ProgramId::Color_Stencil_Equal); + } + else + { + UseProgram(ProgramId::Color_Stencil_Always); + } + } + else + { + UseProgram(ProgramId::Color_Stencil_Disabled); + } + } + else if (m_current_clip_operation == static_cast(Rml::ClipMaskOperation::Intersect)) + { + if (m_is_stencil_enabled) + { + UseProgram(ProgramId::Color_Stencil_Intersect); + } + } + else if (m_current_clip_operation == static_cast(Rml::ClipMaskOperation::Set)) + { + if (m_is_stencil_enabled) + { + UseProgram(ProgramId::Color_Stencil_Set); + } + } + else if (m_current_clip_operation == static_cast(Rml::ClipMaskOperation::SetInverse)) + { + if (m_is_stencil_enabled) + { + UseProgram(ProgramId::Color_Stencil_SetInverse); + } + } + else + { + RMLUI_ASSERT(!"not reached code point, something is missing or corrupted data"[0]); + } + + // SubmitTransformUniform(p_handle_geometry->Get_ConstantBuffer(), translation); + if (p_handle_geometry->Get_ConstantBuffer() == nullptr) + { + p_constant_buffer = Get_ConstantBuffer(m_current_back_buffer_index); + } + else + { + p_constant_buffer = p_handle_geometry->Get_ConstantBuffer(); + p_constant_buffer_root_parameter_indicies = + p_handle_geometry->Get_ConstantBufferRootParameterIndices(amount_of_constant_buffer_root_parameter_indicies); + } + + SubmitTransformUniform(*p_constant_buffer, translation); + } + + if (!m_is_scissor_was_set) + { + D3D12_RECT scissor; + scissor.left = 0; + scissor.top = 0; + scissor.right = m_width; + scissor.bottom = m_height; + + m_p_command_graphics_list->RSSetScissorRects(1, &scissor); + } + + if (p_constant_buffer) + { + auto* p_dx_constant_buffer = m_manager_buffer.Get_BufferByIndex(p_constant_buffer->m_alloc_info.buffer_index); + RMLUI_ASSERTMSG(p_dx_constant_buffer, "must be valid!"); + + if (p_dx_constant_buffer) + { + auto* p_dx_resource = p_dx_constant_buffer->GetResource(); + + RMLUI_ASSERTMSG(p_dx_resource, "must be valid!"); + + RMLUI_ASSERTMSG(p_constant_buffer_root_parameter_indicies, + "you forgot to update the RPI in CompiledGeometry do that only using active_program_id field!"); + + RMLUI_ASSERTMSG(amount_of_constant_buffer_root_parameter_indicies <= MaxConstantBuffersPerShader && + amount_of_constant_buffer_root_parameter_indicies, + "you use too big shaders for UI but honestly you have to change limits and change this field " + "_kRenderBackend_MaxConstantBuffersPerShader for desired amount otherwise value of amount is invalid aka equals to 0 that doesn't " + "make any sense if you have valid p_constant_buffer_root_parameter_indicies pointer"); + + if (p_dx_resource) + { + D3D12_GPU_VIRTUAL_ADDRESS one_shared_cbv_for_different_shaders_address = + p_dx_resource->GetGPUVirtualAddress() + p_constant_buffer->m_alloc_info.offset; + + for (uint8_t i = 0; i < amount_of_constant_buffer_root_parameter_indicies; ++i) + { + uint32_t index = p_constant_buffer_root_parameter_indicies[i]; + + m_p_command_graphics_list->SetGraphicsRootConstantBufferView(index, one_shared_cbv_for_different_shaders_address); + } + } + } + } + + auto* p_dx_buffer_vertex = m_manager_buffer.Get_BufferByIndex(p_handle_geometry->Get_InfoVertex().buffer_index); + + RMLUI_ASSERTMSG(p_dx_buffer_vertex, "must be valid!"); + + m_p_command_graphics_list->IASetPrimitiveTopology(D3D12_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + if (p_dx_buffer_vertex) + { + auto* p_dx_resource = p_dx_buffer_vertex->GetResource(); + + RMLUI_ASSERTMSG(p_dx_resource, "must be valid!"); + + if (p_dx_resource) + { + D3D12_VERTEX_BUFFER_VIEW view_vertex_buffer = {}; + + view_vertex_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + p_handle_geometry->Get_InfoVertex().offset; + view_vertex_buffer.StrideInBytes = sizeof(Rml::Vertex); + view_vertex_buffer.SizeInBytes = static_cast(p_handle_geometry->Get_InfoVertex().size); + + m_p_command_graphics_list->IASetVertexBuffers(0, 1, &view_vertex_buffer); + } + } + + auto* p_dx_buffer_index = m_manager_buffer.Get_BufferByIndex(p_handle_geometry->Get_InfoIndex().buffer_index); + + RMLUI_ASSERTMSG(p_dx_buffer_index, "must be valid!"); + + if (p_dx_buffer_index) + { + auto* p_dx_resource = p_dx_buffer_index->GetResource(); + + RMLUI_ASSERTMSG(p_dx_resource, "must be valid!"); + + if (p_dx_resource) + { + D3D12_INDEX_BUFFER_VIEW view_index_buffer = {}; + + view_index_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + p_handle_geometry->Get_InfoIndex().offset; + view_index_buffer.Format = DXGI_FORMAT::DXGI_FORMAT_R32_UINT; + view_index_buffer.SizeInBytes = static_cast(p_handle_geometry->Get_InfoIndex().size); + + m_p_command_graphics_list->IASetIndexBuffer(&view_index_buffer); + } + } + + m_p_command_graphics_list->DrawIndexedInstanced(p_handle_geometry->Get_NumIndices(), 1, 0, 0, 0); + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +Rml::CompiledGeometryHandle RenderInterface_DX12::CompileGeometry(Rml::Span vertices, Rml::Span indices) +{ + RMLUI_ZoneScopedN("DirectX 12 - CompileGeometry"); + + GeometryHandleType* p_handle = new GeometryHandleType(); + + if (p_handle) + { + m_manager_buffer.Alloc_Vertex(vertices.data(), static_cast(vertices.size()), sizeof(Rml::Vertex), p_handle); + m_manager_buffer.Alloc_Index(indices.data(), static_cast(indices.size()), sizeof(int), p_handle); + + p_handle->Set_HistoryBackBufferFrameIndex(m_current_back_buffer_index); + } + + return reinterpret_cast(p_handle); +} + +void RenderInterface_DX12::ReleaseGeometry(Rml::CompiledGeometryHandle geometry) +{ + RMLUI_ZoneScopedN("DirectX 12 - ReleaseGeometry"); + GeometryHandleType* p_handle = reinterpret_cast(geometry); + m_pending_for_deletion_geometry.push_back(p_handle); +} + +void RenderInterface_DX12::EnableScissorRegion(bool enable) +{ + RMLUI_ZoneScopedN("DirectX 12 - EnableScissorRegion"); + + if (!enable) + { + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "EnableScissorRegion"); + SetScissor(Rml::Rectanglei::MakeInvalid(), false); + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + } +} + +void RenderInterface_DX12::SetScissorRegion(Rml::Rectanglei region) +{ + RMLUI_ZoneScopedN("DirectX 12 - SetScissorRegion"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "SetScissorRegion"); + + SetScissor(region); + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +void RenderInterface_DX12::EnableClipMask(bool enable) +{ + RMLUI_ZoneScopedN("DirectX 12 - EnableClipMask"); + + m_is_stencil_enabled = enable; + + if (enable) + { + RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be valid!"); + if (m_p_command_graphics_list) + { + m_p_command_graphics_list->OMSetStencilRef(m_stencil_ref_value); + } + } + else + { + RMLUI_ASSERTMSG(m_p_command_graphics_list, "mmust be valid!"); + if (m_p_command_graphics_list) + { + m_p_command_graphics_list->OMSetStencilRef(0); + } + } +} + +void RenderInterface_DX12::RenderToClipMask(Rml::ClipMaskOperation mask_operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation) +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderToClipMask"); + RMLUI_ASSERTMSG(m_is_stencil_enabled, "must be enabled!"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "RenderToClipMask"); + + const bool clear_stencil = (mask_operation == Rml::ClipMaskOperation::Set || mask_operation == Rml::ClipMaskOperation::SetInverse); + + if (clear_stencil) + { + Rml::LayerHandle layer_handle = m_manager_render_layer.GetTopLayerHandle(); + const Gfx::FramebufferData& framebuffer = m_manager_render_layer.GetLayer(layer_handle); + + m_p_command_graphics_list->ClearDepthStencilView(framebuffer.Get_SharedDepthStencilTexture()->Get_DescriptorResourceView(), + D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr); + } + + switch (mask_operation) + { + case Rml::ClipMaskOperation::Set: + { + m_current_clip_operation = static_cast(Rml::ClipMaskOperation::Set); + m_stencil_ref_value = 1; + break; + } + case Rml::ClipMaskOperation::SetInverse: + { + m_current_clip_operation = static_cast(Rml::ClipMaskOperation::SetInverse); + m_stencil_ref_value = 0; + break; + } + case Rml::ClipMaskOperation::Intersect: + { + m_current_clip_operation = static_cast(Rml::ClipMaskOperation::Intersect); + m_stencil_ref_value += 1; + break; + } + } + + m_p_command_graphics_list->OMSetStencilRef(1); + + RenderGeometry(geometry, translation, {}); + + m_is_stencil_equal = true; + m_current_clip_operation = -1; + m_p_command_graphics_list->OMSetStencilRef(m_stencil_ref_value); + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +// Set to byte packing, or the compiler will expand our struct, which means it won't read correctly from file +#pragma pack(1) +struct TGAHeader { + char idLength; + char colourMapType; + char dataType; + short int colourMapOrigin; + short int colourMapLength; + char colourMapDepth; + short int xOrigin; + short int yOrigin; + short int width; + short int height; + char bitsPerPixel; + char imageDescriptor; +}; +// Restore packing +#pragma pack() + +static int nGlobalID{}; +Rml::TextureHandle RenderInterface_DX12::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) +{ + RMLUI_ZoneScopedN("DirectX 12 - LoadTexture"); + + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + { + return false; + } + + file_interface->Seek(file_handle, 0, SEEK_END); + size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + if (buffer_size <= sizeof(TGAHeader)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Texture file size is smaller than TGAHeader, file is not a valid TGA image."); + file_interface->Close(file_handle); + return false; + } + + using Rml::byte; + Rml::UniquePtr buffer(new byte[buffer_size]); + file_interface->Read(buffer.get(), buffer_size, file_handle); + file_interface->Close(file_handle); + + TGAHeader header; + memcpy(&header, buffer.get(), sizeof(TGAHeader)); + + int color_mode = header.bitsPerPixel / 8; + const size_t image_size = header.width * header.height * 4; // We always make 32bit textures + + if (header.dataType != 2) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24/32bit uncompressed TGAs are supported."); + return false; + } + + // Ensure we have at least 3 colors + if (color_mode < 3) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24 and 32bit textures are supported."); + return false; + } + + const byte* image_src = buffer.get() + sizeof(TGAHeader); + Rml::UniquePtr image_dest_buffer(new byte[image_size]); + byte* image_dest = image_dest_buffer.get(); + + // Targa is BGR, swap to RGB, flip Y axis, and convert to premultiplied alpha. + for (long y = 0; y < header.height; y++) + { + long read_index = y * header.width * color_mode; + long write_index = ((header.imageDescriptor & 32) != 0) ? read_index : (header.height - y - 1) * header.width * 4; + for (long x = 0; x < header.width; x++) + { + image_dest[write_index] = image_src[read_index + 2]; + image_dest[write_index + 1] = image_src[read_index + 1]; + image_dest[write_index + 2] = image_src[read_index]; + if (color_mode == 4) + { + const byte alpha = image_src[read_index + 3]; + for (size_t j = 0; j < 3; j++) + image_dest[write_index + j] = byte((image_dest[write_index + j] * alpha) / 255); + image_dest[write_index + 3] = alpha; + } + else + image_dest[write_index + 3] = 255; + + write_index += 4; + read_index += color_mode; + } + } + + texture_dimensions.x = header.width; + texture_dimensions.y = header.height; + + return GenerateTexture({image_dest, image_size}, texture_dimensions); +} + +Rml::TextureHandle RenderInterface_DX12::GenerateTexture(Rml::Span source_data, Rml::Vector2i source_dimensions) +{ + RMLUI_ZoneScopedN("DirectX 12 - GenerateTexture"); + + // RMLUI_ASSERTMSG(source_data.data(), "must be valid source"); + RMLUI_ASSERTMSG(m_p_allocator, "backend requires initialized allocator, but it is not initialized"); + + int width = source_dimensions.x; + int height = source_dimensions.y; + + RMLUI_ASSERTMSG(width > 0, "width is less or equal to 0"); + RMLUI_ASSERTMSG(height > 0, "height is less or equal to 0"); + + D3D12_RESOURCE_DESC desc_texture = {}; + desc_texture.MipLevels = 1; + desc_texture.DepthOrArraySize = 1; + desc_texture.Format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_texture.Width = width; + desc_texture.Height = height; + desc_texture.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc_texture.SampleDesc.Count = 1; + desc_texture.SampleDesc.Quality = 0; + + TextureHandleType* p_resource = new TextureHandleType(); + +#ifdef RMLUI_DX_DEBUG + if (p_resource) + { + p_resource->Set_ResourceName(std::to_string(nGlobalID)); + ++nGlobalID; + } +#endif + + m_manager_texture.Alloc_Texture(desc_texture, p_resource, source_data.data() +#ifdef RMLUI_DX_DEBUG + , + p_resource->Get_ResourceName() +#endif + ); + + return reinterpret_cast(p_resource); +} + +void RenderInterface_DX12::ReleaseTexture(Rml::TextureHandle texture_handle) +{ + RMLUI_ZoneScopedN("DirectX 12 - ReleaseTexture"); + TextureHandleType* p_texture = reinterpret_cast(texture_handle); + // m_pending_for_deletion_textures.push_back(p_texture); + + if (p_texture) + { +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::LT_DEBUG, "Destroyed texture: [%s]", p_texture->Get_ResourceName().c_str()); +#endif + + m_manager_texture.Free_Texture(p_texture); + + delete p_texture; + } +} + +void RenderInterface_DX12::SetTransform(const Rml::Matrix4f* transform) +{ + RMLUI_ZoneScopedN("DirectX 12 - SetTransform"); + + m_constant_buffer_data_transform = (transform ? m_projection * (*transform) : m_projection); +} + +RenderInterface_DX12::RenderLayerStack::RenderLayerStack() : + m_msaa_sample_count{1}, m_width{}, m_height{}, m_layers_size{}, m_p_manager_texture{}, m_p_manager_buffer{}, m_p_device{}, + m_p_depth_stencil_for_layers{} +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::Constructor"); + m_fb_postprocess.resize(4); + + // in order to prevent calling dtor when doing push_back on m_fb_layers + // we need to reserve memory, like how much we do expect elements in array (vector) + // otherwise you will get validation assert in dtor of FramebufferData struct and + // that validation supposed to be for memory leaks or wrong resource handling (like you forgot to delete resource somehow) + // if you didn't get it check this: https://en.cppreference.com/w/cpp/container/vector/reserve + + // otherwise if your default implementation requires more layers by default, thus we have a field at compile-time (or at runtime as dynamic + // extension) RMLUI_RENDER_BACKEND_OVERRIDE_FIELD_RESERVECOUNT_OF_RENDERSTACK_LAYERS + m_fb_layers.reserve(RMLUI_RENDER_BACKEND_FIELD_RESERVECOUNT_OF_RENDERSTACK_LAYERS); + m_p_depth_stencil_for_layers = Rml::MakeUnique(); + m_p_depth_stencil_for_layers->Set_RenderTarget(false); +} + +RenderInterface_DX12::RenderLayerStack::~RenderLayerStack() +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::Destructor"); + + m_p_device = nullptr; + m_p_manager_buffer = nullptr; + m_p_manager_texture = nullptr; + m_p_depth_stencil_for_layers.reset(); +} + +void RenderInterface_DX12::RenderLayerStack::Initialize(RenderInterface_DX12* p_owner) +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::Initialize"); + RMLUI_ASSERTMSG(p_owner, "you must pass a valid pointer of RenderInterface_DX12 instance"); + if (p_owner) + { + RMLUI_ASSERTMSG(p_owner->Get_TextureManager().Is_Initialized(), + "early call! you must initialize texture memory manager before calling this method!"); + RMLUI_ASSERTMSG(p_owner->Get_BufferManager().Is_Initialized(), + "early call! you must initialize buffer memory manager before calling this method!"); + RMLUI_ASSERTMSG(p_owner->Get_Device(), "you must initialize DirectX 12 before calling this method! device is nullptr"); + } + + if (p_owner) + { + m_p_device = p_owner->Get_Device(); + m_p_manager_buffer = &p_owner->Get_BufferManager(); + m_p_manager_texture = &p_owner->Get_TextureManager(); + m_msaa_sample_count = p_owner->Get_MSAASampleCount(); + } +} + +void RenderInterface_DX12::RenderLayerStack::Shutdown() +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::Shutdown"); + DestroyFramebuffers(); +} + +Rml::LayerHandle RenderInterface_DX12::RenderLayerStack::PushLayer() +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::PushLayer"); + + RMLUI_ASSERTMSG(m_layers_size <= static_cast(m_fb_layers.size()), "overflow of layers!"); + RMLUI_ASSERTMSG(m_p_depth_stencil_for_layers, "must be valid!"); + + if (m_layers_size == static_cast(m_fb_layers.size())) + { + if (m_p_depth_stencil_for_layers->Get_Texture() == nullptr) + { + CreateFramebuffer(m_p_depth_stencil_for_layers.get(), m_width, m_height, m_msaa_sample_count, true); + +#ifdef RMLUI_DX_DEBUG + if (m_p_depth_stencil_for_layers->Get_Texture()) + { + wchar_t framebuffer_name[64]; + wsprintf(framebuffer_name, L"framebuffer (layer) shared depth-stencil"); + int index_buffer = m_p_depth_stencil_for_layers->Get_Texture()->Get_Info().buffer_index; + RMLUI_ASSERTMSG(index_buffer == -1, "you can't allocate framebuffer as placed resource no sense!"); + + if (index_buffer == -1) + { + RMLUI_ASSERTMSG(m_p_depth_stencil_for_layers->Get_Texture()->Get_Resource(), "failed to allocate framebuffer!"); + D3D12MA::Allocation* p_committed_resource = + static_cast(m_p_depth_stencil_for_layers->Get_Texture()->Get_Resource()); + + if (p_committed_resource) + { + RMLUI_ASSERTMSG(p_committed_resource->GetResource(), "failed to allocate for D3D12MA! (GetResource==nullptr)"); + p_committed_resource->GetResource()->SetName(framebuffer_name); + } + } + } +#endif + } + + m_fb_layers.push_back(Gfx::FramebufferData{}); + auto* p_buffer = &m_fb_layers.back(); + CreateFramebuffer(p_buffer, m_width, m_height, m_msaa_sample_count, false); + p_buffer->Set_ID(static_cast(m_fb_layers.size() - 1)); + +#ifdef RMLUI_DX_DEBUG + wchar_t framebuffer_name[32]; + wsprintf(framebuffer_name, L"framebuffer (layer): %d", m_layers_size); + int index_buffer = p_buffer->Get_Texture()->Get_Info().buffer_index; + RMLUI_ASSERTMSG(index_buffer == -1, "you can't allocate framebuffer as placed resource no sense!"); + + if (index_buffer == -1) + { + RMLUI_ASSERTMSG(p_buffer->Get_Texture()->Get_Resource(), "failed to allocate framebuffer!"); + D3D12MA::Allocation* p_committed_resource = static_cast(p_buffer->Get_Texture()->Get_Resource()); + + if (p_committed_resource->GetResource()) + { + RMLUI_ASSERTMSG(p_committed_resource->GetResource(), "failed to allocate for D3D12MA! (GetResource==nullptr)"); + p_committed_resource->GetResource()->SetName(framebuffer_name); + } + } +#endif + + p_buffer->Set_SharedDepthStencilTexture(m_p_depth_stencil_for_layers.get()); + } + + ++m_layers_size; + + return GetTopLayerHandle(); +} + +void RenderInterface_DX12::RenderLayerStack::PopLayer() +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::PopLayer"); + + RMLUI_ASSERTMSG(m_layers_size > 0, "calculations are wrong, debug your code please!"); + m_layers_size -= 1; +} + +const Gfx::FramebufferData& RenderInterface_DX12::RenderLayerStack::GetLayer(Rml::LayerHandle layer) const +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::GetLayer"); + + RMLUI_ASSERT(static_cast(layer) < static_cast(m_layers_size) && + "overflow or not correct calculation or something is broken, debug the code!"); + return m_fb_layers.at(static_cast(layer)); +} + +const Gfx::FramebufferData& RenderInterface_DX12::RenderLayerStack::GetTopLayer() const +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::GetTopLayer"); + + RMLUI_ASSERTMSG(m_layers_size > 0, "early calling!"); + return m_fb_layers[m_layers_size - 1]; +} + +const Gfx::FramebufferData& RenderInterface_DX12::RenderLayerStack::Get_SharedDepthStencil_Layers() +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::Get_SharedDepthStencil_Layers"); + + RMLUI_ASSERTMSG(m_p_depth_stencil_for_layers, "early calling!"); + return *m_p_depth_stencil_for_layers; +} + +Rml::LayerHandle RenderInterface_DX12::RenderLayerStack::GetTopLayerHandle() const +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::GetTopLayerHandle"); + + RMLUI_ASSERTMSG(m_layers_size > 0, "early calling or something is broken!"); + return static_cast(m_layers_size - 1); +} + +void RenderInterface_DX12::RenderLayerStack::SwapPostprocessPrimarySecondary() +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::SwapPostprocessPrimarySecondary"); + + std::swap(m_fb_postprocess[0], m_fb_postprocess[1]); +} + +void RenderInterface_DX12::RenderLayerStack::BeginFrame(int width_new, int height_new) +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::BeginFrame"); + + RMLUI_ASSERTMSG(m_layers_size == 0, "something is wrong and you forgot to clear/delete something!"); + + if (m_width != width_new || m_height != height_new) + { + m_width = width_new; + m_height = height_new; + + DestroyFramebuffers(); + } + + PushLayer(); +} + +void RenderInterface_DX12::RenderLayerStack::EndFrame() +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::EndFrame"); + + RMLUI_ASSERTMSG(m_layers_size == 1, "order is wrong or something is broken!"); + PopLayer(); +} + +void RenderInterface_DX12::RenderLayerStack::DestroyFramebuffers() +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::DestroyFramebuffers"); + + RMLUI_ASSERTMSG(m_layers_size == 0, "Do not call this during frame rendering, that is, between BeginFrame() and EndFrame()."); + RMLUI_ASSERTMSG(m_p_manager_texture, "you must initialize this manager or it is a early calling or it is a late calling, debug please!"); + + // deleting shared depth stencil + + if (m_p_manager_texture) + { + if (m_p_depth_stencil_for_layers->Get_Texture()) + { + m_p_manager_texture->Free_Texture(m_p_depth_stencil_for_layers.get()); + } + } + + for (auto& fb : m_fb_layers) + { + if (fb.Get_Texture()) + { + DestroyFramebuffer(&fb); + } + } + + m_fb_layers.clear(); + + for (auto& fb : m_fb_postprocess) + { + if (fb.Get_Texture()) + { + DestroyFramebuffer(&fb); + } + } +} + +const Gfx::FramebufferData& RenderInterface_DX12::RenderLayerStack::EnsureFramebufferPostprocess(int index) +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::EnsureFramebufferPostprocess"); + + RMLUI_ASSERTMSG(index < static_cast(m_fb_postprocess.size()), "overflow or wrong calculation, debug the code!"); + + Gfx::FramebufferData& fb = m_fb_postprocess.at(index); + + if (!fb.Get_Texture()) + { + CreateFramebuffer(&fb, m_width, m_height, 1, false); + fb.Set_ID(index); + +#ifdef RMLUI_DX_DEBUG + fb.m_is_allocated_on_stack = false; + if (fb.Get_Texture()) + { + wchar_t framebuffer_name[32]; + wsprintf(framebuffer_name, L"framebuffer (postprocess): %d", index); + int buffer_index = fb.Get_Texture()->Get_Info().buffer_index; + + RMLUI_ASSERTMSG(buffer_index == -1, "you can't allocate framebuffer as placed resource no sense!"); + if (buffer_index == -1) + { + RMLUI_ASSERTMSG(fb.Get_Texture()->Get_Resource(), "must be valid resource failed to allocate!"); + D3D12MA::Allocation* p_alloc = static_cast(fb.Get_Texture()->Get_Resource()); + + if (p_alloc) + { + ID3D12Resource* p_resource = p_alloc->GetResource(); + RMLUI_ASSERTMSG(p_resource, "failed to allocate for D3D12MA"); + + if (p_resource) + { + p_resource->SetName(framebuffer_name); + } + } + } + } +#endif + } + + return fb; +} + +void RenderInterface_DX12::RenderLayerStack::CreateFramebuffer(Gfx::FramebufferData* p_result, int width, int height, int sample_count, + bool is_depth_stencil) +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::CreateFramebuffer"); + + RMLUI_ASSERTMSG(p_result, "you must pass a valid pointer!"); + RMLUI_ASSERTMSG(sample_count > 0, "you must pass a valid value! it must be positive"); + + RMLUI_ASSERTMSG(width > 0, "pass a valid width!"); + RMLUI_ASSERTMSG(height > 0, "pass a valid height"); + RMLUI_ASSERTMSG(m_p_manager_texture, "you must register manager texture befor calling this method (manager texture is nullptr)"); + if (m_p_manager_texture) + { + D3D12_RESOURCE_DESC desc_texture = {}; + + DXGI_FORMAT format{}; + + if (is_depth_stencil) + format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + else + format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + + desc_texture.MipLevels = 1; + desc_texture.DepthOrArraySize = 1; + desc_texture.Format = format; + desc_texture.Width = static_cast(width); + desc_texture.Height = static_cast(height); + desc_texture.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc_texture.SampleDesc.Count = sample_count; + desc_texture.SampleDesc.Quality = 0; + + TextureHandleType* p_resource = new TextureHandleType(); + + RMLUI_ASSERTMSG(p_resource, "[OS][ERROR] not enough memory for allocation!"); + +#ifdef RMLUI_DX_DEBUG + if (p_resource) + { + const char* pWhatTypeOfTexture{}; + + constexpr const char* kHardcodedRenderTargetName = "[RT] "; + constexpr const char* kHardcodedDepthStencilName = "[DS] "; + + if (is_depth_stencil) + pWhatTypeOfTexture = kHardcodedDepthStencilName; + else + pWhatTypeOfTexture = kHardcodedRenderTargetName; + + Rml::String msaa_info; + + if (sample_count > 1) + msaa_info = " | MSAA[" + std::to_string(sample_count) + "]"; + + p_resource->Set_ResourceName(pWhatTypeOfTexture + std::string("Render2Texture[") + std::to_string(m_layers_size) + "]" + msaa_info); + } +#endif + + p_result->Set_Width(width); + p_result->Set_Height(height); + p_result->Set_Texture(p_resource); + D3D12_RESOURCE_FLAGS flags{}; + + if (is_depth_stencil) + flags = D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + else + flags = D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + + D3D12_RESOURCE_STATES states{}; + + if (is_depth_stencil) + states = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_DEPTH_WRITE; + else + states = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_RENDER_TARGET; + +#ifdef RMLUI_DX_DEBUG + const char* pWhatTypeOfTextureForAllocationName{}; + + constexpr const char* kHardcodedNameForDepthStencil = "Render2Texture|Depth-Stencil"; + constexpr const char* kHardcodedNameForRenderTarget = "Render2Texture|Render-Target"; + + if (is_depth_stencil) + pWhatTypeOfTextureForAllocationName = kHardcodedNameForDepthStencil; + else + pWhatTypeOfTextureForAllocationName = kHardcodedNameForRenderTarget; +#endif + + m_p_manager_texture->Alloc_Texture(desc_texture, p_result, flags, states +#ifdef RMLUI_DX_DEBUG + , + pWhatTypeOfTextureForAllocationName +#endif + ); + } +} + +void RenderInterface_DX12::RenderLayerStack::DestroyFramebuffer(Gfx::FramebufferData* p_data) +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::DestroyFramebuffer"); + + RMLUI_ASSERTMSG(p_data, "you must pass a valid data"); + RMLUI_ASSERTMSG(m_p_manager_texture, "early/late calling?"); + + if (p_data) + { + m_p_manager_texture->Free_Texture(p_data); + p_data->Set_Width(-1); + p_data->Set_Height(-1); + } +} + +Rml::LayerHandle RenderInterface_DX12::PushLayer() +{ + RMLUI_ZoneScopedN("DirectX 12 - PushLayer"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "PushLayer"); + + const Rml::LayerHandle layer_handle = m_manager_render_layer.PushLayer(); + + const auto& framebuffer = m_manager_render_layer.GetLayer(layer_handle); + + RMLUI_ASSERTMSG(framebuffer.Get_SharedDepthStencilTexture(), "you have to set shared depth stencil texture for layer!"); + const auto& shared_depthstencil = *framebuffer.Get_SharedDepthStencilTexture(); + + m_p_command_graphics_list->OMSetRenderTargets(1, &framebuffer.Get_DescriptorResourceView(), FALSE, + &shared_depthstencil.Get_DescriptorResourceView()); + + constexpr FLOAT clear_color[] = {0.0f, 0.0f, 0.0f, 0.0f}; + + m_p_command_graphics_list->ClearRenderTargetView(framebuffer.Get_DescriptorResourceView(), clear_color, 0, nullptr); + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + + return layer_handle; +} + +void RenderInterface_DX12::BlitLayerToPostprocessPrimary(Rml::LayerHandle layer_id) +{ + RMLUI_ZoneScopedN("DirectX 12 - BlitLayerToPostprocessPrimary"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "BlitLayerToPostprocessPrimary"); + + const Gfx::FramebufferData& source_framebuffer = m_manager_render_layer.GetLayer(layer_id); + const Gfx::FramebufferData& destination_framebuffer = m_manager_render_layer.GetPostprocessPrimary(); + + RMLUI_ASSERTMSG(source_framebuffer.Get_Texture(), "texture must be presented when you call this method!"); + RMLUI_ASSERTMSG(destination_framebuffer.Get_Texture(), "texture must be presented when you call this method!"); + + RMLUI_ASSERT(source_framebuffer.Get_Texture()->Get_Info().buffer_index == -1 && + "expected that this texture was allocated as committed since it is 'framebuffer' "); + RMLUI_ASSERT(destination_framebuffer.Get_Texture()->Get_Info().buffer_index == -1 && + "expected that this texture was allocated as committed since it is 'framebuffer' "); + + RMLUI_ASSERTMSG(source_framebuffer.Get_Texture()->Get_Resource(), "texture must contain allocated and valid resource!"); + RMLUI_ASSERTMSG(destination_framebuffer.Get_Texture()->Get_Resource(), "texture must contain allocated and valid resource!"); + + ID3D12Resource* p_src = static_cast(source_framebuffer.Get_Texture()->Get_Resource())->GetResource(); + ID3D12Resource* p_dst = static_cast(destination_framebuffer.Get_Texture()->Get_Resource())->GetResource(); + + RMLUI_ASSERTMSG(p_src, "must be valid && allocated"); + RMLUI_ASSERTMSG(p_dst, "must be valid && allocated"); + + RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be initialized before calling this method"); + + if (!m_p_command_graphics_list) + return; + + RMLUI_ASSERTMSG(p_src->GetDesc().Width == p_dst->GetDesc().Width, "must be same otherwise use blitframebuffer"); + RMLUI_ASSERTMSG(p_src->GetDesc().Height == p_dst->GetDesc().Height, "must be same otherwise use blitframebuffer"); + +#if RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT > 1 + D3D12_RESOURCE_BARRIER barriers[2]; + barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[0].Transition.pResource = p_src; + barriers[0].Transition.Subresource = 0; + barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_SOURCE; + barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[1].Transition.pResource = p_dst; + barriers[1].Transition.Subresource = 0; + barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_DEST; + barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + m_p_command_graphics_list->ResourceBarrier(2, barriers); + + m_p_command_graphics_list->ResolveSubresource(p_dst, 0, p_src, 0, RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT); + + barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[0].Transition.pResource = p_dst; + barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_DEST; + barriers[0].Transition.Subresource = 0; + + barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[1].Transition.pResource = p_src; + barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_SOURCE; + barriers[1].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(2, barriers); +#else + D3D12_RESOURCE_BARRIER barriers[2]; + barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[0].Transition.pResource = p_src; + barriers[0].Transition.Subresource = 0; + barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[1].Transition.pResource = p_dst; + barriers[1].Transition.Subresource = 0; + barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + m_p_command_graphics_list->ResourceBarrier(2, barriers); + + m_p_command_graphics_list->CopyResource(p_dst, p_src); + + barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[0].Transition.pResource = p_dst; + barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + barriers[0].Transition.Subresource = 0; + + barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[1].Transition.pResource = p_src; + barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; + barriers[1].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(2, barriers); +#endif + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +static void SigmaToParameters(const float desired_sigma, int& out_pass_level, float& out_sigma) +{ + constexpr int max_num_passes = 10; + static_assert(max_num_passes < 31, ""); + constexpr float max_single_pass_sigma = 3.0f; + out_pass_level = Rml::Math::Clamp(Rml::Math::Log2(int(desired_sigma * (2.f / max_single_pass_sigma))), 0, max_num_passes); + out_sigma = Rml::Math::Clamp(desired_sigma / float(1 << out_pass_level), 0.0f, max_single_pass_sigma); +} + +void RenderInterface_DX12::DrawFullscreenQuad(ConstantBufferType* p_override_constant_buffer) +{ + RMLUI_ZoneScopedN("DirectX 12 - DrawFullscreenQuad()"); + RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be valid!"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "DrawFullscreenQuad"); + + // actually rml doesn't support anything for by passing custom data as additional argument for RenderGeometry method so + // some kind of variant for resolving a such situation + OverrideConstantBufferOfGeometry(m_precompiled_fullscreen_quad_geometry, p_override_constant_buffer); + + RenderGeometry(m_precompiled_fullscreen_quad_geometry, {}, TexturePostprocess); + + if (p_override_constant_buffer) + { + GeometryHandleType* p_geometry = reinterpret_cast(m_precompiled_fullscreen_quad_geometry); + p_geometry->Reset_ConstantBuffer(); + } + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +void RenderInterface_DX12::DrawFullscreenQuad(Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling, ConstantBufferType* p_override_constant_buffer) +{ + RMLUI_ZoneScopedN("DirectX 12 - DrawfullscreenQuad(uv_offset,uv_scaling)"); + RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be valid!"); + + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "DrawFullscreenQuad(uv_offset,uv_scaling)"); + + Rml::Mesh mesh; + Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(-1), Rml::Vector2f(2), {}); + if (uv_offset != Rml::Vector2f() || uv_scaling != Rml::Vector2f(1.f)) + { + for (Rml::Vertex& vertex : mesh.vertices) + vertex.tex_coord = (vertex.tex_coord * uv_scaling) + uv_offset; + } + Rml::CompiledGeometryHandle geometry = CompileGeometry(mesh.vertices, mesh.indices); + + OverrideConstantBufferOfGeometry(geometry, p_override_constant_buffer); + + RenderGeometry(geometry, {}, TexturePostprocess); + ReleaseGeometry(geometry); + + if (p_override_constant_buffer) + { + GeometryHandleType* p_geometry = reinterpret_cast(geometry); + p_geometry->Reset_ConstantBuffer(); + } + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +void RenderInterface_DX12::BindTexture(TextureHandleType* p_texture, UINT root_parameter_index) +{ + RMLUI_ASSERTMSG(p_texture, "you have to pass a valid pointer!"); + RMLUI_ASSERTMSG(m_p_command_graphics_list, "early calling must be initialized before calling this method!"); + RMLUI_ASSERTMSG(m_p_descriptor_heap_shaders, "early calling must be initialzed before calling this method!"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "BindTexture"); + + if (m_p_command_graphics_list) + { + if (p_texture) + { + if (m_p_descriptor_heap_shaders) + { + D3D12_GPU_DESCRIPTOR_HANDLE srv_handle; + srv_handle.ptr = + m_p_descriptor_heap_shaders->GetGPUDescriptorHandleForHeapStart().ptr + p_texture->Get_Allocation_DescriptorHeap().offset; + + m_p_command_graphics_list->SetGraphicsRootDescriptorTable(root_parameter_index, srv_handle); + } + } + } + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +void RenderInterface_DX12::BindRenderTarget(const Gfx::FramebufferData& framebuffer, bool depth_included) +{ + RMLUI_ASSERTMSG(m_p_command_graphics_list, "early calling must be initialized before calling this method!"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "BindRenderTarget"); + + if (m_p_command_graphics_list) + { + const D3D12_CPU_DESCRIPTOR_HANDLE* p_dsv_handle = nullptr; + + if (depth_included) + { + RMLUI_ASSERTMSG(framebuffer.Get_SharedDepthStencilTexture(), "expects that it was allocated and set to render target textures!"); + p_dsv_handle = &framebuffer.Get_SharedDepthStencilTexture()->Get_DescriptorResourceView(); + } + + const D3D12_CPU_DESCRIPTOR_HANDLE* p_rtv_handle = &framebuffer.Get_DescriptorResourceView(); + + m_p_command_graphics_list->OMSetRenderTargets(1, p_rtv_handle, FALSE, p_dsv_handle); + } + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +void RenderInterface_DX12::OverrideConstantBufferOfGeometry(Rml::CompiledGeometryHandle geometry, ConstantBufferType* p_override_constant_buffer) +{ + if (p_override_constant_buffer) + { + GeometryHandleType* p_geometry = reinterpret_cast(geometry); + p_geometry->Set_ConstantBuffer(p_override_constant_buffer); + + switch (m_active_program_id) + { + case ProgramId::Blur: + { + // this for vertex shader + p_geometry->Add_ConstantBufferRootParameterIndices(1); + + // this for pixel shader + p_geometry->Add_ConstantBufferRootParameterIndices(2); + + // so we can't combine and use one root parameters for pixel and vertex shader even if we use same CBV so yeah... + + break; + } + case ProgramId::DropShadow: + { + // for pixel shader + p_geometry->Add_ConstantBufferRootParameterIndices(1); + + break; + } + case ProgramId::ColorMatrix: + { + // for pixel shader + p_geometry->Add_ConstantBufferRootParameterIndices(1); + + break; + } + default: + { + RMLUI_ASSERT(!"FATAL YOU FORGOT TO REGISTER CONSTANT BUFFER OVERRIDE SITUATION!"); + break; + } + } + } +} + +unsigned char RenderInterface_DX12::GetMSAASupportedSampleCount(unsigned char max_samples) +{ + RMLUI_ASSERTMSG(m_p_device, "early calling this must be initialized before calling!"); + + if (m_p_device) + { +#ifdef RMLUI_DX_DEBUG + bool sample_counts[64]{}; +#endif + + D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS desc{}; + desc.SampleCount = static_cast(max_samples); + desc.Format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE; + + unsigned char max_result = 1; + + while (desc.SampleCount > 1) + { + HRESULT result = m_p_device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &desc, sizeof(desc)); + + if (SUCCEEDED(result)) + { +#ifdef RMLUI_DX_DEBUG + sample_counts[desc.SampleCount - 1] = true; +#endif + + if (desc.SampleCount > max_result) + { + RMLUI_ASSERT(desc.SampleCount <= std::numeric_limits::max() && + "report to developers, it is serious but in reality > 32 is not realistic at all"); + max_result = static_cast(desc.SampleCount); + } + +#ifndef RMLUI_DX_DEBUG + // on debug we accumulate information about all possible (for debug purposes) max sample sample_counts[0] = false always be false due + // to sample count checking in while loop + break; +#endif + } + + --desc.SampleCount; + } + + return max_result; + } + + return 1; +} + +void RenderInterface_DX12::BlitFramebuffer(const Gfx::FramebufferData& source, const Gfx::FramebufferData& dest, int srcX0, int srcY0, int srcX1, + int srcY1, int dstX0, int dstY0, int dstX1, int dstY1) +{ + RMLUI_ZoneScopedN("DirectX 12 - BlitFramebuffer"); + RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be initialized renderer before calling this method!"); + + if (!m_p_command_graphics_list) + return; + + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "BlitFramebuffer"); + + int src_width = srcX1 - srcX0; + int src_height = srcY1 - srcY0; + int dest_width = dstX1 - dstX0; + int dest_height = dstY1 - dstY0; + + bool is_flipped = src_width < 0 || src_height < 0 || dest_width < 0 || dest_height < 0; + bool is_stretched = src_width != dest_width || src_height != dest_height; + bool is_full_copy = src_width == dest_width && src_height == dest_height && srcX0 == 0 && srcY0 == 0 && dstX0 == 0 && dstY0 == 0; + + if (is_flipped || is_stretched || !is_full_copy) + { + // Full draw call. Slow path because no equivalent in DX12 + +#ifdef RMLUI_DX_DEBUG + TextureHandleType* p_texture_source = source.Get_Texture(); + TextureHandleType* p_texture_destination = dest.Get_Texture(); + + RMLUI_ASSERTMSG(p_texture_source, "must be valid pointer!"); + RMLUI_ASSERTMSG(p_texture_destination, "must be valid pointer!"); + + RMLUI_ASSERT(p_texture_source->Get_Info().buffer_index == -1 && + "expected that a such texture was allocated as committed (e.g. not placed resource), so in that case you can't cast to " + "D3D12MA::Allocation"); + RMLUI_ASSERT(p_texture_destination->Get_Info().buffer_index == -1 && + "expected that a such texture was allocated as committed (e.g. not placed resource) so in that case you can't cast to " + "D3D12MA::Allocation"); + + RMLUI_ASSERTMSG(p_texture_source->Get_Resource(), "must contain a valid resource"); + RMLUI_ASSERTMSG(p_texture_destination->Get_Resource(), "must contain a valid resource"); + + D3D12MA::Allocation* p_allocation_source = static_cast(p_texture_source->Get_Resource()); + D3D12MA::Allocation* p_allocation_destination = static_cast(p_texture_destination->Get_Resource()); + + RMLUI_ASSERTMSG(p_allocation_source->GetResource(), "allocation must contain a valid pointer to resource! something is broken"); + RMLUI_ASSERTMSG(p_allocation_destination->GetResource(), "allocation must contain a valid pointer to resource! something is broken"); + + ID3D12Resource* p_resource_src = p_allocation_source->GetResource(); + ID3D12Resource* p_resource_dst = p_allocation_destination->GetResource(); + + const D3D12_RESOURCE_DESC& desc_source = p_resource_src->GetDesc(); + const D3D12_RESOURCE_DESC& desc_dest = p_resource_dst->GetDesc(); + + RMLUI_ASSERTMSG(desc_source.SampleDesc.Count <= 1, "expected only NO MSAA texture (source)"); + RMLUI_ASSERTMSG(desc_dest.SampleDesc.Count <= 1, "expected only NO MSAA texture (dest)"); +#endif + + D3D12_VIEWPORT vp{}; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + vp.Width = (float)dest.Get_Width(); + vp.Height = (float)dest.Get_Height(); + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + + m_p_command_graphics_list->RSSetViewports(1, &vp); + + UseProgram(ProgramId::Passthrough_NoBlendAndNoMSAA); + + BindRenderTarget(dest, false); + BindTexture(source.Get_Texture()); + + float uv_x_min = float(srcX0) / float(source.Get_Width()); // Map to 0 + float uv_y_max = float(srcY0) / float(source.Get_Height()); // Map to 0 + float uv_x_max = float(srcX1) / float(source.Get_Width()); // Map to +1 + float uv_y_min = float(srcY1) / float(source.Get_Height()); // Map to +1 + + float pos_x_min = (dstX0 / float(dest.Get_Width())) * 2.0f - 1.0f; + float pos_y_min = ((dest.Get_Height() - dstY0 - dest_height) / float(dest.Get_Height())) * 2.0f - 1.0f; + float pos_x_max = ((dstX0 + dest_width) / float(dest.Get_Width())) * 2.0f - 1.0f; + float pos_y_max = ((dest.Get_Height() - dstY0) / float(dest.Get_Height())) * 2.0f - 1.0f; + + Rml::Array vertices; + constexpr int indices[6]{0, 3, 1, 1, 3, 2}; + + vertices[0].position.x = pos_x_min; + vertices[0].position.y = pos_y_min; + vertices[0].tex_coord.x = uv_x_min; + vertices[0].tex_coord.y = uv_y_min; + + vertices[1].position.x = pos_x_max; + vertices[1].position.y = pos_y_min; + vertices[1].tex_coord.x = uv_x_max; + vertices[1].tex_coord.y = uv_y_min; + + vertices[2].position.x = pos_x_max; + vertices[2].position.y = pos_y_max; + vertices[2].tex_coord.x = uv_x_max; + vertices[2].tex_coord.y = uv_y_max; + + vertices[3].position.x = pos_x_min; + vertices[3].position.y = pos_y_max; + vertices[3].tex_coord.x = uv_x_min; + vertices[3].tex_coord.y = uv_y_max; + + const Rml::CompiledGeometryHandle geometry = + CompileGeometry({&vertices[0], sizeof(vertices) / sizeof(vertices[0])}, {&indices[0], sizeof(indices) / sizeof(indices[0])}); + + RenderGeometry(geometry, {}, TexturePostprocess); + ReleaseGeometry(geometry); + } + else + { + RMLUI_ASSERT(!"use resolvesubresource manually! don't call this method for handling resolvesubresource!!!"); + } + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +void RenderInterface_DX12::ValidateTextureAllocationNotAsPlaced(const Gfx::FramebufferData& source) +{ +#ifdef RMLUI_DX_DEBUG + + TextureHandleType* p_texture_source = source.Get_Texture(); + + RMLUI_ASSERTMSG(p_texture_source, "must be valid pointer!"); + + RMLUI_ASSERT(p_texture_source->Get_Info().buffer_index == -1 && + "expected that a such texture was allocated as committed (e.g. not placed resource), so in that case you can't cast to " + "D3D12MA::Allocation"); + + RMLUI_ASSERTMSG(p_texture_source->Get_Resource(), "must contain a valid resource"); + + D3D12MA::Allocation* p_allocation_source = static_cast(p_texture_source->Get_Resource()); + + RMLUI_ASSERTMSG(p_allocation_source->GetResource(), "allocation must contain a valid pointer to resource! something is broken"); +#else + (void)source; +#endif +} + +ID3D12Resource* RenderInterface_DX12::GetResourceFromFramebufferData(const Gfx::FramebufferData& data) +{ + RMLUI_ZoneScopedN("DirectX 12 - GetResourceFromFramebufferData"); + ID3D12Resource* p_result{}; + + TextureHandleType* p_texture_source = data.Get_Texture(); + + if (p_texture_source == nullptr) + return p_result; + + if (p_texture_source->Get_Info().buffer_index == -1) + { + // committed + + if (p_texture_source->Get_Resource() == nullptr) + return p_result; + + D3D12MA::Allocation* p_allocation_source = static_cast(p_texture_source->Get_Resource()); + + if (p_allocation_source == nullptr) + return p_result; + + if (p_allocation_source->GetResource() == nullptr) + return p_result; + + p_result = p_allocation_source->GetResource(); + } + else + { + // placed + + if (p_texture_source->Get_Resource() == nullptr) + return p_result; + + p_result = static_cast(p_texture_source->Get_Resource()); + } + + return p_result; +} + +void RenderInterface_DX12::RenderBlur(float sigma, const Gfx::FramebufferData& source_destination, const Gfx::FramebufferData& temp, + const Rml::Rectanglei window_flipped) +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderBlur"); + RMLUI_ASSERTMSG(&source_destination != &temp, "you can't pass the same object to source_destination and temp arguments!"); + RMLUI_ASSERTMSG(source_destination.Get_Width() == temp.Get_Width(), "must be equal to the same size!"); + RMLUI_ASSERTMSG(source_destination.Get_Height() == temp.Get_Height(), "must be equal to the same size!"); + RMLUI_ASSERTMSG(window_flipped.Valid(), "must be valid!"); + + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "RenderBlur"); + + int pass_level = 0; + SigmaToParameters(sigma, pass_level, sigma); + + if (sigma == 0) + return; + + const Rml::Rectanglei original_scissor = m_scissor; + Rml::Rectanglei scissor = window_flipped; + + // probably we expect only textures with sample count = 1 otherwise it is MSAA and we should dynamically determine which pipeline state we should + // use in UseProgram method + UseProgram(ProgramId::Passthrough_NoBlendAndNoMSAA); + SetScissor(scissor, true); + + D3D12_VIEWPORT vp{}; + + vp.TopLeftX = 0; + vp.TopLeftY = source_destination.Get_Height() / 2.0f; + vp.Width = source_destination.Get_Width() / 2.0f; + vp.Height = source_destination.Get_Height() / 2.0f; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + + m_p_command_graphics_list->RSSetViewports(1, &vp); + + const Rml::Vector2f uv_scaling = {(source_destination.Get_Width() % 2 == 1) ? (1.f - 1.f / float(source_destination.Get_Width())) : 1.f, + (source_destination.Get_Height() % 2 == 1) ? (1.f - 1.f / float(source_destination.Get_Height())) : 1.f}; + + ID3D12Resource* p_resource{}; + + // be careful! runtime dx changes this thing BUT argument for ResourceBarrier is CONST IDK how they do in their API but def they change that by + // themselves or somehow corrupt the inner data and it is kinda sad SO what i want to say that you have to initialize all fields that are required + // for passing to resource barrier method calling!!! + // if you have a such long situation and you want to pass just using one variable on stack... + D3D12_RESOURCE_BARRIER bars[1]; + for (int i = 0; i < pass_level; i++) + { + scissor.p0 = (scissor.p0 + Rml::Vector2i(1)) / 2; + scissor.p1 = Rml::Math::Max(scissor.p1 / 2, scissor.p0); + const bool from_source = (i % 2 == 0); + + BindRenderTarget(from_source ? temp : source_destination, false); + + RenderInterface_DX12::TextureHandleType* p_texture = nullptr; + + if (from_source) + { + // we expect only committed allocated resources + ValidateTextureAllocationNotAsPlaced(source_destination); + p_resource = GetResourceFromFramebufferData(source_destination); + RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from source_destination framebuffer!"); + } + else + { + // we expect only committed allocated resources + ValidateTextureAllocationNotAsPlaced(temp); + p_resource = GetResourceFromFramebufferData(temp); + RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from source_destination framebuffer!"); + } + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + + p_texture = from_source ? source_destination.Get_Texture() : temp.Get_Texture(); + RMLUI_ASSERTMSG(p_texture, "must be valid!"); + + BindTexture(p_texture); + + SetScissor(scissor, true); + + DrawFullscreenQuad({}, uv_scaling); + + if (from_source) + { + // we expect only committed allocated resources + ValidateTextureAllocationNotAsPlaced(source_destination); + RMLUI_ATTR_ASSERT_VARIABLE ID3D12Resource* p_resource_sd = GetResourceFromFramebufferData(source_destination); + RMLUI_ASSERTMSG(p_resource_sd, "failed to obtain resource from source_destination framebuffer!"); + } + else + { + // we expect only committed allocated resources + ValidateTextureAllocationNotAsPlaced(temp); + RMLUI_ATTR_ASSERT_VARIABLE ID3D12Resource* p_resource_t = GetResourceFromFramebufferData(temp); + RMLUI_ASSERTMSG(p_resource_t, "failed to obtain resource from temp framebuffer!"); + } + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + vp.TopLeftX = 0; + vp.TopLeftY = 0; + vp.Width = static_cast(source_destination.Get_Width()); + vp.Height = static_cast(source_destination.Get_Height()); + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + + m_p_command_graphics_list->RSSetViewports(1, &vp); + const bool transfer_to_temp_buffer = (pass_level % 2 == 0); + + if (transfer_to_temp_buffer) + { + BindRenderTarget(temp, false); + + RenderInterface_DX12::TextureHandleType* p_texture = source_destination.Get_Texture(); + RMLUI_ASSERTMSG(p_texture, "must be valid!"); + + p_resource = GetResourceFromFramebufferData(source_destination); + RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer source_destination"); + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.pResource = p_resource; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + + BindTexture(p_texture); + + DrawFullscreenQuad(); + } + + UseProgram(ProgramId::Blur); + + ConstantBufferType* p_cb = Get_ConstantBuffer(m_current_back_buffer_index); + + RMLUI_ASSERTMSG(p_cb, "unable to allocate constant buffer for blur!"); + std::uint8_t* p_cb_begin = reinterpret_cast(p_cb->m_p_gpu_start_memory_for_binding_data); + + RMLUI_ASSERTMSG(p_cb_begin, "must be valid pointer of buffer where CB was allocated!"); + + std::uint8_t* p_cb_real_begin = p_cb_begin + p_cb->m_alloc_info.offset + sizeof(m_constant_buffer_data_transform) + sizeof(Rml::Vector2f); + + // sadly but here we can't optimize and upload directly using pointer from GPU + // keep allocating on stack still fastest way possible to handle a such small data for uploading :) + struct { + Rml::Vector2f texel_offset; + Rml::Vector4f weights; + Rml::Vector2f texcoord_min; + Rml::Vector2f texcoord_max; + } ShaderConstantBufferMapping_Blur; + + SetBlurWeights(ShaderConstantBufferMapping_Blur.weights, sigma); + SetTexCoordLimits(ShaderConstantBufferMapping_Blur.texcoord_min, ShaderConstantBufferMapping_Blur.texcoord_max, scissor, + {source_destination.Get_Width(), source_destination.Get_Height()}); + + ShaderConstantBufferMapping_Blur.texel_offset = Rml::Vector2f(0.f, 1.f) * (1.0f / float(temp.Get_Height())); + + std::memcpy(p_cb_real_begin, &ShaderConstantBufferMapping_Blur, sizeof(ShaderConstantBufferMapping_Blur)); + + if (transfer_to_temp_buffer) + { + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = GetResourceFromFramebufferData(source_destination); + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + BindRenderTarget(source_destination, false); + + p_resource = GetResourceFromFramebufferData(temp); + RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer temp!"); + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + + BindTexture(temp.Get_Texture()); + DrawFullscreenQuad(p_cb); + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.Subresource = 0; + bars[0].Transition.pResource = GetResourceFromFramebufferData(temp); + m_p_command_graphics_list->ResourceBarrier(1, bars); + + BindRenderTarget(temp, false); + + p_resource = GetResourceFromFramebufferData(source_destination); + RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer source_destination"); + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + + BindTexture(source_destination.Get_Texture()); + + // Add a 1px transparent border around the blur region by first clearing with a padded scissor. This helps prevent + // artifacts when upscaling the blur result in the later step. On Intel and AMD, we have observed that during + // blitting with linear filtering, pixels outside the 'src' region can be blended into the output. On the other + // hand, it looks like Nvidia clamps the pixels to the source edge, which is what we really want. Regardless, we + // work around the issue with this extra step. + SetScissor(scissor.Extend(1), true); + + Rml::Rectanglei scissor_ext = scissor.Extend(1); + scissor_ext = VerticallyFlipped(scissor_ext, m_height); + + // Some render APIs don't like offscreen positions (WebGL in particular), so clamp them to the viewport. + const int x_min = Rml::Math::Clamp(scissor_ext.Left(), 0, m_width); + const int y_min = Rml::Math::Clamp(scissor_ext.Top(), 0, m_height); + const int x_max = Rml::Math::Clamp(scissor_ext.Right(), 0, m_width); + const int y_max = Rml::Math::Clamp(scissor_ext.Bottom(), 0, m_height); + + D3D12_RECT rect_scissor{}; + rect_scissor.left = x_min; + rect_scissor.top = y_min; + rect_scissor.right = x_max; + rect_scissor.bottom = y_max; + + constexpr FLOAT clear_color[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + + m_p_command_graphics_list->ClearRenderTargetView(temp.Get_DescriptorResourceView(), clear_color, 1, &rect_scissor); + SetScissor(scissor, true); + + ShaderConstantBufferMapping_Blur.texel_offset = Rml::Vector2f(1.0f, 0.0f) * (1.0f / float(source_destination.Get_Width())); + + p_cb = Get_ConstantBuffer(m_current_back_buffer_index); + + RMLUI_ASSERTMSG(p_cb, "unable to allocate constant buffer for blur!"); + p_cb_begin = reinterpret_cast(p_cb->m_p_gpu_start_memory_for_binding_data); + + RMLUI_ASSERTMSG(p_cb_begin, "must be valid pointer of buffer where CB was allocated!"); + + p_cb_real_begin = p_cb_begin + p_cb->m_alloc_info.offset + sizeof(m_constant_buffer_data_transform) + sizeof(Rml::Vector2f); + std::memcpy(p_cb_real_begin, &ShaderConstantBufferMapping_Blur, sizeof(ShaderConstantBufferMapping_Blur)); + DrawFullscreenQuad(p_cb); + + p_resource = GetResourceFromFramebufferData(temp); + RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer temp"); + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + + p_resource = GetResourceFromFramebufferData(source_destination); + RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer source_destination"); + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + + // Blit the blurred image to the scissor region with upscaling. + Rml::Rectanglei window_flipped_twice = VerticallyFlipped(window_flipped, m_height); + SetScissor(window_flipped_twice, false); + + const Rml::Vector2i src_min = scissor.p0; + const Rml::Vector2i src_max = scissor.p1; + const Rml::Vector2i dst_min = window_flipped_twice.p0; + const Rml::Vector2i dst_max = window_flipped_twice.p1; + + BlitFramebuffer(temp, source_destination, src_min.x, src_min.y, src_max.x, src_max.y, dst_min.x, dst_max.y, dst_max.x, dst_min.y); + + // DirectX 12 implementation notice: The following note is not implemented for this renderer, see the OpenGL 3 reference for details. + // The above upscale blit might be jittery at low resolutions (large pass levels). This is especially noticeable + // when moving an element with backdrop blur around or when trying to click/hover an element within a blurred + // region since it may be rendered at an offset. For more stable and accurate rendering we next upscale the blur + // image by an exact power-of-two. However, this may not fill the edges completely so we need to do the above + // first. Note that this strategy may sometimes result in visible seams. Alternatively, we could try to enlarge + // the window to the next power-of-two size and then downsample and blur that. + + p_resource = GetResourceFromFramebufferData(temp); + RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer temp"); + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + + // restore things + D3D12_VIEWPORT viewport{}; + viewport.Height = static_cast(m_height); + viewport.Width = static_cast(m_width); + viewport.MaxDepth = 1.0f; + m_p_command_graphics_list->RSSetViewports(1, &viewport); + SetScissor(original_scissor); + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +void RenderInterface_DX12::RenderFilters(Rml::Span filter_handles) +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderFilters"); + + for (const Rml::CompiledFilterHandle filter_handle : filter_handles) + { + const CompiledFilter& filter = *reinterpret_cast(filter_handle); + const FilterType type = filter.type; + +#ifdef RMLUI_DX_DEBUG + const auto filter_type_to_string = [](FilterType type) -> const char* { + switch (type) + { + case FilterType::Invalid: return "Invalid"; + case FilterType::Passthrough: return "Passthrough"; + case FilterType::Blur: return "Blur"; + case FilterType::DropShadow: return "DropShadow"; + case FilterType::ColorMatrix: return "ColorMatrix"; + case FilterType::MaskImage: return "MaskImage"; + default: return "UnknownFilterType"; + } + }; + const auto marker_name = Rml::CreateString("RenderFilter=%s", filter_type_to_string(type)); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, marker_name.c_str()); +#endif + + switch (type) + { + case FilterType::Passthrough: + { + const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary(); + const Gfx::FramebufferData& destination = m_manager_render_layer.GetPostprocessSecondary(); + + TextureHandleType* p_texture_source = source.Get_Texture(); + TextureHandleType* p_texture_destination = destination.Get_Texture(); + + RMLUI_ASSERTMSG(p_texture_source, "must be valid pointer!"); + RMLUI_ASSERTMSG(p_texture_destination, "must be valid pointer!"); + + RMLUI_ASSERT(p_texture_source->Get_Info().buffer_index == -1 && + "expected that a such texture was allocated as committed (e.g. not placed resource), so in that case you can't cast to " + "D3D12MA::Allocation"); + RMLUI_ASSERT(p_texture_destination->Get_Info().buffer_index == -1 && + "expected that a such texture was allocated as committed (e.g. not placed resource) so in that case you can't cast to " + "D3D12MA::Allocation"); + + RMLUI_ASSERTMSG(p_texture_source->Get_Resource(), "must contain a valid resource"); + RMLUI_ASSERTMSG(p_texture_destination->Get_Resource(), "must contain a valid resource"); + + D3D12MA::Allocation* p_allocation_source = static_cast(p_texture_source->Get_Resource()); + RMLUI_ATTR_ASSERT_VARIABLE D3D12MA::Allocation* p_allocation_destination = + static_cast(p_texture_destination->Get_Resource()); + + RMLUI_ASSERTMSG(p_allocation_source->GetResource(), "allocation must contain a valid pointer to resource! something is broken"); + RMLUI_ASSERTMSG(p_allocation_destination->GetResource(), "allocation must contain a valid pointer to resource! something is broken"); + + ID3D12Resource* p_resource_as_srv = p_allocation_source->GetResource(); + + BindRenderTarget(destination, false); + + const FLOAT blend_factor[] = {filter.blend_factor, filter.blend_factor, filter.blend_factor, filter.blend_factor}; + m_p_command_graphics_list->OMSetBlendFactor(blend_factor); + + UseProgram(ProgramId::Passthrough_Opacity); + + { + D3D12_RESOURCE_BARRIER bars[1]; + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource_as_srv; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + BindTexture(p_texture_source); + DrawFullscreenQuad(); + + { + D3D12_RESOURCE_BARRIER bars[1]; + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource_as_srv; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + m_manager_render_layer.SwapPostprocessPrimarySecondary(); + + break; + } + case FilterType::Blur: + { + const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary(); + const Gfx::FramebufferData& temp = m_manager_render_layer.GetPostprocessSecondary(); + + const Rml::Rectanglei window_flipped = VerticallyFlipped(m_scissor, m_height); + RenderBlur(filter.sigma, source, temp, window_flipped); + break; + } + case FilterType::DropShadow: + { + UseProgram(ProgramId::DropShadow); + + const Gfx::FramebufferData& primary = m_manager_render_layer.GetPostprocessPrimary(); + const Gfx::FramebufferData& secondary = m_manager_render_layer.GetPostprocessSecondary(); + + BindRenderTarget(secondary, false); + + ValidateTextureAllocationNotAsPlaced(primary); + + { + ID3D12Resource* pPrimaryResource = GetResourceFromFramebufferData(primary); + RMLUI_ASSERTMSG(pPrimaryResource, "framebuffer primary doesn't contain a valid resource for barrier transition!"); + + D3D12_RESOURCE_BARRIER bars[1]; + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pPrimaryResource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.Subresource = 0; + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + BindTexture(primary.Get_Texture()); + + ConstantBufferType* p_cb_dropshadow = Get_ConstantBuffer(m_current_back_buffer_index); + + RMLUI_ASSERTMSG(p_cb_dropshadow, "failed to obtain constant buffer for drop shadow"); + + struct CBV_DropShadow { + Rml::Vector2f uv_min; + Rml::Vector2f uv_max; + Rml::Vector4f color; + }; + + CBV_DropShadow* pUploadingData = nullptr; + if (p_cb_dropshadow) + { + std::uint8_t* p_gpu_begin = reinterpret_cast(p_cb_dropshadow->m_p_gpu_start_memory_for_binding_data); + RMLUI_ASSERTMSG(p_gpu_begin, "constant buffer must contain information about its GPU location (pointer for data binding/uploading)"); + + if (p_gpu_begin) + { + std::uint8_t* p_gpu_real_begin = p_gpu_begin + p_cb_dropshadow->m_alloc_info.offset; + RMLUI_ASSERTMSG(p_gpu_real_begin, "failed to apply offset for gpu pointer"); + + if (p_gpu_real_begin) + { + pUploadingData = reinterpret_cast(p_gpu_real_begin); + } + } + } + + RMLUI_ASSERTMSG(pUploadingData, "failed to obtain uploading pointer for constant buffer (FATAL)"); + + const Rml::Colourf& color = ConvertToColorf(filter.color); + + pUploadingData->color.x = color.red; + pUploadingData->color.y = color.green; + pUploadingData->color.z = color.blue; + pUploadingData->color.w = color.alpha; + + const Rml::Rectanglei& window_flipped = VerticallyFlipped(m_scissor, m_height); + SetTexCoordLimits(pUploadingData->uv_min, pUploadingData->uv_max, m_scissor, {primary.Get_Width(), primary.Get_Height()}); + + const Rml::Vector2f& uv_offset = filter.offset / Rml::Vector2f(-(float)m_width, (float)m_height); + DrawFullscreenQuad(uv_offset, Rml::Vector2f(1.0f), p_cb_dropshadow); + + if (filter.sigma >= 0.5f) + { + const Gfx::FramebufferData& tertiary = m_manager_render_layer.GetPostprocessTertiary(); + RenderBlur(filter.sigma, secondary, tertiary, window_flipped); + } + + UseProgram(ProgramId::Passthrough_NoDepthStencil); + + BindRenderTarget(secondary, false); + BindTexture(primary.Get_Texture()); + + DrawFullscreenQuad(); + + { + ID3D12Resource* pPrimaryResource = GetResourceFromFramebufferData(primary); + RMLUI_ASSERTMSG(pPrimaryResource, "framebuffer primary doesn't contain a valid resource for barrier transition!"); + + D3D12_RESOURCE_BARRIER bars[1]; + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pPrimaryResource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.Subresource = 0; + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + m_manager_render_layer.SwapPostprocessPrimarySecondary(); + + break; + } + case FilterType::ColorMatrix: + { + UseProgram(ProgramId::ColorMatrix); + + const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary(); + const Gfx::FramebufferData& destination = m_manager_render_layer.GetPostprocessSecondary(); + + BindRenderTarget(destination, false); + + { + ID3D12Resource* pSource = GetResourceFromFramebufferData(source); + RMLUI_ASSERTMSG(pSource, "failed to obtain resource from framebuffer source!"); + + D3D12_RESOURCE_BARRIER bars[1]; + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pSource; + bars[0].Transition.Subresource = 0; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + BindTexture(source.Get_Texture()); + + ConstantBufferType* p_cb = Get_ConstantBuffer(m_current_back_buffer_index); + RMLUI_ASSERTMSG(p_cb, "failed to obtain constant buffer for color matrix"); + + if (p_cb) + { + std::uint8_t* p_cb_begin = reinterpret_cast(p_cb->m_p_gpu_start_memory_for_binding_data); + RMLUI_ASSERTMSG(p_cb_begin, "constant buffer must provide gpu begin binding pointer for uploading data from CPU"); + + if (p_cb_begin) + { + std::uint8_t* p_cb_real_begin = p_cb_begin + p_cb->m_alloc_info.offset; + RMLUI_ASSERTMSG(p_cb_real_begin, + "constant buffer must provide gpu begin binding pointer for upload data from CPU (offset applied)"); + + if (p_cb_real_begin) + { + constexpr bool is_need_transpose = std::is_same::value; + + const float* p_data = is_need_transpose ? filter.color_matrix.Transpose().data() : filter.color_matrix.data(); + + std::memcpy(p_cb_real_begin, p_data, sizeof(filter.color_matrix)); + } + } + } + + DrawFullscreenQuad(p_cb); + + { + ID3D12Resource* pSource = GetResourceFromFramebufferData(source); + RMLUI_ASSERTMSG(pSource, "failed to obtain resource from framebuffer source!"); + + D3D12_RESOURCE_BARRIER bars[1]; + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pSource; + bars[0].Transition.Subresource = 0; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + m_manager_render_layer.SwapPostprocessPrimarySecondary(); + + break; + } + case FilterType::MaskImage: + { + UseProgram(ProgramId::BlendMask); + + const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary(); + const Gfx::FramebufferData& blend_mask = m_manager_render_layer.GetBlendMask(); + const Gfx::FramebufferData& destination = m_manager_render_layer.GetPostprocessSecondary(); + + { + ID3D12Resource* pBM = GetResourceFromFramebufferData(blend_mask); + ID3D12Resource* pSrc = GetResourceFromFramebufferData(source); + + RMLUI_ASSERTMSG(pBM, "failed to obtain resource from framebuffer blend_mask"); + RMLUI_ASSERTMSG(pSrc, "failed to obtain resource from framebuffer destination"); + + D3D12_RESOURCE_BARRIER bars[2]; + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pBM; + bars[0].Transition.Subresource = 0; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + bars[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[1].Transition.pResource = pSrc; + bars[1].Transition.Subresource = 0; + bars[1].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + m_p_command_graphics_list->ResourceBarrier(2, bars); + } + + BindRenderTarget(destination, false); + BindTexture(source.Get_Texture(), 0); + BindTexture(blend_mask.Get_Texture(), 1); + + DrawFullscreenQuad(); + + { + ID3D12Resource* pBM = GetResourceFromFramebufferData(blend_mask); + ID3D12Resource* pSrc = GetResourceFromFramebufferData(source); + + RMLUI_ASSERTMSG(pBM, "failed to obtain resource from framebuffer blend_mask"); + RMLUI_ASSERTMSG(pSrc, "failed to obtain resource from framebuffer destination"); + + D3D12_RESOURCE_BARRIER bars[2]; + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pBM; + bars[0].Transition.Subresource = 0; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + bars[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[1].Transition.pResource = pSrc; + bars[1].Transition.Subresource = 0; + bars[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[1].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + m_p_command_graphics_list->ResourceBarrier(2, bars); + } + + m_manager_render_layer.SwapPostprocessPrimarySecondary(); + + break; + } + case FilterType::Invalid: + { + Rml::Log::Message(Rml::Log::LT_WARNING, "Invalid (Unhandled) render filter: %d", static_cast(type)); + break; + } + default: + { + Rml::Log::Message(Rml::Log::LT_WARNING, "Unknown render filter: %d", static_cast(type)); + break; + } + } + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + } +} + +void RenderInterface_DX12::CompositeLayers(Rml::LayerHandle source, Rml::LayerHandle destination, Rml::BlendMode blend_mode, + Rml::Span filters) +{ + RMLUI_ZoneScopedN("DirectX 12 - CompositeLayers"); + + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "CompositeLayers"); + + BlitLayerToPostprocessPrimary(source); + + RenderFilters(filters); + + // Consider instead to use a separate command list and wait for command queue. + Flush(); + + BindRenderTarget(m_manager_render_layer.GetLayer(destination)); + + if (blend_mode == Rml::BlendMode::Replace) + { + UseProgram(ProgramId::Passthrough_NoBlend); + } + else + { + // since we use msaa render target we should use appropriate version of pipeline + if (m_is_stencil_equal && m_is_stencil_enabled) + { + UseProgram(ProgramId::Passthrough_MSAA_Equal); + } + else + { + UseProgram(ProgramId::Passthrough_MSAA); + } + } + + { + ID3D12Resource* pResource = GetResourceFromFramebufferData(m_manager_render_layer.GetPostprocessPrimary()); + RMLUI_ASSERTMSG(pResource, "failed to obtain resource from m_manager_render_layer.GetPostprocessPrimary()"); + + D3D12_RESOURCE_BARRIER bars[1]; + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pResource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + BindTexture(m_manager_render_layer.GetPostprocessPrimary().Get_Texture()); + + DrawFullscreenQuad(); + + { + ID3D12Resource* pResource = GetResourceFromFramebufferData(m_manager_render_layer.GetPostprocessPrimary()); + RMLUI_ASSERTMSG(pResource, "failed to obtain resource from m_manager_render_layer.GetPostprocessPrimary()"); + + D3D12_RESOURCE_BARRIER bars[1]; + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pResource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + // should we set like return blend state as enabled? + if (blend_mode == Rml::BlendMode::Replace) + { + UseProgram(ProgramId::Passthrough); + } + + if (destination != m_manager_render_layer.GetTopLayerHandle()) + { + BindRenderTarget(m_manager_render_layer.GetTopLayer()); + } + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +void RenderInterface_DX12::PopLayer() +{ + RMLUI_ZoneScopedN("DirectX 12 - PopLayer"); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "PopLayer"); + m_manager_render_layer.PopLayer(); + BindRenderTarget(m_manager_render_layer.GetTopLayer()); + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +Rml::TextureHandle RenderInterface_DX12::SaveLayerAsTexture() +{ + RMLUI_ZoneScopedN("DirectX 12 - SaveLayerAsTexture"); + RMLUI_ASSERT(m_scissor.Valid()); + + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "SaveLayerAsTexture"); + + const Rml::Rectanglei bounds = m_scissor; + + Rml::TextureHandle render_texture = GenerateTexture({}, bounds.Size()); + if (!render_texture) + { + return {}; + } + + BlitLayerToPostprocessPrimary(m_manager_render_layer.GetTopLayerHandle()); + EnableScissorRegion(false); + + const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary(); + const Gfx::FramebufferData& destination = m_manager_render_layer.GetPostprocessSecondary(); + + { + ID3D12Resource* pResource = GetResourceFromFramebufferData(source); + RMLUI_ASSERTMSG(pResource, "failed to obtain resource from framebuffer source"); + + D3D12_RESOURCE_BARRIER bars[1]; + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pResource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + BlitFramebuffer(source, destination, bounds.Left(), source.Get_Height() - bounds.Bottom(), bounds.Right(), source.Get_Height() - bounds.Top(), 0, + bounds.Height(), bounds.Width(), 0); + + { + ID3D12Resource* pResource = GetResourceFromFramebufferData(source); + RMLUI_ASSERTMSG(pResource, "failed to obtain resource from framebuffer source"); + + D3D12_RESOURCE_BARRIER bars[1]; + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pResource; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + D3D12_BOX copy_box{}; + copy_box.left = bounds.Left(); + copy_box.right = bounds.Right(); + copy_box.top = bounds.Top(); + copy_box.bottom = bounds.Bottom(); + copy_box.front = 0; + copy_box.back = 1; + + TextureHandleType* p_casted_rt = reinterpret_cast(render_texture); + ID3D12Resource* p_resource_render_texture = nullptr; + + if (p_casted_rt->Get_Info().buffer_index == -1) + { + // committed + + D3D12MA::Allocation* p_allocation_source = static_cast(p_casted_rt->Get_Resource()); + RMLUI_ASSERTMSG(p_allocation_source, "must be valid!"); + RMLUI_ASSERTMSG(p_allocation_source->GetResource(), "must be valid!"); + + p_resource_render_texture = p_allocation_source->GetResource(); + } + else + { + // placed + + RMLUI_ASSERT(p_casted_rt->Get_Resource()); + + p_resource_render_texture = static_cast(p_casted_rt->Get_Resource()); + } + + RMLUI_ASSERTMSG(p_resource_render_texture, "failed to obtain resource from allocated texture!"); + +#ifdef RMLUI_DX_DEBUG + if (p_resource_render_texture) + { + p_resource_render_texture->SetName(L"SaveLayerAsTexture = Texture dest copy"); + } +#endif + + D3D12_TEXTURE_COPY_LOCATION dest_copy; + dest_copy.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dest_copy.pResource = p_resource_render_texture; + dest_copy.SubresourceIndex = 0; + + ID3D12Resource* p_resource_from_destination = GetResourceFromFramebufferData(destination); + + RMLUI_ASSERTMSG(p_resource_from_destination, "failed to obtain resource from framebuffer destination"); + + D3D12_TEXTURE_COPY_LOCATION src_copy; + src_copy.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + src_copy.SubresourceIndex = 0; + src_copy.pResource = p_resource_from_destination; + + { + D3D12_RESOURCE_BARRIER bars[2]; + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource_from_destination; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.Subresource = 0; + + bars[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[1].Transition.pResource = p_resource_render_texture; + bars[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + bars[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON; + bars[1].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(2, bars); + } + + m_p_command_graphics_list->CopyTextureRegion(&dest_copy, 0, 0, 0, &src_copy, ©_box); + + { + D3D12_RESOURCE_BARRIER bars[2]; + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = p_resource_from_destination; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; + bars[0].Transition.Subresource = 0; + + bars[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[1].Transition.pResource = p_resource_render_texture; + bars[1].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + bars[1].Transition.Subresource = 0; + + m_p_command_graphics_list->ResourceBarrier(2, bars); + } + + SetScissor(bounds); + BindRenderTarget(m_manager_render_layer.GetTopLayer()); + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + + return render_texture; +} + +Rml::CompiledFilterHandle RenderInterface_DX12::SaveLayerAsMaskImage() +{ + RMLUI_ZoneScopedN("DirectX 12 - SaveLayerAsMaskImage"); + + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "SaveLayerAsMaskImage"); + + BlitLayerToPostprocessPrimary(m_manager_render_layer.GetTopLayerHandle()); + + const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary(); + const Gfx::FramebufferData& destination = m_manager_render_layer.GetBlendMask(); + + { + D3D12_RESOURCE_BARRIER bars[1]; + + ID3D12Resource* pSource = GetResourceFromFramebufferData(source); + RMLUI_ASSERTMSG(pSource, "failed to obtain resource from framebuffer source"); + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pSource; + bars[0].Transition.Subresource = 0; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + UseProgram(ProgramId::Passthrough_NoBlendAndNoMSAA); + BindRenderTarget(destination, false); + BindTexture(source.Get_Texture()); + + DrawFullscreenQuad(); + + { + D3D12_RESOURCE_BARRIER bars[1]; + + ID3D12Resource* pSource = GetResourceFromFramebufferData(source); + RMLUI_ASSERTMSG(pSource, "failed to obtain resource from framebuffer source"); + + bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bars[0].Transition.pResource = pSource; + bars[0].Transition.Subresource = 0; + bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + m_p_command_graphics_list->ResourceBarrier(1, bars); + } + + BindRenderTarget(m_manager_render_layer.GetTopLayer()); + + CompiledFilter filter = {}; + filter.type = FilterType::MaskImage; + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + + return reinterpret_cast(new CompiledFilter(std::move(filter))); +} + +Rml::CompiledFilterHandle RenderInterface_DX12::CompileFilter(const Rml::String& name, const Rml::Dictionary& parameters) +{ + RMLUI_ZoneScopedN("DirectX 12 - CompileFilter"); + CompiledFilter filter = {}; + + if (name == "opacity") + { + filter.type = FilterType::Passthrough; + filter.blend_factor = Rml::Get(parameters, "value", 1.0f); + } + else if (name == "blur") + { + filter.type = FilterType::Blur; + filter.sigma = Rml::Get(parameters, "sigma", 1.0f); + } + else if (name == "drop-shadow") + { + filter.type = FilterType::DropShadow; + filter.sigma = Rml::Get(parameters, "sigma", 0.f); + filter.color = Rml::Get(parameters, "color", Rml::Colourb()).ToPremultiplied(); + filter.offset = Rml::Get(parameters, "offset", Rml::Vector2f(0.f)); + } + else if (name == "brightness") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + filter.color_matrix = Rml::Matrix4f::Diag(value, value, value, 1.f); + } + else if (name == "contrast") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + const float grayness = 0.5f - 0.5f * value; + filter.color_matrix = Rml::Matrix4f::Diag(value, value, value, 1.f); + filter.color_matrix.SetColumn(3, Rml::Vector4f(grayness, grayness, grayness, 1.f)); + } + else if (name == "invert") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Math::Clamp(Rml::Get(parameters, "value", 1.0f), 0.f, 1.f); + const float inverted = 1.f - 2.f * value; + filter.color_matrix = Rml::Matrix4f::Diag(inverted, inverted, inverted, 1.f); + filter.color_matrix.SetColumn(3, Rml::Vector4f(value, value, value, 1.f)); + } + else if (name == "grayscale") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + const float rev_value = 1.f - value; + const Rml::Vector3f gray = value * Rml::Vector3f(0.2126f, 0.7152f, 0.0722f); + // clang-format off + filter.color_matrix = Rml::Matrix4f::FromRows( + {gray.x + rev_value, gray.y, gray.z, 0.f}, + {gray.x, gray.y + rev_value, gray.z, 0.f}, + {gray.x, gray.y, gray.z + rev_value, 0.f}, + {0.f, 0.f, 0.f, 1.f} + ); + // clang-format on + } + else if (name == "sepia") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + const float rev_value = 1.f - value; + const Rml::Vector3f r_mix = value * Rml::Vector3f(0.393f, 0.769f, 0.189f); + const Rml::Vector3f g_mix = value * Rml::Vector3f(0.349f, 0.686f, 0.168f); + const Rml::Vector3f b_mix = value * Rml::Vector3f(0.272f, 0.534f, 0.131f); + // clang-format off + filter.color_matrix = Rml::Matrix4f::FromRows( + {r_mix.x + rev_value, r_mix.y, r_mix.z, 0.f}, + {g_mix.x, g_mix.y + rev_value, g_mix.z, 0.f}, + {b_mix.x, b_mix.y, b_mix.z + rev_value, 0.f}, + {0.f, 0.f, 0.f, 1.f} + ); + // clang-format on + } + else if (name == "hue-rotate") + { + // Hue-rotation and saturation values based on: https://www.w3.org/TR/filter-effects-1/#attr-valuedef-type-huerotate + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + const float s = Rml::Math::Sin(value); + const float c = Rml::Math::Cos(value); + // clang-format off + filter.color_matrix = Rml::Matrix4f::FromRows( + {0.213f + 0.787f * c - 0.213f * s, 0.715f - 0.715f * c - 0.715f * s, 0.072f - 0.072f * c + 0.928f * s, 0.f}, + {0.213f - 0.213f * c + 0.143f * s, 0.715f + 0.285f * c + 0.140f * s, 0.072f - 0.072f * c - 0.283f * s, 0.f}, + {0.213f - 0.213f * c - 0.787f * s, 0.715f - 0.715f * c + 0.715f * s, 0.072f + 0.928f * c + 0.072f * s, 0.f}, + {0.f, 0.f, 0.f, 1.f} + ); + // clang-format on + } + else if (name == "saturate") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + // clang-format off + filter.color_matrix = Rml::Matrix4f::FromRows( + {0.213f + 0.787f * value, 0.715f - 0.715f * value, 0.072f - 0.072f * value, 0.f}, + {0.213f - 0.213f * value, 0.715f + 0.285f * value, 0.072f - 0.072f * value, 0.f}, + {0.213f - 0.213f * value, 0.715f - 0.715f * value, 0.072f + 0.928f * value, 0.f}, + {0.f, 0.f, 0.f, 1.f} + ); + // clang-format on + } + + if (filter.type != FilterType::Invalid) + return reinterpret_cast(new CompiledFilter(std::move(filter))); + + Rml::Log::Message(Rml::Log::LT_WARNING, "Unsupported filter type '%s'.", name.c_str()); + return {}; +} + +void RenderInterface_DX12::ReleaseFilter(Rml::CompiledFilterHandle filter) +{ + RMLUI_ZoneScopedN("DirectX 12 - ReleaseFilter"); + delete reinterpret_cast(filter); +} + +Rml::CompiledShaderHandle RenderInterface_DX12::CompileShader(const Rml::String& name, const Rml::Dictionary& parameters) +{ + RMLUI_ZoneScopedN("DirectX 12 - CompileShader"); + auto ApplyColorStopList = [](CompiledShader& shader, const Rml::Dictionary& shader_parameters) { + auto it = shader_parameters.find("color_stop_list"); + RMLUI_ASSERT(it != shader_parameters.end() && it->second.GetType() == Rml::Variant::COLORSTOPLIST); + const Rml::ColorStopList& color_stop_list = it->second.GetReference(); + const int num_stops = Rml::Math::Min((int)color_stop_list.size(), MAX_NUM_STOPS); + + shader.stop_positions.resize(num_stops); + shader.stop_colors.resize(num_stops); + for (int i = 0; i < num_stops; i++) + { + const Rml::ColorStop& stop = color_stop_list[i]; + RMLUI_ASSERT(stop.position.unit == Rml::Unit::NUMBER); + shader.stop_positions[i] = stop.position.number; + shader.stop_colors[i] = ConvertToColorf(stop.color); + } + }; + + CompiledShader shader = {}; + + if (name == "linear-gradient") + { + shader.type = CompiledShaderType::Gradient; + const bool repeating = Rml::Get(parameters, "repeating", false); + shader.gradient_function = (repeating ? ShaderGradientFunction::RepeatingLinear : ShaderGradientFunction::Linear); + shader.p = Rml::Get(parameters, "p0", Rml::Vector2f(0.f)); + shader.v = Rml::Get(parameters, "p1", Rml::Vector2f(0.f)) - shader.p; + ApplyColorStopList(shader, parameters); + } + else if (name == "radial-gradient") + { + shader.type = CompiledShaderType::Gradient; + const bool repeating = Rml::Get(parameters, "repeating", false); + shader.gradient_function = (repeating ? ShaderGradientFunction::RepeatingRadial : ShaderGradientFunction::Radial); + shader.p = Rml::Get(parameters, "center", Rml::Vector2f(0.f)); + shader.v = Rml::Vector2f(1.f) / Rml::Get(parameters, "radius", Rml::Vector2f(1.f)); + ApplyColorStopList(shader, parameters); + } + else if (name == "conic-gradient") + { + shader.type = CompiledShaderType::Gradient; + const bool repeating = Rml::Get(parameters, "repeating", false); + shader.gradient_function = (repeating ? ShaderGradientFunction::RepeatingConic : ShaderGradientFunction::Conic); + shader.p = Rml::Get(parameters, "center", Rml::Vector2f(0.f)); + const float angle = Rml::Get(parameters, "angle", 0.f); + shader.v = {Rml::Math::Cos(angle), Rml::Math::Sin(angle)}; + ApplyColorStopList(shader, parameters); + } + else if (name == "shader") + { + const Rml::String value = Rml::Get(parameters, "value", Rml::String()); + if (value == "creation") + { + shader.type = CompiledShaderType::Creation; + shader.dimensions = Rml::Get(parameters, "dimensions", Rml::Vector2f(0.f)); + } + } + + if (shader.type != CompiledShaderType::Invalid) + return reinterpret_cast(new CompiledShader(std::move(shader))); + + Rml::Log::Message(Rml::Log::LT_WARNING, "Unsupported shader type '%s'.", name.c_str()); + return {}; +} + +void RenderInterface_DX12::RenderShader(Rml::CompiledShaderHandle shader_handle, Rml::CompiledGeometryHandle geometry_handle, + Rml::Vector2f translation, Rml::TextureHandle texture) +{ + RMLUI_ZoneScopedN("DirectX 12 - RenderShader"); + RMLUI_ASSERT(shader_handle && geometry_handle); + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "RenderShader"); + + // fixing unreferenced parameter + (void)(texture); + + const CompiledShader& shader = *reinterpret_cast(shader_handle); + const CompiledShaderType type = shader.type; + const GeometryHandleType* geometry = reinterpret_cast(geometry_handle); + + switch (type) + { + case CompiledShaderType::Gradient: + { + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "Gradient"); + + RMLUI_ASSERT(shader.stop_positions.size() == shader.stop_colors.size()); + const int num_stops = (int)shader.stop_positions.size(); + + UseProgram(ProgramId::Gradient); + ConstantBufferType* p_cb = Get_ConstantBuffer(m_current_back_buffer_index); + RMLUI_ASSERTMSG(p_cb, "failed to obtain constant buffer for gradient"); + + struct CBV_Gradient { + Rml::Matrix4f transform; + Rml::Vector2f translate; + + int func; + int num_stops; + Rml::Vector2f starting_point; + Rml::Vector2f ending_point; + Rml::Vector4f stop_colors[MAX_NUM_STOPS]; + float stop_positions[MAX_NUM_STOPS]; + }; + + if (p_cb) + { + std::uint8_t* p_cb_begin = reinterpret_cast(p_cb->m_p_gpu_start_memory_for_binding_data); + RMLUI_ASSERTMSG(p_cb_begin, "ConstantBuffer must contain valid pointer to begin of gpu binding pointer for uploading data from CPU!"); + + if (p_cb_begin) + { + std::uint8_t* p_cb_begin_real = p_cb_begin + p_cb->m_alloc_info.offset; + RMLUI_ASSERTMSG(p_cb_begin_real, "failed to offset gpu begin pointer for constant buffer!"); + + if (p_cb_begin_real) + { + CBV_Gradient* p_casted = reinterpret_cast(p_cb_begin_real); + + p_casted->transform = m_constant_buffer_data_transform; + p_casted->translate = translation; + p_casted->func = (int)(shader.gradient_function); + p_casted->starting_point = shader.p; + p_casted->ending_point = shader.v; + p_casted->num_stops = num_stops; + std::memset(p_casted->stop_positions, 0, sizeof(CBV_Gradient::stop_positions)); + std::memset(p_casted->stop_colors, 0, sizeof(CBV_Gradient::stop_colors)); + + std::memcpy(&p_casted->stop_positions, shader.stop_positions.data(), num_stops * sizeof(float)); + std::memcpy(&p_casted->stop_colors, shader.stop_colors.data(), num_stops * sizeof(Rml::Vector4f)); + } + } + } + + if (p_cb) + { + auto* p_dx_constant_buffer = m_manager_buffer.Get_BufferByIndex(p_cb->m_alloc_info.buffer_index); + RMLUI_ASSERTMSG(p_dx_constant_buffer, "must be valid!"); + + if (p_dx_constant_buffer) + { + auto* p_dx_resource = p_dx_constant_buffer->GetResource(); + + RMLUI_ASSERTMSG(p_dx_resource, "must be valid!"); + + if (p_dx_resource) + { + D3D12_GPU_VIRTUAL_ADDRESS one_shared_cbv_for_different_shaders_address = + p_dx_resource->GetGPUVirtualAddress() + p_cb->m_alloc_info.offset; + + m_p_command_graphics_list->SetGraphicsRootConstantBufferView(0, one_shared_cbv_for_different_shaders_address); + m_p_command_graphics_list->SetGraphicsRootConstantBufferView(1, one_shared_cbv_for_different_shaders_address); + } + } + } + + m_p_command_graphics_list->IASetPrimitiveTopology(D3D12_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + auto* p_dx_buffer_vertex = m_manager_buffer.Get_BufferByIndex(geometry->Get_InfoVertex().buffer_index); + + RMLUI_ASSERTMSG(p_dx_buffer_vertex, "must be valid!"); + if (p_dx_buffer_vertex) + { + auto* p_dx_resource = p_dx_buffer_vertex->GetResource(); + + RMLUI_ASSERTMSG(p_dx_resource, "must be valid!"); + + if (p_dx_resource) + { + D3D12_VERTEX_BUFFER_VIEW view_vertex_buffer = {}; + + view_vertex_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + geometry->Get_InfoVertex().offset; + view_vertex_buffer.StrideInBytes = sizeof(Rml::Vertex); + view_vertex_buffer.SizeInBytes = static_cast(geometry->Get_InfoVertex().size); + + m_p_command_graphics_list->IASetVertexBuffers(0, 1, &view_vertex_buffer); + } + } + + auto* p_dx_buffer_index = m_manager_buffer.Get_BufferByIndex(geometry->Get_InfoIndex().buffer_index); + + RMLUI_ASSERTMSG(p_dx_buffer_index, "must be valid!"); + + if (p_dx_buffer_index) + { + auto* p_dx_resource = p_dx_buffer_index->GetResource(); + + RMLUI_ASSERTMSG(p_dx_resource, "must be valid!"); + + if (p_dx_resource) + { + D3D12_INDEX_BUFFER_VIEW view_index_buffer = {}; + + view_index_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + geometry->Get_InfoIndex().offset; + view_index_buffer.Format = DXGI_FORMAT::DXGI_FORMAT_R32_UINT; + view_index_buffer.SizeInBytes = static_cast(geometry->Get_InfoIndex().size); + + m_p_command_graphics_list->IASetIndexBuffer(&view_index_buffer); + } + } + + m_p_command_graphics_list->DrawIndexedInstanced(geometry->Get_NumIndices(), 1, 0, 0, 0); + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + + break; + } + case CompiledShaderType::Creation: + { + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "Creation"); + + UseProgram(ProgramId::Creation); + ConstantBufferType* p_cb = Get_ConstantBuffer(m_current_back_buffer_index); + RMLUI_ASSERTMSG(p_cb, "failed to obtain constant buffer for gradient"); + + struct CBV_Creation { + Rml::Matrix4f transform; + Rml::Vector2f translation; + Rml::Vector2f dimensions; + float time; + }; + + if (p_cb) + { + std::uint8_t* p_cb_begin = reinterpret_cast(p_cb->m_p_gpu_start_memory_for_binding_data); + RMLUI_ASSERTMSG(p_cb_begin, "ConstantBuffer must contain valid pointer to begin of gpu binding pointer for uploading data from CPU!"); + + if (p_cb_begin) + { + std::uint8_t* p_cb_begin_real = p_cb_begin + p_cb->m_alloc_info.offset; + RMLUI_ASSERTMSG(p_cb_begin_real, "failed to offset gpu begin pointer for constant buffer!"); + + if (p_cb_begin_real) + { + CBV_Creation* p_casted = reinterpret_cast(p_cb_begin_real); + + p_casted->transform = m_constant_buffer_data_transform; + p_casted->translation = translation; + + const double time = Rml::GetSystemInterface()->GetElapsedTime(); + p_casted->time = (float)time; + p_casted->dimensions = shader.dimensions; + } + } + } + + if (p_cb) + { + auto* p_dx_constant_buffer = m_manager_buffer.Get_BufferByIndex(p_cb->m_alloc_info.buffer_index); + RMLUI_ASSERTMSG(p_dx_constant_buffer, "must be valid!"); + + if (p_dx_constant_buffer) + { + auto* p_dx_resource = p_dx_constant_buffer->GetResource(); + + RMLUI_ASSERTMSG(p_dx_resource, "must be valid!"); + + if (p_dx_resource) + { + D3D12_GPU_VIRTUAL_ADDRESS one_shared_cbv_for_different_shaders_address = + p_dx_resource->GetGPUVirtualAddress() + p_cb->m_alloc_info.offset; + + m_p_command_graphics_list->SetGraphicsRootConstantBufferView(0, one_shared_cbv_for_different_shaders_address); + m_p_command_graphics_list->SetGraphicsRootConstantBufferView(1, one_shared_cbv_for_different_shaders_address); + } + } + } + + m_p_command_graphics_list->IASetPrimitiveTopology(D3D12_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + auto* p_dx_buffer_vertex = m_manager_buffer.Get_BufferByIndex(geometry->Get_InfoVertex().buffer_index); + + RMLUI_ASSERTMSG(p_dx_buffer_vertex, "must be valid!"); + if (p_dx_buffer_vertex) + { + auto* p_dx_resource = p_dx_buffer_vertex->GetResource(); + + RMLUI_ASSERTMSG(p_dx_resource, "must be valid!"); + + if (p_dx_resource) + { + D3D12_VERTEX_BUFFER_VIEW view_vertex_buffer = {}; + + view_vertex_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + geometry->Get_InfoVertex().offset; + view_vertex_buffer.StrideInBytes = sizeof(Rml::Vertex); + view_vertex_buffer.SizeInBytes = static_cast(geometry->Get_InfoVertex().size); + + m_p_command_graphics_list->IASetVertexBuffers(0, 1, &view_vertex_buffer); + } + } + + auto* p_dx_buffer_index = m_manager_buffer.Get_BufferByIndex(geometry->Get_InfoIndex().buffer_index); + + RMLUI_ASSERTMSG(p_dx_buffer_index, "must be valid!"); + + if (p_dx_buffer_index) + { + auto* p_dx_resource = p_dx_buffer_index->GetResource(); + + RMLUI_ASSERTMSG(p_dx_resource, "must be valid!"); + + if (p_dx_resource) + { + D3D12_INDEX_BUFFER_VIEW view_index_buffer = {}; + + view_index_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + geometry->Get_InfoIndex().offset; + view_index_buffer.Format = DXGI_FORMAT::DXGI_FORMAT_R32_UINT; + view_index_buffer.SizeInBytes = static_cast(geometry->Get_InfoIndex().size); + + m_p_command_graphics_list->IASetIndexBuffer(&view_index_buffer); + } + } + + m_p_command_graphics_list->DrawIndexedInstanced(geometry->Get_NumIndices(), 1, 0, 0, 0); + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + + break; + } + case CompiledShaderType::Invalid: + { + Rml::Log::Message(Rml::Log::LT_WARNING, "Unhandled render shader %d", (int)type); + break; + } + } + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); +} + +void RenderInterface_DX12::ReleaseShader(Rml::CompiledShaderHandle effect_handle) +{ + RMLUI_ZoneScopedN("DirectX 12 - ReleaseShader"); + delete reinterpret_cast(effect_handle); +} + +bool RenderInterface_DX12::IsSwapchainValid() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - IsSwapchainValid"); + return (m_p_swapchain != nullptr); +} + +void RenderInterface_DX12::RecreateSwapchain() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - RecreateSwapchain"); + SetViewport(m_width, m_height); +} + +ID3D12Fence* RenderInterface_DX12::Get_Fence() +{ + RMLUI_ZoneScopedN("DirectX 12 - Get_Fence"); + return m_p_backbuffer_fence; +} + +HANDLE RenderInterface_DX12::Get_FenceEvent() +{ + RMLUI_ZoneScopedN("DirectX 12 - Get_FenceEvent"); + return m_p_fence_event; +} + +Rml::Array& RenderInterface_DX12::Get_FenceValues() +{ + RMLUI_ZoneScopedN("DirectX 12 - Get_FenceValues"); + return m_backbuffers_fence_values; +} + +uint32_t RenderInterface_DX12::Get_CurrentFrameIndex() +{ + RMLUI_ZoneScopedN("DirectX 12 - Get_CurrentFrameIndex"); + return m_current_back_buffer_index; +} + +ID3D12Device* RenderInterface_DX12::Get_Device() const +{ + RMLUI_ZoneScopedN("DirectX 12 - Get_Device"); + return m_p_device; +} + +RenderInterface_DX12::TextureMemoryManager& RenderInterface_DX12::Get_TextureManager() +{ + RMLUI_ZoneScopedN("DirectX 12 - Get_TextureManager"); + return m_manager_texture; +} + +RenderInterface_DX12::BufferMemoryManager& RenderInterface_DX12::Get_BufferManager() +{ + RMLUI_ZoneScopedN("DirectX 12 - Get_BufferManager"); + return m_manager_buffer; +} + +unsigned char RenderInterface_DX12::Get_MSAASampleCount() const +{ + return m_msaa_sample_count; +} + +void RenderInterface_DX12::Set_UserFramebufferIndex(unsigned char framebuffer_index) +{ + m_current_back_buffer_index = static_cast(framebuffer_index); +} + +void RenderInterface_DX12::Set_UserRenderTarget(void* rtv_where_we_render_to) +{ + RMLUI_ASSERTMSG(rtv_where_we_render_to, "you can't pass empty rtv for rendering"); + m_p_user_rtv_present = reinterpret_cast(rtv_where_we_render_to); +} + +void RenderInterface_DX12::Set_UserDepthStencil(void* dsv_where_we_render_to) +{ + RMLUI_ASSERTMSG(dsv_where_we_render_to, "you can't pass empty dsv for rendering"); + m_p_user_dsv_present = reinterpret_cast(dsv_where_we_render_to); +} + +bool RenderInterface_DX12::CaptureScreen(int& out_width, int& out_height, int& out_num_components, Rml::UniquePtr& out_data) +{ + out_width = -1; + out_height = -1; + out_num_components = -1; + out_data = {}; + + if (!m_p_device || !m_p_swapchain || !m_p_command_allocator_screenshot || !m_p_command_graphics_list_screenshot || !m_p_command_queue || + !m_p_fence_screenshot || !m_p_fence_event_screenshot) + { + RMLUI_ERRORMSG("Early calling"); + return false; + } + HRESULT status = m_p_command_allocator_screenshot->Reset(); + RMLUI_DX_VERIFY_MSG(status, "failed ID3D12CommandAllocator::Reset (screenshot)"); + + status = m_p_command_graphics_list_screenshot->Reset(m_p_command_allocator_screenshot, nullptr); + RMLUI_DX_VERIFY_MSG(status, "failed ID3D12CommandGraphicsList::Reset (screenshot)"); + + ID3D12Resource* p_back_buffer{}; + status = m_p_swapchain->GetBuffer(m_p_swapchain->GetCurrentBackBufferIndex(), IID_PPV_ARGS(&p_back_buffer)); + + if (!SUCCEEDED(status) || !p_back_buffer) + { + RMLUI_ERRORMSG("failed to obtain swapchain's backbuffer"); + return false; + } + + const D3D12_RESOURCE_DESC& desc = p_back_buffer->GetDesc(); + +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] Obtained swapchain's backbuffer: w=%zu h=%d format=%s", desc.Width, desc.Height, + Rml::DXGIFormatToString(desc.Format)); +#endif + + UINT64 required_size = 0; + D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout = {}; + UINT64 source_row_size_bytes = 0; + UINT num_rows = 0; + + m_p_device->GetCopyableFootprints(&desc, 0, 1, 0, &layout, &num_rows, &source_row_size_bytes, &required_size); + + D3D12_HEAP_PROPERTIES heap_props = {}; + + heap_props.Type = D3D12_HEAP_TYPE_READBACK; + heap_props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heap_props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heap_props.CreationNodeMask = 1; + heap_props.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC cpu_readaccess_buffer_desc = {}; + cpu_readaccess_buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + cpu_readaccess_buffer_desc.Alignment = 0; + cpu_readaccess_buffer_desc.Width = required_size; + cpu_readaccess_buffer_desc.Height = 1; // since we just want Rml::byte array there's no need in two dimensionality + cpu_readaccess_buffer_desc.DepthOrArraySize = 1; + cpu_readaccess_buffer_desc.MipLevels = 1; + cpu_readaccess_buffer_desc.Format = DXGI_FORMAT_UNKNOWN; + cpu_readaccess_buffer_desc.SampleDesc.Count = 1; + cpu_readaccess_buffer_desc.SampleDesc.Quality = 0; + cpu_readaccess_buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + cpu_readaccess_buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE; + + ID3D12Resource* p_cpu_readaccess_buffer{}; + status = m_p_device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &cpu_readaccess_buffer_desc, + D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&p_cpu_readaccess_buffer)); + + if (!SUCCEEDED(status) || !p_cpu_readaccess_buffer) + { + RMLUI_ERRORMSG("failed to create buffer for CPU reading"); + return false; + } + + D3D12_RESOURCE_BARRIER barrier = {}; + + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = p_back_buffer; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + + m_p_command_graphics_list_screenshot->ResourceBarrier(1, &barrier); + + D3D12_TEXTURE_COPY_LOCATION src_loc = {}; + src_loc.pResource = p_back_buffer; + src_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + src_loc.SubresourceIndex = 0; + + D3D12_TEXTURE_COPY_LOCATION dst_loc = {}; + dst_loc.pResource = p_cpu_readaccess_buffer; + dst_loc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + dst_loc.PlacedFootprint = layout; + + m_p_command_graphics_list_screenshot->CopyTextureRegion(&dst_loc, 0, 0, 0, &src_loc, nullptr); + + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + m_p_command_graphics_list_screenshot->ResourceBarrier(1, &barrier); + + status = m_p_command_graphics_list_screenshot->Close(); + RMLUI_DX_VERIFY_MSG(status, "failed ID3D12CommandGraphicsList::Close"); + + ID3D12CommandList* p_lists[] = {m_p_command_graphics_list_screenshot}; + m_p_command_queue->ExecuteCommandLists(1, p_lists); + + status = m_p_command_queue->Signal(m_p_fence_screenshot, m_fence_screenshot_value); + RMLUI_DX_VERIFY_MSG(status, "failed ID3D12CommandQueue::Signal (screenshot)"); + + status = m_p_fence_screenshot->SetEventOnCompletion(m_fence_screenshot_value, m_p_fence_event_screenshot); + RMLUI_DX_VERIFY_MSG(status, "failed ID3D12Fence::SetEventOnCompletion (screenshot)"); + + WaitForSingleObject(m_p_fence_event_screenshot, INFINITE); + ++m_fence_screenshot_value; + + void* p_mapped_data = nullptr; + status = p_cpu_readaccess_buffer->Map(0, nullptr, &p_mapped_data); + RMLUI_DX_VERIFY_MSG(status, "failed to Map buffer (screenshot)"); + + bool result = false; + if (SUCCEEDED(status)) + { + const size_t source_num_components = 4; + const size_t target_num_components = 3; + const size_t width = desc.Width; + const size_t height = desc.Height; + + const size_t target_row_size_bytes = target_num_components * width; + const size_t target_data_size = target_row_size_bytes * height; + out_data.reset(new Rml::byte[target_data_size]); + + for (size_t y = 0; y < height; y++) + { + byte* source_row_origin = reinterpret_cast(p_mapped_data) + (height - 1 - y) * layout.Footprint.RowPitch; + byte* target_row_origin = out_data.get() + y * width * target_num_components; + + for (size_t x = 0; x < width; x++) + { + std::memcpy(target_row_origin + x * target_num_components, source_row_origin + x * source_num_components, target_num_components); + } + } + + out_width = static_cast(width); + out_height = static_cast(height); + out_num_components = target_num_components; + result = true; + } + + p_cpu_readaccess_buffer->Unmap(0, nullptr); + p_back_buffer->Release(); + p_cpu_readaccess_buffer->Release(); + +#ifdef RMLUI_DX_DEBUG + if (result) + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12]: successfully captured data of swapchain backbuffer"); +#endif + + return result; +} + +void RenderInterface_DX12::Initialize_Device() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Initialize_Device"); + RMLUI_ASSERTMSG(m_p_adapter, "you must call this when you initialized adapter!"); + + ID3D12Device2* p_device{}; + + RMLUI_DX_VERIFY_MSG(D3D12CreateDevice(m_p_adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&p_device)), "failed to D3D12CreateDevice"); + + m_p_device = p_device; + +#ifdef RMLUI_DX_DEBUG + ID3D12InfoQueue* p_queue{}; + if (p_device) + { + RMLUI_DX_VERIFY_MSG(p_device->QueryInterface(IID_PPV_ARGS(&p_queue)), "failed to QueryInterface of ID3D12InfoQueue"); + + if (p_queue) + { + // if implementation is good breaking will NOT be caused at all, but if something is bad you will get __debugbreak in Debug after + // ReportLiveObjects calling that means system works not correctly for D3D12 API at all and you must fix these problems what your device + // reported (it might be annoying because you will not see the result of ReportLiveObjects and you should comment these line of + // SetBreakOnSeverity in order to see full report from ReportLiveObjects calling) + p_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, 1); + p_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, 1); + + // Currently we get warnings about scissors being set with zero drawable area, i.e. p0.x == p1.x or p0.y == + // p1.y. This is how the RmlUi API works right now and is expected. We could add more conditions to avoid + // drawing in these situations, eeither on the RmlUi-side or in the renderer. But for now we ignore the + // warning, it shouldn't cause any issues. + // p_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, 1); + + // Suppress messages based on their severity level + D3D12_MESSAGE_SEVERITY p_sevs[] = {D3D12_MESSAGE_SEVERITY_INFO}; + + D3D12_INFO_QUEUE_FILTER info_filter = {}; + info_filter.DenyList.NumSeverities = _countof(p_sevs); + info_filter.DenyList.pSeverityList = p_sevs; + + RMLUI_DX_VERIFY_MSG(p_queue->PushStorageFilter(&info_filter), "failed to PushStorageFilter"); + + p_queue->Release(); + } + + ID3D12DebugDevice* p_sdk_device{}; + auto status = p_device->QueryInterface(IID_PPV_ARGS(&p_sdk_device)); + RMLUI_DX_VERIFY_MSG(status, "failed to obtain debug device!"); + + if (p_sdk_device) + { + status = p_sdk_device->SetFeatureMask(D3D12_DEBUG_FEATURE_CONSERVATIVE_RESOURCE_STATE_TRACKING); + RMLUI_DX_VERIFY_MSG(status, "failed to enable feature conservative resource state tracking"); + + p_sdk_device->Release(); + } + } +#endif +} + +void RenderInterface_DX12::Initialize_Adapter() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Initialize_Adapter"); + if (m_p_adapter) + { + m_p_adapter->Release(); + } + + m_p_adapter = Get_Adapter(false); +} + +void RenderInterface_DX12::Initialize_DebugLayer() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Initialize_DebugLayer"); +#ifdef RMLUI_DX_DEBUG + ID3D12Debug* p_debug{}; + + RMLUI_DX_VERIFY_MSG(D3D12GetDebugInterface(IID_PPV_ARGS(&p_debug)), "failed to D3D12GetDebugInterface"); + + if (p_debug) + { + p_debug->EnableDebugLayer(); + + #ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] Debug Layer = ENABLED!"); + #endif + + ID3D12Debug1* p_sdk_layers{}; + + auto status = p_debug->QueryInterface(IID_PPV_ARGS(&p_sdk_layers)); + + RMLUI_DX_VERIFY_MSG(status, + "failed to enable GPU based validation :( your driver or windows NT sdk doesn't support a such feature report to developers DX12 SDK or " + "to GPU vendor developers"); + + if (SUCCEEDED(status)) + { + p_sdk_layers->SetEnableGPUBasedValidation(TRUE); + p_sdk_layers->SetEnableSynchronizedCommandQueueValidation(TRUE); + p_sdk_layers->Release(); + + #ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] GPU validation = ENABLED!"); + #endif + } + + p_debug->Release(); + } + +#endif +} + +ID3D12CommandQueue* RenderInterface_DX12::Create_CommandQueue(D3D12_COMMAND_LIST_TYPE type) noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_CommandQueue"); + RMLUI_ASSERTMSG(m_p_device, "you must initialize device before calling this method"); + + ID3D12CommandQueue* p_result{}; + if (m_p_device) + { + D3D12_COMMAND_QUEUE_DESC desc = {}; + + desc.Type = type; + desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; + desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + desc.NodeMask = 0; + + RMLUI_DX_VERIFY_MSG(m_p_device->CreateCommandQueue(&desc, IID_PPV_ARGS(&p_result)), "failed to CreateCommandQueue"); + } + + return p_result; +} + +void RenderInterface_DX12::Initialize_Swapchain(int width, int height) noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Initialize_Swapchain"); + RMLUI_ASSERTMSG(width >= 0, "must not be a negative value"); + RMLUI_ASSERTMSG(height >= 0, "must not be a negative value"); + + // in dx12 0 means it will take size of window automatically + if (width < 0) + width = 0; + + if (height < 0) + height = 0; + + IDXGISwapChain4* p_swapchain{}; + IDXGIFactory4* p_factory{}; + + uint32_t create_factory_flags{}; + +#ifdef RMLUI_DX_DEBUG + create_factory_flags = DXGI_CREATE_FACTORY_DEBUG; +#endif + + RMLUI_DX_VERIFY_MSG(CreateDXGIFactory2(create_factory_flags, IID_PPV_ARGS(&p_factory)), "failed to CreateDXGIFactory2"); + + m_desc_sample.Count = m_msaa_sample_count; + m_desc_sample.Quality = 0; + + DXGI_SWAP_CHAIN_DESC1 desc = {}; + + desc.Width = width; + desc.Height = height; + desc.Format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc.Stereo = 0; + // since we can't use rt textures with different sample count than Swapchain's so we create framebuffers and presenting them on NO-MSAA + // Swapchain + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + + uint32_t flags_swapchain{}; + + if (CheckTearingSupport()) + { + flags_swapchain = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + m_is_use_tearing = true; + } + + desc.Flags = flags_swapchain; + + IDXGISwapChain1* p_swapchain1{}; + + RMLUI_DX_VERIFY_MSG(p_factory->CreateSwapChainForHwnd(m_p_command_queue, m_p_window_handle, &desc, nullptr, nullptr, &p_swapchain1), + "failed to CreateSwapChainForHwnd"); + + RMLUI_DX_VERIFY_MSG(p_factory->MakeWindowAssociation(m_p_window_handle, DXGI_MWA_NO_ALT_ENTER), "failed to MakeWindowAssociation"); + + RMLUI_DX_VERIFY_MSG(p_swapchain1->QueryInterface(IID_PPV_ARGS(&p_swapchain)), "failed to QueryInterface of IDXGISwapChain4"); + + m_p_swapchain = p_swapchain; + + if (p_swapchain1) + { + p_swapchain1->Release(); + } + + if (p_factory) + { + p_factory->Release(); + } +} + +void RenderInterface_DX12::Initialize_SyncPrimitives() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Initialize_SyncPrimitives"); + RMLUI_ASSERTMSG(m_p_device, "you must initialize device before calling this method!"); + + if (m_p_device) + { + for (int i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i) + { + m_backbuffers_fence_values[i] = 0; + } + + m_p_device->CreateFence(0, D3D12_FENCE_FLAGS::D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_p_backbuffer_fence)); + + m_p_fence_event = CreateEvent(NULL, FALSE, FALSE, NULL); + RMLUI_ASSERTMSG(m_p_fence_event, "failed to CreateEvent (WinAPI)"); + } +} + +void RenderInterface_DX12::Initialize_SyncPrimitives_Screenshot() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Initialize_SyncPrimitives_Screenshot"); + RMLUI_ASSERTMSG(m_p_device, "you must initialize device before calling this method!"); + + if (m_p_device) + { + HRESULT status = m_p_device->CreateFence(0, D3D12_FENCE_FLAGS::D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_p_fence_screenshot)); + RMLUI_DX_VERIFY_MSG(status, "ID3D12Device::CreateFence failed! (screenshot)"); + + m_p_fence_event_screenshot = CreateEvent(NULL, FALSE, FALSE, NULL); + RMLUI_ASSERTMSG(m_p_fence_event_screenshot, "failed to CreateEvent (Screenshot)"); + } +} + +void RenderInterface_DX12::Initialize_CommandAllocators() +{ + RMLUI_ZoneScopedN("DirectX 12 - Initialize_CommandAllocators"); + for (int i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i) + { + m_backbuffers_allocators[i] = Create_CommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT); + } +} + +void RenderInterface_DX12::Initialize_Allocator() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Initialize_Allocator"); + + // I guess better to isolate allocations from user side and using own allocators under of rmlui's renderer thus we forced for creating allocator + { + RMLUI_ASSERTMSG(!m_p_allocator, "forgot to destroy before initialization!"); + RMLUI_ASSERTMSG(m_p_device, "must be valid when you call this method!"); + RMLUI_ASSERTMSG(m_p_adapter, "must be valid when you call this method!"); + + D3D12MA::ALLOCATOR_DESC desc = {}; + desc.pDevice = m_p_device; + desc.pAdapter = m_p_adapter; + + D3D12MA::Allocator* p_allocator{}; + + RMLUI_DX_VERIFY_MSG(D3D12MA::CreateAllocator(&desc, &p_allocator), "failed to D3D12MA::CreateAllocator!"); + RMLUI_ASSERTMSG(p_allocator, "failed to create allocator!"); + + m_p_allocator = p_allocator; + } +} + +void RenderInterface_DX12::Destroy_Swapchain() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Destroy_Swapchain"); + if (m_p_swapchain) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_swapchain->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + m_p_swapchain = nullptr; +} + +void RenderInterface_DX12::Destroy_SyncPrimitives() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Destroy_SyncPrimitives"); + if (m_p_backbuffer_fence) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_backbuffer_fence->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + m_p_backbuffer_fence = nullptr; +} + +void RenderInterface_DX12::Destroy_CommandAllocators() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Destroy_CommandAllocators"); + for (ID3D12CommandAllocator* p_allocator : m_backbuffers_allocators) + { + RMLUI_ASSERTMSG(p_allocator, "early calling or object is damaged!"); + if (p_allocator) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = p_allocator->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + } +} + +void RenderInterface_DX12::Destroy_CommandList() noexcept {} + +void RenderInterface_DX12::Destroy_Allocator() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Destroy_Allocator"); + if (m_p_allocator) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_allocator->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } +} + +void RenderInterface_DX12::Destroy_SyncPrimitives_Screenshot() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Destroy_SyncPrimitives_Screenshot"); + + if (m_p_fence_screenshot) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_fence_screenshot->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + + m_p_fence_screenshot = nullptr; + } + + if (m_p_fence_event_screenshot) + { + CloseHandle(m_p_fence_event_screenshot); + m_p_fence_event_screenshot = nullptr; + } + + m_fence_screenshot_value = 0; +} + +void RenderInterface_DX12::Flush() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Flush"); + Signal(m_current_back_buffer_index); + RMLUI_ASSERTMSG(m_p_backbuffer_fence, "you must initialize ID3D12Fence first!"); + RMLUI_ASSERTMSG(m_p_fence_event, "you must initialize fence event (HANDLE)"); + + if (m_p_backbuffer_fence) + { + if (m_p_fence_event) + { + RMLUI_DX_VERIFY_MSG( + m_p_backbuffer_fence->SetEventOnCompletion(m_backbuffers_fence_values.at(m_current_back_buffer_index), m_p_fence_event), + "failed to SetEventOnCompletion"); + WaitForSingleObjectEx(m_p_fence_event, INFINITE, FALSE); + } + } + + m_backbuffers_fence_values[m_current_back_buffer_index]++; +} + +uint64_t RenderInterface_DX12::Signal(uint32_t frame_index) noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Signal"); + RMLUI_ASSERTMSG(m_p_command_queue, "you must initialize it first before calling this method!"); + RMLUI_ASSERTMSG(m_p_backbuffer_fence, "you must initialize it first before calling this method!"); + + if (m_p_command_queue) + { + if (m_p_backbuffer_fence) + { + auto value = (m_backbuffers_fence_values.at(frame_index)); + + RMLUI_DX_VERIFY_MSG(m_p_command_queue->Signal(m_p_backbuffer_fence, value), "failed to command queue::Signal!"); + + return value; + } + } + + return 0; +} + +void RenderInterface_DX12::WaitForFenceValue(uint32_t frame_index) +{ + RMLUI_ZoneScopedN("DirectX 12 - WaitForFenceValue"); + RMLUI_ASSERTMSG(m_p_backbuffer_fence, "you must initialize ID3D12Fence first!"); + RMLUI_ASSERTMSG(m_p_fence_event, "you must initialize fence event (HANDLE)"); + + if (m_p_backbuffer_fence) + { + if (m_p_fence_event) + { + if (m_p_backbuffer_fence->GetCompletedValue() < m_backbuffers_fence_values.at(frame_index)) + { + RMLUI_DX_VERIFY_MSG(m_p_backbuffer_fence->SetEventOnCompletion(m_backbuffers_fence_values.at(frame_index), m_p_fence_event), + "failed to SetEventOnCompletion"); + WaitForSingleObjectEx(m_p_fence_event, INFINITE, FALSE); + } + } + } +} + +void RenderInterface_DX12::Create_Resources_DependentOnSize() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resources_DependentOnSize"); + Create_Resource_RenderTargetViews(); + // Create_Resource_Pipelines(); +} + +void RenderInterface_DX12::Destroy_Resources_DependentOnSize() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Destroy_Resources_DependentOnSize"); + // Destroy_Resource_Pipelines(); + Destroy_Resource_RenderTagetViews(); +} + +void RenderInterface_DX12::Create_Resource_DepthStencil() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_DepthStencil"); + RMLUI_ASSERTMSG(m_p_descriptor_heap_depthstencil, "you must initialize this descriptor heap before calling this method!"); + RMLUI_ASSERTMSG(m_p_device, "you must create device!"); + RMLUI_ASSERTMSG(m_width > 0, "invalid width"); + RMLUI_ASSERTMSG(m_height > 0, "invalid height"); + + D3D12MA::ALLOCATION_DESC desc_alloc = {}; + desc_alloc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + + D3D12_RESOURCE_DESC desc_texture = {}; + desc_texture.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc_texture.Format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + desc_texture.MipLevels = 1; + desc_texture.Width = m_width; + desc_texture.Height = m_height; + desc_texture.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + desc_texture.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + desc_texture.SampleDesc.Count = 1; + desc_texture.SampleDesc.Quality = 0; + desc_texture.DepthOrArraySize = 1; + desc_texture.Alignment = 0; + + D3D12_CLEAR_VALUE depth_optimized_clear_value = {}; + depth_optimized_clear_value.Format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + depth_optimized_clear_value.DepthStencil.Depth = RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_DEPTH_VALUE; + depth_optimized_clear_value.DepthStencil.Stencil = RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_STENCIL_VALUE; + + ID3D12Resource* p_temp{}; + auto status = m_p_allocator->CreateResource(&desc_alloc, &desc_texture, D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_DEPTH_WRITE, + &depth_optimized_clear_value, &m_p_depthstencil_resource, IID_PPV_ARGS(&p_temp)); + + RMLUI_DX_VERIFY_MSG(status, "failed to create resource as depth stencil texture!"); + + RMLUI_ASSERTMSG(m_p_depthstencil_resource, "must be created!"); + RMLUI_ASSERTMSG(p_temp, "must be created!"); + +#ifdef RMLUI_DX_DEBUG + m_p_depthstencil_resource->SetName(L"DepthStencil texture (resource)"); +#endif + + D3D12_DEPTH_STENCIL_VIEW_DESC desc_view = {}; + desc_view.Format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + desc_view.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + desc_view.Flags = D3D12_DSV_FLAG_NONE; + + m_p_device->CreateDepthStencilView(m_p_depthstencil_resource->GetResource(), &desc_view, + m_p_descriptor_heap_depthstencil->GetCPUDescriptorHandleForHeapStart()); +} + +void RenderInterface_DX12::Destroy_Resource_DepthStencil() +{ + RMLUI_ZoneScopedN("DirectX 12 - Destroy_Resource_DepthStencil"); + RMLUI_ASSERTMSG(m_p_depthstencil_resource, "you must create resource for calling this method!"); + RMLUI_ASSERTMSG(m_p_depthstencil_resource->GetResource(), "must be valid!"); + + if (m_p_depthstencil_resource) + { + if (m_p_depthstencil_resource->GetResource()) + { + m_p_depthstencil_resource->GetResource()->Release(); + } + + RMLUI_ATTR_ASSERT_VARIABLE auto count = m_p_depthstencil_resource->Release(); + RMLUI_ASSERTMSG(count == 0, "leak!"); + + m_p_depthstencil_resource = nullptr; + } +} + +ID3D12DescriptorHeap* RenderInterface_DX12::Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, + uint32_t descriptor_count) noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_DescriptorHeap"); + RMLUI_ASSERTMSG(m_p_device, "early calling you have to initialize device first"); + + ID3D12DescriptorHeap* p_result{}; + + D3D12_DESCRIPTOR_HEAP_DESC desc = {}; + desc.NumDescriptors = descriptor_count; + desc.Type = type; + desc.Flags = flags; + + RMLUI_DX_VERIFY_MSG(m_p_device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&p_result)), "failed to CreateDescriptorHeap"); + + return p_result; +} + +ID3D12CommandAllocator* RenderInterface_DX12::Create_CommandAllocator(D3D12_COMMAND_LIST_TYPE type) +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_CommandAllocator"); + ID3D12CommandAllocator* p_result{}; + + RMLUI_ASSERTMSG(m_p_device, "you must initialize device first!"); + + if (m_p_device) + { + RMLUI_DX_VERIFY_MSG(m_p_device->CreateCommandAllocator(type, IID_PPV_ARGS(&p_result)), "failed to CreateCommandAllocator"); + + RMLUI_ASSERTMSG(p_result, "can't allocate command allocator!"); + } + + return p_result; +} + +ID3D12GraphicsCommandList* RenderInterface_DX12::Create_CommandList(ID3D12CommandAllocator* p_allocator, D3D12_COMMAND_LIST_TYPE type) noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_CommandList"); + ID3D12GraphicsCommandList* p_result{}; + + RMLUI_ASSERTMSG(m_p_device, "you must initialize device first!"); + RMLUI_ASSERTMSG(p_allocator, "you must pass a valid instance of ID3D12CommandAllocator*"); + + if (m_p_device) + { + RMLUI_DX_VERIFY_MSG(m_p_device->CreateCommandList(0, type, p_allocator, nullptr, IID_PPV_ARGS(&p_result)), "failed to CreateCommandList"); + RMLUI_ASSERTMSG(p_result, "can't allocator command list!"); + + if (p_result) + { + RMLUI_DX_VERIFY_MSG(p_result->Close(), "failed to Close command list!"); + } + } + + return p_result; +} + +void RenderInterface_DX12::Create_Resource_RenderTargetViews() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_RenderTargetViews"); + RMLUI_ASSERTMSG(m_p_device, "early calling you have to initialize device first!"); + RMLUI_ASSERTMSG(m_p_swapchain, "early calling you have to initialize swapchain first!"); + RMLUI_ASSERTMSG(m_p_descriptor_heap_render_target_view, "early calling you have to initialize descriptor heap for render target views first!"); + + if (m_p_device) + { + if (m_p_swapchain) + { + if (m_p_descriptor_heap_render_target_view) + { + auto rtv_size = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle(m_p_descriptor_heap_render_target_view->GetCPUDescriptorHandleForHeapStart()); + + for (auto i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i) + { + ID3D12Resource* p_back_buffer{}; + + RMLUI_DX_VERIFY_MSG(m_p_swapchain->GetBuffer(i, IID_PPV_ARGS(&p_back_buffer)), "failed to GetBuffer from swapchain"); + + m_p_device->CreateRenderTargetView(p_back_buffer, nullptr, rtv_handle); + + m_backbuffers_resources[i] = p_back_buffer; + + rtv_handle.ptr += (rtv_size); + } + } + } + } +} + +void RenderInterface_DX12::Destroy_Resource_RenderTagetViews() +{ + RMLUI_ZoneScopedN("DirectX 12 - Destroy_Resource_RenderTargetViews"); + for (ID3D12Resource* p_backbuffer : m_backbuffers_resources) + { + RMLUI_ASSERTMSG(p_backbuffer, "it is strange must be always valid pointer!"); + + if (p_backbuffer) + { + p_backbuffer->Release(); + } + } + + for (int i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i) + { + m_backbuffers_resources[i] = nullptr; + } +} + +void RenderInterface_DX12::Destroy_Resource_For_Shaders() +{ + RMLUI_ZoneScopedN("DirectX 12 - Destroy_Resource_For_Shaders"); + for (auto& vec_cb_per_frame : m_constantbuffers) + { + for (auto& cb : vec_cb_per_frame) + { + m_manager_buffer.Free_ConstantBuffer(&cb); + } + } + + for (unsigned char i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i) + { + m_constantbuffers[i].clear(); + } + + Update_PendingForDeletion_Geometry(); +} + +void RenderInterface_DX12::Free_Geometry(RenderInterface_DX12::GeometryHandleType* p_handle) +{ + RMLUI_ZoneScopedN("DirectX 12 - Free_Geometry"); + RMLUI_ASSERTMSG(p_handle, "invalid handle"); + + if (p_handle) + { + m_manager_buffer.Free_Geometry(p_handle); + delete p_handle; + } +} + +void RenderInterface_DX12::Free_Texture(RenderInterface_DX12::TextureHandleType* p_handle) +{ + RMLUI_ZoneScopedN("DirectX 12 - Free_Texture"); + RMLUI_ASSERTMSG(p_handle, "must be valid!"); + + if (p_handle) + { +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] Destroyed texture: [%s]", p_handle->Get_ResourceName().c_str()); +#endif + + m_manager_texture.Free_Texture(p_handle); + delete p_handle; + } +} + +void RenderInterface_DX12::Update_PendingForDeletion_Geometry() +{ + RMLUI_ZoneScopedN("DirectX 12 - Update_PendingForDeletion_Geometry"); + for (auto& p_handle : m_pending_for_deletion_geometry) + { + Free_Geometry(p_handle); + } + + m_pending_for_deletion_geometry.clear(); +} + +void RenderInterface_DX12::Create_Resource_Pipelines() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipelines"); + for (auto& vec_cb : m_constantbuffers) + { + vec_cb.resize(RMLUI_RENDER_BACKEND_FIELD_PREALLOCATED_CONSTANTBUFFERS); + } + + for (auto& vec_cb : m_constantbuffers) + { + for (auto& cb : vec_cb) + { + const auto& info = m_manager_buffer.Alloc_ConstantBuffer(&cb, kAllocationSizeMax_ConstantBuffer); + cb.m_alloc_info = info; + } + } + + m_pending_for_deletion_geometry.reserve(RMLUI_RENDER_BACKEND_FIELD_PREALLOCATED_CONSTANTBUFFERS); + + Create_Resource_Pipeline_BlendMask(); + Create_Resource_Pipeline_Blur(); + Create_Resource_Pipeline_Color(); + Create_Resource_Pipeline_ColorMatrix(); + Create_Resource_Pipeline_Creation(); + Create_Resource_Pipeline_DropShadow(); + Create_Resource_Pipeline_Gradient(); + Create_Resource_Pipeline_Passthrough(); + Create_Resource_Pipeline_Passthrough_NoBlend(); + Create_Resource_Pipeline_Texture(); +} + +void RenderInterface_DX12::Create_Resource_Pipeline_Color() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Color"); + RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!"); + + if (m_p_device) + { + D3D12_ROOT_DESCRIPTOR descriptor_cbv; + descriptor_cbv.RegisterSpace = 0; + descriptor_cbv.ShaderRegister = 0; + + D3D12_ROOT_PARAMETER parameters[1]; + + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[0].Descriptor = descriptor_cbv; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; + + D3D12_ROOT_SIGNATURE_DESC desc_rootsignature; + desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + 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; + desc_rootsignature.NumStaticSamplers = 0; + desc_rootsignature.pParameters = parameters; + desc_rootsignature.NumParameters = _countof(parameters); + desc_rootsignature.pStaticSamplers = nullptr; + + ID3DBlob* p_signature{}; + ID3DBlob* p_error{}; + auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error); + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Color_Stencil_Always)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + // stencil version + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Color_Stencil_Intersect)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Color_Stencil_Set)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Color_Stencil_SetInverse)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Color_Stencil_Equal)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Color_Stencil_Disabled)])); + RMLUI_DX_VERIFY_MSG(status, "failed to Color_Stencil_Disabled"); + + if (p_signature) + { + p_signature->Release(); + p_signature = nullptr; + } + + if (p_error) + { + p_error->Release(); + p_error = nullptr; + } + + ID3DBlob* p_shader_vertex{}; + ID3DBlob* p_shader_pixel{}; + ID3DBlob* p_error_buff{}; + + status = D3DCompile(pShaderSourceText_Vertex, sizeof(pShaderSourceText_Vertex), nullptr, nullptr, nullptr, "main", "vs_5_0", + m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer()); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + status = D3DCompile(pShaderSourceText_Color, sizeof(pShaderSourceText_Color), nullptr, nullptr, nullptr, "main", "ps_5_0", + m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)(p_error_buff->GetBufferPointer())); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {}; + desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize(); + desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer(); + + D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {}; + desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize(); + desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer(); + + D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; + + D3D12_INPUT_LAYOUT_DESC desc_input_layout = {}; + + desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC); + desc_input_layout.pInputElementDescs = desc_input_layout_elements; + + D3D12_RASTERIZER_DESC desc_rasterizer = {}; + + desc_rasterizer.FillMode = D3D12_FILL_MODE_SOLID; + desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE; + desc_rasterizer.FrontCounterClockwise = FALSE; + desc_rasterizer.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + desc_rasterizer.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + desc_rasterizer.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + desc_rasterizer.DepthClipEnable = FALSE; + desc_rasterizer.MultisampleEnable = FALSE; + desc_rasterizer.AntialiasedLineEnable = FALSE; + desc_rasterizer.ForcedSampleCount = 0; + desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + + D3D12_BLEND_DESC desc_blend_state = {}; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + TRUE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc; + + D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {}; + + desc_depth_stencil.DepthEnable = FALSE; + desc_depth_stencil.StencilEnable = TRUE; + desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + desc_depth_stencil.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {}; + + desc_pipeline.InputLayout = desc_input_layout; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Color_Stencil_Always)]; + desc_pipeline.VS = desc_bytecode_vertex_shader; + desc_pipeline.PS = desc_bytecode_pixel_shader; + desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_pipeline.DSVFormat = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + desc_pipeline.SampleDesc = m_desc_sample; + desc_pipeline.SampleMask = 0xffffffff; + desc_pipeline.RasterizerState = desc_rasterizer; + desc_pipeline.BlendState = desc_blend_state; + desc_pipeline.DepthStencilState = desc_depth_stencil; + desc_pipeline.NumRenderTargets = 1; + + status = + m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Color_Stencil_Always)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (color)"); + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_INCR; + desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace; + + desc_pipeline.DepthStencilState = desc_depth_stencil; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Color_Stencil_Intersect)]; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC editedRenderTargetBlend = { + TRUE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + 0, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = editedRenderTargetBlend; + + desc_pipeline.BlendState = desc_blend_state; + + status = + m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Color_Stencil_Intersect)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (color_stencil_intersect)"); + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE; + desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace; + + desc_pipeline.DepthStencilState = desc_depth_stencil; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Color_Stencil_SetInverse)]; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, + IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Color_Stencil_SetInverse)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (color_stencil_setinverse)"); + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE; + desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace; + + desc_pipeline.DepthStencilState = desc_depth_stencil; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Color_Stencil_Set)]; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Color_Stencil_Set)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (color_stencil_set)"); + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace; + + desc_pipeline.DepthStencilState = desc_depth_stencil; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Color_Stencil_Equal)]; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC editedRenderTargetBlend2 = { + TRUE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = editedRenderTargetBlend2; + + desc_pipeline.BlendState = desc_blend_state; + + status = + m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Color_Stencil_Equal)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Color_Stencil_Equal)"); + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace; + desc_depth_stencil.StencilEnable = FALSE; + + desc_pipeline.DepthStencilState = desc_depth_stencil; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Color_Stencil_Disabled)]; + + desc_pipeline.BlendState = desc_blend_state; + + status = + m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Color_Stencil_Disabled)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Color_Stencil_Disabled)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::Color_Stencil_Always)]->SetName(TEXT("rs of Color_Stencil_Always")); + m_pipelines[static_cast(ProgramId::Color_Stencil_Always)]->SetName(TEXT("pipeline Color_Stencil_Always")); + + m_root_signatures[static_cast(ProgramId::Color_Stencil_Set)]->SetName(TEXT("rs of Color_Stencil_Set")); + m_pipelines[static_cast(ProgramId::Color_Stencil_Set)]->SetName(TEXT("pipeline Color_Stencil_Set")); + + m_root_signatures[static_cast(ProgramId::Color_Stencil_SetInverse)]->SetName(TEXT("rs of Color_Stencil_SetInverse")); + m_pipelines[static_cast(ProgramId::Color_Stencil_SetInverse)]->SetName(TEXT("pipeline Color_Stencil_SetInverse")); + + m_root_signatures[static_cast(ProgramId::Color_Stencil_Intersect)]->SetName(TEXT("rs of Color_Stencil_Intersect")); + m_pipelines[static_cast(ProgramId::Color_Stencil_Intersect)]->SetName(TEXT("pipeline Color_Stencil_Intersect")); + + m_root_signatures[static_cast(ProgramId::Color_Stencil_Equal)]->SetName(TEXT("rs of Color_Stencil_Equal")); + m_pipelines[static_cast(ProgramId::Color_Stencil_Equal)]->SetName(TEXT("pipeline Color_Stencil_Equal")); + + m_root_signatures[static_cast(ProgramId::Color_Stencil_Disabled)]->SetName(TEXT("rs of Color_Stencil_Disabled")); + m_pipelines[static_cast(ProgramId::Color_Stencil_Disabled)]->SetName(TEXT("pipeline Color_Stencil_Disabled")); +#endif + } +} + +void RenderInterface_DX12::Create_Resource_Pipeline_Texture() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Texture"); + RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!"); + + if (m_p_device) + { + D3D12_DESCRIPTOR_RANGE ranges[1]; + ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + ranges[0].NumDescriptors = 1; + ranges[0].BaseShaderRegister = 0; + ranges[0].RegisterSpace = 0; + ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_DESCRIPTOR_TABLE table{}; + table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0])); + table.pDescriptorRanges = ranges; + + D3D12_ROOT_DESCRIPTOR descriptor_cbv{}; + descriptor_cbv.RegisterSpace = 0; + descriptor_cbv.ShaderRegister = 0; + + D3D12_ROOT_PARAMETER parameters[2]; + parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[1].DescriptorTable = table; + parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[0].Descriptor = descriptor_cbv; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; + + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_ROOT_SIGNATURE_DESC desc_rootsignature; + desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + 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; + desc_rootsignature.NumParameters = _countof(parameters); + desc_rootsignature.pParameters = parameters; + desc_rootsignature.pStaticSamplers = &sampler; + desc_rootsignature.NumStaticSamplers = 1; + + ID3DBlob* p_signature{}; + ID3DBlob* p_error{}; + auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s", + (char*)p_error->GetBufferPointer()); + } +#endif + + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Texture_Stencil_Always)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Texture_Stencil_Equal)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Texture_Stencil_Disabled)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + if (p_signature) + { + p_signature->Release(); + p_signature = nullptr; + } + + if (p_error) + { + p_error->Release(); + p_error = nullptr; + } + + ID3DBlob* p_shader_vertex{}; + ID3DBlob* p_shader_pixel{}; + ID3DBlob* p_error_buff{}; + + const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL}; + + status = D3DCompile(pShaderSourceText_Vertex, sizeof(pShaderSourceText_Vertex), nullptr, macros, nullptr, "main", "vs_5_0", + m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer()); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + status = D3DCompile(pShaderSourceText_Texture, sizeof(pShaderSourceText_Texture), nullptr, nullptr, nullptr, "main", "ps_5_0", + m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", + (char*)(p_error_buff->GetBufferPointer())); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {}; + desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize(); + desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer(); + + D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {}; + desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize(); + desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer(); + + D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; + + D3D12_INPUT_LAYOUT_DESC desc_input_layout = {}; + + desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC); + desc_input_layout.pInputElementDescs = desc_input_layout_elements; + + D3D12_RASTERIZER_DESC desc_rasterizer = {}; + + desc_rasterizer.AntialiasedLineEnable = FALSE; + desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE; + desc_rasterizer.DepthBias = 0; + desc_rasterizer.DepthBiasClamp = 0; + desc_rasterizer.DepthClipEnable = FALSE; + desc_rasterizer.ForcedSampleCount = 0; + desc_rasterizer.FrontCounterClockwise = FALSE; + desc_rasterizer.MultisampleEnable = FALSE; + desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID; + desc_rasterizer.SlopeScaledDepthBias = 0; + + D3D12_BLEND_DESC desc_blend_state = {}; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + TRUE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc; + + D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {}; + + desc_depth_stencil.DepthEnable = FALSE; + desc_depth_stencil.StencilEnable = TRUE; + desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + desc_depth_stencil.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {}; + + desc_pipeline.InputLayout = desc_input_layout; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Texture_Stencil_Always)]; + desc_pipeline.VS = desc_bytecode_vertex_shader; + desc_pipeline.PS = desc_bytecode_pixel_shader; + desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_pipeline.DSVFormat = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + desc_pipeline.SampleDesc = m_desc_sample; + desc_pipeline.SampleMask = 0xffffffff; + desc_pipeline.RasterizerState = desc_rasterizer; + desc_pipeline.BlendState = desc_blend_state; + desc_pipeline.NumRenderTargets = 1; + desc_pipeline.DepthStencilState = desc_depth_stencil; + + status = + m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Texture_Stencil_Always)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Texture_Stencil_Always)"); + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace; + + desc_pipeline.DepthStencilState = desc_depth_stencil; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Texture_Stencil_Equal)]; + + status = + m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Texture_Stencil_Equal)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Texture_Stencil_Equal)"); + + desc_depth_stencil.StencilEnable = FALSE; + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace; + + desc_pipeline.DepthStencilState = desc_depth_stencil; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Texture_Stencil_Disabled)]; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, + IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Texture_Stencil_Disabled)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Texture_Stencil_Disabled)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::Texture_Stencil_Always)]->SetName(TEXT("rs of Texture_Stencil_Always")); + m_pipelines[static_cast(ProgramId::Texture_Stencil_Always)]->SetName(TEXT("pipeline Texture_Stencil_Always")); + + m_root_signatures[static_cast(ProgramId::Texture_Stencil_Equal)]->SetName(TEXT("rs of Texture_Stencil_Equal")); + m_pipelines[static_cast(ProgramId::Texture_Stencil_Equal)]->SetName(TEXT("pipeline Texture_Stencil_Equal")); + + m_root_signatures[static_cast(ProgramId::Texture_Stencil_Disabled)]->SetName(TEXT("rs of Texture_Stencil_Disabled")); + m_pipelines[static_cast(ProgramId::Texture_Stencil_Disabled)]->SetName(TEXT("pipeline Texture_Stencil_Disabled")); +#endif + } +} + +void RenderInterface_DX12::Create_Resource_Pipeline_Gradient() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Gradient"); + RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!"); + + if (m_p_device) + { + D3D12_ROOT_DESCRIPTOR descriptor_cbv{}; + descriptor_cbv.RegisterSpace = 0; + descriptor_cbv.ShaderRegister = 0; + D3D12_ROOT_PARAMETER parameters[2]; + + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[0].Descriptor = descriptor_cbv; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; + + parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[1].Descriptor = descriptor_cbv; + parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_ROOT_SIGNATURE_DESC desc_rootsignature; + desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + 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; + desc_rootsignature.NumStaticSamplers = 0; + desc_rootsignature.pParameters = parameters; + desc_rootsignature.NumParameters = _countof(parameters); + desc_rootsignature.pStaticSamplers = nullptr; + + ID3DBlob* p_signature{}; + ID3DBlob* p_error{}; + auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error); + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Gradient)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + if (p_signature) + { + p_signature->Release(); + p_signature = nullptr; + } + + if (p_error) + { + p_error->Release(); + p_error = nullptr; + } + + ID3DBlob* p_shader_vertex{}; + ID3DBlob* p_shader_pixel{}; + ID3DBlob* p_error_buff{}; + + status = D3DCompile(pShaderSourceText_Vertex, sizeof(pShaderSourceText_Vertex), nullptr, nullptr, nullptr, "main", "vs_5_0", + m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer()); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + status = D3DCompile(pShaderSourceText_Pixel_Gradient, sizeof(pShaderSourceText_Pixel_Gradient), nullptr, nullptr, nullptr, "main", "ps_5_0", + m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)(p_error_buff->GetBufferPointer())); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {}; + desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize(); + desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer(); + + D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {}; + desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize(); + desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer(); + + D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; + + D3D12_INPUT_LAYOUT_DESC desc_input_layout = {}; + + desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC); + desc_input_layout.pInputElementDescs = desc_input_layout_elements; + + D3D12_RASTERIZER_DESC desc_rasterizer = {}; + + desc_rasterizer.FillMode = D3D12_FILL_MODE_SOLID; + desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE; + desc_rasterizer.FrontCounterClockwise = FALSE; + desc_rasterizer.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + desc_rasterizer.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + desc_rasterizer.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + desc_rasterizer.DepthClipEnable = FALSE; + desc_rasterizer.MultisampleEnable = FALSE; + desc_rasterizer.AntialiasedLineEnable = FALSE; + desc_rasterizer.ForcedSampleCount = 0; + desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + + D3D12_BLEND_DESC desc_blend_state = {}; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + TRUE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc; + + D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {}; + + desc_depth_stencil.DepthEnable = FALSE; + desc_depth_stencil.StencilEnable = TRUE; + desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + desc_depth_stencil.StencilWriteMask = 0x0; + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {}; + + desc_pipeline.InputLayout = desc_input_layout; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Gradient)]; + desc_pipeline.VS = desc_bytecode_vertex_shader; + desc_pipeline.PS = desc_bytecode_pixel_shader; + desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_pipeline.DSVFormat = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + desc_pipeline.SampleDesc = m_desc_sample; + desc_pipeline.SampleMask = 0xffffffff; + desc_pipeline.RasterizerState = desc_rasterizer; + desc_pipeline.BlendState = desc_blend_state; + desc_pipeline.DepthStencilState = desc_depth_stencil; + desc_pipeline.NumRenderTargets = 1; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Gradient)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Gradient)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::Gradient)]->SetName(TEXT("rs of Gradient")); + m_pipelines[static_cast(ProgramId::Gradient)]->SetName(TEXT("pipeline Gradient")); +#endif + } +} + +void RenderInterface_DX12::Create_Resource_Pipeline_Creation() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Creation"); + + RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!"); + + if (m_p_device) + { + D3D12_ROOT_DESCRIPTOR descriptor_cbv{}; + descriptor_cbv.RegisterSpace = 0; + descriptor_cbv.ShaderRegister = 0; + D3D12_ROOT_PARAMETER parameters[2]; + + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[0].Descriptor = descriptor_cbv; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; + + parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[1].Descriptor = descriptor_cbv; + parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_ROOT_SIGNATURE_DESC desc_rootsignature; + desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + 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; + desc_rootsignature.NumStaticSamplers = 0; + desc_rootsignature.pParameters = parameters; + desc_rootsignature.NumParameters = _countof(parameters); + desc_rootsignature.pStaticSamplers = nullptr; + + ID3DBlob* p_signature{}; + ID3DBlob* p_error{}; + auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error); + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Creation)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + if (p_signature) + { + p_signature->Release(); + p_signature = nullptr; + } + + if (p_error) + { + p_error->Release(); + p_error = nullptr; + } + + ID3DBlob* p_shader_vertex{}; + ID3DBlob* p_shader_pixel{}; + ID3DBlob* p_error_buff{}; + + status = D3DCompile(pShaderSourceText_Vertex, sizeof(pShaderSourceText_Vertex), nullptr, nullptr, nullptr, "main", "vs_5_0", + m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer()); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + status = D3DCompile(pShaderSourceText_Pixel_Creation, sizeof(pShaderSourceText_Pixel_Creation), nullptr, nullptr, nullptr, "main", "ps_5_0", + m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)(p_error_buff->GetBufferPointer())); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {}; + desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize(); + desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer(); + + D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {}; + desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize(); + desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer(); + + D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; + + D3D12_INPUT_LAYOUT_DESC desc_input_layout = {}; + + desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC); + desc_input_layout.pInputElementDescs = desc_input_layout_elements; + + D3D12_RASTERIZER_DESC desc_rasterizer = {}; + + desc_rasterizer.FillMode = D3D12_FILL_MODE_SOLID; + desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE; + desc_rasterizer.FrontCounterClockwise = FALSE; + desc_rasterizer.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + desc_rasterizer.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + desc_rasterizer.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + desc_rasterizer.DepthClipEnable = FALSE; + desc_rasterizer.MultisampleEnable = FALSE; + desc_rasterizer.AntialiasedLineEnable = FALSE; + desc_rasterizer.ForcedSampleCount = 0; + desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + + D3D12_BLEND_DESC desc_blend_state = {}; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + TRUE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc; + + D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {}; + + desc_depth_stencil.DepthEnable = FALSE; + desc_depth_stencil.StencilEnable = TRUE; + desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + desc_depth_stencil.StencilWriteMask = 0x0; + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {}; + + desc_pipeline.InputLayout = desc_input_layout; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Creation)]; + desc_pipeline.VS = desc_bytecode_vertex_shader; + desc_pipeline.PS = desc_bytecode_pixel_shader; + desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_pipeline.DSVFormat = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + desc_pipeline.SampleDesc = m_desc_sample; + desc_pipeline.SampleMask = 0xffffffff; + desc_pipeline.RasterizerState = desc_rasterizer; + desc_pipeline.BlendState = desc_blend_state; + desc_pipeline.DepthStencilState = desc_depth_stencil; + desc_pipeline.NumRenderTargets = 1; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Creation)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Creation)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::Creation)]->SetName(TEXT("rs of Creation")); + m_pipelines[static_cast(ProgramId::Creation)]->SetName(TEXT("pipeline Creation")); +#endif + } +} + +void RenderInterface_DX12::Create_Resource_Pipeline_Passthrough() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Passthrough"); + RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!"); + + if (m_p_device) + { + D3D12_DESCRIPTOR_RANGE ranges[1]; + ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + ranges[0].NumDescriptors = 1; + ranges[0].BaseShaderRegister = 0; + ranges[0].RegisterSpace = 0; + ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_DESCRIPTOR_TABLE table{}; + table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0])); + table.pDescriptorRanges = ranges; + + D3D12_ROOT_PARAMETER parameters[1]; + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[0].DescriptorTable = table; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_ROOT_SIGNATURE_DESC desc_rootsignature; + desc_rootsignature.NumParameters = _countof(parameters); + desc_rootsignature.pParameters = parameters; + desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + 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; + desc_rootsignature.NumStaticSamplers = 1; + desc_rootsignature.pStaticSamplers = &sampler; + + ID3DBlob* p_signature{}; + ID3DBlob* p_error{}; + auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s", + (char*)p_error->GetBufferPointer()); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Passthrough)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Passthrough_NoDepthStencil)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Passthrough_Opacity)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Passthrough_MSAA)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Passthrough_MSAA_Equal)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + if (p_signature) + { + p_signature->Release(); + p_signature = nullptr; + } + + if (p_error) + { + p_error->Release(); + p_error = nullptr; + } + + ID3DBlob* p_shader_vertex{}; + ID3DBlob* p_shader_pixel{}; + ID3DBlob* p_error_buff{}; + + const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL}; + + status = D3DCompile(pShaderSourceText_Vertex_PassThrough, sizeof(pShaderSourceText_Vertex_PassThrough), nullptr, macros, nullptr, "main", + "vs_5_0", m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer()); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + status = D3DCompile(pShaderSourceText_Pixel_Passthrough, sizeof(pShaderSourceText_Pixel_Passthrough), nullptr, nullptr, nullptr, "main", + "ps_5_0", m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", + (char*)(p_error_buff->GetBufferPointer())); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {}; + desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize(); + desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer(); + + D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {}; + desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize(); + desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer(); + + D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; + + D3D12_INPUT_LAYOUT_DESC desc_input_layout = {}; + + desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC); + desc_input_layout.pInputElementDescs = desc_input_layout_elements; + + D3D12_RASTERIZER_DESC desc_rasterizer = {}; + + desc_rasterizer.AntialiasedLineEnable = FALSE; + desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE; + desc_rasterizer.DepthBias = 0; + desc_rasterizer.DepthBiasClamp = 0; + desc_rasterizer.DepthClipEnable = FALSE; + desc_rasterizer.ForcedSampleCount = 0; + desc_rasterizer.FrontCounterClockwise = FALSE; + desc_rasterizer.MultisampleEnable = FALSE; + desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID; + desc_rasterizer.SlopeScaledDepthBias = 0; + + D3D12_BLEND_DESC desc_blend_state = {}; + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC default_render_target_blend_desc = { + TRUE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = default_render_target_blend_desc; + + D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {}; + + desc_depth_stencil.DepthEnable = FALSE; + desc_depth_stencil.StencilEnable = TRUE; + desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + desc_depth_stencil.StencilWriteMask = 0; + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {}; + + desc_pipeline.InputLayout = desc_input_layout; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Passthrough)]; + desc_pipeline.VS = desc_bytecode_vertex_shader; + desc_pipeline.PS = desc_bytecode_pixel_shader; + desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_pipeline.DSVFormat = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + + // since it is used for presenting MSAA texture on screen, we create swapchain and all RTs as NO-MSAA, keep this in mind + // otherwise it is wrong to mix target texture with different sample count than swapchain's + desc_pipeline.SampleDesc.Count = 1; + desc_pipeline.SampleDesc.Quality = 0; + + desc_pipeline.SampleMask = 0xffffffff; + desc_pipeline.RasterizerState = desc_rasterizer; + desc_pipeline.BlendState = desc_blend_state; + desc_pipeline.NumRenderTargets = 1; + desc_pipeline.DepthStencilState = desc_depth_stencil; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Passthrough)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::Passthrough)]->SetName(TEXT("rs of Passthrough")); + m_pipelines[static_cast(ProgramId::Passthrough)]->SetName(TEXT("pipeline Passthrough")); +#endif + + desc_pipeline.SampleDesc = m_desc_sample; + desc_pipeline.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_pipeline.DepthStencilState.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Passthrough_MSAA)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough_MSAA)"); + + desc_pipeline.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_pipeline.DepthStencilState.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + status = + m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Passthrough_MSAA_Equal)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough_MSAA_Equal)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::Passthrough_MSAA)]->SetName(TEXT("rs of Passthrough_MSAA")); + m_pipelines[static_cast(ProgramId::Passthrough_MSAA)]->SetName(TEXT("pipeline Passthrough_MSAA")); + + m_root_signatures[static_cast(ProgramId::Passthrough_MSAA_Equal)]->SetName(TEXT("rs of Passthrough_MSAA_Equal")); + m_pipelines[static_cast(ProgramId::Passthrough_MSAA_Equal)]->SetName(TEXT("pipeline Passthrough_MSAA_Equal")); +#endif + + desc_pipeline.SampleDesc.Count = 1; + desc_pipeline.SampleDesc.Quality = 0; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Passthrough_NoDepthStencil)]; + desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, + IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Passthrough_NoDepthStencil)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPieplineState (Passthrough_NoDepthStencil)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::Passthrough_NoDepthStencil)]->SetName(TEXT("rs of Passthrough_NoDepthStencil")); + m_pipelines[static_cast(ProgramId::Passthrough_NoDepthStencil)]->SetName(TEXT("pipeline Passthrough_NoDepthStencil")); +#endif + + D3D12_RENDER_TARGET_BLEND_DESC opacity_blend_desc; + opacity_blend_desc.BlendEnable = TRUE; + opacity_blend_desc.LogicOpEnable = FALSE; + opacity_blend_desc.SrcBlend = D3D12_BLEND_BLEND_FACTOR; + opacity_blend_desc.DestBlend = D3D12_BLEND_ZERO; + opacity_blend_desc.BlendOp = D3D12_BLEND_OP_ADD; + opacity_blend_desc.SrcBlendAlpha = D3D12_BLEND_BLEND_FACTOR; + opacity_blend_desc.DestBlendAlpha = D3D12_BLEND_ZERO; + opacity_blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD; + opacity_blend_desc.LogicOp = D3D12_LOGIC_OP_NOOP; + opacity_blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + + D3D12_BLEND_DESC desc_blend_state_opacity = {}; + desc_blend_state_opacity.AlphaToCoverageEnable = FALSE; + desc_blend_state_opacity.IndependentBlendEnable = FALSE; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state_opacity.RenderTarget[i] = opacity_blend_desc; + + desc_pipeline.BlendState = desc_blend_state_opacity; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Passthrough_Opacity)]; + + status = + m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Passthrough_Opacity)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough_Opacity)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::Passthrough_Opacity)]->SetName(TEXT("rs of Passthrough_Opacity")); + m_pipelines[static_cast(ProgramId::Passthrough_Opacity)]->SetName(TEXT("pipeline Passthrough_Opacity")); +#endif + } +} + +void RenderInterface_DX12::Create_Resource_Pipeline_Passthrough_NoBlend() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Passthrough"); + RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!"); + + if (m_p_device) + { + D3D12_DESCRIPTOR_RANGE ranges[1]; + ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + ranges[0].NumDescriptors = 1; + ranges[0].BaseShaderRegister = 0; + ranges[0].RegisterSpace = 0; + ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_DESCRIPTOR_TABLE table{}; + table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0])); + table.pDescriptorRanges = ranges; + + D3D12_ROOT_PARAMETER parameters[1]; + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[0].DescriptorTable = table; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_ROOT_SIGNATURE_DESC desc_rootsignature; + desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + 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; + desc_rootsignature.NumParameters = _countof(parameters); + desc_rootsignature.pParameters = parameters; + desc_rootsignature.NumStaticSamplers = 1; + desc_rootsignature.pStaticSamplers = &sampler; + + ID3DBlob* p_signature{}; + ID3DBlob* p_error{}; + auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s", + (char*)p_error->GetBufferPointer()); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Passthrough_NoBlend)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Passthrough_NoBlendAndNoMSAA)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + if (p_signature) + { + p_signature->Release(); + p_signature = nullptr; + } + + if (p_error) + { + p_error->Release(); + p_error = nullptr; + } + + ID3DBlob* p_shader_vertex{}; + ID3DBlob* p_shader_pixel{}; + ID3DBlob* p_error_buff{}; + + const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL}; + + status = D3DCompile(pShaderSourceText_Vertex_PassThrough, sizeof(pShaderSourceText_Vertex_PassThrough), nullptr, macros, nullptr, "main", + "vs_5_0", m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer()); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + status = D3DCompile(pShaderSourceText_Pixel_Passthrough, sizeof(pShaderSourceText_Pixel_Passthrough), nullptr, nullptr, nullptr, "main", + "ps_5_0", m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", + (char*)(p_error_buff->GetBufferPointer())); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {}; + desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize(); + desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer(); + + D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {}; + desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize(); + desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer(); + + D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; + + D3D12_INPUT_LAYOUT_DESC desc_input_layout = {}; + + desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC); + desc_input_layout.pInputElementDescs = desc_input_layout_elements; + + D3D12_RASTERIZER_DESC desc_rasterizer = {}; + + desc_rasterizer.AntialiasedLineEnable = FALSE; + desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE; + desc_rasterizer.DepthBias = 0; + desc_rasterizer.DepthBiasClamp = 0; + desc_rasterizer.DepthClipEnable = FALSE; + desc_rasterizer.ForcedSampleCount = 0; + desc_rasterizer.FrontCounterClockwise = FALSE; + desc_rasterizer.MultisampleEnable = FALSE; + desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID; + desc_rasterizer.SlopeScaledDepthBias = 0; + + D3D12_BLEND_DESC desc_blend_state = {}; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + FALSE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc; + + D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {}; + + desc_depth_stencil.DepthEnable = FALSE; + desc_depth_stencil.StencilEnable = TRUE; + desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + desc_depth_stencil.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {}; + + desc_pipeline.InputLayout = desc_input_layout; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Passthrough_NoBlend)]; + desc_pipeline.VS = desc_bytecode_vertex_shader; + desc_pipeline.PS = desc_bytecode_pixel_shader; + desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN; + desc_pipeline.SampleDesc = m_desc_sample; // depends on layer texture and layer texture depends on msaa level that user specifies + desc_pipeline.SampleMask = 0xffffffff; + desc_pipeline.RasterizerState = desc_rasterizer; + desc_pipeline.BlendState = desc_blend_state; + desc_pipeline.NumRenderTargets = 1; + desc_pipeline.DepthStencilState = desc_depth_stencil; + + status = + m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Passthrough_NoBlend)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough_NoBlend)"); + + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Passthrough_NoBlendAndNoMSAA)]; + desc_pipeline.SampleDesc.Count = 1; + desc_pipeline.SampleDesc.Quality = 0; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, + IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Passthrough_NoBlendAndNoMSAA)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough_NoBlendAndNoMSAA"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::Passthrough_NoBlend)]->SetName(TEXT("rs of Passthrough_NoBlend")); + m_pipelines[static_cast(ProgramId::Passthrough_NoBlend)]->SetName(TEXT("pipeline Passthrough_NoBlend")); + + m_root_signatures[static_cast(ProgramId::Passthrough_NoBlendAndNoMSAA)]->SetName(TEXT("rs of Passthrough_NoBlendAndNoMSAA")); + m_pipelines[static_cast(ProgramId::Passthrough_NoBlendAndNoMSAA)]->SetName(TEXT("pipeline Passthrough_NoBlendAndNoMSAA")); +#endif + } +} + +void RenderInterface_DX12::Create_Resource_Pipeline_ColorMatrix() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_ColorMatrix"); + RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!"); + + if (m_p_device) + { + D3D12_DESCRIPTOR_RANGE ranges[1]; + ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + ranges[0].NumDescriptors = 1; + ranges[0].BaseShaderRegister = 0; + ranges[0].RegisterSpace = 0; + ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_DESCRIPTOR_TABLE table{}; + table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0])); + table.pDescriptorRanges = ranges; + + D3D12_ROOT_DESCRIPTOR descriptor_cbv{}; + descriptor_cbv.RegisterSpace = 0; + descriptor_cbv.ShaderRegister = 0; + + D3D12_ROOT_PARAMETER parameters[2]; + + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[0].DescriptorTable = table; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[1].Descriptor = descriptor_cbv; + parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_ROOT_SIGNATURE_DESC desc_rootsignature; + desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + 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; + desc_rootsignature.NumParameters = _countof(parameters); + desc_rootsignature.pParameters = parameters; + desc_rootsignature.NumStaticSamplers = 1; + desc_rootsignature.pStaticSamplers = &sampler; + + ID3DBlob* p_signature{}; + ID3DBlob* p_error{}; + auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s", + (char*)p_error->GetBufferPointer()); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::ColorMatrix)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + if (p_signature) + { + p_signature->Release(); + p_signature = nullptr; + } + + if (p_error) + { + p_error->Release(); + p_error = nullptr; + } + + ID3DBlob* p_shader_vertex{}; + ID3DBlob* p_shader_pixel{}; + ID3DBlob* p_error_buff{}; + + const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL}; + + status = D3DCompile(pShaderSourceText_Vertex_PassThrough, sizeof(pShaderSourceText_Vertex_PassThrough), nullptr, macros, nullptr, "main", + "vs_5_0", m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer()); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + status = D3DCompile(pShaderSourceTet_Pixel_ColorMatrix, sizeof(pShaderSourceTet_Pixel_ColorMatrix), nullptr, nullptr, nullptr, "main", + "ps_5_0", m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", + (char*)(p_error_buff->GetBufferPointer())); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {}; + desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize(); + desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer(); + + D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {}; + desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize(); + desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer(); + + D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; + + D3D12_INPUT_LAYOUT_DESC desc_input_layout = {}; + + desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC); + desc_input_layout.pInputElementDescs = desc_input_layout_elements; + + D3D12_RASTERIZER_DESC desc_rasterizer = {}; + + desc_rasterizer.AntialiasedLineEnable = FALSE; + desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE; + desc_rasterizer.DepthBias = 0; + desc_rasterizer.DepthBiasClamp = 0; + desc_rasterizer.DepthClipEnable = FALSE; + desc_rasterizer.ForcedSampleCount = 0; + desc_rasterizer.FrontCounterClockwise = FALSE; + desc_rasterizer.MultisampleEnable = FALSE; + desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID; + desc_rasterizer.SlopeScaledDepthBias = 0; + + D3D12_BLEND_DESC desc_blend_state = {}; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + FALSE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc; + + D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {}; + + desc_depth_stencil.DepthEnable = FALSE; + desc_depth_stencil.StencilEnable = TRUE; + desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + desc_depth_stencil.StencilWriteMask = 0; + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {}; + + desc_pipeline.InputLayout = desc_input_layout; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::ColorMatrix)]; + desc_pipeline.VS = desc_bytecode_vertex_shader; + desc_pipeline.PS = desc_bytecode_pixel_shader; + desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN; + + // desc_pipeline.SampleDesc = m_desc_sample; + desc_pipeline.SampleDesc.Count = 1; + desc_pipeline.SampleDesc.Quality = 0; + + desc_pipeline.SampleMask = 0xffffffff; + desc_pipeline.RasterizerState = desc_rasterizer; + desc_pipeline.BlendState = desc_blend_state; + desc_pipeline.NumRenderTargets = 1; + desc_pipeline.DepthStencilState = desc_depth_stencil; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::ColorMatrix)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (ColorMatrix)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::ColorMatrix)]->SetName(TEXT("rs of ColorMatrix")); + m_pipelines[static_cast(ProgramId::ColorMatrix)]->SetName(TEXT("pipeline ColorMatrix")); +#endif + } +} + +void RenderInterface_DX12::Create_Resource_Pipeline_BlendMask() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_BlendMask"); + RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!"); + + if (m_p_device) + { + D3D12_DESCRIPTOR_RANGE ranges[1]; + ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + ranges[0].NumDescriptors = 1; + ranges[0].BaseShaderRegister = 0; + ranges[0].RegisterSpace = 0; + ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_DESCRIPTOR_TABLE table{}; + table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0])); + table.pDescriptorRanges = ranges; + + D3D12_ROOT_PARAMETER parameters[2]; + + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[0].DescriptorTable = table; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_DESCRIPTOR_RANGE slot2_ranges[1]; + slot2_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + slot2_ranges[0].NumDescriptors = 1; + slot2_ranges[0].BaseShaderRegister = 1; + slot2_ranges[0].RegisterSpace = 0; + slot2_ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_DESCRIPTOR_TABLE slot2_table{}; + slot2_table.NumDescriptorRanges = sizeof(slot2_ranges) / sizeof(decltype(slot2_ranges[0])); + slot2_table.pDescriptorRanges = slot2_ranges; + + parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[1].DescriptorTable = slot2_table; + parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_ROOT_SIGNATURE_DESC desc_rootsignature; + desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + 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; + desc_rootsignature.NumParameters = _countof(parameters); + desc_rootsignature.pParameters = parameters; + desc_rootsignature.NumStaticSamplers = 1; + desc_rootsignature.pStaticSamplers = &sampler; + + ID3DBlob* p_signature{}; + ID3DBlob* p_error{}; + auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s", + (char*)p_error->GetBufferPointer()); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::BlendMask)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + if (p_signature) + { + p_signature->Release(); + p_signature = nullptr; + } + + if (p_error) + { + p_error->Release(); + p_error = nullptr; + } + + ID3DBlob* p_shader_vertex{}; + ID3DBlob* p_shader_pixel{}; + ID3DBlob* p_error_buff{}; + + const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL}; + + status = D3DCompile(pShaderSourceText_Vertex_PassThrough, sizeof(pShaderSourceText_Vertex_PassThrough), nullptr, macros, nullptr, "main", + "vs_5_0", m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer()); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + status = D3DCompile(pShaderSourceText_Pixel_BlendMask, sizeof(pShaderSourceText_Pixel_BlendMask), nullptr, nullptr, nullptr, "main", "ps_5_0", + m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", + (char*)(p_error_buff->GetBufferPointer())); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {}; + desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize(); + desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer(); + + D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {}; + desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize(); + desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer(); + + D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; + + D3D12_INPUT_LAYOUT_DESC desc_input_layout = {}; + + desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC); + desc_input_layout.pInputElementDescs = desc_input_layout_elements; + + D3D12_RASTERIZER_DESC desc_rasterizer = {}; + + desc_rasterizer.AntialiasedLineEnable = FALSE; + desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE; + desc_rasterizer.DepthBias = 0; + desc_rasterizer.DepthBiasClamp = 0; + desc_rasterizer.DepthClipEnable = FALSE; + desc_rasterizer.ForcedSampleCount = 0; + desc_rasterizer.FrontCounterClockwise = FALSE; + desc_rasterizer.MultisampleEnable = FALSE; + desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID; + desc_rasterizer.SlopeScaledDepthBias = 0; + + D3D12_BLEND_DESC desc_blend_state = {}; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + FALSE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc; + + D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {}; + + desc_depth_stencil.DepthEnable = FALSE; + desc_depth_stencil.StencilEnable = TRUE; + desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + desc_depth_stencil.StencilWriteMask = 0; + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {}; + + desc_pipeline.InputLayout = desc_input_layout; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::BlendMask)]; + desc_pipeline.VS = desc_bytecode_vertex_shader; + desc_pipeline.PS = desc_bytecode_pixel_shader; + desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN; + + // desc_pipeline.SampleDesc = m_desc_sample; + desc_pipeline.SampleDesc.Count = 1; + desc_pipeline.SampleDesc.Quality = 0; + + desc_pipeline.SampleMask = 0xffffffff; + desc_pipeline.RasterizerState = desc_rasterizer; + desc_pipeline.BlendState = desc_blend_state; + desc_pipeline.NumRenderTargets = 1; + desc_pipeline.DepthStencilState = desc_depth_stencil; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::BlendMask)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (BlendMask)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::BlendMask)]->SetName(TEXT("rs of BlendMask")); + m_pipelines[static_cast(ProgramId::BlendMask)]->SetName(TEXT("pipeline BlendMask")); +#endif + } +} + +void RenderInterface_DX12::Create_Resource_Pipeline_Blur() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Blur"); + RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!"); + + if (m_p_device) + { + D3D12_DESCRIPTOR_RANGE ranges[1]; + ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + ranges[0].NumDescriptors = 1; + ranges[0].BaseShaderRegister = 0; + ranges[0].RegisterSpace = 0; + ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_DESCRIPTOR_TABLE table{}; + table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0])); + table.pDescriptorRanges = ranges; + + D3D12_ROOT_DESCRIPTOR descriptor_cbv{}; + descriptor_cbv.RegisterSpace = 0; + descriptor_cbv.ShaderRegister = 0; + + D3D12_ROOT_PARAMETER parameters[3]; + + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[0].DescriptorTable = table; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[1].Descriptor = descriptor_cbv; + parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; + + parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[2].Descriptor = descriptor_cbv; + parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_ROOT_SIGNATURE_DESC desc_rootsignature; + desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + 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; + desc_rootsignature.NumParameters = _countof(parameters); + desc_rootsignature.pParameters = parameters; + desc_rootsignature.NumStaticSamplers = 1; + desc_rootsignature.pStaticSamplers = &sampler; + + ID3DBlob* p_signature{}; + ID3DBlob* p_error{}; + auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s", + (char*)p_error->GetBufferPointer()); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::Blur)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + if (p_signature) + { + p_signature->Release(); + p_signature = nullptr; + } + + if (p_error) + { + p_error->Release(); + p_error = nullptr; + } + + ID3DBlob* p_shader_vertex{}; + ID3DBlob* p_shader_pixel{}; + ID3DBlob* p_error_buff{}; + + const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL}; + + status = D3DCompile(pShaderSourceText_Vertex_Blur, sizeof(pShaderSourceText_Vertex_Blur), nullptr, macros, nullptr, "main", "vs_5_0", + m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer()); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + status = D3DCompile(pShaderSourceText_Pixel_Blur, sizeof(pShaderSourceText_Pixel_Blur), nullptr, nullptr, nullptr, "main", "ps_5_0", + m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", + (char*)(p_error_buff->GetBufferPointer())); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {}; + desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize(); + desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer(); + + D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {}; + desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize(); + desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer(); + + D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; + + D3D12_INPUT_LAYOUT_DESC desc_input_layout = {}; + + desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC); + desc_input_layout.pInputElementDescs = desc_input_layout_elements; + + D3D12_RASTERIZER_DESC desc_rasterizer = {}; + + desc_rasterizer.AntialiasedLineEnable = FALSE; + desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE; + desc_rasterizer.DepthBias = 0; + desc_rasterizer.DepthBiasClamp = 0; + desc_rasterizer.DepthClipEnable = FALSE; + desc_rasterizer.ForcedSampleCount = 0; + desc_rasterizer.FrontCounterClockwise = FALSE; + desc_rasterizer.MultisampleEnable = FALSE; + desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID; + desc_rasterizer.SlopeScaledDepthBias = 0; + + D3D12_BLEND_DESC desc_blend_state = {}; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + FALSE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc; + + D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {}; + + desc_depth_stencil.DepthEnable = FALSE; + desc_depth_stencil.StencilEnable = TRUE; + desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + desc_depth_stencil.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {}; + + desc_pipeline.InputLayout = desc_input_layout; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::Blur)]; + desc_pipeline.VS = desc_bytecode_vertex_shader; + desc_pipeline.PS = desc_bytecode_pixel_shader; + desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN; + + // desc_pipeline.SampleDesc = m_desc_sample; + desc_pipeline.SampleDesc.Count = 1; + desc_pipeline.SampleDesc.Quality = 0; + + desc_pipeline.SampleMask = 0xffffffff; + desc_pipeline.RasterizerState = desc_rasterizer; + desc_pipeline.BlendState = desc_blend_state; + desc_pipeline.NumRenderTargets = 1; + desc_pipeline.DepthStencilState = desc_depth_stencil; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::Blur)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Blur)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::Blur)]->SetName(TEXT("rs of Blur")); + m_pipelines[static_cast(ProgramId::Blur)]->SetName(TEXT("pipeline Blur")); +#endif + } +} + +void RenderInterface_DX12::Create_Resource_Pipeline_DropShadow() +{ + RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_DropShadow"); + RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!"); + + if (m_p_device) + { + D3D12_DESCRIPTOR_RANGE ranges[1]; + ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + ranges[0].NumDescriptors = 1; + ranges[0].BaseShaderRegister = 0; + ranges[0].RegisterSpace = 0; + ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_DESCRIPTOR_TABLE table{}; + table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0])); + table.pDescriptorRanges = ranges; + + D3D12_ROOT_DESCRIPTOR descriptor_cbv{}; + descriptor_cbv.RegisterSpace = 0; + descriptor_cbv.ShaderRegister = 0; + + D3D12_ROOT_PARAMETER parameters[2]; + + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[0].DescriptorTable = table; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[1].Descriptor = descriptor_cbv; + parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_ROOT_SIGNATURE_DESC desc_rootsignature; + desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + 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; + desc_rootsignature.NumParameters = _countof(parameters); + desc_rootsignature.pParameters = parameters; + desc_rootsignature.NumStaticSamplers = 1; + desc_rootsignature.pStaticSamplers = &sampler; + + ID3DBlob* p_signature{}; + ID3DBlob* p_error{}; + auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s", + (char*)p_error->GetBufferPointer()); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature"); + + status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(), + IID_PPV_ARGS(&m_root_signatures[static_cast(ProgramId::DropShadow)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature"); + + if (p_signature) + { + p_signature->Release(); + p_signature = nullptr; + } + + if (p_error) + { + p_error->Release(); + p_error = nullptr; + } + + ID3DBlob* p_shader_vertex{}; + ID3DBlob* p_shader_pixel{}; + ID3DBlob* p_error_buff{}; + + const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL}; + + status = D3DCompile(pShaderSourceText_Vertex_PassThrough, sizeof(pShaderSourceText_Vertex_PassThrough), nullptr, macros, nullptr, "main", + "vs_5_0", m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff); + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer()); + } +#endif + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + status = D3DCompile(pShaderSourceText_Pixel_DropShadow, sizeof(pShaderSourceText_Pixel_DropShadow), nullptr, nullptr, nullptr, "main", + "ps_5_0", m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff); +#ifdef RMLUI_DX_DEBUG + if (FAILED(status)) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", + (char*)(p_error_buff->GetBufferPointer())); + } +#endif + RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile"); + + if (p_error_buff) + { + p_error_buff->Release(); + p_error_buff = nullptr; + } + + D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {}; + desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize(); + desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer(); + + D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {}; + desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize(); + desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer(); + + D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; + + D3D12_INPUT_LAYOUT_DESC desc_input_layout = {}; + + desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC); + desc_input_layout.pInputElementDescs = desc_input_layout_elements; + + D3D12_RASTERIZER_DESC desc_rasterizer = {}; + + desc_rasterizer.AntialiasedLineEnable = FALSE; + desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE; + desc_rasterizer.DepthBias = 0; + desc_rasterizer.DepthBiasClamp = 0; + desc_rasterizer.DepthClipEnable = FALSE; + desc_rasterizer.ForcedSampleCount = 0; + desc_rasterizer.FrontCounterClockwise = FALSE; + desc_rasterizer.MultisampleEnable = FALSE; + desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID; + desc_rasterizer.SlopeScaledDepthBias = 0; + + D3D12_BLEND_DESC desc_blend_state = {}; + + desc_blend_state.AlphaToCoverageEnable = FALSE; + desc_blend_state.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + FALSE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc; + + D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {}; + + desc_depth_stencil.DepthEnable = FALSE; + desc_depth_stencil.StencilEnable = TRUE; + desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + desc_depth_stencil.StencilWriteMask = 0; + + desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; + desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; + desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {}; + + desc_pipeline.InputLayout = desc_input_layout; + desc_pipeline.pRootSignature = m_root_signatures[static_cast(ProgramId::DropShadow)]; + desc_pipeline.VS = desc_bytecode_vertex_shader; + desc_pipeline.PS = desc_bytecode_pixel_shader; + desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN; + + // desc_pipeline.SampleDesc = m_desc_sample; + desc_pipeline.SampleDesc.Count = 1; + desc_pipeline.SampleDesc.Quality = 0; + + desc_pipeline.SampleMask = 0xffffffff; + desc_pipeline.RasterizerState = desc_rasterizer; + desc_pipeline.BlendState = desc_blend_state; + desc_pipeline.NumRenderTargets = 1; + desc_pipeline.DepthStencilState = desc_depth_stencil; + + status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast(ProgramId::DropShadow)])); + RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (DropShadow)"); + +#ifdef RMLUI_DX_DEBUG + m_root_signatures[static_cast(ProgramId::DropShadow)]->SetName(TEXT("rs of DropShadow")); + m_pipelines[static_cast(ProgramId::DropShadow)]->SetName(TEXT("pipeline DropShadow")); +#endif + } +} + +void RenderInterface_DX12::Destroy_Resource_Pipelines() +{ + RMLUI_ZoneScopedN("DirectX 12 - Destroy_Resource_Pipelines"); + Destroy_Resource_For_Shaders(); + + if (m_p_depthstencil_resource) + { + Destroy_Resource_DepthStencil(); + } + + for (int i = 1; i < static_cast(ProgramId::Count); ++i) + { + if (m_pipelines[i]) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_pipelines[i]->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + m_pipelines[i] = nullptr; + } + + if (m_root_signatures[i]) + { + m_root_signatures[i]->Release(); + m_root_signatures[i] = nullptr; + } + } +} + +bool RenderInterface_DX12::CheckTearingSupport() noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - CheckTearingSupport"); + int result{}; + + IDXGIFactory4* pFactory4{}; + + if (SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&pFactory4)))) + { + IDXGIFactory5* pFactory5{}; + + if (SUCCEEDED(pFactory4->QueryInterface(IID_PPV_ARGS(&pFactory5)))) + { + if (FAILED(pFactory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &result, sizeof(result)))) + { + result = 0; + } + + if (pFactory5) + { + pFactory5->Release(); + } + } + } + + if (pFactory4) + { + pFactory4->Release(); + } + + return result == 1; +} + +IDXGIAdapter* RenderInterface_DX12::Get_Adapter(bool is_use_warp) noexcept +{ + RMLUI_ZoneScopedN("DirectX 12 - Get_Adapter"); + IDXGIFactory4* p_factory{}; + + uint32_t create_flags{}; + +#ifdef RMLUI_DX_DEBUG + create_flags = DXGI_CREATE_FACTORY_DEBUG; +#endif + + RMLUI_DX_VERIFY_MSG(CreateDXGIFactory2(create_flags, IID_PPV_ARGS(&p_factory)), "failed to CreateDXGIFactory2"); + + IDXGIAdapter* p_adapter{}; + IDXGIAdapter1* p_adapter1{}; + IDXGIAdapter4* p_adapter4{}; + + if (is_use_warp) + { + if (p_factory) + { + RMLUI_DX_VERIFY_MSG(p_factory->EnumWarpAdapter(IID_PPV_ARGS(&p_adapter)), "failed to EnumAdapters"); + RMLUI_ASSERTMSG(p_adapter, "returned invalid COM object from EnumWarpAdapter"); + + if (p_adapter) + { + p_adapter->QueryInterface(IID_PPV_ARGS(&p_adapter4)); + RMLUI_ASSERTMSG(p_adapter4, "returned invalid COM object from QueryInterface"); + PrintAdapterDesc(p_adapter); + } + } + } + else + { + if (p_factory) + { + size_t max_dedicated_video_memory{}; + + for (uint32_t i = 0; p_factory->EnumAdapters(i, &p_adapter) != DXGI_ERROR_NOT_FOUND; ++i) + { + if (p_adapter) + { + DXGI_ADAPTER_DESC1 desc; + + RMLUI_DX_VERIFY_MSG(p_adapter->QueryInterface(IID_PPV_ARGS(&p_adapter1)), "failed to QueryInterface of IDXGIAdapter1"); + + if (p_adapter1) + { + p_adapter1->GetDesc1(&desc); + + ID3D12Device* p_test_device{}; + + if ((desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0 && + SUCCEEDED(D3D12CreateDevice(p_adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&p_test_device))) && + desc.DedicatedVideoMemory > max_dedicated_video_memory) + { + max_dedicated_video_memory = desc.DedicatedVideoMemory; + + // found 'device' with bigger memory means we found our discrete video card (not cpu integrated) + if (p_adapter4) + { + p_adapter4->Release(); + p_adapter4 = nullptr; + } + + RMLUI_DX_VERIFY_MSG(p_adapter->QueryInterface(IID_PPV_ARGS(&p_adapter4)), "failed to QueryInterface of IDXGIAdapter4"); + + PrintAdapterDesc(p_adapter); + } + + if (p_test_device) + { + p_test_device->Release(); + } + } + + if (p_adapter) + { + p_adapter->Release(); + } + + if (p_adapter1) + { + p_adapter1->Release(); + p_adapter1 = nullptr; + } + } + } + } + } + + if (p_factory) + { + p_factory->Release(); + } + + if (p_adapter) + { + p_adapter->Release(); + } + + if (p_adapter1) + { + p_adapter1->Release(); + } + + return p_adapter4; +} + +void RenderInterface_DX12::PrintAdapterDesc(IDXGIAdapter* p_adapter) +{ + RMLUI_ZoneScopedN("DirectX 12 - PrintAdapterDesc"); + RMLUI_ASSERTMSG(p_adapter, "you can't pass invalid argument"); + + if (p_adapter) + { + DXGI_ADAPTER_DESC desc; + p_adapter->GetDesc(&desc); + + char p_converted[_countof(desc.Description)]; + memset(p_converted, 0, sizeof(p_converted)); + +#ifdef UNICODE + sprintf(p_converted, "%ls", desc.Description); +#else + p_converted = desc.Description; +#endif + + float vid_mem_in_bytes = static_cast(desc.DedicatedVideoMemory); + float vid_mem_in_kilobytes = vid_mem_in_bytes / 1024.0f; + float vid_mem_in_megabytes = vid_mem_in_kilobytes / 1024.0f; + float vid_mem_in_gigabytes = vid_mem_in_megabytes / 1024.0f; + + Rml::Log::Message(Rml::Log::LT_INFO, "Monitor[%s]\n VideoMemory[%f Bytes][%f Mbs][%f Gbs]\n", p_converted, vid_mem_in_bytes, + vid_mem_in_megabytes, vid_mem_in_gigabytes); + } +} + +void RenderInterface_DX12::SetScissor(Rml::Rectanglei region, bool vertically_flip) +{ + RMLUI_ZoneScopedN("DirectX 12 - SetScissor"); + + if (region.Valid() != m_scissor.Valid()) + { + if (!region.Valid()) + { + m_is_scissor_was_set = false; + + if (m_p_command_graphics_list) + { + D3D12_RECT disable_scissor = {}; + disable_scissor.left = 0; + disable_scissor.top = 0; + disable_scissor.right = m_width; + disable_scissor.bottom = m_height; + m_p_command_graphics_list->RSSetScissorRects(1, &disable_scissor); + } + + return; + } + } + + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "SetScissor"); + + if (region.Valid() && vertically_flip) + { + region = VerticallyFlipped(region, m_height); + } + + if (region.Valid()) + { + if (m_p_command_graphics_list) + { + D3D12_RECT scissor; + + const int x_min = Rml::Math::Clamp(region.Left(), 0, m_width); + const int y_min = Rml::Math::Clamp(region.Top(), 0, m_height); + const int x_max = Rml::Math::Clamp(region.Right(), 0, m_width); + const int y_max = Rml::Math::Clamp(region.Bottom(), 0, m_height); + + scissor.left = x_min; + scissor.right = x_max; + scissor.bottom = y_max; + scissor.top = y_min; + + m_p_command_graphics_list->RSSetScissorRects(1, &scissor); + m_is_scissor_was_set = true; + } + } + + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + + m_scissor = region; +} + +void RenderInterface_DX12::SubmitTransformUniform(ConstantBufferType& constant_buffer, const Rml::Vector2f& translation) +{ + RMLUI_ZoneScopedN("DirectX 12 - SubmitTransformUniform"); + static_assert((size_t)ProgramId::Count < RMLUI_RENDER_BACKEND_FIELD_MAXNUMPROGRAMS, "Maximum number of pipelines exceeded"); + + std::uint8_t* p_gpu_binding_start = reinterpret_cast(constant_buffer.m_p_gpu_start_memory_for_binding_data); + + { + RMLUI_ASSERTMSG(p_gpu_binding_start, + "your allocated constant buffer must contain a valid pointer of beginning mapping of its GPU buffer. Otherwise you destroyed it!"); + + if (p_gpu_binding_start) + { + std::uint8_t* p_gpu_binding_offset_to_transform = p_gpu_binding_start + constant_buffer.m_alloc_info.offset; + + std::memcpy(p_gpu_binding_offset_to_transform, m_constant_buffer_data_transform.data(), sizeof(m_constant_buffer_data_transform)); + } + } + + if (p_gpu_binding_start) + { + std::uint8_t* p_gpu_binding_offset_to_translate = + p_gpu_binding_start + (constant_buffer.m_alloc_info.offset + sizeof(m_constant_buffer_data_transform)); + + std::memcpy(p_gpu_binding_offset_to_translate, &translation.x, sizeof(translation)); + } +} + +constexpr const char* translate_programId(ProgramId id) +{ + switch (id) + { + case ProgramId::None: return "None"; + case ProgramId::Color_Stencil_Always: return "Color_Stencil_Always"; + case ProgramId::Color_Stencil_Equal: return "Color_Stencil_Equal"; + case ProgramId::Color_Stencil_Set: return "Color_Stencil_Set"; + case ProgramId::Color_Stencil_SetInverse: return "Color_Stencil_SetInverse"; + case ProgramId::Color_Stencil_Intersect: return "Color_Stencil_Intersect"; + case ProgramId::Color_Stencil_Disabled: return "Color_Stencil_Disabled"; + case ProgramId::Texture_Stencil_Always: return "Texture_Stencil_Always"; + case ProgramId::Texture_Stencil_Equal: return "Texture_Stencil_Equal"; + case ProgramId::Texture_Stencil_Disabled: return "Texture_Stencil_Disabled"; + case ProgramId::Gradient: return "Gradient"; + case ProgramId::Creation: return "Creation"; + case ProgramId::Passthrough: return "Passthrough"; + case ProgramId::Passthrough_NoDepthStencil: return "Passthrough_NoDepthStencil"; + case ProgramId::Passthrough_Opacity: return "Passthrough_Opacity"; + case ProgramId::Passthrough_MSAA: return "Passthrough_MSAA"; + case ProgramId::Passthrough_MSAA_Equal: return "Passthrough_MSAA_Equal"; + case ProgramId::Passthrough_NoBlend: return "Passthrough_NoBlend"; + case ProgramId::Passthrough_NoBlendAndNoMSAA: return "Passthrough_NoBlendAndNoMSAA"; + case ProgramId::ColorMatrix: return "ColorMatrix"; + case ProgramId::BlendMask: return "BlendMask"; + case ProgramId::Blur: return "Blur"; + case ProgramId::DropShadow: return "DropShadow"; + case ProgramId::Count: return "Count"; + default: return "UNKNOWN_PROGRAMID"; + } +} + +void RenderInterface_DX12::UseProgram(ProgramId pipeline_id) +{ + RMLUI_ZoneScopedN("DirectX 12 - UseProgram"); + RMLUI_ASSERTMSG(pipeline_id < ProgramId::Count, "overflow, too big value for indexing"); + +#ifdef RMLUI_DX_DEBUG + char msg[64]; + std::sprintf(msg, "UseProgram = %s", translate_programId(pipeline_id)); +#endif + + RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, msg); + if (pipeline_id != ProgramId::None) + { + RMLUI_ASSERTMSG(m_pipelines[static_cast(pipeline_id)], "you forgot to initialize or deleted!"); + RMLUI_ASSERTMSG(m_root_signatures[static_cast(pipeline_id)], "you forgot to initialize or deleted!"); + + m_p_command_graphics_list->SetPipelineState(m_pipelines[static_cast(pipeline_id)]); + m_p_command_graphics_list->SetGraphicsRootSignature(m_root_signatures[static_cast(pipeline_id)]); + + ID3D12DescriptorHeap* p_heaps[] = {m_p_descriptor_heap_shaders}; + m_p_command_graphics_list->SetDescriptorHeaps(_countof(p_heaps), p_heaps); + } + RMLUI_DX_MARKER_END(m_p_command_graphics_list); + + m_active_program_id = pipeline_id; +} + +RenderInterface_DX12::ConstantBufferType* RenderInterface_DX12::Get_ConstantBuffer(uint32_t current_back_buffer_index) +{ + RMLUI_ZoneScopedN("DirectX 12 - Get_ConstantBuffer"); + RMLUI_ASSERTMSG(current_back_buffer_index != uint32_t(-1), "invalid index!"); + + auto max_index = RMLUI_RENDER_BACKEND_FIELD_PREALLOCATED_CONSTANTBUFFERS - 1; + + if (m_constantbuffers[current_back_buffer_index].size() > RMLUI_RENDER_BACKEND_FIELD_PREALLOCATED_CONSTANTBUFFERS) + max_index = static_cast(m_constantbuffers[current_back_buffer_index].size() - 1); + + auto current_constant_buffer_index = m_constant_buffer_count_per_frame[current_back_buffer_index]; + if (current_constant_buffer_index > max_index) + { + // resizing... + for (auto& vec : m_constantbuffers) + { + vec.emplace_back(std::move(ConstantBufferType())); + } + +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] allocated new constant buffer instance for frame[%d], current size of storage[%zu]", + current_back_buffer_index, m_constantbuffers.at(current_back_buffer_index).size()); +#endif + } + + auto& vec_cbs = m_constantbuffers.at(current_back_buffer_index); + + ConstantBufferType* p_result = &vec_cbs[current_constant_buffer_index]; + if (p_result->m_alloc_info.buffer_index == -1) + { + p_result->m_alloc_info = m_manager_buffer.Alloc_ConstantBuffer(p_result, kAllocationSizeMax_ConstantBuffer); + } + + ++m_constant_buffer_count_per_frame[current_back_buffer_index]; + + return p_result; +} + +RenderInterface_DX12::BufferMemoryManager::BufferMemoryManager() : + m_descriptor_increment_size_srv_cbv_uav{}, m_size_for_allocation_in_bytes{}, m_size_alignment_in_bytes{}, m_p_device{}, m_p_allocator{}, + m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav{}, m_p_start_pointer_of_descriptor_heap_srv_cbv_uav{} +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Constructor"); +} + +RenderInterface_DX12::BufferMemoryManager::~BufferMemoryManager() +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Destructor"); +} + +bool RenderInterface_DX12::BufferMemoryManager::Is_Initialized() const +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferManagerManager::Is_Initialized"); + return static_cast(m_p_device != nullptr); +} + +void RenderInterface_DX12::BufferMemoryManager::Initialize(ID3D12Device* p_device, D3D12MA::Allocator* p_allocator, + OffsetAllocator::Allocator* p_offset_allocator_for_descriptor_heap_srv_cbv_uav, D3D12_CPU_DESCRIPTOR_HANDLE* p_handle, + uint32_t size_descriptor_srv_cbv_uav, size_t size_for_allocation, size_t size_alignment) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Initialize"); + RMLUI_ASSERTMSG(p_allocator, "must be valid!"); + RMLUI_ASSERTMSG(size_for_allocation, "must be greater than 0"); + RMLUI_ASSERTMSG(size_alignment, "must be greater than 0"); + RMLUI_ASSERTMSG(p_offset_allocator_for_descriptor_heap_srv_cbv_uav, "must be valid!"); + RMLUI_ASSERTMSG(p_handle, "must be valid!"); + RMLUI_ASSERTMSG(p_device, "must be valid!"); + + m_p_allocator = p_allocator; + m_size_for_allocation_in_bytes = size_for_allocation; + m_size_alignment_in_bytes = size_alignment; + m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav = p_offset_allocator_for_descriptor_heap_srv_cbv_uav; + m_p_start_pointer_of_descriptor_heap_srv_cbv_uav = p_handle; + m_descriptor_increment_size_srv_cbv_uav = size_descriptor_srv_cbv_uav; + m_p_device = p_device; + + Alloc_Buffer(size_for_allocation + +#ifdef RMLUI_DX_DEBUG + , + std::wstring(L"buffer[") + std::to_wstring(m_buffers.size()) + L"]" +#endif + ); +} + +void RenderInterface_DX12::BufferMemoryManager::Shutdown() +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Shutdown"); + for (auto& pair : m_buffers) + { + auto* p_allocation = pair.first; + + if (p_allocation) + { + if (p_allocation->GetResource()) + { + p_allocation->GetResource()->Unmap(0, nullptr); + p_allocation->GetResource()->Release(); + } + + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = p_allocation->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak!"); + } + } + + for (auto* p_block : m_virtual_buffers) + { + if (p_block) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = p_block->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak! (virtual block)"); + } + } + + if (m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav) + { + m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav = nullptr; + } + + if (m_p_allocator) + { + m_p_allocator = nullptr; + } + + m_buffers.clear(); + m_virtual_buffers.clear(); + m_p_device = nullptr; +} + +void RenderInterface_DX12::BufferMemoryManager::Alloc_Vertex(const void* p_data, int num_vertices, size_t size_of_one_element_in_p_data, + GeometryHandleType* p_handle) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Alloc_Vertex"); + RMLUI_ASSERTMSG(p_data, "data for mapping to buffer must valid!"); + RMLUI_ASSERTMSG(num_vertices, "amount of vertices must be greater than zero!"); + RMLUI_ASSERTMSG(size_of_one_element_in_p_data > 0, "size of one element must be greater than 0"); + RMLUI_ASSERTMSG(p_handle, "must be valid!"); + + if (p_handle) + { + RMLUI_ASSERT(p_handle->Get_InfoVertex().buffer_index == -1 && + "info is already initialized that means you didn't destroy your buffer! Something is wrong!"); + + p_handle->Set_NumVertices(num_vertices); + p_handle->Set_SizeOfOneVertex(size_of_one_element_in_p_data); + + GraphicsAllocationInfo info; + Alloc(info, num_vertices * size_of_one_element_in_p_data); + + void* p_writable_part = Get_WritableMemoryFromBufferByOffset(info); + + RMLUI_ASSERTMSG(p_writable_part, "something is wrong!"); + + if (p_writable_part) + { + std::memcpy(p_writable_part, p_data, info.size); + } + + p_handle->Set_InfoVertex(info); + +#ifdef RMLUI_DX_DEBUG + auto* p_block = m_virtual_buffers.at(info.buffer_index); + + RMLUI_ASSERTMSG(p_block, "can't be invalid!"); + + D3D12MA::Statistics stats; + p_block->GetStatistics(&stats); + + auto available_memory = stats.BlockBytes - stats.AllocationBytes; + + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, + "[DirectX-12] allocated vertex buffer with size[%zu] (in bytes) in buffer[%d] available memory for this buffer [%zu] (in bytes)", + info.size, info.buffer_index, available_memory); +#endif + } +} + +void RenderInterface_DX12::BufferMemoryManager::Alloc_Index(const void* p_data, int num_vertices, size_t size_of_one_element_in_p_data, + GeometryHandleType* p_handle) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Alloc_Index"); + RMLUI_ASSERTMSG(p_data, "data for mapping to buffer must valid!"); + RMLUI_ASSERTMSG(num_vertices, "amount of vertices must be greater than zero!"); + RMLUI_ASSERTMSG(size_of_one_element_in_p_data > 0, "size of one element must be greater than 0"); + RMLUI_ASSERTMSG(p_handle, "must be valid!"); + + if (p_handle) + { + RMLUI_ASSERT(p_handle->Get_InfoIndex().buffer_index == -1 && + "info is already initialized that means you didn't destroy your buffer! Something is wrong!"); + + p_handle->Set_NumIndices(num_vertices); + p_handle->Set_SizeOfOneIndex(size_of_one_element_in_p_data); + + GraphicsAllocationInfo info; + Alloc(info, num_vertices * size_of_one_element_in_p_data); + + void* p_writable_part = Get_WritableMemoryFromBufferByOffset(info); + + RMLUI_ASSERTMSG(p_writable_part, "something is wrong!"); + + if (p_writable_part) + { + std::memcpy(p_writable_part, p_data, num_vertices * size_of_one_element_in_p_data); + } + + p_handle->Set_InfoIndex(info); + +#ifdef RMLUI_DX_DEBUG + auto* p_block = m_virtual_buffers.at(info.buffer_index); + + RMLUI_ASSERTMSG(p_block, "can't be invalid!"); + + D3D12MA::Statistics stats; + p_block->GetStatistics(&stats); + + auto available_memory = stats.BlockBytes - stats.AllocationBytes; + + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, + "[DirectX-12] allocated index buffer with size[%zu] (in bytes) in buffer[%d] available memory for this buffer [%zu] (in bytes)", + info.size, info.buffer_index, available_memory); +#endif + } +} + +RenderInterface_DX12::GraphicsAllocationInfo RenderInterface_DX12::BufferMemoryManager::Alloc_ConstantBuffer(ConstantBufferType* p_resource, + size_t size) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Alloc_ConstantBuffer"); + RMLUI_ASSERTMSG(!m_buffers.empty(), "you forgot to allocate buffer on initialize stage of this manager!"); + RMLUI_ASSERTMSG(p_resource, "must be valid!"); + + GraphicsAllocationInfo result; + auto result_index = Alloc(result, size, 256); + + if (p_resource) + { + if (result_index != -1) + { +#ifdef RMLUI_DX_DEBUG + auto* p_block = m_virtual_buffers.at(result.buffer_index); + + RMLUI_ASSERTMSG(p_block, "can't be invalid!"); + + D3D12MA::Statistics stats; + p_block->GetStatistics(&stats); + + auto available_memory = stats.BlockBytes - stats.AllocationBytes; + + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, + "[DirectX-12] allocated constant buffer with size[%zu] (in bytes) in buffer[%d] available memory for this buffer [%zu] (in bytes)", + result.size, result.buffer_index, available_memory); +#endif + + auto* p_dx_allocation = m_buffers.at(result_index).first; + RMLUI_ATTR_ASSERT_VARIABLE auto* p_dx_resource = p_dx_allocation->GetResource(); + + RMLUI_ASSERTMSG(p_dx_allocation, "something is broken!"); + RMLUI_ASSERTMSG(p_dx_resource, "something is broken!"); + + p_resource->m_p_gpu_start_memory_for_binding_data = m_buffers.at(result_index).second; + } + } + + return result; +} + +void RenderInterface_DX12::BufferMemoryManager::Free_ConstantBuffer(ConstantBufferType* p_constantbuffer) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Free_ConstantBuffer"); + RMLUI_ASSERTMSG(p_constantbuffer, "you must pass a valid object!"); + + if (p_constantbuffer) + { + const auto& info = p_constantbuffer->m_alloc_info; + RMLUI_ASSERTMSG(info.buffer_index != -1, "must be valid data of this info"); + +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] deallocated constant buffer with size[%zu] in buffer[%d]", info.size, + info.buffer_index); +#endif + + if (m_virtual_buffers.empty() == false) + { + if (D3D12MA::VirtualBlock* p_block = m_virtual_buffers.at(info.buffer_index)) + { + p_block->FreeAllocation(info.alloc_info); + p_constantbuffer->m_alloc_info = {}; + } + } + } +} + +void RenderInterface_DX12::BufferMemoryManager::Free_Geometry(GeometryHandleType* p_handle) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Free_Geometry"); + RMLUI_ASSERTMSG(p_handle, "must be valid!"); + RMLUI_ASSERTMSG(p_handle->Get_InfoVertex().buffer_index != -1, "not initialized, maybe you passing twice for deallocation?"); + RMLUI_ASSERTMSG(p_handle->Get_InfoIndex().buffer_index != -1, "not initialized, maybe you passing twice for deallocation?"); + + if (p_handle) + { + const auto& info_vertex = p_handle->Get_InfoVertex(); + const auto& info_index = p_handle->Get_InfoIndex(); + + if (m_virtual_buffers.empty() == false) + { + D3D12MA::VirtualBlock* p_block_vertex = m_virtual_buffers.at(info_vertex.buffer_index); + D3D12MA::VirtualBlock* p_block_index = m_virtual_buffers.at(info_index.buffer_index); + + if (p_block_vertex) + { +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] deallocated vertex buffer with size[%zu] in buffer[%d] frame[%d]", + info_vertex.size, info_vertex.buffer_index, p_handle->Get_HistoryBackBufferFrameIndex()); +#endif + + p_block_vertex->FreeAllocation(info_vertex.alloc_info); + + GraphicsAllocationInfo invalidate; + p_handle->Set_InfoVertex(invalidate); + } + + if (p_block_index) + { +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] deallocated index buffer with size[%zu] in buffer[%d]", info_index.size, + info_index.buffer_index); +#endif + + p_block_index->FreeAllocation(info_index.alloc_info); + + GraphicsAllocationInfo invalidate; + p_handle->Set_InfoIndex(invalidate); + } + + // Free_ConstantBuffer(&p_handle->Get_ConstantBuffer()); + } + } +} + +void* RenderInterface_DX12::BufferMemoryManager::Get_WritableMemoryFromBufferByOffset(const GraphicsAllocationInfo& info) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Get_WritableMemoryFromBufferByOffset"); + RMLUI_ASSERTMSG(info.buffer_index != -1, "you pass not initialized graphics allocation info!"); + + void* p_result{}; + if (info.buffer_index != -1) + { + std::uint8_t* p_begin = reinterpret_cast(m_buffers.at(info.buffer_index).second); + + RMLUI_ASSERTMSG(p_begin, "being pointer is not valid! it's terribly wrong thing!!!!"); + + p_result = p_begin + info.offset; + } + + return p_result; +} + +D3D12MA::Allocation* RenderInterface_DX12::BufferMemoryManager::Get_BufferByIndex(int buffer_index) +{ + RMLUI_ZoneScopedN("DirectX 12 - Get_BufferByIndex"); + RMLUI_ASSERTMSG(buffer_index >= 0, "index must be valid!"); + RMLUI_ASSERTMSG(buffer_index < m_buffers.size(), "overflow index!"); + + D3D12MA::Allocation* p_result{}; + + if (buffer_index >= 0) + { + if (buffer_index < m_buffers.size()) + { + p_result = m_buffers.at(buffer_index).first; + } + } + + return p_result; +} + +void RenderInterface_DX12::BufferMemoryManager::Alloc_Buffer(size_t size +#ifdef RMLUI_DX_DEBUG + , + const std::wstring& debug_name +#endif +) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Alloc_Buffer"); + RMLUI_ASSERTMSG(size, "must be greater than 0"); + + D3D12_RESOURCE_DESC desc_constantbuffer = {}; + desc_constantbuffer.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + desc_constantbuffer.Alignment = 0; + desc_constantbuffer.Width = size; + desc_constantbuffer.Height = 1; + desc_constantbuffer.DepthOrArraySize = 1; + desc_constantbuffer.MipLevels = 1; + desc_constantbuffer.Format = DXGI_FORMAT_UNKNOWN; + desc_constantbuffer.SampleDesc.Count = 1; + desc_constantbuffer.SampleDesc.Quality = 0; + desc_constantbuffer.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + desc_constantbuffer.Flags = D3D12_RESOURCE_FLAG_NONE; + + D3D12MA::ALLOCATION_DESC desc_allocation = {}; + desc_allocation.HeapType = D3D12_HEAP_TYPE_UPLOAD; + + ID3D12Resource* p_resource{}; + D3D12MA::Allocation* p_allocation{}; + + auto result = m_p_allocator->CreateResource(&desc_allocation, &desc_constantbuffer, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &p_allocation, + IID_PPV_ARGS(&p_resource)); + + RMLUI_DX_VERIFY_MSG(result, "failed to CreateResource"); + + void* p_begin_writable_data{}; + D3D12_RANGE range; + range.Begin = 0; + range.End = 0; + + result = p_allocation->GetResource()->Map(0, &range, &p_begin_writable_data); + + RMLUI_DX_VERIFY_MSG(result, "failed to ID3D12Resource::Map"); + +#ifdef RMLUI_DX_DEBUG + p_allocation->SetName(debug_name.c_str()); +#endif + + m_buffers.push_back({p_allocation, p_begin_writable_data}); + + D3D12MA::VIRTUAL_BLOCK_DESC desc_virtual = {}; + desc_virtual.Size = size; + + D3D12MA::VirtualBlock* p_block{}; + result = D3D12MA::CreateVirtualBlock(&desc_virtual, &p_block); + + RMLUI_DX_VERIFY_MSG(result, "failed to D3D12MA::CreateVirtualBlock"); + + m_virtual_buffers.push_back(p_block); +} + +D3D12MA::VirtualBlock* RenderInterface_DX12::BufferMemoryManager::Get_AvailableBlock(size_t size_for_allocation, int* result_index) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Get_AvailableBlock"); + RMLUI_ASSERTMSG(result_index, "must be valid part of memory!"); + + D3D12MA::VirtualBlock* p_result{}; + + int index{}; + for (auto* p_block : m_virtual_buffers) + { + if (p_block) + { + D3D12MA::Statistics stats; + p_block->GetStatistics(&stats); + + if ((stats.BlockBytes - stats.AllocationBytes) >= size_for_allocation) + { + p_result = p_block; + *result_index = index; + break; + } + } + ++index; + } + + return p_result; +} + +D3D12MA::VirtualBlock* RenderInterface_DX12::BufferMemoryManager::Get_NotOutOfMemoryAndAvailableBlock(size_t size_for_allocation, int* result_index) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Get_NotOutOfMemoryAndAvailableBlock"); + RMLUI_ASSERTMSG(result_index, "must be valid part of memory!"); + RMLUI_ASSERTMSG(*result_index != -1, + "use this method when you found of available block then tried to allocate from it but got out of memory status!"); + + D3D12MA::VirtualBlock* p_result{}; + + // we skip out of memory block since it shows to us as available + auto from = *result_index + 1; + + for (int i = from; i < m_virtual_buffers.size(); ++i) + { + auto* p_block = m_virtual_buffers.at(i); + if (p_block) + { + D3D12MA::Statistics stats; + p_block->GetStatistics(&stats); + + if ((stats.BlockBytes - stats.AllocationBytes) >= size_for_allocation) + { + p_result = p_block; + *result_index = i; + break; + } + } + } + + return p_result; +} + +int RenderInterface_DX12::BufferMemoryManager::Alloc(GraphicsAllocationInfo& info, size_t size, size_t alignment) +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Alloc"); + RMLUI_ASSERTMSG(!m_buffers.empty(), "you forgot to allocate buffer on initialize stage of this manager!"); + + // we don't want to use any recursions because it is slow af + constexpr int kHowManyRequestsWeCanDoForResolvingOutOfMemory = 15; + int result_index{-1}; + + if (alignment > 0) + size = AlignUp(size, alignment); + + auto* p_block = Get_AvailableBlock(size, &result_index); + + if (p_block) + { + D3D12MA::VIRTUAL_ALLOCATION_DESC desc_alloc = {}; + desc_alloc.Size = size; + + if (alignment % 2 == 0) + desc_alloc.Alignment = alignment; + + D3D12MA::VirtualAllocation alloc; + UINT64 offset{}; + + auto status = p_block->Allocate(&desc_alloc, &alloc, &offset); + + if (status == E_OUTOFMEMORY) + { + for (int i = 0; i < kHowManyRequestsWeCanDoForResolvingOutOfMemory; ++i) + { + p_block = Get_NotOutOfMemoryAndAvailableBlock(size, &result_index); + if (!p_block) + { + if (size > m_size_for_allocation_in_bytes) + { +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] auto correction size for buffer from [%zu] to [%zu]", + m_size_for_allocation_in_bytes, size); +#endif + + m_size_for_allocation_in_bytes = size; + } + + Alloc_Buffer(m_size_for_allocation_in_bytes +#ifdef RMLUI_DX_DEBUG + , + std::wstring(L"buffer[") + std::to_wstring(m_buffers.size()) + L"]" +#endif + ); + result_index = static_cast(m_buffers.size() - 1); + } + + p_block = m_virtual_buffers.at(result_index); + + desc_alloc = {}; + desc_alloc.Size = size; + if (alignment % 2 == 0) + desc_alloc.Alignment = alignment; + + auto status_allocation = p_block->Allocate(&desc_alloc, &alloc, &offset); + if (status_allocation == E_OUTOFMEMORY) + { + continue; + } + else if (status_allocation == S_OK) + { + break; + } + else + { + RMLUI_ERRORMSG("Unknown allocation status"); + } + } + + RMLUI_ASSERTMSG(offset == UINT64_MAX, + "it was last greedy try for allocating, it is really hard case for handling (report and describe your case on github), try to " + "optimize your 'stuff' (very calmly saying) " + "by your own, it is only for really rare specials cases where user doesn't want to think but UI must survive no matter what"); + } + + info.size = size; + info.alloc_info = alloc; + info.offset = offset; + info.buffer_index = result_index; + } + else + { + if (size > m_size_for_allocation_in_bytes) + { +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] auto correction size for buffer from [%zu] to [%zu]", + m_size_for_allocation_in_bytes, size); +#endif + + m_size_for_allocation_in_bytes = size; + } + + Alloc_Buffer(m_size_for_allocation_in_bytes +#ifdef RMLUI_DX_DEBUG + , + std::wstring(L"buffer[") + std::to_wstring(m_buffers.size()) + L"]" +#endif + ); + + result_index = -1; + p_block = Get_AvailableBlock(size, &result_index); + + RMLUI_ASSERTMSG(p_block, "can't be because in previous line of code you added new allocated fresh buffer!"); + + if (p_block) + { + D3D12MA::VIRTUAL_ALLOCATION_DESC desc_alloc = {}; + desc_alloc.Size = size; + + if (alignment % 2 == 0) + desc_alloc.Alignment = alignment; + + D3D12MA::VirtualAllocation alloc; + UINT64 offset{}; + + auto status = p_block->Allocate(&desc_alloc, &alloc, &offset); + + RMLUI_DX_VERIFY_MSG(status, "failed to Allocate"); + + info.size = size; + info.alloc_info = alloc; + info.offset = offset; + info.buffer_index = result_index; + } + } + + TryToFreeAvailableBlock(); + + return result_index; +} + +void RenderInterface_DX12::BufferMemoryManager::TryToFreeAvailableBlock() +{ + RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::TryToFreeAvailableBlock"); + Rml::Array::const_iterator, Rml::Vector>::const_iterator>, 1> + max_for_free; + + for (size_t i = 0; i < max_for_free.size(); ++i) + { + max_for_free[i].first = m_virtual_buffers.end(); + max_for_free[i].second = m_buffers.end(); + } + + int total_count{}; + int limit_for_break{static_cast(max_for_free.size())}; + int index{}; + + for (auto cur = m_virtual_buffers.begin(); cur != m_virtual_buffers.end(); ++cur) + { + if (total_count == limit_for_break) + break; + + if (*cur) + { + auto* p_block = *cur; + D3D12MA::Statistics stats; + p_block->GetStatistics(&stats); + + if (stats.AllocationCount == 0 && stats.BlockCount == 0) + { + auto ref_count = p_block->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + + ref_count = m_buffers.at(index).first->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + + m_buffers.at(index).second = nullptr; + + max_for_free[total_count].first = cur; + max_for_free[total_count].second = m_buffers.begin() + index; + + ++total_count; + } + } + ++index; + } + + for (int i = 0; i < total_count; ++i) + { + auto& pair = max_for_free.at(i); + + m_virtual_buffers.erase(pair.first); + m_buffers.erase(pair.second); + } +} + +RenderInterface_DX12::TextureMemoryManager::TextureMemoryManager() : + m_size_for_placed_heap{}, m_size_limit_for_being_placed{}, m_size_srv_cbv_uav_descriptor{}, m_size_rtv_descriptor{}, m_size_dsv_descriptor{}, + m_fence_value{}, m_p_allocator{}, m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav{}, m_p_device{}, m_p_copy_command_list{}, + m_p_backend_command_list{}, m_p_command_allocator{}, m_p_descriptor_heap_srv{}, m_p_copy_queue{}, m_p_fence{}, m_p_fence_event{}, m_p_handle{}, + m_p_renderer{}, m_p_virtual_block_for_render_target_heap_allocations{}, m_p_virtual_block_for_depth_stencil_heap_allocations{}, + m_p_descriptor_heap_rtv{}, m_p_descriptor_heap_dsv(), m_p_upload_buffer() +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Constructor"); +} + +RenderInterface_DX12::TextureMemoryManager::~TextureMemoryManager() +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Destructor"); +} + +bool RenderInterface_DX12::TextureMemoryManager::Is_Initialized() const +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Is_Initialized"); + return static_cast(m_p_device != nullptr); +} + +void RenderInterface_DX12::TextureMemoryManager::Initialize(D3D12MA::Allocator* p_allocator, + OffsetAllocator::Allocator* p_offset_allocator_for_descriptor_heap_srv_cbv_uav, ID3D12Device* p_device, + ID3D12GraphicsCommandList* p_copy_command_list, ID3D12GraphicsCommandList* p_backend_command_list, ID3D12CommandAllocator* p_allocator_command, + ID3D12DescriptorHeap* p_descriptor_heap_srv, ID3D12DescriptorHeap* p_descriptor_heap_rtv, ID3D12DescriptorHeap* p_descriptor_heap_dsv, + ID3D12CommandQueue* p_copy_queue, D3D12_CPU_DESCRIPTOR_HANDLE* p_handle, RenderInterface_DX12* p_renderer, size_t size_for_placed_heap) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Initialize"); + RMLUI_ASSERTMSG(p_allocator, "you must pass a valid allocator pointer"); + RMLUI_ASSERTMSG(size_for_placed_heap > 0, "there's no point in creating in such small heap"); + RMLUI_ASSERTMSG(size_for_placed_heap != size_t(-1), "invalid value!"); + RMLUI_ASSERTMSG(p_device, "must be valid!"); + RMLUI_ASSERTMSG(p_copy_command_list, "must be valid!"); + RMLUI_ASSERTMSG(p_backend_command_list, "must be valid!"); + RMLUI_ASSERTMSG(p_allocator_command, "must be valid!"); + RMLUI_ASSERTMSG(p_descriptor_heap_srv, "must be valid!"); + RMLUI_ASSERTMSG(p_copy_queue, "must be valid!"); + RMLUI_ASSERTMSG(p_handle, "must be valid!"); + RMLUI_ASSERTMSG(p_renderer, "must be valid!"); + RMLUI_ASSERTMSG(p_offset_allocator_for_descriptor_heap_srv_cbv_uav, "must be valid!"); + RMLUI_ASSERTMSG(p_descriptor_heap_rtv, "must be valid!"); + RMLUI_ASSERTMSG(p_descriptor_heap_dsv, "must be valid!"); + + m_p_device = p_device; + m_p_allocator = p_allocator; + m_p_copy_command_list = p_copy_command_list; + m_p_backend_command_list = p_backend_command_list; + m_p_command_allocator = p_allocator_command; + m_size_for_placed_heap = size_for_placed_heap; + m_p_descriptor_heap_srv = p_descriptor_heap_srv; + m_p_descriptor_heap_rtv = p_descriptor_heap_rtv; + m_p_descriptor_heap_dsv = p_descriptor_heap_dsv; + m_p_copy_queue = p_copy_queue; + m_p_renderer = p_renderer; + m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav = p_offset_allocator_for_descriptor_heap_srv_cbv_uav; + + // if I have 4 MB then 1 MB is optimal as a maximum size for determing that resource can be allocated as placed resource so we just take 25% from + // size_for_placed_heap value + m_size_limit_for_being_placed = size_t((double(size_for_placed_heap) / 100.0) * 25.0); + + if (m_p_device) + { + m_size_srv_cbv_uav_descriptor = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + m_size_rtv_descriptor = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + m_size_dsv_descriptor = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV); + } + + m_p_handle = p_handle; + + RMLUI_ASSERTMSG(m_size_srv_cbv_uav_descriptor > 0, "must be positive"); + RMLUI_ASSERTMSG(m_size_rtv_descriptor > 0, "must be positive"); + + RMLUI_ASSERTMSG(m_size_limit_for_being_placed > 0 && m_size_limit_for_being_placed != size_t(-1), "something is wrong!"); + + if (m_p_device) + { + RMLUI_DX_VERIFY_MSG(m_p_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_p_fence)), "failed to create fence for upload queue"); + + m_p_fence_event = CreateEvent(NULL, FALSE, FALSE, TEXT("Event for copy queue fence")); + } + + if (m_p_descriptor_heap_rtv) + { + D3D12MA::VIRTUAL_BLOCK_DESC desc_block{}; + desc_block.Size = m_size_rtv_descriptor * RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_RTV; + + auto status = D3D12MA::CreateVirtualBlock(&desc_block, &m_p_virtual_block_for_render_target_heap_allocations); + + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12MA::CreateVirtualBlock (for rtv descriptor heap, texture manager)"); + } + + if (m_p_descriptor_heap_dsv) + { + D3D12MA::VIRTUAL_BLOCK_DESC desc_block{}; + desc_block.Size = m_size_dsv_descriptor * RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_DSV; + + auto status = D3D12MA::CreateVirtualBlock(&desc_block, &m_p_virtual_block_for_depth_stencil_heap_allocations); + + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12MA::CreateVirtualBlock (for dsv descriptor heap, texture manager)"); + } + +#if RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED == 1 + if (m_p_allocator) + { + D3D12MA::Allocation* p_allocation = nullptr; + + ID3D12Resource* p_upload_buffer{}; + + D3D12_RESOURCE_DESC buffer_desc; + + buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + buffer_desc.Alignment = 0; + buffer_desc.Width = RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_SIZE; + buffer_desc.Height = 1; + buffer_desc.DepthOrArraySize = 1; + buffer_desc.MipLevels = 1; + buffer_desc.Format = DXGI_FORMAT_UNKNOWN; + buffer_desc.SampleDesc.Count = 1; + buffer_desc.SampleDesc.Quality = 0; + buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE; + + D3D12MA::ALLOCATION_DESC desc_alloc{}; + desc_alloc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + + auto status = m_p_allocator->CreateResource(&desc_alloc, &buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &p_allocation, + IID_PPV_ARGS(&p_upload_buffer)); + + RMLUI_DX_VERIFY_MSG(status, "failed to CreateResource (upload buffer for texture)"); + + m_p_upload_buffer = p_allocation; + } +#endif +} + +void RenderInterface_DX12::TextureMemoryManager::Shutdown() +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Shutdown"); + if (m_p_fence) + { + m_p_fence->Release(); + m_p_fence = nullptr; + } + + if (m_p_fence_event) + { + CloseHandle(m_p_fence_event); + m_p_fence_event = nullptr; + } + + size_t index{}; + for (auto* p_heap : m_heaps_placed) + { + p_heap->Release(); + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_blocks.at(index)->Release(); + + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + ++index; + } + + if (m_p_virtual_block_for_render_target_heap_allocations) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_virtual_block_for_render_target_heap_allocations->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + + if (m_p_virtual_block_for_depth_stencil_heap_allocations) + { + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_virtual_block_for_depth_stencil_heap_allocations->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak"); + } + +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12]: TextureMemoryManager -> total blocks in session were allocated = %zu", + m_blocks.size()); +#endif + + m_blocks.clear(); + m_heaps_placed.clear(); + +#if RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED == 1 + if (m_p_upload_buffer) + { + if (m_p_upload_buffer->GetResource()) + { + m_p_upload_buffer->GetResource()->Release(); + } + + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_upload_buffer->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak!"); + } +#endif + + m_p_allocator = nullptr; + m_p_device = nullptr; + m_p_copy_command_list = nullptr; + m_p_backend_command_list = nullptr; + m_p_command_allocator = nullptr; + m_p_descriptor_heap_srv = nullptr; + m_p_copy_queue = nullptr; + m_p_handle = nullptr; + m_p_renderer = nullptr; + m_p_descriptor_heap_rtv = nullptr; + m_p_virtual_block_for_render_target_heap_allocations = nullptr; + m_p_virtual_block_for_depth_stencil_heap_allocations = nullptr; +} + +ID3D12Resource* RenderInterface_DX12::TextureMemoryManager::Alloc_Texture(D3D12_RESOURCE_DESC& desc, TextureHandleType* p_impl, + const Rml::byte* p_data +#ifdef RMLUI_DX_DEBUG + , + const Rml::String& debug_name +#endif +) +{ +#ifdef RMLUI_DX_DEBUG + (void)(debug_name); +#endif + + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_Texture"); + RMLUI_ASSERTMSG(desc.Dimension == D3D12_RESOURCE_DIMENSION::D3D12_RESOURCE_DIMENSION_TEXTURE2D, + "this manager doesn't support 1D or 3D textures. (why do we need to support it?)"); + RMLUI_ASSERTMSG(desc.DepthOrArraySize <= 1, "we don't support a such sizes"); + RMLUI_ASSERTMSG(desc.SampleDesc.Count == 1, "this manager not for allocating render targets or depth stencil!"); + RMLUI_ASSERTMSG(desc.Width, "must specify value for width field"); + RMLUI_ASSERTMSG(desc.Height, "must specify value for height field"); + RMLUI_ASSERTMSG(p_impl, "must be valid!"); + + ID3D12Resource* p_result{}; + + // this size stands for real size of texture and how much it will occupy for allocated block, because if it is larger than 1 MB it is better to + // allocate as committed resource instead of using placed resource technique (by default we allocate block for 4MB because 1.0 MB textures can be + // occupied 4 times and smaller even more times, but it is not reasonable to use placed resources for >1MB textures) + auto base_memory_size_for_allocation_in_bytes = desc.Width * desc.Height * BytesPerPixel(desc.Format); + size_t total_memory_for_allocation = base_memory_size_for_allocation_in_bytes; + size_t mipmemory = base_memory_size_for_allocation_in_bytes; + for (int i = 2; i <= desc.MipLevels; ++i) + { + if (mipmemory <= 1) + { + break; + } + + mipmemory /= 4; + total_memory_for_allocation += mipmemory; + } + +#ifdef RMLUI_DX_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] allocating texture with base memory[%zu] and total memory with mips levels[%zu]", + base_memory_size_for_allocation_in_bytes, total_memory_for_allocation); +#endif + + // we need to pass full memory with mipmaps otherwise the DirectX API will validate it by its own. we must ensure what we do. + if (CanBePlacedResource(total_memory_for_allocation)) + { + Alloc_As_Placed(base_memory_size_for_allocation_in_bytes, total_memory_for_allocation, desc, p_impl, p_data); + p_result = static_cast(p_impl->Get_Resource()); + } + else + { + Alloc_As_Committed(base_memory_size_for_allocation_in_bytes, total_memory_for_allocation, desc, p_impl, p_data); + p_result = static_cast(p_impl->Get_Resource())->GetResource(); + } + + return p_result; +} + +ID3D12Resource* RenderInterface_DX12::TextureMemoryManager::Alloc_Texture(D3D12_RESOURCE_DESC& desc, Gfx::FramebufferData* p_impl, + D3D12_RESOURCE_FLAGS flags, D3D12_RESOURCE_STATES initial_state +#ifdef RMLUI_DX_DEBUG + , + const Rml::String& debug_name +#endif +) +{ +#ifdef RMLUI_DX_DEBUG + (void)(debug_name); +#endif + + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_Texture"); + RMLUI_ASSERTMSG(desc.Dimension == D3D12_RESOURCE_DIMENSION::D3D12_RESOURCE_DIMENSION_TEXTURE2D, + "this manager doesn't support 1D or 3D textures. (why do we need to support it?)"); + RMLUI_ASSERTMSG(desc.DepthOrArraySize <= 1, "we don't support a such sizes"); + RMLUI_ASSERTMSG(desc.Width, "must specify value for width field"); + RMLUI_ASSERTMSG(desc.Height, "must specify value for height field"); + RMLUI_ASSERTMSG(p_impl, "must be valid!"); + RMLUI_ASSERT(p_impl->Get_Texture() && + "you must allocate Gfx::FramebufferData's field (texture) before calling this method! So you need to set your allocated pointer to " + "Set_Texture method, dude..."); + + ID3D12Resource* p_result{}; + + auto base_memory_size_for_allocation_in_bytes = desc.Width * desc.Height * BytesPerPixel(desc.Format); + size_t total_memory_for_allocation = base_memory_size_for_allocation_in_bytes; + size_t mipmemory = base_memory_size_for_allocation_in_bytes; + for (int i = 2; i <= desc.MipLevels; ++i) + { + if (mipmemory <= 1) + { + break; + } + + mipmemory /= 4; + total_memory_for_allocation += mipmemory; + } + + desc.Flags = flags; + +#ifdef RMLUI_DX_DEBUG + bool is_rt = flags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + bool is_ds = flags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + + RMLUI_ASSERTMSG(is_rt || is_ds, "must be allow for RT or DS otherwise you will get assert from DX12"); + + const char* type_of_flag_for_texture_debug_name{}; + + constexpr const char* hardcoded_name_render_target = "render target"; + constexpr const char* hardcoded_name_depth_stencil = "depth-stencil"; + constexpr const char* hardcoded_name_unknown = "unknown"; + + if (is_rt) + { + type_of_flag_for_texture_debug_name = hardcoded_name_render_target; + } + else if (is_ds) + { + type_of_flag_for_texture_debug_name = hardcoded_name_depth_stencil; + } + else + { + type_of_flag_for_texture_debug_name = hardcoded_name_unknown; + } + + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, + "[DirectX-12] allocating render2texture (%s) with base memory[%zu] and total memory with mips levels[%zu]", + type_of_flag_for_texture_debug_name, base_memory_size_for_allocation_in_bytes, total_memory_for_allocation); +#endif + + Alloc_As_Committed(base_memory_size_for_allocation_in_bytes, total_memory_for_allocation, desc, initial_state, p_impl->Get_Texture(), p_impl); + + p_result = static_cast(p_impl->Get_Texture()->Get_Resource())->GetResource(); + + // dx12 sdk was updated and if we don't do that operation we get a validation error see + // https://asawicki.info/news_1724_initializing_dx12_textures_after_allocation_and_aliasing direct quote: "If the metadata of such compression are + // uninitialized, it might have consequences more severe than observing random colors. It's actually an undefined behavior." + if (p_result) + { + bool is_render_target = flags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + + RMLUI_ASSERTMSG(m_p_backend_command_list, "must be initialized"); + RMLUI_ASSERTMSG(p_impl, "must be valid"); + + if (is_render_target) + { + constexpr FLOAT clear_color[] = {0.0f, 0.0f, 0.0f, 0.0f}; + m_p_backend_command_list->ClearRenderTargetView(p_impl->Get_DescriptorResourceView(), clear_color, 0, nullptr); + } + } + + return p_result; +} + +void RenderInterface_DX12::TextureMemoryManager::Free_Texture(TextureHandleType* p_texture) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Free_Texture(TextureHandleType)"); + RMLUI_ASSERTMSG(p_texture, "must be valid"); + + if (p_texture) + { + auto index = p_texture->Get_Info().buffer_index; + RMLUI_ASSERTMSG(p_texture->Get_Resource(), "must be valid! can't be! data is corrupted! or was already destroyed!"); + + if (index != -1) + { + m_blocks.at(index)->FreeAllocation(p_texture->Get_Info().alloc_info); + } + + if (m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav) + { + m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav->free(p_texture->Get_Allocation_DescriptorHeap()); + } + + p_texture->Destroy(); + } +} + +void RenderInterface_DX12::TextureMemoryManager::Free_Texture(Gfx::FramebufferData* p_impl) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Free_Texture(FramebufferData)"); + RMLUI_ASSERTMSG(p_impl, "must be valid!"); + RMLUI_ASSERTMSG(p_impl->Get_Texture(), "must be valid!"); + RMLUI_ASSERTMSG(m_p_virtual_block_for_render_target_heap_allocations, "must be valid! early calling"); + RMLUI_ASSERTMSG(m_p_virtual_block_for_depth_stencil_heap_allocations, "must be valid! early calling"); + + if (p_impl) + { + Free_Texture(p_impl->Get_Texture(), p_impl->Is_RenderTarget(), *(p_impl->Get_VirtualAllocation_Descriptor())); + + delete p_impl->Get_Texture(); + p_impl->Set_Texture(nullptr); + + p_impl->Set_DescriptorResourceView({}); + p_impl->Set_ID(-1); + } +} + +void RenderInterface_DX12::TextureMemoryManager::Free_Texture(TextureHandleType* p_texture, bool is_rt, D3D12MA::VirtualAllocation& allocation) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Free_Texture(TextureHandleType,bool,D3D12MA::VirtualAllocation)"); + RMLUI_ASSERTMSG(p_texture, "you must pass a valid pointer!"); + + Free_Texture(p_texture); + + if (is_rt) + { + if (m_p_virtual_block_for_render_target_heap_allocations) + { + m_p_virtual_block_for_render_target_heap_allocations->FreeAllocation(allocation); + } + } + else + { + if (m_p_virtual_block_for_depth_stencil_heap_allocations) + { + m_p_virtual_block_for_depth_stencil_heap_allocations->FreeAllocation(allocation); + } + } +} + +bool RenderInterface_DX12::TextureMemoryManager::CanAllocate(size_t total_memory_for_allocation, D3D12MA::VirtualBlock* p_block) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::CanAllocate(size_t, D3D12MA::VirtualBlock)"); + RMLUI_ASSERTMSG(total_memory_for_allocation > 0 && total_memory_for_allocation != size_t(-1), "must be a valid number!"); + + RMLUI_ASSERTMSG(p_block, "must be valid virtual block"); + + bool result{}; + + if (p_block) + { + D3D12MA::Statistics stats; + p_block->GetStatistics(&stats); + + result = (stats.BlockBytes - stats.AllocationBytes) >= total_memory_for_allocation; + } + + return result; +} + +bool RenderInterface_DX12::TextureMemoryManager::CanBePlacedResource(size_t total_memory_for_allocation) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::CanBePlacedResource"); + RMLUI_ASSERTMSG(total_memory_for_allocation > 0 && total_memory_for_allocation != size_t(-1), "must be a valid number!"); + + bool result{}; + + if (total_memory_for_allocation <= m_size_limit_for_being_placed) + result = true; + + return result; +} + +bool RenderInterface_DX12::TextureMemoryManager::CanBeSmallResource(size_t base_memory) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::CanBeSmallResource"); + RMLUI_ASSERTMSG(base_memory > 0, "must be greater than zero!"); + RMLUI_ASSERTMSG(base_memory != size_t(-1), "must be a valid number!"); + + bool result{}; + + constexpr size_t _kNonMSAASmallResourceMemoryLimit = 128 * 128 * 4; + + // if this is not MSAA texture (because for now we didn't implement a such support) + if (base_memory <= _kNonMSAASmallResourceMemoryLimit) + result = true; + + return result; +} + +D3D12MA::VirtualBlock* RenderInterface_DX12::TextureMemoryManager::Get_AvailableBlock(size_t total_memory_for_allocation, int* result_index) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Get_AvailableBlock"); + RMLUI_ASSERTMSG(m_p_device, "must be valid!"); + RMLUI_ASSERTMSG(result_index, "must be valid!"); + RMLUI_ASSERTMSG(total_memory_for_allocation <= m_size_limit_for_being_placed, "you can't pass a such size here!"); + RMLUI_ASSERTMSG(m_size_limit_for_being_placed < m_size_for_placed_heap, "something is wrong and you initialized your manager wrong!!!!"); + + D3D12MA::VirtualBlock* p_result{}; + + if (m_blocks.empty()) + { + RMLUI_ASSERTMSG(m_heaps_placed.empty(), "if blocks are empty heaps must be too!"); + + auto pair = Create_HeapPlaced(m_size_for_placed_heap); + + p_result = pair.second; + *result_index = 0; + } + else + { + int index{}; + for (auto* p_block : m_blocks) + { + if (p_block) + { + if (CanAllocate(total_memory_for_allocation, p_block)) + { + p_result = p_block; + *result_index = index; + + break; + } + else + { + if (index >= m_blocks.size() - 1) + { + auto pair = Create_HeapPlaced(m_size_for_placed_heap); + ++index; + *result_index = index; + p_result = pair.second; + + break; + } + } + } + ++index; + } + } + + return p_result; +} + +void RenderInterface_DX12::TextureMemoryManager::Alloc_As_Committed(RMLUI_ATTR_ASSERT_VARIABLE size_t base_memory, + RMLUI_ATTR_ASSERT_VARIABLE size_t total_memory, D3D12_RESOURCE_DESC& desc, TextureHandleType* p_impl, const Rml::byte* p_data) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_As_Committed"); + RMLUI_ASSERTMSG(base_memory > 0, "must be greater than zero!"); + RMLUI_ASSERTMSG(total_memory > 0, "must be greater than zero!"); + RMLUI_ASSERTMSG(base_memory != size_t(-1), "must be valid number!"); + RMLUI_ASSERTMSG(total_memory != size_t(-1), "must be valid number!"); + RMLUI_ASSERTMSG(m_p_device, "must be valid!"); + RMLUI_ASSERTMSG(p_impl, "must be valid!"); + RMLUI_ASSERTMSG(m_p_command_allocator, "must be valid!"); + RMLUI_ASSERTMSG(m_p_copy_command_list, "must be valid!"); + RMLUI_ASSERTMSG(m_p_allocator, "allocator must be valid!"); + + if (m_p_allocator) + { + D3D12MA::ALLOCATION_DESC desc_allocation = {}; + desc_allocation.HeapType = D3D12_HEAP_TYPE_DEFAULT; + + ID3D12Resource* p_resource{}; + D3D12MA::Allocation* p_allocation{}; + auto status = + m_p_allocator->CreateResource(&desc_allocation, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr, &p_allocation, IID_PPV_ARGS(&p_resource)); + + RMLUI_DX_VERIFY_MSG(status, "failed to CreateResource (D3D12MA)"); + + if (p_impl) + { + p_impl->Set_Resource(p_allocation); + } + + if (m_p_command_allocator) + { + auto status_reset = m_p_command_allocator->Reset(); + RMLUI_DX_VERIFY_MSG(status_reset, "failed to Reset (command allocator)"); + } + + if (m_p_copy_command_list) + { + auto status_reset = m_p_copy_command_list->Reset(m_p_command_allocator, nullptr); + RMLUI_DX_VERIFY_MSG(status_reset, "failed to Reset (command list)"); + } + + Upload(true, p_impl, desc, p_data, p_resource); + } +} + +void RenderInterface_DX12::TextureMemoryManager::Alloc_As_Committed(RMLUI_ATTR_ASSERT_VARIABLE size_t base_memory, + RMLUI_ATTR_ASSERT_VARIABLE size_t total_memory, D3D12_RESOURCE_DESC& desc, D3D12_RESOURCE_STATES initial_state, TextureHandleType* p_texture, + Gfx::FramebufferData* p_impl) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_As_Committed"); + RMLUI_ASSERTMSG(base_memory > 0, "must be greater than zero!"); + RMLUI_ASSERTMSG(total_memory > 0, "must be greater than zero!"); + RMLUI_ASSERTMSG(base_memory != size_t(-1), "must be valid number!"); + RMLUI_ASSERTMSG(total_memory != size_t(-1), "must be valid number!"); + RMLUI_ASSERTMSG(m_p_device, "must be valid!"); + RMLUI_ASSERTMSG(p_impl, "must be valid!"); + RMLUI_ASSERTMSG(m_p_command_allocator, "must be valid!"); + RMLUI_ASSERTMSG(m_p_copy_command_list, "must be valid!"); + RMLUI_ASSERTMSG(m_p_allocator, "allocator must be valid!"); + RMLUI_ASSERTMSG(p_texture, "must be valid!"); + + if (m_p_allocator) + { + D3D12MA::ALLOCATION_DESC desc_allocation = {}; + desc_allocation.HeapType = D3D12_HEAP_TYPE_DEFAULT; + + D3D12_CLEAR_VALUE optimized_clear_value = {}; + + if (p_impl->Is_RenderTarget()) + { + optimized_clear_value.Format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + + optimized_clear_value.Color[0] = 0.0f; + optimized_clear_value.Color[1] = 0.0f; + optimized_clear_value.Color[2] = 0.0f; + optimized_clear_value.Color[3] = 0.0f; + } + else + { + optimized_clear_value.Format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + optimized_clear_value.DepthStencil.Depth = RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_DEPTH_VALUE; + optimized_clear_value.DepthStencil.Stencil = RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_STENCIL_VALUE; + } + + ID3D12Resource* p_resource{}; + D3D12MA::Allocation* p_allocation{}; + auto status = + m_p_allocator->CreateResource(&desc_allocation, &desc, initial_state, &optimized_clear_value, &p_allocation, IID_PPV_ARGS(&p_resource)); + + RMLUI_DX_VERIFY_MSG(status, "failed to CreateResource (D3D12MA) (RenderTargetTexture)"); + + if (p_texture) + { + p_texture->Set_Resource(p_allocation); + } + + D3D12_SHADER_RESOURCE_VIEW_DESC desc_srv{}; + desc_srv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + + bool is_rt = desc.Flags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + bool is_ds = desc.Flags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + + if (is_rt) + desc_srv.Format = desc.Format; + else if (is_ds) + desc_srv.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; + + if (desc.SampleDesc.Count > 1) + { + desc_srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS; + } + else + { + desc_srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + } + + desc_srv.Texture2D.MipLevels = desc.MipLevels; + + RMLUI_ASSERTMSG((is_rt || is_ds), "this method for dsv or rtv resources"); + + auto descriptor_allocation = + m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav->allocate(static_cast(m_size_srv_cbv_uav_descriptor)); + + auto offset_pointer = SIZE_T(INT64(m_p_handle->ptr)) + INT64(descriptor_allocation.offset); + D3D12_CPU_DESCRIPTOR_HANDLE cast_offset_pointer; + cast_offset_pointer.ptr = offset_pointer; + + m_p_device->CreateShaderResourceView(p_resource, &desc_srv, cast_offset_pointer); + p_texture->Set_Allocation_DescriptorHeap(descriptor_allocation); + + if (is_rt) + p_impl->Set_DescriptorResourceView(Alloc_RenderTargetResourceView(p_resource, p_impl->Get_VirtualAllocation_Descriptor())); + else if (is_ds) + p_impl->Set_DescriptorResourceView(Alloc_DepthStencilResourceView(p_resource, p_impl->Get_VirtualAllocation_Descriptor())); + } +} + +void RenderInterface_DX12::TextureMemoryManager::Alloc_As_Placed(size_t base_memory, RMLUI_ATTR_ASSERT_VARIABLE size_t total_memory, + D3D12_RESOURCE_DESC& desc, TextureHandleType* p_impl, const Rml::byte* p_data) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_As_Placed"); + RMLUI_ASSERTMSG(base_memory > 0, "must be greater than zero!"); + RMLUI_ASSERTMSG(total_memory > 0, "must be greater than zero!"); + RMLUI_ASSERTMSG(base_memory != size_t(-1), "must be valid number!"); + RMLUI_ASSERTMSG(total_memory != size_t(-1), "must be valid number!"); + RMLUI_ASSERTMSG(m_p_device, "must be valid!"); + RMLUI_ASSERTMSG(p_impl, "must be valid!"); + RMLUI_ASSERTMSG(m_p_command_allocator, "must be valid!"); + RMLUI_ASSERTMSG(m_p_copy_command_list, "must be valid!"); + + D3D12_RESOURCE_ALLOCATION_INFO info_for_alloc{}; + + if (CanBeSmallResource(base_memory)) + { + desc.Alignment = D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT; + info_for_alloc = m_p_device->GetResourceAllocationInfo(0, 1, &desc); + + RMLUI_ASSERTMSG(info_for_alloc.Alignment == D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT, "wrong calculation! check CanBeSmallResource method!"); + +#ifdef RMLUI_DX_DEBUG + if (total_memory != info_for_alloc.SizeInBytes) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, + "[DirectX-12]: WARNING! Probably not aligned size of texture for creation w=[%zu] h=[%d] size=[%zu] align_size=[%zu] since gpu " + "driver wants to have align data " + "for allocating you should keep resources dimensions to be multiple of two!", + desc.Width, desc.Height, total_memory, info_for_alloc.SizeInBytes); + } +#endif + } + else + { + desc.Alignment = 0; + info_for_alloc = m_p_device->GetResourceAllocationInfo(0, 1, &desc); + + RMLUI_ASSERTMSG(info_for_alloc.Alignment != D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT, "wrong calculation! check CanBeSmallResource method!"); + +#ifdef RMLUI_DX_DEBUG + if (total_memory != info_for_alloc.SizeInBytes) + { + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, + "[DirectX-12]: WARNING! Probably not aligned size of texture for creation w=[%zu] h=[%d] size=[%zu] align_size=[%zu] since gpu " + "driver wants to have align data " + "for allocating you should keep resources dimensions to be multiple of two!", + desc.Width, desc.Height, total_memory, info_for_alloc.SizeInBytes); + } +#endif + } + + int heap_index{-1}; + auto* p_block = Get_AvailableBlock(info_for_alloc.SizeInBytes, &heap_index); + + RMLUI_ASSERTMSG(heap_index != -1, "something is wrong!"); + RMLUI_ASSERTMSG(p_block, "something is wrong!"); + + auto* p_heap = m_heaps_placed.at(heap_index); + RMLUI_ASSERTMSG(p_heap, "something is wrong!"); + + D3D12_RESOURCE_BARRIER bar; + + D3D12MA::VIRTUAL_ALLOCATION_DESC desc_alloc{}; + desc_alloc.Size = info_for_alloc.SizeInBytes; + desc_alloc.Alignment = info_for_alloc.Alignment; + D3D12MA::VirtualAllocation alloc_virtual; + UINT64 offset{}; + + // wh1t3lord: yeah, we can get a situation of fast allocating things that like visual test sample where we can hold left button and just + // switch across all samples fast but we could defragment our block that formally the unused space is ENOUGH for allocation but for real there's + // no free block and D3D12MA doesn't provide a good way for resolving accurately using stats or something else, so if we receive from block that + // was found in Get_AvailableBlock still can return from ->Allocate E_OUTOFMEMORY and it means we need try to find another one until we tried all + // and if all failed we have to allocate new block sadly + auto status = p_block->Allocate(&desc_alloc, &alloc_virtual, &offset); + + // found a block but need to resolve it + if (status == E_OUTOFMEMORY) + { + // let's find another block if it is available + + D3D12MA::VirtualBlock* p_valid_block_obtained{}; + heap_index = -1; + p_heap = nullptr; + for (int i = 0; i < m_blocks.size(); ++i) + { + D3D12MA::VirtualBlock* p_candidate_block = m_blocks[i]; + + RMLUI_ASSERTMSG(p_candidate_block, "probably we can't keep nullptr in valid container"); + + if (p_candidate_block != p_block) + { + if (CanAllocate(info_for_alloc.SizeInBytes, p_candidate_block)) + { + status = p_candidate_block->Allocate(&desc_alloc, &alloc_virtual, &offset); + + if (SUCCEEDED(status)) + { + p_valid_block_obtained = p_candidate_block; + p_heap = m_heaps_placed[i]; + heap_index = i; + break; + } + } + } + } + + if (!p_valid_block_obtained) + { + const auto& new_heap_and_block = Create_HeapPlaced(m_size_for_placed_heap); + RMLUI_ASSERTMSG(new_heap_and_block.second, "must be valid!"); + RMLUI_ASSERTMSG(new_heap_and_block.first, "must be valid!"); + + if (new_heap_and_block.second) + { + status = new_heap_and_block.second->Allocate(&desc_alloc, &alloc_virtual, &offset); + RMLUI_DX_VERIFY_MSG(SUCCEEDED(status), "can't resolve allocation for resource, report to developers!"); + + p_heap = new_heap_and_block.first; + + for (int i = 0; i < m_heaps_placed.size(); ++i) + { + if (m_heaps_placed[i] == p_heap) + { + heap_index = i; + break; + } + } + } + } + } + + RMLUI_DX_VERIFY_MSG(status, "can't allocate virtual alloc!"); + + ID3D12Resource* p_resource{}; + status = m_p_device->CreatePlacedResource(p_heap, offset, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&p_resource)); + + RMLUI_DX_VERIFY_MSG(status, "failed to CreatePlacedResource!"); + RMLUI_ASSERTMSG(p_resource, "must be valid pointer of ID3D12Resource*!"); + + if (p_impl) + { + GraphicsAllocationInfo info_graphics; + info_graphics.size = info_for_alloc.SizeInBytes; + info_graphics.buffer_index = heap_index; + info_graphics.offset = offset; + info_graphics.alloc_info = alloc_virtual; + + p_impl->Set_Info(info_graphics); + p_impl->Set_Resource(p_resource); + } + + bar.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE; + bar.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_ALIASING; + bar.Aliasing.pResourceBefore = nullptr; + bar.Aliasing.pResourceAfter = p_resource; + + if (m_p_command_allocator) + { + auto status_reset = m_p_command_allocator->Reset(); + RMLUI_DX_VERIFY_MSG(status_reset, "failed to Reset (command allocator)"); + } + + if (m_p_copy_command_list) + { + auto status_reset = m_p_copy_command_list->Reset(m_p_command_allocator, nullptr); + RMLUI_DX_VERIFY_MSG(status_reset, "failed to Reset (command list)"); + + m_p_copy_command_list->ResourceBarrier(1, &bar); + } + + Upload(false, p_impl, desc, p_data, p_resource); +} + +void RenderInterface_DX12::TextureMemoryManager::Upload(bool is_committed, TextureHandleType* p_texture_handle, const D3D12_RESOURCE_DESC& desc, + const Rml::byte* p_data, ID3D12Resource* p_resource) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Upload"); + RMLUI_ASSERTMSG(m_p_device, "must be valid!"); + // RMLUI_ASSERTMSG(p_data, "must be valid!"); + RMLUI_ASSERTMSG(p_resource, "must be valid!"); + RMLUI_ASSERTMSG(m_p_descriptor_heap_srv, "must be valid!"); + RMLUI_ASSERTMSG(m_p_allocator, "must be valid!"); + RMLUI_ASSERTMSG(m_p_copy_command_list, "must be valid!"); + RMLUI_ASSERTMSG(m_p_handle, "must be valid!"); + RMLUI_ASSERTMSG(m_p_copy_queue, "must be valid!"); + RMLUI_ASSERTMSG(m_p_renderer, "must be valid!"); + RMLUI_ASSERTMSG(m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav, "must be valid!"); + RMLUI_ASSERTMSG(p_texture_handle, "must be valid!"); + RMLUI_ASSERTMSG(m_p_fence, "must be valid!"); + RMLUI_ASSERTMSG(m_p_fence_event, "must be valid!"); + + auto upload_size = GetRequiredIntermediateSize(p_resource, 0, 1); + + D3D12MA::Allocation* p_allocation{}; + + HRESULT status = S_OK; + +#if RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED == 1 + RMLUI_ASSERTMSG(m_p_upload_buffer, "must be valid and initialized"); + + p_allocation = m_p_upload_buffer; + + bool was_allocated_temp = false; + + RMLUI_ASSERTMSG(m_p_upload_buffer->GetResource(), "must be initialized"); + if (p_allocation->GetResource()) + { + if (p_allocation->GetResource()->GetDesc().Width < upload_size) + { + Rml::Log::Message(Rml::Log::Type::LT_WARNING, + "! [DirectX 12]: you are trying to upload huge texture = %zu bytes (%zu MB), so if you can't optimize its size for loading then " + "ignore " + "this " + "message, otherwise we " + "expect you to upload using staging buffer from UploadResourceManager instance", + upload_size, upload_size / (1024 * 1024)); + + p_allocation = nullptr; + + ID3D12Resource* p_upload_buffer{}; + + D3D12_RESOURCE_DESC buffer_desc; + + buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + buffer_desc.Alignment = 0; + buffer_desc.Width = upload_size; + buffer_desc.Height = 1; + buffer_desc.DepthOrArraySize = 1; + buffer_desc.MipLevels = 1; + buffer_desc.Format = DXGI_FORMAT_UNKNOWN; + buffer_desc.SampleDesc.Count = 1; + buffer_desc.SampleDesc.Quality = 0; + buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE; + + D3D12MA::ALLOCATION_DESC desc_alloc{}; + desc_alloc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + + status = m_p_allocator->CreateResource(&desc_alloc, &buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &p_allocation, + IID_PPV_ARGS(&p_upload_buffer)); + + RMLUI_DX_VERIFY_MSG(status, "failed to CreateResource (upload buffer for texture)"); + + was_allocated_temp = true; + } + } + +#else + ID3D12Resource* p_upload_buffer{}; + + D3D12_RESOURCE_DESC buffer_desc; + + buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + buffer_desc.Alignment = 0; + buffer_desc.Width = upload_size; + buffer_desc.Height = 1; + buffer_desc.DepthOrArraySize = 1; + buffer_desc.MipLevels = 1; + buffer_desc.Format = DXGI_FORMAT_UNKNOWN; + buffer_desc.SampleDesc.Count = 1; + buffer_desc.SampleDesc.Quality = 0; + buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE; + + D3D12MA::ALLOCATION_DESC desc_alloc{}; + desc_alloc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + + status = m_p_allocator->CreateResource(&desc_alloc, &buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &p_allocation, + IID_PPV_ARGS(&p_upload_buffer)); + + RMLUI_DX_VERIFY_MSG(status, "failed to CreateResource (upload buffer for texture)"); +#endif + + D3D12_SUBRESOURCE_DATA desc_data{}; + desc_data.pData = p_data; + desc_data.RowPitch = desc.Width * BytesPerPixel(desc.Format); + desc_data.SlicePitch = desc_data.RowPitch * desc.Height; + + auto allocated_size = UpdateSubresources<1>(m_p_copy_command_list, p_resource, p_allocation->GetResource(), 0, 0, 1, &desc_data); + +#ifdef RMLUI_DX_DEBUG + constexpr const char* p_committed = "committed"; + constexpr const char* p_placed = "placed"; + + const char* p_current_resource_type_name{}; + + if (is_committed) + p_current_resource_type_name = p_committed; + else + p_current_resource_type_name = p_placed; + + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX 12] allocated %s resource to GPU with size: [%zu]", p_current_resource_type_name, + allocated_size); +#else + (void)is_committed; + (void)allocated_size; +#endif + + D3D12_SHADER_RESOURCE_VIEW_DESC desc_srv{}; + desc_srv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + desc_srv.Format = desc.Format; + desc_srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + desc_srv.Texture2D.MipLevels = desc.MipLevels; + + auto descriptor_allocation = + m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav->allocate(static_cast(m_size_srv_cbv_uav_descriptor)); + + auto offset_pointer = SIZE_T(INT64(m_p_handle->ptr) + INT64(descriptor_allocation.offset)); + D3D12_CPU_DESCRIPTOR_HANDLE cast_offset_pointer; + cast_offset_pointer.ptr = offset_pointer; + + m_p_device->CreateShaderResourceView(p_resource, &desc_srv, cast_offset_pointer); + p_texture_handle->Set_Allocation_DescriptorHeap(descriptor_allocation); + + status = m_p_copy_command_list->Close(); + + RMLUI_DX_VERIFY_MSG(status, "failed to Close (command list for copy)"); + + ID3D12CommandList* p_lists[] = {m_p_copy_command_list}; + + m_p_copy_queue->ExecuteCommandLists(1, p_lists); + + // wait queue for completion + if (m_p_device) + { + ++m_fence_value; + + RMLUI_DX_VERIFY_MSG(m_p_copy_queue->Signal(m_p_fence, m_fence_value), "failed to signal copy queue's fence"); + + if (m_p_fence->GetCompletedValue() < m_fence_value) + { + RMLUI_DX_VERIFY_MSG(m_p_fence->SetEventOnCompletion(m_fence_value, m_p_fence_event), "failed to SetEventOnCompletion"); + + WaitForSingleObjectEx(m_p_fence_event, INFINITE, FALSE); + } + } + +#if RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED == 1 + if (was_allocated_temp) + { + if (p_allocation) + { + if (p_allocation->GetResource()) + { + p_allocation->GetResource()->Release(); + } + + RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = p_allocation->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak!"); + } + } +#else + if (p_allocation) + { + if (p_allocation->GetResource()) + { + p_allocation->GetResource()->Release(); + } + + auto ref_count = p_allocation->Release(); + RMLUI_ASSERTMSG(ref_count == 0, "leak!"); + } +#endif +} + +size_t RenderInterface_DX12::TextureMemoryManager::BytesPerPixel(DXGI_FORMAT format) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::BytesPerPixel"); + return BitsPerPixel(format) / static_cast(8); +} + +size_t RenderInterface_DX12::TextureMemoryManager::BitsPerPixel(DXGI_FORMAT format) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::BitsPerPixel"); + switch (format) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: return 128; + + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: return 96; + + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + case DXGI_FORMAT_Y416: + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: return 64; + + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + case DXGI_FORMAT_AYUV: + case DXGI_FORMAT_Y410: + case DXGI_FORMAT_YUY2: return 32; + + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: return 24; + + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + case DXGI_FORMAT_A8P8: + case DXGI_FORMAT_B4G4R4A4_UNORM: return 16; + + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_420_OPAQUE: + case DXGI_FORMAT_NV11: return 12; + + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + case DXGI_FORMAT_AI44: + case DXGI_FORMAT_IA44: + case DXGI_FORMAT_P8: return 8; + + case DXGI_FORMAT_R1_UNORM: return 1; + + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: return 4; + + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: return 8; + + default: + { + RMLUI_ASSERT(!"failed to determine texture type that wasn't registered report to developers (https://github.com/mikke89/RmlUi/issues)"); + return 0; + } + } +} + +Rml::Pair RenderInterface_DX12::TextureMemoryManager::Create_HeapPlaced(size_t size_for_creation) +{ + (void)(size_for_creation); + + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Create_HeapPlaced"); + RMLUI_ASSERTMSG(m_p_device, "must be valid!"); + + D3D12MA::VIRTUAL_BLOCK_DESC desc_block{}; + + desc_block.Size = m_size_for_placed_heap; + D3D12MA::VirtualBlock* p_block{}; + auto status = D3D12MA::CreateVirtualBlock(&desc_block, &p_block); + + RMLUI_DX_VERIFY_MSG(status, "failed to D3D12MA::CreateVirtualBlock"); + + m_blocks.push_back(p_block); + + D3D12_HEAP_DESC desc_heap; + desc_heap.Flags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES; + desc_heap.SizeInBytes = m_size_for_placed_heap; + desc_heap.Alignment = 0; + desc_heap.Properties.Type = D3D12_HEAP_TYPE::D3D12_HEAP_TYPE_DEFAULT; + desc_heap.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + desc_heap.Properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + desc_heap.Properties.CreationNodeMask = 1; + desc_heap.Properties.VisibleNodeMask = 1; + + ID3D12Heap* p_heap{}; + status = m_p_device->CreateHeap(&desc_heap, IID_PPV_ARGS(&p_heap)); + + RMLUI_DX_VERIFY_MSG(status, "failed to CreateHeap!"); + + m_heaps_placed.push_back(p_heap); + + return {p_heap, p_block}; +} + +D3D12_CPU_DESCRIPTOR_HANDLE RenderInterface_DX12::TextureMemoryManager::Alloc_DepthStencilResourceView(ID3D12Resource* p_resource, + D3D12MA::VirtualAllocation* p_alloc) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_DepthStecilResourceView"); + RMLUI_ASSERTMSG(p_resource, "must be allocated!"); + RMLUI_ASSERTMSG(m_p_device, "must be allocated!"); + RMLUI_ASSERTMSG(m_p_virtual_block_for_depth_stencil_heap_allocations, "must be initialized"); + RMLUI_ASSERTMSG(p_alloc, "must be valid!"); + RMLUI_ASSERTMSG(m_p_descriptor_heap_dsv, "must be valid!"); + + D3D12_CPU_DESCRIPTOR_HANDLE calculated_offset{}; + + if (p_resource) + { + if (m_p_virtual_block_for_depth_stencil_heap_allocations) + { + if (m_p_device) + { + if (m_p_descriptor_heap_dsv) + { + D3D12MA::VIRTUAL_ALLOCATION_DESC desc_alloc = {}; + desc_alloc.Size = m_size_dsv_descriptor; + UINT64 offset{}; + + auto status = m_p_virtual_block_for_depth_stencil_heap_allocations->Allocate(&desc_alloc, p_alloc, &offset); + + RMLUI_DX_VERIFY_MSG(status, + "failed to allocate descriptor rtv, it means you need to resize and set higher size than previous, overflow (see " + "RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_RTV constant)"); + + D3D12_DEPTH_STENCIL_VIEW_DESC desc_rtv = {}; + desc_rtv.Format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT; + desc_rtv.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; + + calculated_offset.ptr = (m_p_descriptor_heap_dsv->GetCPUDescriptorHandleForHeapStart().ptr + offset); + + m_p_device->CreateDepthStencilView(p_resource, &desc_rtv, calculated_offset); + } + } + } + } + + return calculated_offset; +} + +D3D12_CPU_DESCRIPTOR_HANDLE RenderInterface_DX12::TextureMemoryManager::Alloc_RenderTargetResourceView(ID3D12Resource* p_resource, + D3D12MA::VirtualAllocation* p_alloc) +{ + RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_RenderTargetResourceView"); + RMLUI_ASSERTMSG(p_resource, "must be allocated!"); + RMLUI_ASSERTMSG(m_p_device, "must be allocated!"); + RMLUI_ASSERTMSG(m_p_virtual_block_for_render_target_heap_allocations, "must be initialized"); + RMLUI_ASSERTMSG(p_alloc, "must be valid!"); + RMLUI_ASSERTMSG(m_p_descriptor_heap_rtv, "must be valid!"); + + D3D12_CPU_DESCRIPTOR_HANDLE calculated_offset{}; + + if (p_resource) + { + if (m_p_virtual_block_for_render_target_heap_allocations) + { + if (m_p_device) + { + if (m_p_descriptor_heap_rtv) + { + D3D12MA::VIRTUAL_ALLOCATION_DESC desc_alloc = {}; + desc_alloc.Size = m_size_rtv_descriptor; + UINT64 offset{}; + + auto status = m_p_virtual_block_for_render_target_heap_allocations->Allocate(&desc_alloc, p_alloc, &offset); + + RMLUI_DX_VERIFY_MSG(status, + "failed to allocate descriptor rtv, it means you need to resize and set higher size than previous, overflow (see " + "RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_RTV constant)"); + + D3D12_RENDER_TARGET_VIEW_DESC desc_rtv = {}; + desc_rtv.Format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT; + desc_rtv.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; + + calculated_offset.ptr = (m_p_descriptor_heap_rtv->GetCPUDescriptorHandleForHeapStart().ptr + offset); + + m_p_device->CreateRenderTargetView(p_resource, &desc_rtv, calculated_offset); + } + } + } + } + + return calculated_offset; +} + +#if defined _MSC_VER + #pragma warning(push, 0) + #pragma warning(push, 4189) +#elif defined __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wall" + #pragma clang diagnostic ignored "-Wextra" + #pragma clang diagnostic ignored "-Wnullability-extension" + #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" + #pragma clang diagnostic ignored "-Wnullability-completeness" +#elif defined __GNUC__ + #pragma GCC system_header +#endif + +#include "RmlUi_DirectX/D3D12MemAlloc.cpp" +#include "RmlUi_DirectX/offsetAllocator.cpp" + +#if defined _MSC_VER + #pragma warning(pop) +#elif defined __clang__ + #pragma clang diagnostic pop +#endif diff --git a/vendor/rmlui_backend/RmlUi_Renderer_DX12.h b/vendor/rmlui_backend/RmlUi_Renderer_DX12.h new file mode 100644 index 0000000..00ea8c6 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_DX12.h @@ -0,0 +1,696 @@ +#pragma once + +#include + +#ifndef RMLUI_PLATFORM_WIN32 + #error "DirectX 12 renderer only supported on Windows" +#endif + +// clang-format off +#include "RmlUi_Include_Windows.h" +#include "RmlUi_Include_DirectX_12.h" +// clang-format on + +enum class ProgramId; + +class RenderLayerStack; +namespace Gfx { +struct ProgramData; +struct FramebufferData; +} // namespace Gfx + +namespace Backend { +struct RmlRenderInitInfo; +struct RmlRendererSettings; +} // namespace Backend + +class RenderInterface_DX12 : public Rml::RenderInterface { +private: + static constexpr uint32_t InvalidConstantBuffer_RootParameterIndex = std::numeric_limits::max(); + static constexpr uint8_t MaxConstantBuffersPerShader = 3; + static constexpr size_t NumPrograms = 23; + +public: + struct GraphicsAllocationInfo { + int buffer_index = -1; + size_t offset = {}; + size_t size = {}; + D3D12MA::VirtualAllocation alloc_info = {}; + }; + + class TextureHandleType : Rml::NonCopyMoveable { + public: + TextureHandleType() = default; + ~TextureHandleType() + { +#ifdef RMLUI_DX_DEBUG + RMLUI_ASSERTMSG(m_is_destroyed, "The texture was not destroyed"); +#endif + } + + const GraphicsAllocationInfo& Get_Info() const noexcept { return m_info; } + void Set_Info(const GraphicsAllocationInfo& info) { m_info = info; } + void Set_Resource(void* p_resource) { m_p_resource = p_resource; } + void* Get_Resource() const noexcept { return m_p_resource; } + +#ifdef RMLUI_DX_DEBUG + /// @brief returns debug resource name. Implementation of this method exists only when DEBUG is enabled for this project + /// @return debug resource name when the source string is passed in LoadTexture method + const Rml::String& Get_ResourceName() const { return m_debug_resource_name; } + + /// @brief sets m_debug_resource_name field with specified argument as resource_name + /// @param resource_name this string is getting from LoadTexture method from source argument + void Set_ResourceName(const Rml::String& resource_name) { m_debug_resource_name = resource_name; } +#endif + + // this method calls texture manager! don't call it manually because you must ensure that you freed virtual block if it had + void Destroy() + { +#ifdef RMLUI_DX_DEBUG + RMLUI_ASSERTMSG(!m_is_destroyed, "The texture has already been destroyed"); + m_is_destroyed = true; +#endif + + if (m_p_resource) + { + if (m_info.buffer_index != -1) + { + static_cast(m_p_resource)->Release(); + } + else + { + D3D12MA::Allocation* p_committed_resource = static_cast(m_p_resource); + if (auto* underlying_resource = p_committed_resource->GetResource()) + underlying_resource->Release(); + + p_committed_resource->Release(); + } + + m_info = GraphicsAllocationInfo(); + m_p_resource = nullptr; + } + } + + const OffsetAllocator::Allocation& Get_Allocation_DescriptorHeap() const noexcept { return m_allocation_descriptor_heap; } + void Set_Allocation_DescriptorHeap(const OffsetAllocator::Allocation& allocation) noexcept { m_allocation_descriptor_heap = allocation; } + + private: + GraphicsAllocationInfo m_info; + OffsetAllocator::Allocation m_allocation_descriptor_heap; + void* m_p_resource = nullptr; // placed or committed (CreateResource from D3D12MA) + +#ifdef RMLUI_DX_DEBUG + Rml::String m_debug_resource_name; + bool m_is_destroyed = false; +#endif + }; + + struct ConstantBufferType { + GraphicsAllocationInfo m_alloc_info; + void* m_p_gpu_start_memory_for_binding_data = nullptr; + }; + + class GeometryHandleType : Rml::NonCopyMoveable { + public: + GeometryHandleType() = default; + + void Set_InfoVertex(const GraphicsAllocationInfo& info) { m_info_vertex = info; } + const GraphicsAllocationInfo& Get_InfoVertex() const noexcept { return m_info_vertex; } + + void Set_InfoIndex(const GraphicsAllocationInfo& info) { m_info_index = info; } + const GraphicsAllocationInfo& Get_InfoIndex() const noexcept { return m_info_index; } + + void Set_NumVertices(int num) { m_num_vertices = num; } + int Get_NumVertices() const { return m_num_vertices; } + + void Set_NumIndices(int num) { m_num_indices = num; } + int Get_NumIndices() const { return m_num_indices; } + + void Set_SizeOfOneVertex(size_t size) { m_one_element_vertex_size = size; } + size_t Get_SizeOfOneVertex() const { return m_one_element_vertex_size; } + + void Set_SizeOfOneIndex(size_t size) { m_one_element_index_size = size; } + size_t Get_SizeOfOneIndex() const { return m_one_element_index_size; } + + const OffsetAllocator::Allocation& Get_Allocation_DescriptorHeap() const noexcept { return m_allocation_descriptor_heap; } + void Set_Allocation_DescriptorHeap(const OffsetAllocator::Allocation& allocation) noexcept { m_allocation_descriptor_heap = allocation; } + + int Get_HistoryBackBufferFrameIndex(void) const { return m_history_backbuffer_frame_index; } + void Set_HistoryBackBufferFrameIndex(int frame_index) { m_history_backbuffer_frame_index = frame_index; } + + void Set_ConstantBuffer(ConstantBufferType* p_constant_buffer) + { + RMLUI_ASSERTMSG(p_constant_buffer, "must be valid constant buffer!"); + m_p_constant_buffer_override = p_constant_buffer; + } + + void Reset_ConstantBuffer() + { + m_p_constant_buffer_override = nullptr; + for (uint8_t i = 0; i < MaxConstantBuffersPerShader; ++i) + { + m_constant_buffer_root_parameter_indices[i] = InvalidConstantBuffer_RootParameterIndex; + } + } + + ConstantBufferType* Get_ConstantBuffer() const { return m_p_constant_buffer_override; } + + // use this only for shared CBV that will be used among all shaders otherwise we need to provide a new array that will hold a GPU addresses to + // different cbv and their indices but for now it is only for ONE CBV that can be used among vertex and pixel shaders + void Add_ConstantBufferRootParameterIndices(uint32_t index) + { +#ifdef RMLUI_DX_DEBUG + bool found_empty_slot = false; +#endif + + for (unsigned char i = 0; i < MaxConstantBuffersPerShader; ++i) + { + if (m_constant_buffer_root_parameter_indices[i] == InvalidConstantBuffer_RootParameterIndex) + { +#ifdef RMLUI_DX_DEBUG + found_empty_slot = true; +#endif + + m_constant_buffer_root_parameter_indices[i] = index; + + break; + } + } + +#ifdef RMLUI_DX_DEBUG + RMLUI_ASSERTMSG(found_empty_slot, + "failed to obtain empty slot in such case you have to set another limits for engine using " + "MaxConstantBuffersPerShader field"); +#endif + } + + const uint32_t* Get_ConstantBufferRootParameterIndices(uint8_t& amount_of_indices) const + { + amount_of_indices = 0; + + // might be slow for big arrays otherwise provide a cache field for this class just like field that will show current size of + // m_constant_buffer_root_parameter_indices member in terms of current entries that are filled the array for now just to reduce the + // memory footprint for this class and I don't use this cache field as size so we determine in runtime + for (uint8_t i = 0; i < MaxConstantBuffersPerShader; ++i) + { + if (m_constant_buffer_root_parameter_indices[i] != InvalidConstantBuffer_RootParameterIndex) + { + amount_of_indices = i + 1; + } + } + + return m_constant_buffer_root_parameter_indices; + } + + private: + int m_num_vertices = {}; + int m_num_indices = {}; + int m_history_backbuffer_frame_index = -1; + ConstantBufferType* m_p_constant_buffer_override = {}; + size_t m_one_element_vertex_size = {}; + size_t m_one_element_index_size = {}; + uint32_t m_constant_buffer_root_parameter_indices[MaxConstantBuffersPerShader] = { + InvalidConstantBuffer_RootParameterIndex, + InvalidConstantBuffer_RootParameterIndex, + InvalidConstantBuffer_RootParameterIndex, + }; + GraphicsAllocationInfo m_info_vertex; + GraphicsAllocationInfo m_info_index; + OffsetAllocator::Allocation m_allocation_descriptor_heap; + }; + + class BufferMemoryManager : Rml::NonCopyMoveable { + public: + BufferMemoryManager(); + ~BufferMemoryManager(); + + void Initialize(ID3D12Device* m_p_device, D3D12MA::Allocator* p_allocator, + OffsetAllocator::Allocator* p_offset_allocator_for_descriptor_heap_srv_cbv_uav, D3D12_CPU_DESCRIPTOR_HANDLE* p_handle, + uint32_t size_descriptor_element, size_t size_for_allocation = RMLUI_RENDER_BACKEND_FIELD_VIDEOMEMORY_FOR_BUFFER_ALLOCATION, + size_t size_alignment = RMLUI_RENDER_BACKEND_FIELD_ALIGNMENT_FOR_BUFFER); + void Shutdown(); + + void Alloc_Vertex(const void* p_data, int num_vertices, size_t size_of_one_element_in_p_data, GeometryHandleType* p_handle); + void Alloc_Index(const void* p_data, int num_vertices, size_t size_of_one_element_in_p_data, GeometryHandleType* p_handle); + + GraphicsAllocationInfo Alloc_ConstantBuffer(ConstantBufferType* p_resource, size_t size); + + void Free_ConstantBuffer(ConstantBufferType* p_constantbuffer); + void Free_Geometry(GeometryHandleType* p_geometryhandle); + + void* Get_WritableMemoryFromBufferByOffset(const GraphicsAllocationInfo& info); + + D3D12MA::Allocation* Get_BufferByIndex(int buffer_index); + + bool Is_Initialized() const; + + private: + void Alloc_Buffer(size_t size +#ifdef RMLUI_DX_DEBUG + , + const std::wstring& debug_name +#endif + ); + + // Searches for block that has enough memory for requested allocation size, otherwise returns nullptr that means no block! + D3D12MA::VirtualBlock* Get_AvailableBlock(size_t size_for_allocation, int* p_result_buffer_index); + + D3D12MA::VirtualBlock* Get_NotOutOfMemoryAndAvailableBlock(size_t size_for_allocation, int* p_result_buffer_index); + + int Alloc(GraphicsAllocationInfo& info, size_t size, size_t alignment = 0); + + void TryToFreeAvailableBlock(); + + uint32_t m_descriptor_increment_size_srv_cbv_uav; + size_t m_size_for_allocation_in_bytes; + size_t m_size_alignment_in_bytes; + ID3D12Device* m_p_device; + D3D12MA::Allocator* m_p_allocator; + OffsetAllocator::Allocator* m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav; + D3D12_CPU_DESCRIPTOR_HANDLE* m_p_start_pointer_of_descriptor_heap_srv_cbv_uav; + /// @brief this is for sub allocating purposes using 'Virtual Allocation' from D3D12MA + Rml::Vector m_virtual_buffers; + /// @brief this is physical representation of VRAM and uses from CPU side for binding data + Rml::Vector> m_buffers; + }; + + /** + * The key feature of this manager is texture management and if texture size is less or equal to 1.0 MB it will + * allocate heap for placing resources and if resource is less (or equal) to 1 MB then it will be written to that + * heap Otherwise will be used heap per texture (committed resource) + */ + class TextureMemoryManager : Rml::NonCopyMoveable { + public: + TextureMemoryManager(); + ~TextureMemoryManager(); + + void Initialize(D3D12MA::Allocator* p_allocator, OffsetAllocator::Allocator* p_offset_allocator_for_descriptor_heap_srv_cbv_uav, + ID3D12Device* p_device, ID3D12GraphicsCommandList* p_copy_command_list, ID3D12GraphicsCommandList* p_backend_command_list, + ID3D12CommandAllocator* p_copy_allocator_command, ID3D12DescriptorHeap* p_descriptor_heap_srv, + ID3D12DescriptorHeap* p_descriptor_heap_rtv, ID3D12DescriptorHeap* p_descriptor_heap_dsv, ID3D12CommandQueue* p_copy_queue, + D3D12_CPU_DESCRIPTOR_HANDLE* p_handle, RenderInterface_DX12* p_renderer, + size_t size_for_placed_heap = RMLUI_RENDER_BACKEND_FIELD_VIDEOMEMORY_FOR_TEXTURE_ALLOCATION); + void Shutdown(); + + ID3D12Resource* Alloc_Texture(D3D12_RESOURCE_DESC& desc, TextureHandleType* p_impl, const Rml::byte* p_data +#ifdef RMLUI_DX_DEBUG + , + const Rml::String& debug_name +#endif + ); + + // if you want to create texture for rendering to it aka render target texture + ID3D12Resource* Alloc_Texture(D3D12_RESOURCE_DESC& desc, Gfx::FramebufferData* p_impl, D3D12_RESOURCE_FLAGS flags, + D3D12_RESOURCE_STATES initial_state +#ifdef RMLUI_DX_DEBUG + , + const Rml::String& debug_name +#endif + ); + + void Free_Texture(TextureHandleType* type); + void Free_Texture(Gfx::FramebufferData* p_texture); + void Free_Texture(TextureHandleType* p_allocated_texture_with_class, bool is_rt, D3D12MA::VirtualAllocation& allocation); + + bool Is_Initialized() const; + + private: + bool CanAllocate(size_t total_memory_for_allocation, D3D12MA::VirtualBlock* p_block); + bool CanBePlacedResource(size_t total_memory_for_allocation); + // don't use it for MSAA texture because we don't implement a such feature! + bool CanBeSmallResource(size_t base_memory); + + // use index for obtaining heap from m_heaps_placed + D3D12MA::VirtualBlock* Get_AvailableBlock(size_t total_memory_for_allocation, int* result_index); + + void Alloc_As_Committed(size_t base_memory, size_t total_memory, D3D12_RESOURCE_DESC& desc, TextureHandleType* p_impl, + const Rml::byte* p_data); + // we don't upload to GPU because it is render target and needed to be written + void Alloc_As_Committed(size_t base_memory, size_t total_memory, D3D12_RESOURCE_DESC& desc, D3D12_RESOURCE_STATES initial_state, + TextureHandleType* p_texture, Gfx::FramebufferData* p_impl); + void Alloc_As_Placed(size_t base_memory, size_t total_memory, D3D12_RESOURCE_DESC& desc, TextureHandleType* p_impl, const Rml::byte* p_data); + + void Upload(bool is_committed, TextureHandleType* p_texture_handle, const D3D12_RESOURCE_DESC& desc, const Rml::byte* p_data, + ID3D12Resource* p_impl); + + size_t BytesPerPixel(DXGI_FORMAT format); + size_t BitsPerPixel(DXGI_FORMAT format); + + Rml::Pair Create_HeapPlaced(size_t size_for_creation); + + D3D12_CPU_DESCRIPTOR_HANDLE Alloc_RenderTargetResourceView(ID3D12Resource* p_resource, D3D12MA::VirtualAllocation* p_allocation); + D3D12_CPU_DESCRIPTOR_HANDLE Alloc_DepthStencilResourceView(ID3D12Resource* p_resource, D3D12MA::VirtualAllocation* p_allocation); + + size_t m_size_for_placed_heap; + /// @brief limit size that we define as acceptable. On 4 Mb it is enough for placing 1 Mb but higher it is bad. 1 Mb occupies 25% from 4 Mb + /// and that means that m_size_limit_for_being_placed will be determined as 25% * m_size_for_placed_heap; + size_t m_size_limit_for_being_placed; + size_t m_size_srv_cbv_uav_descriptor; + size_t m_size_rtv_descriptor; + size_t m_size_dsv_descriptor; + size_t m_fence_value; + D3D12MA::Allocator* m_p_allocator; + OffsetAllocator::Allocator* m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav; + ID3D12Device* m_p_device; + ID3D12GraphicsCommandList* m_p_copy_command_list; + /// @brief command list from drawing + ID3D12GraphicsCommandList* m_p_backend_command_list; + ID3D12CommandAllocator* m_p_command_allocator; + ID3D12DescriptorHeap* m_p_descriptor_heap_srv; + ID3D12CommandQueue* m_p_copy_queue; + ID3D12Fence* m_p_fence; + HANDLE m_p_fence_event; + D3D12_CPU_DESCRIPTOR_HANDLE* m_p_handle; + RenderInterface_DX12* m_p_renderer; + D3D12MA::VirtualBlock* m_p_virtual_block_for_render_target_heap_allocations; + D3D12MA::VirtualBlock* m_p_virtual_block_for_depth_stencil_heap_allocations; + ID3D12DescriptorHeap* m_p_descriptor_heap_rtv; + ID3D12DescriptorHeap* m_p_descriptor_heap_dsv; +#if RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED == 1 + D3D12MA::Allocation* m_p_upload_buffer; +#endif + Rml::Vector m_blocks; + Rml::Vector m_heaps_placed; + }; + + /* + Manages render targets, including the layer stack and postprocessing framebuffers. + + Layers can be pushed and popped, creating new framebuffers as needed. Typically, geometry is rendered to the top + layer. The layer framebuffers may have MSAA enabled. + + Postprocessing framebuffers are separate from the layers, and are commonly used to apply texture-wide effects + such as filters. They are used both as input and output during rendering, and do not use MSAA. + */ + class RenderLayerStack : Rml::NonCopyMoveable { + public: + RenderLayerStack(); + ~RenderLayerStack(); + + void Initialize(RenderInterface_DX12* p_owner); + void Shutdown(); + // Push a new layer. All references to previously retrieved layers are invalidated. + Rml::LayerHandle PushLayer(); + + // Pop the top layer. All references to previously retrieved layers are invalidated. + void PopLayer(); + + const Gfx::FramebufferData& GetLayer(Rml::LayerHandle layer) const; + const Gfx::FramebufferData& GetTopLayer() const; + const Gfx::FramebufferData& Get_SharedDepthStencil_Layers(); + // const Gfx::FramebufferData& Get_SharedDepthStencil_Postprocess(); + Rml::LayerHandle GetTopLayerHandle() const; + + const Gfx::FramebufferData& GetPostprocessPrimary() { return EnsureFramebufferPostprocess(0); } + const Gfx::FramebufferData& GetPostprocessSecondary() { return EnsureFramebufferPostprocess(1); } + const Gfx::FramebufferData& GetPostprocessTertiary() { return EnsureFramebufferPostprocess(2); } + const Gfx::FramebufferData& GetBlendMask() { return EnsureFramebufferPostprocess(3); } + + void SwapPostprocessPrimarySecondary(); + + void BeginFrame(int new_width, int new_height); + void EndFrame(); + + private: + void DestroyFramebuffers(); + const Gfx::FramebufferData& EnsureFramebufferPostprocess(int index); + + void CreateFramebuffer(Gfx::FramebufferData* p_result, int width, int height, int sample_count, bool is_depth_stencil); + void DestroyFramebuffer(Gfx::FramebufferData* p_data); + + unsigned char m_msaa_sample_count; + int m_width, m_height; + // The number of active layers is manually tracked since we re-use the framebuffers stored in the fb_layers stack. + int m_layers_size; + TextureMemoryManager* m_p_manager_texture; + BufferMemoryManager* m_p_manager_buffer; + ID3D12Device* m_p_device; + Rml::UniquePtr m_p_depth_stencil_for_layers; + Rml::Vector m_fb_layers; + Rml::Vector m_fb_postprocess; + }; + +public: + RenderInterface_DX12(void* p_window_handle, const Backend::RmlRendererSettings& settings); + ~RenderInterface_DX12(); + + // Returns true if the renderer was successfully constructed. + explicit operator bool() const; + + // The viewport should be updated whenever the window size changes. + void SetViewport(int viewport_width, int viewport_height); + + // Sets up OpenGL states for taking rendering commands from RmlUi. + void BeginFrame(); + // Draws the result to the backbuffer and restores OpenGL state. + void EndFrame(); + + // Optional, can be used to clear the active framebuffer. + void Clear(); + + // -- Inherited from Rml::RenderInterface -- + + Rml::CompiledGeometryHandle CompileGeometry(Rml::Span vertices, Rml::Span indices) override; + void RenderGeometry(Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation, Rml::TextureHandle texture) override; + void ReleaseGeometry(Rml::CompiledGeometryHandle geometry) override; + + void EnableScissorRegion(bool enable) override; + void SetScissorRegion(Rml::Rectanglei region) override; + + void EnableClipMask(bool enable) override; + void RenderToClipMask(Rml::ClipMaskOperation mask_operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation) override; + + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override; + Rml::TextureHandle GenerateTexture(Rml::Span source_data, Rml::Vector2i source_dimensions) override; + void ReleaseTexture(Rml::TextureHandle texture_handle) override; + + void SetTransform(const Rml::Matrix4f* transform) override; + + Rml::LayerHandle PushLayer() override; + void CompositeLayers(Rml::LayerHandle source, Rml::LayerHandle destination, Rml::BlendMode blend_mode, + Rml::Span filters) override; + void PopLayer() override; + + Rml::TextureHandle SaveLayerAsTexture() override; + + Rml::CompiledFilterHandle SaveLayerAsMaskImage() override; + + Rml::CompiledFilterHandle CompileFilter(const Rml::String& name, const Rml::Dictionary& parameters) override; + void ReleaseFilter(Rml::CompiledFilterHandle filter) override; + + Rml::CompiledShaderHandle CompileShader(const Rml::String& name, const Rml::Dictionary& parameters) override; + void RenderShader(Rml::CompiledShaderHandle shader_handle, Rml::CompiledGeometryHandle geometry_handle, Rml::Vector2f translation, + Rml::TextureHandle texture) override; + void ReleaseShader(Rml::CompiledShaderHandle effect_handle) override; + + // Can be passed to RenderGeometry() to enable texture rendering without changing the bound texture. + static constexpr Rml::TextureHandle TextureEnableWithoutBinding = Rml::TextureHandle(-1); + // Can be passed to RenderGeometry() to leave the bound texture and used program unchanged. + static constexpr Rml::TextureHandle TexturePostprocess = Rml::TextureHandle(-2); + + bool IsSwapchainValid() noexcept; + void RecreateSwapchain() noexcept; + + ID3D12Fence* Get_Fence(); + HANDLE Get_FenceEvent(); + Rml::Array& Get_FenceValues(); + uint32_t Get_CurrentFrameIndex(); + + ID3D12Device* Get_Device() const; + TextureMemoryManager& Get_TextureManager(); + BufferMemoryManager& Get_BufferManager(); + + unsigned char Get_MSAASampleCount() const; + + void Set_UserFramebufferIndex(unsigned char framebuffer_index); + void Set_UserRenderTarget(void* rtv_where_we_render_to); + void Set_UserDepthStencil(void* dsv_where_we_render_to); + + bool CaptureScreen(int& width, int& height, int& num_components, Rml::UniquePtr& data); + +private: + void Initialize_Device() noexcept; + void Initialize_Adapter() noexcept; + void Initialize_DebugLayer() noexcept; + + void Initialize_Swapchain(int width, int height) noexcept; + void Initialize_SyncPrimitives() noexcept; + void Initialize_SyncPrimitives_Screenshot() noexcept; + void Initialize_CommandAllocators(); + + void Initialize_Allocator() noexcept; + + void Destroy_Swapchain() noexcept; + void Destroy_SyncPrimitives() noexcept; + void Destroy_CommandAllocators() noexcept; + void Destroy_CommandList() noexcept; + void Destroy_Allocator() noexcept; + void Destroy_SyncPrimitives_Screenshot() noexcept; + + void Flush() noexcept; + uint64_t Signal(uint32_t frame_index) noexcept; + void WaitForFenceValue(uint32_t frame_index); + + void Create_Resources_DependentOnSize() noexcept; + void Destroy_Resources_DependentOnSize() noexcept; + + ID3D12DescriptorHeap* Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, + uint32_t descriptor_count) noexcept; + + ID3D12CommandAllocator* Create_CommandAllocator(D3D12_COMMAND_LIST_TYPE type); + ID3D12GraphicsCommandList* Create_CommandList(ID3D12CommandAllocator* p_allocator, D3D12_COMMAND_LIST_TYPE type) noexcept; + + ID3D12CommandQueue* Create_CommandQueue(D3D12_COMMAND_LIST_TYPE type) noexcept; + + void Create_Resource_RenderTargetViews(); + void Destroy_Resource_RenderTagetViews(); + + void Destroy_Resource_For_Shaders(); + + void Create_Resource_Pipelines(); + void Create_Resource_Pipeline_Color(); + void Create_Resource_Pipeline_Texture(); + void Create_Resource_Pipeline_Gradient(); + void Create_Resource_Pipeline_Creation(); + void Create_Resource_Pipeline_Passthrough(); + void Create_Resource_Pipeline_Passthrough_NoBlend(); + void Create_Resource_Pipeline_ColorMatrix(); + void Create_Resource_Pipeline_BlendMask(); + void Create_Resource_Pipeline_Blur(); + void Create_Resource_Pipeline_DropShadow(); + + void Create_Resource_DepthStencil(); + void Destroy_Resource_DepthStencil(); + + void Destroy_Resource_Pipelines(); + + bool CheckTearingSupport() noexcept; + + IDXGIAdapter* Get_Adapter(bool is_use_warp) noexcept; + void PrintAdapterDesc(IDXGIAdapter* p_adapter); + + void SetScissor(Rml::Rectanglei region, bool vertically_flip = false); + + void SubmitTransformUniform(ConstantBufferType& constant_buffer, const Rml::Vector2f& translation); + + void UseProgram(ProgramId pipeline_id); + + ConstantBufferType* Get_ConstantBuffer(uint32_t current_back_buffer_index); + + void Free_Geometry(GeometryHandleType* p_handle); + void Free_Texture(TextureHandleType* p_handle); + + void Update_PendingForDeletion_Geometry(); + + void BlitLayerToPostprocessPrimary(Rml::LayerHandle layer_id); + + void RenderFilters(Rml::Span filter_handles); + void RenderBlur(float sigma, const Gfx::FramebufferData& source_destination, const Gfx::FramebufferData& temp, + const Rml::Rectanglei window_flipped); + + void DrawFullscreenQuad(ConstantBufferType* p_override_constant_buffer = nullptr); + void DrawFullscreenQuad(Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling = Rml::Vector2f(1.f), + ConstantBufferType* p_override_constant_buffer = nullptr); + + void BindTexture(TextureHandleType* p_texture, UINT root_parameter_index = 0); + void BindRenderTarget(const Gfx::FramebufferData& framebuffer, bool depth_included = true); + + void OverrideConstantBufferOfGeometry(Rml::CompiledGeometryHandle geometry, ConstantBufferType* p_override_constant_buffer); + + // 1 means not supported + // otherwise return max value of supported multisample count + unsigned char GetMSAASupportedSampleCount(unsigned char max_samples); + + void BlitFramebuffer(const Gfx::FramebufferData& source, const Gfx::FramebufferData& dest, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, + int dstY0, int dstX1, int dstY1); + + // debug only + void ValidateTextureAllocationNotAsPlaced(const Gfx::FramebufferData& data); + + ID3D12Resource* GetResourceFromFramebufferData(const Gfx::FramebufferData& data); + + bool m_is_use_vsync; + bool m_is_use_tearing; + bool m_is_scissor_was_set; + bool m_is_stencil_enabled; + bool m_is_stencil_equal; + bool m_is_use_msaa; + bool m_is_execute_when_end_frame_issued; + bool m_is_command_list_user; + unsigned char m_msaa_sample_count; + unsigned char m_user_framebuffer_index; + // Current viewport's width + int m_width; + // Current viewport's height + int m_height; + int m_current_clip_operation; + ProgramId m_active_program_id; + Rml::Rectanglei m_scissor; + uint32_t m_size_descriptor_heap_render_target_view; + uint32_t m_size_descriptor_heap_shaders; + UINT m_current_back_buffer_index; + UINT m_stencil_ref_value; + // For debug builds: D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION, otherwise 0. + UINT m_default_shader_flags; + ID3D12Device* m_p_device; + ID3D12CommandQueue* m_p_command_queue; + ID3D12CommandQueue* m_p_copy_queue; + IDXGISwapChain4* m_p_swapchain; + ID3D12GraphicsCommandList* m_p_command_graphics_list; + ID3D12GraphicsCommandList* m_p_command_graphics_list_screenshot; + ID3D12CommandAllocator* m_p_command_allocator_screenshot; + ID3D12DescriptorHeap* m_p_descriptor_heap_render_target_view; + ID3D12DescriptorHeap* m_p_descriptor_heap_render_target_view_for_texture_manager; + ID3D12DescriptorHeap* m_p_descriptor_heap_depth_stencil_view_for_texture_manager; + // cbv; srv; uav; all in one + ID3D12DescriptorHeap* m_p_descriptor_heap_shaders; + ID3D12DescriptorHeap* m_p_descriptor_heap_depthstencil; + D3D12MA::Allocation* m_p_depthstencil_resource; + ID3D12Fence* m_p_backbuffer_fence; + ID3D12Fence* m_p_fence_screenshot; + IDXGIAdapter* m_p_adapter; + + ID3D12PipelineState* m_pipelines[NumPrograms]; + ID3D12RootSignature* m_root_signatures[NumPrograms]; + + ID3D12CommandAllocator* m_p_copy_allocator; + ID3D12GraphicsCommandList* m_p_copy_command_list; + + D3D12MA::Allocator* m_p_allocator; + OffsetAllocator::Allocator* m_p_offset_allocator_for_descriptor_heap_shaders; + // where user wants to render rmlui final image + D3D12_CPU_DESCRIPTOR_HANDLE* m_p_user_rtv_present; + // as well as rtv just dsv + D3D12_CPU_DESCRIPTOR_HANDLE* m_p_user_dsv_present; + HWND m_p_window_handle; + HANDLE m_p_fence_event; + HANDLE m_p_fence_event_screenshot; + uint64_t m_fence_value; + uint64_t m_fence_screenshot_value; + Rml::CompiledGeometryHandle m_precompiled_fullscreen_quad_geometry; + + Rml::Array m_backbuffers_resources; + Rml::Array m_backbuffers_allocators; + Rml::Array m_backbuffers_fence_values; + Rml::Array m_constant_buffer_count_per_frame; + Rml::Array m_vertex_and_index_buffer_count_per_frame; + // per object (per draw) + Rml::Array, RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT> m_constantbuffers; + Rml::Vector m_pending_for_deletion_geometry; + + DXGI_SAMPLE_DESC m_desc_sample; + D3D12_CPU_DESCRIPTOR_HANDLE m_handle_shaders; + BufferMemoryManager m_manager_buffer; + TextureMemoryManager m_manager_texture; + Rml::Matrix4f m_constant_buffer_data_transform; + Rml::Matrix4f m_constant_buffer_data_projection; + Rml::Matrix4f m_projection; + RenderLayerStack m_manager_render_layer; +}; + +namespace Backend { +struct RmlRendererSettings { + bool vsync; + unsigned char msaa_sample_count; +}; +} // namespace Backend diff --git a/vendor/rmlui_backend/RmlUi_Renderer_GL2.cpp b/vendor/rmlui_backend/RmlUi_Renderer_GL2.cpp new file mode 100644 index 0000000..b09457e --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_GL2.cpp @@ -0,0 +1,325 @@ +#include "RmlUi_Renderer_GL2.h" +#include +#include +#include +#include +#include + +#if defined RMLUI_PLATFORM_WIN32 + #include "RmlUi_Include_Windows.h" + #include +#elif defined RMLUI_PLATFORM_MACOSX + #include + #include + #include +#elif defined RMLUI_PLATFORM_UNIX + #include "RmlUi_Include_Xlib.h" + #include + #include + #include +#endif + +#define GL_CLAMP_TO_EDGE 0x812F + +RenderInterface_GL2::RenderInterface_GL2() {} + +void RenderInterface_GL2::SetViewport(int in_viewport_width, int in_viewport_height) +{ + viewport_width = in_viewport_width; + viewport_height = in_viewport_height; +} + +void RenderInterface_GL2::BeginFrame() +{ + RMLUI_ASSERT(viewport_width >= 0 && viewport_height >= 0); + glViewport(0, 0, viewport_width, viewport_height); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 1, GLuint(-1)); + glStencilMask(GLuint(-1)); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + Rml::Matrix4f projection = Rml::Matrix4f::ProjectOrtho(0, (float)viewport_width, (float)viewport_height, 0, -10000, 10000); + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(projection.data()); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + transform_enabled = false; +} + +void RenderInterface_GL2::EndFrame() {} + +void RenderInterface_GL2::Clear() +{ + glClearStencil(0); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +Rml::CompiledGeometryHandle RenderInterface_GL2::CompileGeometry(Rml::Span vertices, Rml::Span indices) +{ + GeometryView* data = new GeometryView{vertices, indices}; + return reinterpret_cast(data); +} + +void RenderInterface_GL2::ReleaseGeometry(Rml::CompiledGeometryHandle geometry) +{ + delete reinterpret_cast(geometry); +} + +void RenderInterface_GL2::RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) +{ + const GeometryView* geometry = reinterpret_cast(handle); + const Rml::Vertex* vertices = geometry->vertices.data(); + const int* indices = geometry->indices.data(); + const int num_indices = (int)geometry->indices.size(); + + glPushMatrix(); + glTranslatef(translation.x, translation.y, 0); + + glVertexPointer(2, GL_FLOAT, sizeof(Rml::Vertex), &vertices[0].position); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Rml::Vertex), &vertices[0].colour); + + if (!texture) + { + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + else + { + glEnable(GL_TEXTURE_2D); + + if (texture != TextureEnableWithoutBinding) + glBindTexture(GL_TEXTURE_2D, (GLuint)texture); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, sizeof(Rml::Vertex), &vertices[0].tex_coord); + } + + glDrawElements(GL_TRIANGLES, num_indices, GL_UNSIGNED_INT, indices); + + glPopMatrix(); +} + +void RenderInterface_GL2::EnableScissorRegion(bool enable) +{ + if (enable) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); +} + +void RenderInterface_GL2::SetScissorRegion(Rml::Rectanglei region) +{ + glScissor(region.Left(), viewport_height - region.Bottom(), region.Width(), region.Height()); +} + +void RenderInterface_GL2::EnableClipMask(bool enable) +{ + if (enable) + glEnable(GL_STENCIL_TEST); + else + glDisable(GL_STENCIL_TEST); +} + +void RenderInterface_GL2::RenderToClipMask(Rml::ClipMaskOperation operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation) +{ + RMLUI_ASSERT(glIsEnabled(GL_STENCIL_TEST)); + using Rml::ClipMaskOperation; + + const bool clear_stencil = (operation == ClipMaskOperation::Set || operation == ClipMaskOperation::SetInverse); + if (clear_stencil) + { + // @performance Increment the reference value instead of clearing each time. + glClear(GL_STENCIL_BUFFER_BIT); + } + + GLint stencil_test_value = 0; + glGetIntegerv(GL_STENCIL_REF, &stencil_test_value); + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glStencilFunc(GL_ALWAYS, GLint(1), GLuint(-1)); + + switch (operation) + { + case ClipMaskOperation::Set: + { + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + stencil_test_value = 1; + } + break; + case ClipMaskOperation::SetInverse: + { + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + stencil_test_value = 0; + } + break; + case ClipMaskOperation::Intersect: + { + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + stencil_test_value += 1; + } + break; + } + + RenderGeometry(geometry, translation, {}); + + // Restore state + // @performance Cache state so we don't toggle it unnecessarily. + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, stencil_test_value, GLuint(-1)); +} + +// Set to byte packing, or the compiler will expand our struct, which means it won't read correctly from file +#pragma pack(1) +struct TGAHeader { + char idLength; + char colourMapType; + char dataType; + short int colourMapOrigin; + short int colourMapLength; + char colourMapDepth; + short int xOrigin; + short int yOrigin; + short int width; + short int height; + char bitsPerPixel; + char imageDescriptor; +}; +// Restore packing +#pragma pack() + +Rml::TextureHandle RenderInterface_GL2::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) +{ + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + { + return false; + } + + file_interface->Seek(file_handle, 0, SEEK_END); + size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + if (buffer_size <= sizeof(TGAHeader)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Texture file size is smaller than TGAHeader, file is not a valid TGA image."); + file_interface->Close(file_handle); + return false; + } + + using Rml::byte; + Rml::UniquePtr buffer(new byte[buffer_size]); + file_interface->Read(buffer.get(), buffer_size, file_handle); + file_interface->Close(file_handle); + + TGAHeader header; + memcpy(&header, buffer.get(), sizeof(TGAHeader)); + + int color_mode = header.bitsPerPixel / 8; + const size_t image_size = header.width * header.height * 4; // We always make 32bit textures + + if (header.dataType != 2) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24/32bit uncompressed TGAs are supported."); + return false; + } + + // Ensure we have at least 3 colors + if (color_mode < 3) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24 and 32bit textures are supported."); + return false; + } + + const byte* image_src = buffer.get() + sizeof(TGAHeader); + Rml::UniquePtr image_dest_buffer(new byte[image_size]); + byte* image_dest = image_dest_buffer.get(); + const bool top_to_bottom_order = ((header.imageDescriptor & 32) != 0); + + // Targa is BGR, swap to RGB, flip Y axis as necessary, and convert to premultiplied alpha. + for (long y = 0; y < header.height; y++) + { + long read_index = y * header.width * color_mode; + long write_index = top_to_bottom_order ? (y * header.width * 4) : (header.height - y - 1) * header.width * 4; + for (long x = 0; x < header.width; x++) + { + image_dest[write_index] = image_src[read_index + 2]; + image_dest[write_index + 1] = image_src[read_index + 1]; + image_dest[write_index + 2] = image_src[read_index]; + if (color_mode == 4) + { + const byte alpha = image_src[read_index + 3]; + for (size_t j = 0; j < 3; j++) + image_dest[write_index + j] = byte((image_dest[write_index + j] * alpha) / 255); + image_dest[write_index + 3] = alpha; + } + else + image_dest[write_index + 3] = 255; + + write_index += 4; + read_index += color_mode; + } + } + + texture_dimensions.x = header.width; + texture_dimensions.y = header.height; + + return GenerateTexture({image_dest, image_size}, texture_dimensions); +} + +Rml::TextureHandle RenderInterface_GL2::GenerateTexture(Rml::Span source, Rml::Vector2i source_dimensions) +{ + RMLUI_ASSERT(source.data() && source.size() == size_t(source_dimensions.x * source_dimensions.y * 4)); + + GLuint texture_id = 0; + glGenTextures(1, &texture_id); + if (texture_id == 0) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to generate texture."); + return {}; + } + + glBindTexture(GL_TEXTURE_2D, texture_id); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, source_dimensions.x, source_dimensions.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, source.data()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + return (Rml::TextureHandle)texture_id; +} + +void RenderInterface_GL2::ReleaseTexture(Rml::TextureHandle texture_handle) +{ + glDeleteTextures(1, (GLuint*)&texture_handle); +} + +void RenderInterface_GL2::SetTransform(const Rml::Matrix4f* transform) +{ + transform_enabled = (transform != nullptr); + + if (transform) + { + if (std::is_same_v) + glLoadMatrixf(transform->data()); + else if (std::is_same_v) + glLoadMatrixf(transform->Transpose().data()); + } + else + glLoadIdentity(); +} diff --git a/vendor/rmlui_backend/RmlUi_Renderer_GL2.h b/vendor/rmlui_backend/RmlUi_Renderer_GL2.h new file mode 100644 index 0000000..9d9e6a8 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_GL2.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +class RenderInterface_GL2 : public Rml::RenderInterface { +public: + RenderInterface_GL2(); + + // The viewport should be updated whenever the window size changes. + void SetViewport(int viewport_width, int viewport_height); + + // Sets up OpenGL states for taking rendering commands from RmlUi. + void BeginFrame(); + void EndFrame(); + + // Optional, can be used to clear the framebuffer. + void Clear(); + + // -- Inherited from Rml::RenderInterface -- + + Rml::CompiledGeometryHandle CompileGeometry(Rml::Span vertices, Rml::Span indices) override; + void ReleaseGeometry(Rml::CompiledGeometryHandle geometry) override; + void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override; + + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override; + Rml::TextureHandle GenerateTexture(Rml::Span source, Rml::Vector2i source_dimensions) override; + void ReleaseTexture(Rml::TextureHandle texture_handle) override; + + void EnableScissorRegion(bool enable) override; + void SetScissorRegion(Rml::Rectanglei region) override; + + void EnableClipMask(bool enable) override; + void RenderToClipMask(Rml::ClipMaskOperation operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation) override; + + void SetTransform(const Rml::Matrix4f* transform) override; + + // Can be passed to RenderGeometry() to enable texture rendering without changing the bound texture. + static const Rml::TextureHandle TextureEnableWithoutBinding = Rml::TextureHandle(-1); + +private: + struct GeometryView { + Rml::Span vertices; + Rml::Span indices; + }; + + int viewport_width = 0; + int viewport_height = 0; + bool transform_enabled = false; +}; diff --git a/vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp b/vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp new file mode 100644 index 0000000..014f3d5 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp @@ -0,0 +1,2171 @@ +#include "RmlUi_Renderer_GL3.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined RMLUI_PLATFORM_WIN32_NATIVE + // function call missing argument list + #pragma warning(disable : 4551) + // unreferenced local function has been removed + #pragma warning(disable : 4505) +#endif + +#if defined RMLUI_PLATFORM_EMSCRIPTEN + #define RMLUI_SHADER_HEADER_VERSION "#version 300 es\nprecision highp float;\n" + #include +#elif defined __ANDROID__ + #define RMLUI_SHADER_HEADER_VERSION "#version 320 es\nprecision highp float;\n" + #include +#elif defined RMLUI_GL3_CUSTOM_LOADER + #define RMLUI_SHADER_HEADER_VERSION "#version 330\n" + #include RMLUI_GL3_CUSTOM_LOADER +#else + #define RMLUI_SHADER_HEADER_VERSION "#version 330\n" + #define GLAD_GL_IMPLEMENTATION + #include "RmlUi_Include_GL3.h" +#endif + +// Determines the anti-aliasing quality when creating layers. Enables better-looking visuals, especially when transforms are applied. +#ifndef RMLUI_NUM_MSAA_SAMPLES + #define RMLUI_NUM_MSAA_SAMPLES 2 +#endif + +#define MAX_NUM_STOPS 16 +#define BLUR_SIZE 7 +#define BLUR_NUM_WEIGHTS ((BLUR_SIZE + 1) / 2) + +#define RMLUI_STRINGIFY_IMPL(x) #x +#define RMLUI_STRINGIFY(x) RMLUI_STRINGIFY_IMPL(x) + +#define RMLUI_SHADER_HEADER \ + RMLUI_SHADER_HEADER_VERSION "#define MAX_NUM_STOPS " RMLUI_STRINGIFY(MAX_NUM_STOPS) "\n#line " RMLUI_STRINGIFY(__LINE__) "\n" + +static const char* shader_vert_main = RMLUI_SHADER_HEADER R"( +uniform vec2 _translate; +uniform mat4 _transform; + +in vec2 inPosition; +in vec4 inColor0; +in vec2 inTexCoord0; + +out vec2 fragTexCoord; +out vec4 fragColor; + +void main() { + fragTexCoord = inTexCoord0; + fragColor = inColor0; + + vec2 translatedPos = inPosition + _translate; + vec4 outPos = _transform * vec4(translatedPos, 0.0, 1.0); + + gl_Position = outPos; +} +)"; +static const char* shader_frag_texture = RMLUI_SHADER_HEADER R"( +uniform sampler2D _tex; +in vec2 fragTexCoord; +in vec4 fragColor; + +out vec4 finalColor; + +void main() { + vec4 texColor = texture(_tex, fragTexCoord); + finalColor = fragColor * texColor; +} +)"; +static const char* shader_frag_color = RMLUI_SHADER_HEADER R"( +in vec2 fragTexCoord; +in vec4 fragColor; + +out vec4 finalColor; + +void main() { + finalColor = fragColor; +} +)"; + +enum class ShaderGradientFunction { Linear, Radial, Conic, RepeatingLinear, RepeatingRadial, RepeatingConic }; // Must match shader definitions below. + +static const char* shader_frag_gradient = RMLUI_SHADER_HEADER R"( +#define LINEAR 0 +#define RADIAL 1 +#define CONIC 2 +#define REPEATING_LINEAR 3 +#define REPEATING_RADIAL 4 +#define REPEATING_CONIC 5 +#define PI 3.14159265 + +uniform int _func; // one of the above definitions +uniform vec2 _p; // linear: starting point, radial: center, conic: center +uniform vec2 _v; // linear: vector to ending point, radial: 2d curvature (inverse radius), conic: angled unit vector +uniform vec4 _stop_colors[MAX_NUM_STOPS]; +uniform float _stop_positions[MAX_NUM_STOPS]; // normalized, 0 -> starting point, 1 -> ending point +uniform int _num_stops; + +in vec2 fragTexCoord; +in vec4 fragColor; +out vec4 finalColor; + +vec4 mix_stop_colors(float t) { + vec4 color = _stop_colors[0]; + + for (int i = 1; i < _num_stops; i++) + color = mix(color, _stop_colors[i], smoothstep(_stop_positions[i-1], _stop_positions[i], t)); + + return color; +} + +void main() { + float t = 0.0; + + if (_func == LINEAR || _func == REPEATING_LINEAR) + { + float dist_square = dot(_v, _v); + vec2 V = fragTexCoord - _p; + t = dot(_v, V) / dist_square; + } + else if (_func == RADIAL || _func == REPEATING_RADIAL) + { + vec2 V = fragTexCoord - _p; + t = length(_v * V); + } + else if (_func == CONIC || _func == REPEATING_CONIC) + { + mat2 R = mat2(_v.x, -_v.y, _v.y, _v.x); + vec2 V = R * (fragTexCoord - _p); + t = 0.5 + atan(-V.x, V.y) / (2.0 * PI); + } + + if (_func == REPEATING_LINEAR || _func == REPEATING_RADIAL || _func == REPEATING_CONIC) + { + float t0 = _stop_positions[0]; + float t1 = _stop_positions[_num_stops - 1]; + t = t0 + mod(t - t0, t1 - t0); + } + + finalColor = fragColor * mix_stop_colors(t); +} +)"; + +// "Creation" by Danilo Guanabara, based on: https://www.shadertoy.com/view/XsXXDn +static const char* shader_frag_creation = RMLUI_SHADER_HEADER R"( +uniform float _value; +uniform vec2 _dimensions; + +in vec2 fragTexCoord; +in vec4 fragColor; +out vec4 finalColor; + +void main() { + float t = _value; + vec3 c; + float l; + for (int i = 0; i < 3; i++) { + vec2 p = fragTexCoord; + vec2 uv = p; + p -= .5; + p.x *= _dimensions.x / _dimensions.y; + float z = t + float(i) * .07; + l = length(p); + uv += p / l * (sin(z) + 1.) * abs(sin(l * 9. - z - z)); + c[i] = .01 / length(mod(uv, 1.) - .5); + } + finalColor = vec4(c / l, fragColor.a); +} +)"; + +static const char* shader_vert_passthrough = RMLUI_SHADER_HEADER R"( +in vec2 inPosition; +in vec2 inTexCoord0; + +out vec2 fragTexCoord; + +void main() { + fragTexCoord = inTexCoord0; + gl_Position = vec4(inPosition, 0.0, 1.0); +} +)"; +static const char* shader_frag_passthrough = RMLUI_SHADER_HEADER R"( +uniform sampler2D _tex; +in vec2 fragTexCoord; +out vec4 finalColor; + +void main() { + finalColor = texture(_tex, fragTexCoord); +} +)"; +static const char* shader_frag_color_matrix = RMLUI_SHADER_HEADER R"( +uniform sampler2D _tex; +uniform mat4 _color_matrix; + +in vec2 fragTexCoord; +out vec4 finalColor; + +void main() { + // The general case uses a 4x5 color matrix for full rgba transformation, plus a constant term with the last column. + // However, we only consider the case of rgb transformations. Thus, we could in principle use a 3x4 matrix, but we + // keep the alpha row for simplicity. + // In the general case we should do the matrix transformation in non-premultiplied space. However, without alpha + // transformations, we can do it directly in premultiplied space to avoid the extra division and multiplication + // steps. In this space, the constant term needs to be multiplied by the alpha value, instead of unity. + vec4 texColor = texture(_tex, fragTexCoord); + vec3 transformedColor = vec3(_color_matrix * texColor); + finalColor = vec4(transformedColor, texColor.a); +} +)"; +static const char* shader_frag_blend_mask = RMLUI_SHADER_HEADER R"( +uniform sampler2D _tex; +uniform sampler2D _texMask; + +in vec2 fragTexCoord; +out vec4 finalColor; + +void main() { + vec4 texColor = texture(_tex, fragTexCoord); + float maskAlpha = texture(_texMask, fragTexCoord).a; + finalColor = texColor * maskAlpha; +} +)"; + +#define RMLUI_SHADER_BLUR_HEADER \ + RMLUI_SHADER_HEADER "\n#define BLUR_SIZE " RMLUI_STRINGIFY(BLUR_SIZE) "\n#define BLUR_NUM_WEIGHTS " RMLUI_STRINGIFY(BLUR_NUM_WEIGHTS) + +static const char* shader_vert_blur = RMLUI_SHADER_BLUR_HEADER R"( +uniform vec2 _texelOffset; + +in vec3 inPosition; +in vec2 inTexCoord0; + +out vec2 fragTexCoord[BLUR_SIZE]; + +void main() { + for(int i = 0; i < BLUR_SIZE; i++) + fragTexCoord[i] = inTexCoord0 - float(i - BLUR_NUM_WEIGHTS + 1) * _texelOffset; + gl_Position = vec4(inPosition, 1.0); +} +)"; +static const char* shader_frag_blur = RMLUI_SHADER_BLUR_HEADER R"( +uniform sampler2D _tex; +uniform float _weights[BLUR_NUM_WEIGHTS]; +uniform vec2 _texCoordMin; +uniform vec2 _texCoordMax; + +in vec2 fragTexCoord[BLUR_SIZE]; +out vec4 finalColor; + +void main() { + vec4 color = vec4(0.0, 0.0, 0.0, 0.0); + for(int i = 0; i < BLUR_SIZE; i++) + { + vec2 in_region = step(_texCoordMin, fragTexCoord[i]) * step(fragTexCoord[i], _texCoordMax); + color += texture(_tex, fragTexCoord[i]) * in_region.x * in_region.y * _weights[abs(i - BLUR_NUM_WEIGHTS + 1)]; + } + finalColor = color; +} +)"; +static const char* shader_frag_drop_shadow = RMLUI_SHADER_HEADER R"( +uniform sampler2D _tex; +uniform vec2 _texCoordMin; +uniform vec2 _texCoordMax; +uniform vec4 _color; + +in vec2 fragTexCoord; +out vec4 finalColor; + +void main() { + vec2 in_region = step(_texCoordMin, fragTexCoord) * step(fragTexCoord, _texCoordMax); + finalColor = texture(_tex, fragTexCoord).a * in_region.x * in_region.y * _color; +} +)"; + +enum class ProgramId { + None, + Color, + Texture, + Gradient, + Creation, + Passthrough, + ColorMatrix, + BlendMask, + Blur, + DropShadow, + Count, +}; +enum class VertShaderId { + Main, + Passthrough, + Blur, + Count, +}; +enum class FragShaderId { + Color, + Texture, + Gradient, + Creation, + Passthrough, + ColorMatrix, + BlendMask, + Blur, + DropShadow, + Count, +}; +enum class UniformId { + Translate, + Transform, + Tex, + Color, + ColorMatrix, + TexelOffset, + TexCoordMin, + TexCoordMax, + TexMask, + Weights, + Func, + P, + V, + StopColors, + StopPositions, + NumStops, + Value, + Dimensions, + Count, +}; + +namespace Gfx { + +static const char* const program_uniform_names[(size_t)UniformId::Count] = {"_translate", "_transform", "_tex", "_color", "_color_matrix", + "_texelOffset", "_texCoordMin", "_texCoordMax", "_texMask", "_weights[0]", "_func", "_p", "_v", "_stop_colors[0]", "_stop_positions[0]", + "_num_stops", "_value", "_dimensions"}; + +enum class VertexAttribute { Position, Color0, TexCoord0, Count }; +static const char* const vertex_attribute_names[(size_t)VertexAttribute::Count] = {"inPosition", "inColor0", "inTexCoord0"}; + +struct VertShaderDefinition { + VertShaderId id; + const char* name_str; + const char* code_str; +}; +struct FragShaderDefinition { + FragShaderId id; + const char* name_str; + const char* code_str; +}; +struct ProgramDefinition { + ProgramId id; + const char* name_str; + VertShaderId vert_shader; + FragShaderId frag_shader; +}; + +// clang-format off +static const VertShaderDefinition vert_shader_definitions[] = { + {VertShaderId::Main, "main", shader_vert_main}, + {VertShaderId::Passthrough, "passthrough", shader_vert_passthrough}, + {VertShaderId::Blur, "blur", shader_vert_blur}, +}; +static const FragShaderDefinition frag_shader_definitions[] = { + {FragShaderId::Color, "color", shader_frag_color}, + {FragShaderId::Texture, "texture", shader_frag_texture}, + {FragShaderId::Gradient, "gradient", shader_frag_gradient}, + {FragShaderId::Creation, "creation", shader_frag_creation}, + {FragShaderId::Passthrough, "passthrough", shader_frag_passthrough}, + {FragShaderId::ColorMatrix, "color_matrix", shader_frag_color_matrix}, + {FragShaderId::BlendMask, "blend_mask", shader_frag_blend_mask}, + {FragShaderId::Blur, "blur", shader_frag_blur}, + {FragShaderId::DropShadow, "drop_shadow", shader_frag_drop_shadow}, +}; +static const ProgramDefinition program_definitions[] = { + {ProgramId::Color, "color", VertShaderId::Main, FragShaderId::Color}, + {ProgramId::Texture, "texture", VertShaderId::Main, FragShaderId::Texture}, + {ProgramId::Gradient, "gradient", VertShaderId::Main, FragShaderId::Gradient}, + {ProgramId::Creation, "creation", VertShaderId::Main, FragShaderId::Creation}, + {ProgramId::Passthrough, "passthrough", VertShaderId::Passthrough, FragShaderId::Passthrough}, + {ProgramId::ColorMatrix, "color_matrix", VertShaderId::Passthrough, FragShaderId::ColorMatrix}, + {ProgramId::BlendMask, "blend_mask", VertShaderId::Passthrough, FragShaderId::BlendMask}, + {ProgramId::Blur, "blur", VertShaderId::Blur, FragShaderId::Blur}, + {ProgramId::DropShadow, "drop_shadow", VertShaderId::Passthrough, FragShaderId::DropShadow}, +}; +// clang-format on + +template +class EnumArray { +public: + const T& operator[](Enum id) const + { + RMLUI_ASSERT((size_t)id < (size_t)Enum::Count); + return ids[size_t(id)]; + } + T& operator[](Enum id) + { + RMLUI_ASSERT((size_t)id < (size_t)Enum::Count); + return ids[size_t(id)]; + } + auto begin() const { return ids.begin(); } + auto end() const { return ids.end(); } + +private: + Rml::Array ids = {}; +}; + +using Programs = EnumArray; +using VertShaders = EnumArray; +using FragShaders = EnumArray; + +class Uniforms { +public: + GLint Get(ProgramId id, UniformId uniform) const + { + auto it = map.find(ToKey(id, uniform)); + if (it != map.end()) + return it->second; + return -1; + } + void Insert(ProgramId id, UniformId uniform, GLint location) { map[ToKey(id, uniform)] = location; } + +private: + using Key = uint64_t; + Key ToKey(ProgramId id, UniformId uniform) const { return (static_cast(id) << 32) | static_cast(uniform); } + Rml::UnorderedMap map; +}; + +struct ProgramData { + Programs programs; + VertShaders vert_shaders; + FragShaders frag_shaders; + Uniforms uniforms; +}; + +struct CompiledGeometryData { + GLuint vao; + GLuint vbo; + GLuint ibo; + GLsizei draw_count; +}; + +struct FramebufferData { + int width, height; + GLuint framebuffer; + GLuint color_tex_buffer; + GLuint color_render_buffer; + GLuint depth_stencil_buffer; + bool owns_depth_stencil_buffer; +}; + +enum class FramebufferAttachment { None, DepthStencil }; + +static void CheckGLError(const char* operation_name) +{ +#ifdef RMLUI_DEBUG + GLenum error_code = glGetError(); + if (error_code != GL_NO_ERROR) + { + static const Rml::Pair error_names[] = {{GL_INVALID_ENUM, "GL_INVALID_ENUM"}, {GL_INVALID_VALUE, "GL_INVALID_VALUE"}, + {GL_INVALID_OPERATION, "GL_INVALID_OPERATION"}, {GL_OUT_OF_MEMORY, "GL_OUT_OF_MEMORY"}}; + const char* error_str = "''"; + for (auto& err : error_names) + { + if (err.first == error_code) + { + error_str = err.second; + break; + } + } + Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL error during %s. Error code 0x%x (%s).", operation_name, error_code, error_str); + } +#endif + (void)operation_name; +} + +// Create the shader, 'shader_type' is either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. +static bool CreateShader(GLuint& out_shader_id, GLenum shader_type, const char* code_string) +{ + RMLUI_ASSERT(shader_type == GL_VERTEX_SHADER || shader_type == GL_FRAGMENT_SHADER); + + GLuint id = glCreateShader(shader_type); + glShaderSource(id, 1, (const GLchar**)&code_string, NULL); + glCompileShader(id); + + GLint status = 0; + glGetShaderiv(id, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) + { + GLint info_log_length = 0; + glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_log_length); + char* info_log_string = new char[info_log_length + 1]; + glGetShaderInfoLog(id, info_log_length, NULL, info_log_string); + + Rml::Log::Message(Rml::Log::LT_ERROR, "Compile failure in OpenGL shader: %s", info_log_string); + delete[] info_log_string; + glDeleteShader(id); + return false; + } + + CheckGLError("CreateShader"); + + out_shader_id = id; + return true; +} + +static bool CreateProgram(GLuint& out_program, Uniforms& inout_uniform_map, ProgramId program_id, GLuint vertex_shader, GLuint fragment_shader) +{ + GLuint id = glCreateProgram(); + RMLUI_ASSERT(id); + + for (GLuint i = 0; i < (GLuint)VertexAttribute::Count; i++) + glBindAttribLocation(id, i, vertex_attribute_names[i]); + + CheckGLError("BindAttribLocations"); + + glAttachShader(id, vertex_shader); + glAttachShader(id, fragment_shader); + + glLinkProgram(id); + + glDetachShader(id, vertex_shader); + glDetachShader(id, fragment_shader); + + GLint status = 0; + glGetProgramiv(id, GL_LINK_STATUS, &status); + if (status == GL_FALSE) + { + GLint info_log_length = 0; + glGetProgramiv(id, GL_INFO_LOG_LENGTH, &info_log_length); + char* info_log_string = new char[info_log_length + 1]; + glGetProgramInfoLog(id, info_log_length, NULL, info_log_string); + + Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL program linking failure: %s", info_log_string); + delete[] info_log_string; + glDeleteProgram(id); + return false; + } + + out_program = id; + + // Make a lookup table for the uniform locations. + GLint num_active_uniforms = 0; + glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &num_active_uniforms); + + constexpr size_t name_size = 64; + GLchar name_buf[name_size] = ""; + for (int unif = 0; unif < num_active_uniforms; ++unif) + { + GLint array_size = 0; + GLenum type = 0; + GLsizei actual_length = 0; + glGetActiveUniform(id, unif, name_size, &actual_length, &array_size, &type, name_buf); + GLint location = glGetUniformLocation(id, name_buf); + + // See if we have the name in our pre-defined name list. + UniformId program_uniform = UniformId::Count; + for (int i = 0; i < (int)UniformId::Count; i++) + { + const char* uniform_name = program_uniform_names[i]; + if (strcmp(name_buf, uniform_name) == 0) + { + program_uniform = (UniformId)i; + break; + } + } + + if ((size_t)program_uniform < (size_t)UniformId::Count) + { + inout_uniform_map.Insert(program_id, program_uniform, location); + } + else + { + Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL program uses unknown uniform '%s'.", name_buf); + return false; + } + } + + CheckGLError("CreateProgram"); + + return true; +} + +static bool CreateFramebuffer(FramebufferData& out_fb, int width, int height, int samples, FramebufferAttachment attachment, + GLuint shared_depth_stencil_buffer) +{ +#if defined(RMLUI_PLATFORM_EMSCRIPTEN) || defined(__ANDROID__) + constexpr GLint wrap_mode = GL_CLAMP_TO_EDGE; +#else + constexpr GLint wrap_mode = GL_CLAMP_TO_BORDER; // GL_REPEAT GL_MIRRORED_REPEAT GL_CLAMP_TO_EDGE +#endif + + constexpr GLenum color_format = GL_RGBA8; // GL_RGBA8 GL_SRGB8_ALPHA8 GL_RGBA16F + constexpr GLint min_mag_filter = GL_LINEAR; // GL_NEAREST + const Rml::Colourf border_color(0.f, 0.f); + + GLuint framebuffer = 0; + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + + GLuint color_tex_buffer = 0; + GLuint color_render_buffer = 0; + if (samples > 0) + { + glGenRenderbuffers(1, &color_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, color_render_buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, color_format, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_render_buffer); + } + else + { + glGenTextures(1, &color_tex_buffer); + glBindTexture(GL_TEXTURE_2D, color_tex_buffer); + glTexImage2D(GL_TEXTURE_2D, 0, color_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_mag_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, min_mag_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode); +#if !defined(RMLUI_PLATFORM_EMSCRIPTEN) && !defined(__ANDROID__) + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &border_color[0]); +#endif + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex_buffer, 0); + } + + // Create depth/stencil buffer storage attachment. + GLuint depth_stencil_buffer = 0; + if (attachment != FramebufferAttachment::None) + { + if (shared_depth_stencil_buffer) + { + // Share depth/stencil buffer + depth_stencil_buffer = shared_depth_stencil_buffer; + } + else + { + // Create new depth/stencil buffer + glGenRenderbuffers(1, &depth_stencil_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer); + + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8, width, height); + } + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depth_stencil_buffer); + } + + const GLuint framebuffer_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (framebuffer_status != GL_FRAMEBUFFER_COMPLETE) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL framebuffer could not be generated. Error code %x.", framebuffer_status); + return false; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + CheckGLError("CreateFramebuffer"); + + out_fb = {}; + out_fb.width = width; + out_fb.height = height; + out_fb.framebuffer = framebuffer; + out_fb.color_tex_buffer = color_tex_buffer; + out_fb.color_render_buffer = color_render_buffer; + out_fb.depth_stencil_buffer = depth_stencil_buffer; + out_fb.owns_depth_stencil_buffer = !shared_depth_stencil_buffer; + + return true; +} + +static void DestroyFramebuffer(FramebufferData& fb) +{ + if (fb.framebuffer) + glDeleteFramebuffers(1, &fb.framebuffer); + if (fb.color_tex_buffer) + glDeleteTextures(1, &fb.color_tex_buffer); + if (fb.color_render_buffer) + glDeleteRenderbuffers(1, &fb.color_render_buffer); + if (fb.owns_depth_stencil_buffer && fb.depth_stencil_buffer) + glDeleteRenderbuffers(1, &fb.depth_stencil_buffer); + fb = {}; +} + +static GLuint CreateTexture(Rml::Span source_data, Rml::Vector2i source_dimensions) +{ + GLuint texture_id = 0; + glGenTextures(1, &texture_id); + if (texture_id == 0) + return 0; + + glBindTexture(GL_TEXTURE_2D, texture_id); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, source_dimensions.x, source_dimensions.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, source_data.data()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBindTexture(GL_TEXTURE_2D, 0); + + return texture_id; +} + +static void BindTexture(const FramebufferData& fb) +{ + if (!fb.color_tex_buffer) + { + RMLUI_ERRORMSG("Only framebuffers with color textures can be bound as textures. This framebuffer probably uses multisampling which needs a " + "blit step first."); + } + + glBindTexture(GL_TEXTURE_2D, fb.color_tex_buffer); +} + +static bool CreateShaders(ProgramData& data) +{ + RMLUI_ASSERT(std::all_of(data.vert_shaders.begin(), data.vert_shaders.end(), [](auto&& value) { return value == 0; })); + RMLUI_ASSERT(std::all_of(data.frag_shaders.begin(), data.frag_shaders.end(), [](auto&& value) { return value == 0; })); + RMLUI_ASSERT(std::all_of(data.programs.begin(), data.programs.end(), [](auto&& value) { return value == 0; })); + auto ReportError = [](const char* type, const char* name) { + Rml::Log::Message(Rml::Log::LT_ERROR, "Could not create OpenGL %s: '%s'.", type, name); + return false; + }; + + for (const VertShaderDefinition& def : vert_shader_definitions) + { + if (!CreateShader(data.vert_shaders[def.id], GL_VERTEX_SHADER, def.code_str)) + return ReportError("vertex shader", def.name_str); + } + + for (const FragShaderDefinition& def : frag_shader_definitions) + { + if (!CreateShader(data.frag_shaders[def.id], GL_FRAGMENT_SHADER, def.code_str)) + return ReportError("fragment shader", def.name_str); + } + + for (const ProgramDefinition& def : program_definitions) + { + if (!CreateProgram(data.programs[def.id], data.uniforms, def.id, data.vert_shaders[def.vert_shader], data.frag_shaders[def.frag_shader])) + return ReportError("program", def.name_str); + } + + glUseProgram(data.programs[ProgramId::BlendMask]); + glUniform1i(data.uniforms.Get(ProgramId::BlendMask, UniformId::TexMask), 1); + + glUseProgram(0); + + return true; +} + +static void DestroyShaders(const ProgramData& data) +{ + for (GLuint id : data.programs) + glDeleteProgram(id); + + for (GLuint id : data.vert_shaders) + glDeleteShader(id); + + for (GLuint id : data.frag_shaders) + glDeleteShader(id); +} + +} // namespace Gfx + +RenderInterface_GL3::RenderInterface_GL3() +{ + auto mut_program_data = Rml::MakeUnique(); + if (Gfx::CreateShaders(*mut_program_data)) + { + program_data = std::move(mut_program_data); + Rml::Mesh mesh; + Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(-1), Rml::Vector2f(2), {}); + fullscreen_quad_geometry = RenderInterface_GL3::CompileGeometry(mesh.vertices, mesh.indices); + } +} + +RenderInterface_GL3::~RenderInterface_GL3() +{ + if (fullscreen_quad_geometry) + { + RenderInterface_GL3::ReleaseGeometry(fullscreen_quad_geometry); + fullscreen_quad_geometry = {}; + } + + if (program_data) + { + Gfx::DestroyShaders(*program_data); + program_data.reset(); + } +} + +void RenderInterface_GL3::SetViewport(int width, int height, int offset_x, int offset_y) +{ + viewport_width = Rml::Math::Max(width, 1); + viewport_height = Rml::Math::Max(height, 1); + viewport_offset_x = offset_x; + viewport_offset_y = offset_y; + projection = Rml::Matrix4f::ProjectOrtho(0, (float)viewport_width, (float)viewport_height, 0, -10000, 10000); +} + +void RenderInterface_GL3::BeginFrame() +{ + RMLUI_ASSERT(viewport_width >= 1 && viewport_height >= 1); + + // Backup GL state. + glstate_backup.enable_cull_face = glIsEnabled(GL_CULL_FACE); + glstate_backup.enable_blend = glIsEnabled(GL_BLEND); + glstate_backup.enable_stencil_test = glIsEnabled(GL_STENCIL_TEST); + glstate_backup.enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + glstate_backup.enable_depth_test = glIsEnabled(GL_DEPTH_TEST); + + glGetIntegerv(GL_VIEWPORT, glstate_backup.viewport); + glGetIntegerv(GL_SCISSOR_BOX, glstate_backup.scissor); + + glGetIntegerv(GL_ACTIVE_TEXTURE, &glstate_backup.active_texture); + + glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &glstate_backup.stencil_clear_value); + glGetFloatv(GL_COLOR_CLEAR_VALUE, glstate_backup.color_clear_value); + glGetBooleanv(GL_COLOR_WRITEMASK, glstate_backup.color_writemask); + + glGetIntegerv(GL_BLEND_EQUATION_RGB, &glstate_backup.blend_equation_rgb); + glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &glstate_backup.blend_equation_alpha); + glGetIntegerv(GL_BLEND_SRC_RGB, &glstate_backup.blend_src_rgb); + glGetIntegerv(GL_BLEND_DST_RGB, &glstate_backup.blend_dst_rgb); + glGetIntegerv(GL_BLEND_SRC_ALPHA, &glstate_backup.blend_src_alpha); + glGetIntegerv(GL_BLEND_DST_ALPHA, &glstate_backup.blend_dst_alpha); + + glGetIntegerv(GL_STENCIL_FUNC, &glstate_backup.stencil_front.func); + glGetIntegerv(GL_STENCIL_REF, &glstate_backup.stencil_front.ref); + glGetIntegerv(GL_STENCIL_VALUE_MASK, &glstate_backup.stencil_front.value_mask); + glGetIntegerv(GL_STENCIL_WRITEMASK, &glstate_backup.stencil_front.writemask); + glGetIntegerv(GL_STENCIL_FAIL, &glstate_backup.stencil_front.fail); + glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &glstate_backup.stencil_front.pass_depth_fail); + glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &glstate_backup.stencil_front.pass_depth_pass); + + glGetIntegerv(GL_STENCIL_BACK_FUNC, &glstate_backup.stencil_back.func); + glGetIntegerv(GL_STENCIL_BACK_REF, &glstate_backup.stencil_back.ref); + glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &glstate_backup.stencil_back.value_mask); + glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &glstate_backup.stencil_back.writemask); + glGetIntegerv(GL_STENCIL_BACK_FAIL, &glstate_backup.stencil_back.fail); + glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &glstate_backup.stencil_back.pass_depth_fail); + glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &glstate_backup.stencil_back.pass_depth_pass); + + // Setup expected GL state. + glViewport(0, 0, viewport_width, viewport_height); + + glClearStencil(0); + glClearColor(0, 0, 0, 0); + + glActiveTexture(GL_TEXTURE0); + + glDisable(GL_SCISSOR_TEST); + glDisable(GL_CULL_FACE); + + // Set blending function for premultiplied alpha. + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + +#if !defined(RMLUI_PLATFORM_EMSCRIPTEN) && !defined(__ANDROID__) + // We do blending in nonlinear sRGB space because that is the common practice and gives results that we are used to. + glDisable(GL_FRAMEBUFFER_SRGB); +#endif + + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 1, GLuint(-1)); + glStencilMask(GLuint(-1)); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + glDisable(GL_DEPTH_TEST); + + SetTransform(nullptr); + + render_layers.BeginFrame(viewport_width, viewport_height); + glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetTopLayer().framebuffer); + glClear(GL_COLOR_BUFFER_BIT); + + UseProgram(ProgramId::None); + program_transform_dirty.set(); + scissor_state = Rml::Rectanglei::MakeInvalid(); + + Gfx::CheckGLError("BeginFrame"); +} + +void RenderInterface_GL3::EndFrame() +{ + const Gfx::FramebufferData& fb_active = render_layers.GetTopLayer(); + const Gfx::FramebufferData& fb_postprocess = render_layers.GetPostprocessPrimary(); + + // Resolve MSAA to postprocess framebuffer. + glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_active.framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_postprocess.framebuffer); + + glBlitFramebuffer(0, 0, fb_active.width, fb_active.height, 0, 0, fb_postprocess.width, fb_postprocess.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + // Draw to backbuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(viewport_offset_x, viewport_offset_y, viewport_width, viewport_height); + + // Assuming we have an opaque background, we can just write to it with the premultiplied alpha blend mode and we'll get the correct result. + // Instead, if we had a transparent destination that didn't use premultiplied alpha, we would need to perform a manual un-premultiplication step. + glActiveTexture(GL_TEXTURE0); + Gfx::BindTexture(fb_postprocess); + UseProgram(ProgramId::Passthrough); + DrawFullscreenQuad(); + + render_layers.EndFrame(); + + // Restore GL state. + if (glstate_backup.enable_cull_face) + glEnable(GL_CULL_FACE); + else + glDisable(GL_CULL_FACE); + + if (glstate_backup.enable_blend) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + + if (glstate_backup.enable_stencil_test) + glEnable(GL_STENCIL_TEST); + else + glDisable(GL_STENCIL_TEST); + + if (glstate_backup.enable_scissor_test) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); + + if (glstate_backup.enable_depth_test) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + + glViewport(glstate_backup.viewport[0], glstate_backup.viewport[1], glstate_backup.viewport[2], glstate_backup.viewport[3]); + glScissor(glstate_backup.scissor[0], glstate_backup.scissor[1], glstate_backup.scissor[2], glstate_backup.scissor[3]); + + glActiveTexture(glstate_backup.active_texture); + + glClearStencil(glstate_backup.stencil_clear_value); + glClearColor(glstate_backup.color_clear_value[0], glstate_backup.color_clear_value[1], glstate_backup.color_clear_value[2], + glstate_backup.color_clear_value[3]); + glColorMask(glstate_backup.color_writemask[0], glstate_backup.color_writemask[1], glstate_backup.color_writemask[2], + glstate_backup.color_writemask[3]); + + glBlendEquationSeparate(glstate_backup.blend_equation_rgb, glstate_backup.blend_equation_alpha); + glBlendFuncSeparate(glstate_backup.blend_src_rgb, glstate_backup.blend_dst_rgb, glstate_backup.blend_src_alpha, glstate_backup.blend_dst_alpha); + + glStencilFuncSeparate(GL_FRONT, glstate_backup.stencil_front.func, glstate_backup.stencil_front.ref, glstate_backup.stencil_front.value_mask); + glStencilMaskSeparate(GL_FRONT, glstate_backup.stencil_front.writemask); + glStencilOpSeparate(GL_FRONT, glstate_backup.stencil_front.fail, glstate_backup.stencil_front.pass_depth_fail, + glstate_backup.stencil_front.pass_depth_pass); + + glStencilFuncSeparate(GL_BACK, glstate_backup.stencil_back.func, glstate_backup.stencil_back.ref, glstate_backup.stencil_back.value_mask); + glStencilMaskSeparate(GL_BACK, glstate_backup.stencil_back.writemask); + glStencilOpSeparate(GL_BACK, glstate_backup.stencil_back.fail, glstate_backup.stencil_back.pass_depth_fail, + glstate_backup.stencil_back.pass_depth_pass); + + Gfx::CheckGLError("EndFrame"); +} + +void RenderInterface_GL3::Clear() +{ + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); +} + +Rml::CompiledGeometryHandle RenderInterface_GL3::CompileGeometry(Rml::Span vertices, Rml::Span indices) +{ + constexpr GLenum draw_usage = GL_STATIC_DRAW; + + GLuint vao = 0; + GLuint vbo = 0; + GLuint ibo = 0; + + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + glGenBuffers(1, &ibo); + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(Rml::Vertex) * vertices.size(), (const void*)vertices.data(), draw_usage); + + glEnableVertexAttribArray((GLuint)Gfx::VertexAttribute::Position); + glVertexAttribPointer((GLuint)Gfx::VertexAttribute::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Rml::Vertex), + (const GLvoid*)(offsetof(Rml::Vertex, position))); + + glEnableVertexAttribArray((GLuint)Gfx::VertexAttribute::Color0); + glVertexAttribPointer((GLuint)Gfx::VertexAttribute::Color0, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Rml::Vertex), + (const GLvoid*)(offsetof(Rml::Vertex, colour))); + + glEnableVertexAttribArray((GLuint)Gfx::VertexAttribute::TexCoord0); + glVertexAttribPointer((GLuint)Gfx::VertexAttribute::TexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Rml::Vertex), + (const GLvoid*)(offsetof(Rml::Vertex, tex_coord))); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * indices.size(), (const void*)indices.data(), draw_usage); + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + Gfx::CheckGLError("CompileGeometry"); + + Gfx::CompiledGeometryData* geometry = new Gfx::CompiledGeometryData; + geometry->vao = vao; + geometry->vbo = vbo; + geometry->ibo = ibo; + geometry->draw_count = (GLsizei)indices.size(); + + return (Rml::CompiledGeometryHandle)geometry; +} + +void RenderInterface_GL3::RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) +{ + Gfx::CompiledGeometryData* geometry = (Gfx::CompiledGeometryData*)handle; + + if (texture == TexturePostprocess) + { + // Do nothing. + } + else if (texture) + { + UseProgram(ProgramId::Texture); + SubmitTransformUniform(translation); + if (texture != TextureEnableWithoutBinding) + glBindTexture(GL_TEXTURE_2D, (GLuint)texture); + } + else + { + UseProgram(ProgramId::Color); + glBindTexture(GL_TEXTURE_2D, 0); + SubmitTransformUniform(translation); + } + + glBindVertexArray(geometry->vao); + glDrawElements(GL_TRIANGLES, geometry->draw_count, GL_UNSIGNED_INT, (const GLvoid*)0); + + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); + + Gfx::CheckGLError("RenderCompiledGeometry"); +} + +void RenderInterface_GL3::ReleaseGeometry(Rml::CompiledGeometryHandle handle) +{ + Gfx::CompiledGeometryData* geometry = (Gfx::CompiledGeometryData*)handle; + + glDeleteVertexArrays(1, &geometry->vao); + glDeleteBuffers(1, &geometry->vbo); + glDeleteBuffers(1, &geometry->ibo); + + delete geometry; +} + +/// Flip the vertical axis of the rectangle, and move its origin to the vertically opposite side of the viewport. +/// @note Changes the coordinate system from RmlUi to OpenGL, or equivalently in reverse. +/// @note The Rectangle::Top and Rectangle::Bottom members will have reverse meaning in the returned rectangle. +static Rml::Rectanglei VerticallyFlipped(Rml::Rectanglei rect, int viewport_height) +{ + RMLUI_ASSERT(rect.Valid()); + Rml::Rectanglei flipped_rect = rect; + flipped_rect.p0.y = viewport_height - rect.p1.y; + flipped_rect.p1.y = viewport_height - rect.p0.y; + return flipped_rect; +} + +void RenderInterface_GL3::SetScissor(Rml::Rectanglei region, bool vertically_flip) +{ + if (region.Valid() != scissor_state.Valid()) + { + if (region.Valid()) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); + } + + if (region.Valid() && vertically_flip) + region = VerticallyFlipped(region, viewport_height); + + if (region.Valid() && region != scissor_state) + { + // Some render APIs don't like offscreen positions (WebGL in particular), so clamp them to the viewport. + const int x = Rml::Math::Clamp(region.Left(), 0, viewport_width); + const int y = Rml::Math::Clamp(viewport_height - region.Bottom(), 0, viewport_height); + + glScissor(x, y, region.Width(), region.Height()); + } + + Gfx::CheckGLError("SetScissorRegion"); + scissor_state = region; +} + +void RenderInterface_GL3::EnableScissorRegion(bool enable) +{ + // Assume enable is immediately followed by a SetScissorRegion() call, and ignore it here. + if (!enable) + SetScissor(Rml::Rectanglei::MakeInvalid(), false); +} + +void RenderInterface_GL3::SetScissorRegion(Rml::Rectanglei region) +{ + SetScissor(region); +} + +void RenderInterface_GL3::EnableClipMask(bool enable) +{ + if (enable) + glEnable(GL_STENCIL_TEST); + else + glDisable(GL_STENCIL_TEST); +} + +void RenderInterface_GL3::RenderToClipMask(Rml::ClipMaskOperation operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation) +{ + RMLUI_ASSERT(glIsEnabled(GL_STENCIL_TEST)); + using Rml::ClipMaskOperation; + + GLint stencil_write_value = 1; + GLint stencil_test_value = 1; + switch (operation) + { + case ClipMaskOperation::Set: + { + // @performance Increment the reference value instead of clearing each time. + glClear(GL_STENCIL_BUFFER_BIT); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + } + break; + case ClipMaskOperation::SetInverse: + { + glClearStencil(1); + glClear(GL_STENCIL_BUFFER_BIT); + glClearStencil(0); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + stencil_write_value = 0; + } + break; + case ClipMaskOperation::Intersect: + { + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + glGetIntegerv(GL_STENCIL_REF, &stencil_test_value); + stencil_test_value += 1; + } + break; + } + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glStencilFunc(GL_ALWAYS, stencil_write_value, GLuint(-1)); + + RenderGeometry(geometry, translation, {}); + + // Restore state + // @performance Cache state so we don't toggle it unnecessarily. + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, stencil_test_value, GLuint(-1)); +} + +// Set to byte packing, or the compiler will expand our struct, which means it won't read correctly from file +#pragma pack(1) +struct TGAHeader { + char idLength; + char colourMapType; + char dataType; + short int colourMapOrigin; + short int colourMapLength; + char colourMapDepth; + short int xOrigin; + short int yOrigin; + short int width; + short int height; + char bitsPerPixel; + char imageDescriptor; +}; +// Restore packing +#pragma pack() + +Rml::TextureHandle RenderInterface_GL3::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) +{ + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + { + return false; + } + + file_interface->Seek(file_handle, 0, SEEK_END); + size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + if (buffer_size <= sizeof(TGAHeader)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Texture file size is smaller than TGAHeader, file is not a valid TGA image."); + file_interface->Close(file_handle); + return false; + } + + using Rml::byte; + Rml::UniquePtr buffer(new byte[buffer_size]); + file_interface->Read(buffer.get(), buffer_size, file_handle); + file_interface->Close(file_handle); + + TGAHeader header; + memcpy(&header, buffer.get(), sizeof(TGAHeader)); + + int color_mode = header.bitsPerPixel / 8; + const size_t image_size = header.width * header.height * 4; // We always make 32bit textures + + if (header.dataType != 2) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24/32bit uncompressed TGAs are supported."); + return false; + } + + // Ensure we have at least 3 colors + if (color_mode < 3) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24 and 32bit textures are supported."); + return false; + } + + const byte* image_src = buffer.get() + sizeof(TGAHeader); + Rml::UniquePtr image_dest_buffer(new byte[image_size]); + byte* image_dest = image_dest_buffer.get(); + const bool top_to_bottom_order = ((header.imageDescriptor & 32) != 0); + + // Targa is BGR, swap to RGB, flip Y axis as necessary, and convert to premultiplied alpha. + for (long y = 0; y < header.height; y++) + { + long read_index = y * header.width * color_mode; + long write_index = top_to_bottom_order ? (y * header.width * 4) : (header.height - y - 1) * header.width * 4; + for (long x = 0; x < header.width; x++) + { + image_dest[write_index] = image_src[read_index + 2]; + image_dest[write_index + 1] = image_src[read_index + 1]; + image_dest[write_index + 2] = image_src[read_index]; + if (color_mode == 4) + { + const byte alpha = image_src[read_index + 3]; + for (size_t j = 0; j < 3; j++) + image_dest[write_index + j] = byte((image_dest[write_index + j] * alpha) / 255); + image_dest[write_index + 3] = alpha; + } + else + image_dest[write_index + 3] = 255; + + write_index += 4; + read_index += color_mode; + } + } + + texture_dimensions.x = header.width; + texture_dimensions.y = header.height; + + return GenerateTexture({image_dest, image_size}, texture_dimensions); +} + +Rml::TextureHandle RenderInterface_GL3::GenerateTexture(Rml::Span source_data, Rml::Vector2i source_dimensions) +{ + RMLUI_ASSERT(source_data.data() && source_data.size() == size_t(source_dimensions.x * source_dimensions.y * 4)); + + GLuint texture_id = Gfx::CreateTexture(source_data, source_dimensions); + if (texture_id == 0) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to generate texture."); + return {}; + } + return (Rml::TextureHandle)texture_id; +} + +void RenderInterface_GL3::DrawFullscreenQuad() +{ + RenderGeometry(fullscreen_quad_geometry, {}, RenderInterface_GL3::TexturePostprocess); +} + +void RenderInterface_GL3::DrawFullscreenQuad(Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling) +{ + Rml::Mesh mesh; + Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(-1), Rml::Vector2f(2), {}); + if (uv_offset != Rml::Vector2f() || uv_scaling != Rml::Vector2f(1.f)) + { + for (Rml::Vertex& vertex : mesh.vertices) + vertex.tex_coord = (vertex.tex_coord * uv_scaling) + uv_offset; + } + const Rml::CompiledGeometryHandle geometry = CompileGeometry(mesh.vertices, mesh.indices); + RenderGeometry(geometry, {}, RenderInterface_GL3::TexturePostprocess); + ReleaseGeometry(geometry); +} + +static Rml::Colourf ConvertToColorf(Rml::ColourbPremultiplied c0) +{ + Rml::Colourf result; + for (int i = 0; i < 4; i++) + result[i] = (1.f / 255.f) * float(c0[i]); + return result; +} + +static void SigmaToParameters(const float desired_sigma, int& out_pass_level, float& out_sigma) +{ + constexpr int max_num_passes = 10; + static_assert(max_num_passes < 31, ""); + constexpr float max_single_pass_sigma = 3.0f; + out_pass_level = Rml::Math::Clamp(Rml::Math::Log2(int(desired_sigma * (2.f / max_single_pass_sigma))), 0, max_num_passes); + out_sigma = Rml::Math::Clamp(desired_sigma / float(1 << out_pass_level), 0.0f, max_single_pass_sigma); +} + +static void SetTexCoordLimits(GLint tex_coord_min_location, GLint tex_coord_max_location, Rml::Rectanglei rectangle_flipped, + Rml::Vector2i framebuffer_size) +{ + // Offset by half-texel values so that texture lookups are clamped to fragment centers, thereby avoiding color + // bleeding from neighboring texels due to bilinear interpolation. + const Rml::Vector2f min = (Rml::Vector2f(rectangle_flipped.p0) + Rml::Vector2f(0.5f)) / Rml::Vector2f(framebuffer_size); + const Rml::Vector2f max = (Rml::Vector2f(rectangle_flipped.p1) - Rml::Vector2f(0.5f)) / Rml::Vector2f(framebuffer_size); + + glUniform2f(tex_coord_min_location, min.x, min.y); + glUniform2f(tex_coord_max_location, max.x, max.y); +} + +static void SetBlurWeights(GLint weights_location, float sigma) +{ + constexpr int num_weights = BLUR_NUM_WEIGHTS; + float weights[num_weights]; + float normalization = 0.0f; + for (int i = 0; i < num_weights; i++) + { + if (Rml::Math::Absolute(sigma) < 0.1f) + weights[i] = float(i == 0); + else + weights[i] = Rml::Math::Exp(-float(i * i) / (2.0f * sigma * sigma)) / (Rml::Math::SquareRoot(2.f * Rml::Math::RMLUI_PI) * sigma); + + normalization += (i == 0 ? 1.f : 2.0f) * weights[i]; + } + for (int i = 0; i < num_weights; i++) + weights[i] /= normalization; + + glUniform1fv(weights_location, (GLsizei)num_weights, &weights[0]); +} + +void RenderInterface_GL3::RenderBlur(float sigma, const Gfx::FramebufferData& source_destination, const Gfx::FramebufferData& temp, + const Rml::Rectanglei window_flipped) +{ + RMLUI_ASSERT(&source_destination != &temp && source_destination.width == temp.width && source_destination.height == temp.height); + RMLUI_ASSERT(window_flipped.Valid()); + + int pass_level = 0; + SigmaToParameters(sigma, pass_level, sigma); + + const Rml::Rectanglei original_scissor = scissor_state; + + // Begin by downscaling so that the blur pass can be done at a reduced resolution for large sigma. + Rml::Rectanglei scissor = window_flipped; + + UseProgram(ProgramId::Passthrough); + SetScissor(scissor, true); + + // Downscale by iterative half-scaling with bilinear filtering, to reduce aliasing. + glViewport(0, 0, source_destination.width / 2, source_destination.height / 2); + + // Scale UVs if we have even dimensions, such that texture fetches align perfectly between texels, thereby producing a 50% blend of + // neighboring texels. + const Rml::Vector2f uv_scaling = {(source_destination.width % 2 == 1) ? (1.f - 1.f / float(source_destination.width)) : 1.f, + (source_destination.height % 2 == 1) ? (1.f - 1.f / float(source_destination.height)) : 1.f}; + + for (int i = 0; i < pass_level; i++) + { + scissor.p0 = (scissor.p0 + Rml::Vector2i(1)) / 2; + scissor.p1 = Rml::Math::Max(scissor.p1 / 2, scissor.p0); + const bool from_source = (i % 2 == 0); + Gfx::BindTexture(from_source ? source_destination : temp); + glBindFramebuffer(GL_FRAMEBUFFER, (from_source ? temp : source_destination).framebuffer); + SetScissor(scissor, true); + + DrawFullscreenQuad({}, uv_scaling); + } + + glViewport(0, 0, source_destination.width, source_destination.height); + + // Ensure texture data end up in the temp buffer. Depending on the last downscaling, we might need to move it from the source_destination buffer. + const bool transfer_to_temp_buffer = (pass_level % 2 == 0); + if (transfer_to_temp_buffer) + { + Gfx::BindTexture(source_destination); + glBindFramebuffer(GL_FRAMEBUFFER, temp.framebuffer); + DrawFullscreenQuad(); + } + + // Set up uniforms. + UseProgram(ProgramId::Blur); + SetBlurWeights(GetUniformLocation(UniformId::Weights), sigma); + SetTexCoordLimits(GetUniformLocation(UniformId::TexCoordMin), GetUniformLocation(UniformId::TexCoordMax), scissor, + {source_destination.width, source_destination.height}); + + const GLint texel_offset_location = GetUniformLocation(UniformId::TexelOffset); + auto SetTexelOffset = [texel_offset_location](Rml::Vector2f blur_direction, int texture_dimension) { + const Rml::Vector2f texel_offset = blur_direction * (1.0f / float(texture_dimension)); + glUniform2f(texel_offset_location, texel_offset.x, texel_offset.y); + }; + + // Blur render pass - vertical. + Gfx::BindTexture(temp); + glBindFramebuffer(GL_FRAMEBUFFER, source_destination.framebuffer); + + SetTexelOffset({0.f, 1.f}, temp.height); + DrawFullscreenQuad(); + + // Blur render pass - horizontal. + Gfx::BindTexture(source_destination); + glBindFramebuffer(GL_FRAMEBUFFER, temp.framebuffer); + + // Add a 1px transparent border around the blur region by first clearing with a padded scissor. This helps prevent + // artifacts when upscaling the blur result in the later step. On Intel and AMD, we have observed that during + // blitting with linear filtering, pixels outside the 'src' region can be blended into the output. On the other + // hand, it looks like Nvidia clamps the pixels to the source edge, which is what we really want. Regardless, we + // work around the issue with this extra step. + SetScissor(scissor.Extend(1), true); + glClear(GL_COLOR_BUFFER_BIT); + SetScissor(scissor, true); + + SetTexelOffset({1.f, 0.f}, source_destination.width); + DrawFullscreenQuad(); + + // Blit the blurred image to the scissor region with upscaling. + SetScissor(window_flipped, true); + glBindFramebuffer(GL_READ_FRAMEBUFFER, temp.framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, source_destination.framebuffer); + + const Rml::Vector2i src_min = scissor.p0; + const Rml::Vector2i src_max = scissor.p1; + const Rml::Vector2i dst_min = window_flipped.p0; + const Rml::Vector2i dst_max = window_flipped.p1; + glBlitFramebuffer(src_min.x, src_min.y, src_max.x, src_max.y, dst_min.x, dst_min.y, dst_max.x, dst_max.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + // The above upscale blit might be jittery at low resolutions (large pass levels). This is especially noticeable when moving an element with + // backdrop blur around or when trying to click/hover an element within a blurred region since it may be rendered at an offset. For more stable + // and accurate rendering we next upscale the blur image by an exact power-of-two. However, this may not fill the edges completely so we need to + // do the above first. Note that this strategy may sometimes result in visible seams. Alternatively, we could try to enlarge the window to the + // next power-of-two size and then downsample and blur that. + const Rml::Vector2i target_min = src_min * (1 << pass_level); + const Rml::Vector2i target_max = src_max * (1 << pass_level); + if (target_min != dst_min || target_max != dst_max) + { + glBlitFramebuffer(src_min.x, src_min.y, src_max.x, src_max.y, target_min.x, target_min.y, target_max.x, target_max.y, GL_COLOR_BUFFER_BIT, + GL_LINEAR); + } + + // Restore render state. + SetScissor(original_scissor); + + Gfx::CheckGLError("Blur"); +} + +void RenderInterface_GL3::ReleaseTexture(Rml::TextureHandle texture_handle) +{ + glDeleteTextures(1, (GLuint*)&texture_handle); +} + +void RenderInterface_GL3::SetTransform(const Rml::Matrix4f* new_transform) +{ + transform = (new_transform ? (projection * (*new_transform)) : projection); + program_transform_dirty.set(); +} + +enum class FilterType { Invalid = 0, Passthrough, Blur, DropShadow, ColorMatrix, MaskImage }; +struct CompiledFilter { + FilterType type; + + // Passthrough + float blend_factor; + + // Blur + float sigma; + + // Drop shadow + Rml::Vector2f offset; + Rml::ColourbPremultiplied color; + + // ColorMatrix + Rml::Matrix4f color_matrix; +}; + +Rml::CompiledFilterHandle RenderInterface_GL3::CompileFilter(const Rml::String& name, const Rml::Dictionary& parameters) +{ + CompiledFilter filter = {}; + + if (name == "opacity") + { + filter.type = FilterType::Passthrough; + filter.blend_factor = Rml::Get(parameters, "value", 1.0f); + } + else if (name == "blur") + { + filter.type = FilterType::Blur; + filter.sigma = Rml::Get(parameters, "sigma", 1.0f); + } + else if (name == "drop-shadow") + { + filter.type = FilterType::DropShadow; + filter.sigma = Rml::Get(parameters, "sigma", 0.f); + filter.color = Rml::Get(parameters, "color", Rml::Colourb()).ToPremultiplied(); + filter.offset = Rml::Get(parameters, "offset", Rml::Vector2f(0.f)); + } + else if (name == "brightness") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + filter.color_matrix = Rml::Matrix4f::Diag(value, value, value, 1.f); + } + else if (name == "contrast") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + const float grayness = 0.5f - 0.5f * value; + filter.color_matrix = Rml::Matrix4f::Diag(value, value, value, 1.f); + filter.color_matrix.SetColumn(3, Rml::Vector4f(grayness, grayness, grayness, 1.f)); + } + else if (name == "invert") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Math::Clamp(Rml::Get(parameters, "value", 1.0f), 0.f, 1.f); + const float inverted = 1.f - 2.f * value; + filter.color_matrix = Rml::Matrix4f::Diag(inverted, inverted, inverted, 1.f); + filter.color_matrix.SetColumn(3, Rml::Vector4f(value, value, value, 1.f)); + } + else if (name == "grayscale") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + const float rev_value = 1.f - value; + const Rml::Vector3f gray = value * Rml::Vector3f(0.2126f, 0.7152f, 0.0722f); + // clang-format off + filter.color_matrix = Rml::Matrix4f::FromRows( + {gray.x + rev_value, gray.y, gray.z, 0.f}, + {gray.x, gray.y + rev_value, gray.z, 0.f}, + {gray.x, gray.y, gray.z + rev_value, 0.f}, + {0.f, 0.f, 0.f, 1.f} + ); + // clang-format on + } + else if (name == "sepia") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + const float rev_value = 1.f - value; + const Rml::Vector3f r_mix = value * Rml::Vector3f(0.393f, 0.769f, 0.189f); + const Rml::Vector3f g_mix = value * Rml::Vector3f(0.349f, 0.686f, 0.168f); + const Rml::Vector3f b_mix = value * Rml::Vector3f(0.272f, 0.534f, 0.131f); + // clang-format off + filter.color_matrix = Rml::Matrix4f::FromRows( + {r_mix.x + rev_value, r_mix.y, r_mix.z, 0.f}, + {g_mix.x, g_mix.y + rev_value, g_mix.z, 0.f}, + {b_mix.x, b_mix.y, b_mix.z + rev_value, 0.f}, + {0.f, 0.f, 0.f, 1.f} + ); + // clang-format on + } + else if (name == "hue-rotate") + { + // Hue-rotation and saturation values based on: https://www.w3.org/TR/filter-effects-1/#attr-valuedef-type-huerotate + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + const float s = Rml::Math::Sin(value); + const float c = Rml::Math::Cos(value); + // clang-format off + filter.color_matrix = Rml::Matrix4f::FromRows( + {0.213f + 0.787f * c - 0.213f * s, 0.715f - 0.715f * c - 0.715f * s, 0.072f - 0.072f * c + 0.928f * s, 0.f}, + {0.213f - 0.213f * c + 0.143f * s, 0.715f + 0.285f * c + 0.140f * s, 0.072f - 0.072f * c - 0.283f * s, 0.f}, + {0.213f - 0.213f * c - 0.787f * s, 0.715f - 0.715f * c + 0.715f * s, 0.072f + 0.928f * c + 0.072f * s, 0.f}, + {0.f, 0.f, 0.f, 1.f} + ); + // clang-format on + } + else if (name == "saturate") + { + filter.type = FilterType::ColorMatrix; + const float value = Rml::Get(parameters, "value", 1.0f); + // clang-format off + filter.color_matrix = Rml::Matrix4f::FromRows( + {0.213f + 0.787f * value, 0.715f - 0.715f * value, 0.072f - 0.072f * value, 0.f}, + {0.213f - 0.213f * value, 0.715f + 0.285f * value, 0.072f - 0.072f * value, 0.f}, + {0.213f - 0.213f * value, 0.715f - 0.715f * value, 0.072f + 0.928f * value, 0.f}, + {0.f, 0.f, 0.f, 1.f} + ); + // clang-format on + } + + if (filter.type != FilterType::Invalid) + return reinterpret_cast(new CompiledFilter(std::move(filter))); + + Rml::Log::Message(Rml::Log::LT_WARNING, "Unsupported filter type '%s'.", name.c_str()); + return {}; +} + +void RenderInterface_GL3::ReleaseFilter(Rml::CompiledFilterHandle filter) +{ + delete reinterpret_cast(filter); +} + +enum class CompiledShaderType { Invalid = 0, Gradient, Creation }; +struct CompiledShader { + CompiledShaderType type; + + // Gradient + ShaderGradientFunction gradient_function; + Rml::Vector2f p; + Rml::Vector2f v; + Rml::Vector stop_positions; + Rml::Vector stop_colors; + + // Shader + Rml::Vector2f dimensions; +}; + +Rml::CompiledShaderHandle RenderInterface_GL3::CompileShader(const Rml::String& name, const Rml::Dictionary& parameters) +{ + auto ApplyColorStopList = [](CompiledShader& shader, const Rml::Dictionary& shader_parameters) { + auto it = shader_parameters.find("color_stop_list"); + RMLUI_ASSERT(it != shader_parameters.end() && it->second.GetType() == Rml::Variant::COLORSTOPLIST); + const Rml::ColorStopList& color_stop_list = it->second.GetReference(); + const int num_stops = Rml::Math::Min((int)color_stop_list.size(), MAX_NUM_STOPS); + + shader.stop_positions.resize(num_stops); + shader.stop_colors.resize(num_stops); + for (int i = 0; i < num_stops; i++) + { + const Rml::ColorStop& stop = color_stop_list[i]; + RMLUI_ASSERT(stop.position.unit == Rml::Unit::NUMBER); + shader.stop_positions[i] = stop.position.number; + shader.stop_colors[i] = ConvertToColorf(stop.color); + } + }; + + CompiledShader shader = {}; + + if (name == "linear-gradient") + { + shader.type = CompiledShaderType::Gradient; + const bool repeating = Rml::Get(parameters, "repeating", false); + shader.gradient_function = (repeating ? ShaderGradientFunction::RepeatingLinear : ShaderGradientFunction::Linear); + shader.p = Rml::Get(parameters, "p0", Rml::Vector2f(0.f)); + shader.v = Rml::Get(parameters, "p1", Rml::Vector2f(0.f)) - shader.p; + ApplyColorStopList(shader, parameters); + } + else if (name == "radial-gradient") + { + shader.type = CompiledShaderType::Gradient; + const bool repeating = Rml::Get(parameters, "repeating", false); + shader.gradient_function = (repeating ? ShaderGradientFunction::RepeatingRadial : ShaderGradientFunction::Radial); + shader.p = Rml::Get(parameters, "center", Rml::Vector2f(0.f)); + shader.v = Rml::Vector2f(1.f) / Rml::Get(parameters, "radius", Rml::Vector2f(1.f)); + ApplyColorStopList(shader, parameters); + } + else if (name == "conic-gradient") + { + shader.type = CompiledShaderType::Gradient; + const bool repeating = Rml::Get(parameters, "repeating", false); + shader.gradient_function = (repeating ? ShaderGradientFunction::RepeatingConic : ShaderGradientFunction::Conic); + shader.p = Rml::Get(parameters, "center", Rml::Vector2f(0.f)); + const float angle = Rml::Get(parameters, "angle", 0.f); + shader.v = {Rml::Math::Cos(angle), Rml::Math::Sin(angle)}; + ApplyColorStopList(shader, parameters); + } + else if (name == "shader") + { + const Rml::String value = Rml::Get(parameters, "value", Rml::String()); + if (value == "creation") + { + shader.type = CompiledShaderType::Creation; + shader.dimensions = Rml::Get(parameters, "dimensions", Rml::Vector2f(0.f)); + } + } + + if (shader.type != CompiledShaderType::Invalid) + return reinterpret_cast(new CompiledShader(std::move(shader))); + + Rml::Log::Message(Rml::Log::LT_WARNING, "Unsupported shader type '%s'.", name.c_str()); + return {}; +} + +void RenderInterface_GL3::RenderShader(Rml::CompiledShaderHandle shader_handle, Rml::CompiledGeometryHandle geometry_handle, + Rml::Vector2f translation, Rml::TextureHandle /*texture*/) +{ + RMLUI_ASSERT(shader_handle && geometry_handle); + const CompiledShader& shader = *reinterpret_cast(shader_handle); + const CompiledShaderType type = shader.type; + const Gfx::CompiledGeometryData& geometry = *reinterpret_cast(geometry_handle); + + switch (type) + { + case CompiledShaderType::Gradient: + { + RMLUI_ASSERT(shader.stop_positions.size() == shader.stop_colors.size()); + const int num_stops = (int)shader.stop_positions.size(); + + UseProgram(ProgramId::Gradient); + glUniform1i(GetUniformLocation(UniformId::Func), static_cast(shader.gradient_function)); + glUniform2f(GetUniformLocation(UniformId::P), shader.p.x, shader.p.y); + glUniform2f(GetUniformLocation(UniformId::V), shader.v.x, shader.v.y); + glUniform1i(GetUniformLocation(UniformId::NumStops), num_stops); + glUniform1fv(GetUniformLocation(UniformId::StopPositions), num_stops, shader.stop_positions.data()); + glUniform4fv(GetUniformLocation(UniformId::StopColors), num_stops, shader.stop_colors[0]); + + SubmitTransformUniform(translation); + glBindVertexArray(geometry.vao); + glDrawElements(GL_TRIANGLES, geometry.draw_count, GL_UNSIGNED_INT, (const GLvoid*)0); + glBindVertexArray(0); + } + break; + case CompiledShaderType::Creation: + { + const double time = Rml::GetSystemInterface()->GetElapsedTime(); + + UseProgram(ProgramId::Creation); + glUniform1f(GetUniformLocation(UniformId::Value), (float)time); + glUniform2f(GetUniformLocation(UniformId::Dimensions), shader.dimensions.x, shader.dimensions.y); + + SubmitTransformUniform(translation); + glBindVertexArray(geometry.vao); + glDrawElements(GL_TRIANGLES, geometry.draw_count, GL_UNSIGNED_INT, (const GLvoid*)0); + glBindVertexArray(0); + } + break; + case CompiledShaderType::Invalid: + { + Rml::Log::Message(Rml::Log::LT_WARNING, "Unhandled render shader %d.", (int)type); + } + break; + } + + Gfx::CheckGLError("RenderShader"); +} + +void RenderInterface_GL3::ReleaseShader(Rml::CompiledShaderHandle shader_handle) +{ + delete reinterpret_cast(shader_handle); +} + +void RenderInterface_GL3::BlitLayerToPostprocessPrimary(Rml::LayerHandle layer_handle) +{ + const Gfx::FramebufferData& source = render_layers.GetLayer(layer_handle); + const Gfx::FramebufferData& destination = render_layers.GetPostprocessPrimary(); + glBindFramebuffer(GL_READ_FRAMEBUFFER, source.framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination.framebuffer); + + // Blit and resolve MSAA. Any active scissor state will restrict the size of the blit region. + glBlitFramebuffer(0, 0, source.width, source.height, 0, 0, destination.width, destination.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); +} + +void RenderInterface_GL3::RenderFilters(Rml::Span filter_handles) +{ + for (const Rml::CompiledFilterHandle filter_handle : filter_handles) + { + const CompiledFilter& filter = *reinterpret_cast(filter_handle); + const FilterType type = filter.type; + + switch (type) + { + case FilterType::Passthrough: + { + UseProgram(ProgramId::Passthrough); + glBlendFunc(GL_CONSTANT_COLOR, GL_ZERO); + glBlendColor(filter.blend_factor, filter.blend_factor, filter.blend_factor, filter.blend_factor); + + const Gfx::FramebufferData& source = render_layers.GetPostprocessPrimary(); + const Gfx::FramebufferData& destination = render_layers.GetPostprocessSecondary(); + Gfx::BindTexture(source); + glBindFramebuffer(GL_FRAMEBUFFER, destination.framebuffer); + + DrawFullscreenQuad(); + + render_layers.SwapPostprocessPrimarySecondary(); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + break; + case FilterType::Blur: + { + glDisable(GL_BLEND); + + const Gfx::FramebufferData& source_destination = render_layers.GetPostprocessPrimary(); + const Gfx::FramebufferData& temp = render_layers.GetPostprocessSecondary(); + + const Rml::Rectanglei window_flipped = VerticallyFlipped(scissor_state, viewport_height); + RenderBlur(filter.sigma, source_destination, temp, window_flipped); + + glEnable(GL_BLEND); + } + break; + case FilterType::DropShadow: + { + UseProgram(ProgramId::DropShadow); + glDisable(GL_BLEND); + + Rml::Colourf color = ConvertToColorf(filter.color); + glUniform4fv(GetUniformLocation(UniformId::Color), 1, &color[0]); + + const Gfx::FramebufferData& primary = render_layers.GetPostprocessPrimary(); + const Gfx::FramebufferData& secondary = render_layers.GetPostprocessSecondary(); + Gfx::BindTexture(primary); + glBindFramebuffer(GL_FRAMEBUFFER, secondary.framebuffer); + + const Rml::Rectanglei window_flipped = VerticallyFlipped(scissor_state, viewport_height); + SetTexCoordLimits(GetUniformLocation(UniformId::TexCoordMin), GetUniformLocation(UniformId::TexCoordMax), window_flipped, + {primary.width, primary.height}); + + const Rml::Vector2f uv_offset = filter.offset / Rml::Vector2f(-(float)viewport_width, (float)viewport_height); + DrawFullscreenQuad(uv_offset); + + if (filter.sigma >= 0.5f) + { + const Gfx::FramebufferData& tertiary = render_layers.GetPostprocessTertiary(); + RenderBlur(filter.sigma, secondary, tertiary, window_flipped); + } + + UseProgram(ProgramId::Passthrough); + BindTexture(primary); + glEnable(GL_BLEND); + DrawFullscreenQuad(); + + render_layers.SwapPostprocessPrimarySecondary(); + } + break; + case FilterType::ColorMatrix: + { + UseProgram(ProgramId::ColorMatrix); + glDisable(GL_BLEND); + + const GLint uniform_location = program_data->uniforms.Get(ProgramId::ColorMatrix, UniformId::ColorMatrix); + constexpr bool transpose = std::is_same_v; + glUniformMatrix4fv(uniform_location, 1, transpose, filter.color_matrix.data()); + + const Gfx::FramebufferData& source = render_layers.GetPostprocessPrimary(); + const Gfx::FramebufferData& destination = render_layers.GetPostprocessSecondary(); + Gfx::BindTexture(source); + glBindFramebuffer(GL_FRAMEBUFFER, destination.framebuffer); + + DrawFullscreenQuad(); + + render_layers.SwapPostprocessPrimarySecondary(); + glEnable(GL_BLEND); + } + break; + case FilterType::MaskImage: + { + UseProgram(ProgramId::BlendMask); + glDisable(GL_BLEND); + + const Gfx::FramebufferData& source = render_layers.GetPostprocessPrimary(); + const Gfx::FramebufferData& blend_mask = render_layers.GetBlendMask(); + const Gfx::FramebufferData& destination = render_layers.GetPostprocessSecondary(); + + Gfx::BindTexture(source); + glActiveTexture(GL_TEXTURE1); + Gfx::BindTexture(blend_mask); + glActiveTexture(GL_TEXTURE0); + + glBindFramebuffer(GL_FRAMEBUFFER, destination.framebuffer); + + DrawFullscreenQuad(); + + render_layers.SwapPostprocessPrimarySecondary(); + glEnable(GL_BLEND); + } + break; + case FilterType::Invalid: + { + Rml::Log::Message(Rml::Log::LT_WARNING, "Unhandled render filter %d.", (int)type); + } + break; + } + } + + Gfx::CheckGLError("RenderFilter"); +} + +Rml::LayerHandle RenderInterface_GL3::PushLayer() +{ + const Rml::LayerHandle layer_handle = render_layers.PushLayer(); + + glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetLayer(layer_handle).framebuffer); + glClear(GL_COLOR_BUFFER_BIT); + + return layer_handle; +} + +void RenderInterface_GL3::CompositeLayers(Rml::LayerHandle source_handle, Rml::LayerHandle destination_handle, Rml::BlendMode blend_mode, + Rml::Span filters) +{ + using Rml::BlendMode; + + // Blit source layer to postprocessing buffer. Do this regardless of whether we actually have any filters to be + // applied, because we need to resolve the multi-sampled framebuffer in any case. + // @performance If we have BlendMode::Replace and no filters or mask then we can just blit directly to the destination. + BlitLayerToPostprocessPrimary(source_handle); + + // Render the filters, the PostprocessPrimary framebuffer is used for both input and output. + RenderFilters(filters); + + // Render to the destination layer. + glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetLayer(destination_handle).framebuffer); + Gfx::BindTexture(render_layers.GetPostprocessPrimary()); + + UseProgram(ProgramId::Passthrough); + + if (blend_mode == BlendMode::Replace) + glDisable(GL_BLEND); + + DrawFullscreenQuad(); + + if (blend_mode == BlendMode::Replace) + glEnable(GL_BLEND); + + if (destination_handle != render_layers.GetTopLayerHandle()) + glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetTopLayer().framebuffer); + + Gfx::CheckGLError("CompositeLayers"); +} + +void RenderInterface_GL3::PopLayer() +{ + render_layers.PopLayer(); + glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetTopLayer().framebuffer); +} + +Rml::TextureHandle RenderInterface_GL3::SaveLayerAsTexture() +{ + RMLUI_ASSERT(scissor_state.Valid()); + const Rml::Rectanglei bounds = scissor_state; + + GLuint render_texture = Gfx::CreateTexture({}, bounds.Size()); + if (render_texture == 0) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to create render texture."); + return {}; + } + + BlitLayerToPostprocessPrimary(render_layers.GetTopLayerHandle()); + + EnableScissorRegion(false); + + const Gfx::FramebufferData& source = render_layers.GetPostprocessPrimary(); + const Gfx::FramebufferData& destination = render_layers.GetPostprocessSecondary(); + glBindFramebuffer(GL_READ_FRAMEBUFFER, source.framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination.framebuffer); + + // Flip the image vertically, as that convention is used for textures, and move to origin. + glBlitFramebuffer( // + bounds.Left(), source.height - bounds.Bottom(), // src0 + bounds.Right(), source.height - bounds.Top(), // src1 + 0, bounds.Height(), // dst0 + bounds.Width(), 0, // dst1 + GL_COLOR_BUFFER_BIT, GL_NEAREST // + ); + + glBindTexture(GL_TEXTURE_2D, render_texture); + + const Gfx::FramebufferData& texture_source = destination; + glBindFramebuffer(GL_READ_FRAMEBUFFER, texture_source.framebuffer); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, bounds.Width(), bounds.Height()); + + SetScissor(bounds); + glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetTopLayer().framebuffer); + Gfx::CheckGLError("SaveLayerAsTexture"); + + return (Rml::TextureHandle)render_texture; +} + +Rml::CompiledFilterHandle RenderInterface_GL3::SaveLayerAsMaskImage() +{ + BlitLayerToPostprocessPrimary(render_layers.GetTopLayerHandle()); + + const Gfx::FramebufferData& source = render_layers.GetPostprocessPrimary(); + const Gfx::FramebufferData& destination = render_layers.GetBlendMask(); + + glBindFramebuffer(GL_FRAMEBUFFER, destination.framebuffer); + BindTexture(source); + UseProgram(ProgramId::Passthrough); + glDisable(GL_BLEND); + + DrawFullscreenQuad(); + + glEnable(GL_BLEND); + glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetTopLayer().framebuffer); + Gfx::CheckGLError("SaveLayerAsMaskImage"); + + CompiledFilter filter = {}; + filter.type = FilterType::MaskImage; + return reinterpret_cast(new CompiledFilter(std::move(filter))); +} + +void RenderInterface_GL3::UseProgram(ProgramId program_id) +{ + RMLUI_ASSERT(program_data); + if (active_program != program_id) + { + if (program_id != ProgramId::None) + glUseProgram(program_data->programs[program_id]); + active_program = program_id; + } +} + +int RenderInterface_GL3::GetUniformLocation(UniformId uniform_id) const +{ + return program_data->uniforms.Get(active_program, uniform_id); +} + +void RenderInterface_GL3::SubmitTransformUniform(Rml::Vector2f translation) +{ + static_assert((size_t)ProgramId::Count < MaxNumPrograms, "Maximum number of programs exceeded."); + const size_t program_index = (size_t)active_program; + + if (program_transform_dirty.test(program_index)) + { + glUniformMatrix4fv(GetUniformLocation(UniformId::Transform), 1, false, transform.data()); + program_transform_dirty.set(program_index, false); + } + + glUniform2fv(GetUniformLocation(UniformId::Translate), 1, &translation.x); + + Gfx::CheckGLError("SubmitTransformUniform"); +} + +RenderInterface_GL3::RenderLayerStack::RenderLayerStack() +{ + fb_postprocess.resize(4); +} + +RenderInterface_GL3::RenderLayerStack::~RenderLayerStack() +{ + DestroyFramebuffers(); +} + +Rml::LayerHandle RenderInterface_GL3::RenderLayerStack::PushLayer() +{ + RMLUI_ASSERT(layers_size <= (int)fb_layers.size()); + + if (layers_size == (int)fb_layers.size()) + { + // All framebuffers should share a single stencil buffer. + GLuint shared_depth_stencil = (fb_layers.empty() ? 0 : fb_layers.front().depth_stencil_buffer); + + fb_layers.push_back(Gfx::FramebufferData{}); + Gfx::CreateFramebuffer(fb_layers.back(), width, height, RMLUI_NUM_MSAA_SAMPLES, Gfx::FramebufferAttachment::DepthStencil, + shared_depth_stencil); + } + + layers_size += 1; + return GetTopLayerHandle(); +} + +void RenderInterface_GL3::RenderLayerStack::PopLayer() +{ + RMLUI_ASSERT(layers_size > 0); + layers_size -= 1; +} + +const Gfx::FramebufferData& RenderInterface_GL3::RenderLayerStack::GetLayer(Rml::LayerHandle layer) const +{ + RMLUI_ASSERT((size_t)layer < (size_t)layers_size); + return fb_layers[layer]; +} + +const Gfx::FramebufferData& RenderInterface_GL3::RenderLayerStack::GetTopLayer() const +{ + return GetLayer(GetTopLayerHandle()); +} + +Rml::LayerHandle RenderInterface_GL3::RenderLayerStack::GetTopLayerHandle() const +{ + RMLUI_ASSERT(layers_size > 0); + return static_cast(layers_size - 1); +} + +void RenderInterface_GL3::RenderLayerStack::SwapPostprocessPrimarySecondary() +{ + std::swap(fb_postprocess[0], fb_postprocess[1]); +} + +void RenderInterface_GL3::RenderLayerStack::BeginFrame(int new_width, int new_height) +{ + RMLUI_ASSERT(layers_size == 0); + + if (new_width != width || new_height != height) + { + width = new_width; + height = new_height; + + DestroyFramebuffers(); + } + + PushLayer(); +} + +void RenderInterface_GL3::RenderLayerStack::EndFrame() +{ + RMLUI_ASSERT(layers_size == 1); + PopLayer(); +} + +void RenderInterface_GL3::RenderLayerStack::DestroyFramebuffers() +{ + RMLUI_ASSERTMSG(layers_size == 0, "Do not call this during frame rendering, that is, between BeginFrame() and EndFrame()."); + + for (Gfx::FramebufferData& fb : fb_layers) + Gfx::DestroyFramebuffer(fb); + + fb_layers.clear(); + + for (Gfx::FramebufferData& fb : fb_postprocess) + Gfx::DestroyFramebuffer(fb); +} + +const Gfx::FramebufferData& RenderInterface_GL3::RenderLayerStack::EnsureFramebufferPostprocess(int index) +{ + RMLUI_ASSERT(index < (int)fb_postprocess.size()) + Gfx::FramebufferData& fb = fb_postprocess[index]; + if (!fb.framebuffer) + Gfx::CreateFramebuffer(fb, width, height, 0, Gfx::FramebufferAttachment::None, 0); + return fb; +} + +const Rml::Matrix4f& RenderInterface_GL3::GetTransform() const +{ + return transform; +} + +void RenderInterface_GL3::ResetProgram() +{ + UseProgram(ProgramId::None); +} + +bool RmlGL3::Initialize(Rml::String* out_message) +{ +#if defined(RMLUI_PLATFORM_EMSCRIPTEN) + if (out_message) + *out_message = "Started Emscripten WebGL renderer."; +#elif defined(__ANDROID__) + if (out_message) + *out_message = "Started OpenGL ES 3 renderer."; +#elif !defined RMLUI_GL3_CUSTOM_LOADER + const int gl_version = gladLoaderLoadGL(); + if (gl_version == 0) + { + if (out_message) + *out_message = "Failed to initialize OpenGL context."; + return false; + } + + if (out_message) + *out_message = Rml::CreateString("Loaded OpenGL %d.%d.", GLAD_VERSION_MAJOR(gl_version), GLAD_VERSION_MINOR(gl_version)); +#endif + + return true; +} + +void RmlGL3::Shutdown() +{ +#if !defined(RMLUI_PLATFORM_EMSCRIPTEN) && !defined(__ANDROID__) && !defined(RMLUI_GL3_CUSTOM_LOADER) + gladLoaderUnloadGL(); +#endif +} diff --git a/vendor/rmlui_backend/RmlUi_Renderer_GL3.h b/vendor/rmlui_backend/RmlUi_Renderer_GL3.h new file mode 100644 index 0000000..059d846 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_GL3.h @@ -0,0 +1,210 @@ +#pragma once + +#include +#include +#include + +enum class ProgramId; +enum class UniformId; +class RenderLayerStack; +namespace Gfx { +struct ProgramData; +struct FramebufferData; +} // namespace Gfx + +class RenderInterface_GL3 : public Rml::RenderInterface { +public: + RenderInterface_GL3(); + ~RenderInterface_GL3(); + + // Returns true if the renderer was successfully constructed. + explicit operator bool() const { return static_cast(program_data); } + + // The viewport should be updated whenever the window size changes. + void SetViewport(int viewport_width, int viewport_height, int viewport_offset_x = 0, int viewport_offset_y = 0); + + // Sets up OpenGL states for taking rendering commands from RmlUi. + void BeginFrame(); + // Draws the result to the backbuffer and restores OpenGL state. + void EndFrame(); + + // Optional, can be used to clear the active framebuffer. + void Clear(); + + // -- Inherited from Rml::RenderInterface -- + + Rml::CompiledGeometryHandle CompileGeometry(Rml::Span vertices, Rml::Span indices) override; + void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override; + void ReleaseGeometry(Rml::CompiledGeometryHandle handle) override; + + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override; + Rml::TextureHandle GenerateTexture(Rml::Span source_data, Rml::Vector2i source_dimensions) override; + void ReleaseTexture(Rml::TextureHandle texture_handle) override; + + void EnableScissorRegion(bool enable) override; + void SetScissorRegion(Rml::Rectanglei region) override; + + void EnableClipMask(bool enable) override; + void RenderToClipMask(Rml::ClipMaskOperation mask_operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation) override; + + void SetTransform(const Rml::Matrix4f* transform) override; + + Rml::LayerHandle PushLayer() override; + void CompositeLayers(Rml::LayerHandle source, Rml::LayerHandle destination, Rml::BlendMode blend_mode, + Rml::Span filters) override; + void PopLayer() override; + + Rml::TextureHandle SaveLayerAsTexture() override; + + Rml::CompiledFilterHandle SaveLayerAsMaskImage() override; + + Rml::CompiledFilterHandle CompileFilter(const Rml::String& name, const Rml::Dictionary& parameters) override; + void ReleaseFilter(Rml::CompiledFilterHandle filter) override; + + Rml::CompiledShaderHandle CompileShader(const Rml::String& name, const Rml::Dictionary& parameters) override; + void RenderShader(Rml::CompiledShaderHandle shader_handle, Rml::CompiledGeometryHandle geometry_handle, Rml::Vector2f translation, + Rml::TextureHandle texture) override; + void ReleaseShader(Rml::CompiledShaderHandle effect_handle) override; + + // Can be passed to RenderGeometry() to enable texture rendering without changing the bound texture. + static constexpr Rml::TextureHandle TextureEnableWithoutBinding = Rml::TextureHandle(-1); + // Can be passed to RenderGeometry() to leave the bound texture and used program unchanged. + static constexpr Rml::TextureHandle TexturePostprocess = Rml::TextureHandle(-2); + + // -- Utility functions for clients -- + + const Rml::Matrix4f& GetTransform() const; + void ResetProgram(); + +private: + void UseProgram(ProgramId program_id); + int GetUniformLocation(UniformId uniform_id) const; + void SubmitTransformUniform(Rml::Vector2f translation); + + void BlitLayerToPostprocessPrimary(Rml::LayerHandle layer_handle); + void RenderFilters(Rml::Span filter_handles); + + void SetScissor(Rml::Rectanglei region, bool vertically_flip = false); + + void DrawFullscreenQuad(); + void DrawFullscreenQuad(Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling = Rml::Vector2f(1.f)); + + void RenderBlur(float sigma, const Gfx::FramebufferData& source_destination, const Gfx::FramebufferData& temp, Rml::Rectanglei window_flipped); + + static constexpr size_t MaxNumPrograms = 32; + std::bitset program_transform_dirty; + + Rml::Matrix4f transform; + Rml::Matrix4f projection; + + ProgramId active_program = {}; + Rml::Rectanglei scissor_state; + + int viewport_width = 0; + int viewport_height = 0; + int viewport_offset_x = 0; + int viewport_offset_y = 0; + + Rml::CompiledGeometryHandle fullscreen_quad_geometry = {}; + + Rml::UniquePtr program_data; + + /* + Manages render targets, including the layer stack and postprocessing framebuffers. + + Layers can be pushed and popped, creating new framebuffers as needed. Typically, geometry is rendered to the top + layer. The layer framebuffers may have MSAA enabled. + + Postprocessing framebuffers are separate from the layers, and are commonly used to apply texture-wide effects + such as filters. They are used both as input and output during rendering, and do not use MSAA. + */ + class RenderLayerStack { + public: + RenderLayerStack(); + ~RenderLayerStack(); + + // Push a new layer. All references to previously retrieved layers are invalidated. + Rml::LayerHandle PushLayer(); + + // Pop the top layer. All references to previously retrieved layers are invalidated. + void PopLayer(); + + const Gfx::FramebufferData& GetLayer(Rml::LayerHandle layer) const; + const Gfx::FramebufferData& GetTopLayer() const; + Rml::LayerHandle GetTopLayerHandle() const; + + const Gfx::FramebufferData& GetPostprocessPrimary() { return EnsureFramebufferPostprocess(0); } + const Gfx::FramebufferData& GetPostprocessSecondary() { return EnsureFramebufferPostprocess(1); } + const Gfx::FramebufferData& GetPostprocessTertiary() { return EnsureFramebufferPostprocess(2); } + const Gfx::FramebufferData& GetBlendMask() { return EnsureFramebufferPostprocess(3); } + + void SwapPostprocessPrimarySecondary(); + + void BeginFrame(int new_width, int new_height); + void EndFrame(); + + private: + void DestroyFramebuffers(); + const Gfx::FramebufferData& EnsureFramebufferPostprocess(int index); + + int width = 0, height = 0; + + // The number of active layers is manually tracked since we re-use the framebuffers stored in the fb_layers stack. + int layers_size = 0; + + Rml::Vector fb_layers; + Rml::Vector fb_postprocess; + }; + + RenderLayerStack render_layers; + + struct GLStateBackup { + bool enable_cull_face; + bool enable_blend; + bool enable_stencil_test; + bool enable_scissor_test; + bool enable_depth_test; + + int viewport[4]; + int scissor[4]; + + int active_texture; + + int stencil_clear_value; + float color_clear_value[4]; + unsigned char color_writemask[4]; + + int blend_equation_rgb; + int blend_equation_alpha; + int blend_src_rgb; + int blend_dst_rgb; + int blend_src_alpha; + int blend_dst_alpha; + + struct Stencil { + int func; + int ref; + int value_mask; + int writemask; + int fail; + int pass_depth_fail; + int pass_depth_pass; + }; + Stencil stencil_front; + Stencil stencil_back; + }; + GLStateBackup glstate_backup = {}; +}; + +/** + Helper functions for the OpenGL 3 renderer. + */ +namespace RmlGL3 { + +// Loads OpenGL functions. Optionally, the out message describes the loaded GL version or an error message on failure. +bool Initialize(Rml::String* out_message = nullptr); + +// Unloads OpenGL functions. +void Shutdown(); + +} // namespace RmlGL3 diff --git a/vendor/rmlui_backend/RmlUi_Renderer_SDL.cpp b/vendor/rmlui_backend/RmlUi_Renderer_SDL.cpp new file mode 100644 index 0000000..5f5665a --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_SDL.cpp @@ -0,0 +1,212 @@ +#include "RmlUi_Renderer_SDL.h" +#include +#include +#include + +#if SDL_MAJOR_VERSION >= 3 + #include +#else + #include +#endif + +#if SDL_MAJOR_VERSION == 2 && !(SDL_VIDEO_RENDER_OGL) + #error "Only the OpenGL SDL backend is supported." +#endif + +static void SetRenderClipRect(SDL_Renderer* renderer, const SDL_Rect* rect) +{ +#if SDL_MAJOR_VERSION >= 3 + SDL_SetRenderClipRect(renderer, rect); +#else + SDL_RenderSetClipRect(renderer, rect); +#endif +} +static void SetRenderViewport(SDL_Renderer* renderer, const SDL_Rect* rect) +{ +#if SDL_MAJOR_VERSION >= 3 + SDL_SetRenderViewport(renderer, rect); +#else + SDL_RenderSetViewport(renderer, rect); +#endif +} + +RenderInterface_SDL::RenderInterface_SDL(SDL_Renderer* renderer) : renderer(renderer) +{ + // RmlUi serves vertex colors and textures with premultiplied alpha, set the blend mode accordingly. + // Equivalent to glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA). + blend_mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE, + SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD); +} + +void RenderInterface_SDL::BeginFrame() +{ + SetRenderViewport(renderer, nullptr); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_SetRenderDrawBlendMode(renderer, blend_mode); +} + +void RenderInterface_SDL::EndFrame() {} + +Rml::CompiledGeometryHandle RenderInterface_SDL::CompileGeometry(Rml::Span vertices, Rml::Span indices) +{ + GeometryView* data = new GeometryView{vertices, indices}; + return reinterpret_cast(data); +} + +void RenderInterface_SDL::ReleaseGeometry(Rml::CompiledGeometryHandle geometry) +{ + delete reinterpret_cast(geometry); +} + +void RenderInterface_SDL::RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) +{ + const GeometryView* geometry = reinterpret_cast(handle); + const Rml::Vertex* vertices = geometry->vertices.data(); + const size_t num_vertices = geometry->vertices.size(); + const int* indices = geometry->indices.data(); + const size_t num_indices = geometry->indices.size(); + + if (sdl_vertices_size < num_vertices) + { + sdl_vertices_size = num_vertices * 1.5; + sdl_vertices.reset(new SDL_Vertex[sdl_vertices_size]); + } + + for (size_t i = 0; i < num_vertices; i++) + { + SDL_Vertex& sdl_vertex = sdl_vertices[i]; + sdl_vertex.position = {vertices[i].position.x + translation.x, vertices[i].position.y + translation.y}; + sdl_vertex.tex_coord = {vertices[i].tex_coord.x, vertices[i].tex_coord.y}; + + const auto& color = vertices[i].colour; +#if SDL_MAJOR_VERSION >= 3 + sdl_vertex.color = {color.red / 255.f, color.green / 255.f, color.blue / 255.f, color.alpha / 255.f}; +#else + sdl_vertex.color = {color.red, color.green, color.blue, color.alpha}; +#endif + } + + SDL_Texture* sdl_texture = (SDL_Texture*)texture; + + SDL_RenderGeometry(renderer, sdl_texture, sdl_vertices.get(), (int)num_vertices, indices, (int)num_indices); +} + +void RenderInterface_SDL::EnableScissorRegion(bool enable) +{ + if (enable) + SetRenderClipRect(renderer, &rect_scissor); + else + SetRenderClipRect(renderer, nullptr); + + scissor_region_enabled = enable; +} + +void RenderInterface_SDL::SetScissorRegion(Rml::Rectanglei region) +{ + rect_scissor.x = region.Left(); + rect_scissor.y = region.Top(); + rect_scissor.w = region.Width(); + rect_scissor.h = region.Height(); + + if (scissor_region_enabled) + SetRenderClipRect(renderer, &rect_scissor); +} + +Rml::TextureHandle RenderInterface_SDL::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) +{ + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + return {}; + + file_interface->Seek(file_handle, 0, SEEK_END); + size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + using Rml::byte; + Rml::UniquePtr buffer(new byte[buffer_size]); + file_interface->Read(buffer.get(), buffer_size, file_handle); + file_interface->Close(file_handle); + + const size_t i_ext = source.rfind('.'); + Rml::String extension = (i_ext == Rml::String::npos ? Rml::String() : source.substr(i_ext + 1)); + +#if SDL_MAJOR_VERSION >= 3 + auto CreateSurface = [&]() { return IMG_LoadTyped_IO(SDL_IOFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str()); }; + auto GetSurfaceFormat = [](SDL_Surface* surface) { return surface->format; }; + auto ConvertSurface = [](SDL_Surface* surface, SDL_PixelFormat format) { return SDL_ConvertSurface(surface, format); }; + auto DestroySurface = [](SDL_Surface* surface) { SDL_DestroySurface(surface); }; +#else + auto CreateSurface = [&]() { return IMG_LoadTyped_RW(SDL_RWFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str()); }; + auto GetSurfaceFormat = [](SDL_Surface* surface) { return surface->format->format; }; + auto ConvertSurface = [](SDL_Surface* surface, Uint32 format) { return SDL_ConvertSurfaceFormat(surface, format, 0); }; + auto DestroySurface = [](SDL_Surface* surface) { SDL_FreeSurface(surface); }; +#endif + + SDL_Surface* surface = CreateSurface(); + if (!surface) + return {}; + + texture_dimensions = {surface->w, surface->h}; + + if (GetSurfaceFormat(surface) != SDL_PIXELFORMAT_RGBA32 && GetSurfaceFormat(surface) != SDL_PIXELFORMAT_BGRA32) + { + SDL_Surface* converted_surface = ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); + DestroySurface(surface); + if (!converted_surface) + return {}; + + surface = converted_surface; + } + + // Convert colors to premultiplied alpha, which is necessary for correct alpha compositing. + const size_t pixels_byte_size = surface->w * surface->h * 4; + byte* pixels = static_cast(surface->pixels); + for (size_t i = 0; i < pixels_byte_size; i += 4) + { + const byte alpha = pixels[i + 3]; + for (size_t j = 0; j < 3; ++j) + pixels[i + j] = byte(int(pixels[i + j]) * int(alpha) / 255); + } + + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + texture_dimensions = Rml::Vector2i(surface->w, surface->h); + DestroySurface(surface); + + if (texture) + SDL_SetTextureBlendMode(texture, blend_mode); + + return (Rml::TextureHandle)texture; +} + +Rml::TextureHandle RenderInterface_SDL::GenerateTexture(Rml::Span source, Rml::Vector2i source_dimensions) +{ + RMLUI_ASSERT(source.data() && source.size() == size_t(source_dimensions.x * source_dimensions.y * 4)); + +#if SDL_MAJOR_VERSION >= 3 + auto CreateSurface = [&]() { + return SDL_CreateSurfaceFrom(source_dimensions.x, source_dimensions.y, SDL_PIXELFORMAT_RGBA32, (void*)source.data(), source_dimensions.x * 4); + }; + auto DestroySurface = [](SDL_Surface* surface) { SDL_DestroySurface(surface); }; +#else + auto CreateSurface = [&]() { + return SDL_CreateRGBSurfaceWithFormatFrom((void*)source.data(), source_dimensions.x, source_dimensions.y, 32, source_dimensions.x * 4, + SDL_PIXELFORMAT_RGBA32); + }; + auto DestroySurface = [](SDL_Surface* surface) { SDL_FreeSurface(surface); }; +#endif + + SDL_Surface* surface = CreateSurface(); + + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_SetTextureBlendMode(texture, blend_mode); + + DestroySurface(surface); + return (Rml::TextureHandle)texture; +} + +void RenderInterface_SDL::ReleaseTexture(Rml::TextureHandle texture_handle) +{ + SDL_DestroyTexture((SDL_Texture*)texture_handle); +} diff --git a/vendor/rmlui_backend/RmlUi_Renderer_SDL.h b/vendor/rmlui_backend/RmlUi_Renderer_SDL.h new file mode 100644 index 0000000..aa4469f --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_SDL.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#if RMLUI_SDL_VERSION_MAJOR == 3 + #include +#elif RMLUI_SDL_VERSION_MAJOR == 2 + #include +#else + #error "Unspecified RMLUI_SDL_VERSION_MAJOR. Please set this definition to the major version of the SDL library being linked to." +#endif + +class RenderInterface_SDL : public Rml::RenderInterface { +public: + RenderInterface_SDL(SDL_Renderer* renderer); + + // Sets up OpenGL states for taking rendering commands from RmlUi. + void BeginFrame(); + void EndFrame(); + + // -- Inherited from Rml::RenderInterface -- + + Rml::CompiledGeometryHandle CompileGeometry(Rml::Span vertices, Rml::Span indices) override; + void ReleaseGeometry(Rml::CompiledGeometryHandle geometry) override; + void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override; + + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override; + Rml::TextureHandle GenerateTexture(Rml::Span source, Rml::Vector2i source_dimensions) override; + void ReleaseTexture(Rml::TextureHandle texture_handle) override; + + void EnableScissorRegion(bool enable) override; + void SetScissorRegion(Rml::Rectanglei region) override; + +private: + struct GeometryView { + Rml::Span vertices; + Rml::Span indices; + }; + + SDL_Renderer* renderer; + SDL_BlendMode blend_mode = {}; + SDL_Rect rect_scissor = {}; + bool scissor_region_enabled = false; + Rml::UniquePtr sdl_vertices; + size_t sdl_vertices_size = 0; +}; diff --git a/vendor/rmlui_backend/RmlUi_Renderer_SDL_GPU.cpp b/vendor/rmlui_backend/RmlUi_Renderer_SDL_GPU.cpp new file mode 100644 index 0000000..8d48679 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_SDL_GPU.cpp @@ -0,0 +1,630 @@ +#include "RmlUi_Renderer_SDL_GPU.h" +#include "RmlUi_SDL_GPU/ShadersCompiledSPV.h" +#include +#include +#include +#include +#include + +using namespace Rml; + +enum ShaderType { + ShaderTypeColor, + ShaderTypeTexture, + ShaderTypeVert, + ShaderTypeCount, +}; + +enum ShaderFormat { + ShaderFormatSPIRV, + ShaderFormatMSL, + ShaderFormatDXIL, + ShaderFormatCount, +}; + +struct Shader { + Span data[ShaderFormatCount]; + int uniforms; + int samplers; + SDL_GPUShaderStage stage; +}; + +#undef X +#define X(name) \ + Span \ + { \ + name, sizeof(name) \ + } + +static const Shader shaders[ShaderTypeCount] = { + {{X(shader_frag_color_spirv), X(shader_frag_color_msl), X(shader_frag_color_dxil)}, 0, 0, SDL_GPU_SHADERSTAGE_FRAGMENT}, + {{X(shader_frag_texture_spirv), X(shader_frag_texture_msl), X(shader_frag_texture_dxil)}, 0, 1, SDL_GPU_SHADERSTAGE_FRAGMENT}, + {{X(shader_vert_spirv), X(shader_vert_msl), X(shader_vert_dxil)}, 2, 0, SDL_GPU_SHADERSTAGE_VERTEX}}; + +#undef X + +static SDL_GPUShader* CreateShaderFromMemory(SDL_GPUDevice* device, ShaderType type) +{ + SDL_GPUShaderFormat sdl_shader_format = SDL_GetGPUShaderFormats(device); + ShaderFormat format = ShaderFormatCount; + const char* entrypoint = nullptr; + if (sdl_shader_format & SDL_GPU_SHADERFORMAT_SPIRV) + { + sdl_shader_format = SDL_GPU_SHADERFORMAT_SPIRV; + format = ShaderFormatSPIRV; + entrypoint = "main"; + } + else if (sdl_shader_format & SDL_GPU_SHADERFORMAT_DXIL) + { + sdl_shader_format = SDL_GPU_SHADERFORMAT_DXIL; + format = ShaderFormatDXIL; + entrypoint = "main"; + } + else if (sdl_shader_format & SDL_GPU_SHADERFORMAT_MSL) + { + sdl_shader_format = SDL_GPU_SHADERFORMAT_MSL; + format = ShaderFormatMSL; + entrypoint = "main0"; + } + else + { + RMLUI_ERRORMSG("Invalid shader format"); + return nullptr; + } + const Shader& shader = shaders[type]; + SDL_GPUShaderCreateInfo info{}; + info.code = static_cast(shader.data[format].data()); + info.code_size = shader.data[format].size(); + info.entrypoint = entrypoint; + info.format = sdl_shader_format; + info.stage = shader.stage; + info.num_samplers = shader.samplers; + info.num_uniform_buffers = shader.uniforms; + SDL_GPUShader* sdl_shader = SDL_CreateGPUShader(device, &info); + if (!sdl_shader) + { + Log::Message(Log::LT_ERROR, "Failed to create shader: %s", SDL_GetError()); + RMLUI_ERROR; + } + return sdl_shader; +} + +void RenderInterface_SDL_GPU::CreatePipelines() +{ + SDL_GPUShader* color_shader = CreateShaderFromMemory(device, ShaderTypeColor); + SDL_GPUShader* texture_shader = CreateShaderFromMemory(device, ShaderTypeTexture); + SDL_GPUShader* vert_shader = CreateShaderFromMemory(device, ShaderTypeVert); + + SDL_GPUColorTargetDescription target{}; + target.format = SDL_GetGPUSwapchainTextureFormat(device, window); + target.blend_state.enable_blend = true; + target.blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD; + target.blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD; + target.blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE; + target.blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE; + target.blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + target.blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + + SDL_GPUVertexAttribute attrib[3]{}; + attrib[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2; + attrib[0].location = 0; + attrib[0].offset = offsetof(Vertex, position); + attrib[1].format = SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM; + attrib[1].location = 1; + attrib[1].offset = offsetof(Vertex, colour); + attrib[2].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2; + attrib[2].location = 2; + attrib[2].offset = offsetof(Vertex, tex_coord); + + SDL_GPUVertexBufferDescription buffer{}; + buffer.pitch = sizeof(Vertex); + + SDL_GPUGraphicsPipelineCreateInfo info{}; + info.vertex_shader = vert_shader; + + info.target_info.num_color_targets = 1; + info.target_info.color_target_descriptions = ⌖ + + info.vertex_input_state.num_vertex_attributes = 3; + info.vertex_input_state.num_vertex_buffers = 1; + info.vertex_input_state.vertex_attributes = attrib; + info.vertex_input_state.vertex_buffer_descriptions = &buffer; + + info.fragment_shader = color_shader; + color_pipeline = SDL_CreateGPUGraphicsPipeline(device, &info); + if (!color_pipeline) + { + Log::Message(Log::LT_ERROR, "Failed to create color pipeline: %s", SDL_GetError()); + RMLUI_ERROR; + } + + info.fragment_shader = texture_shader; + texture_pipeline = SDL_CreateGPUGraphicsPipeline(device, &info); + if (!texture_pipeline) + { + Log::Message(Log::LT_ERROR, "Failed to create texture pipeline: %s", SDL_GetError()); + RMLUI_ERROR; + } + + SDL_ReleaseGPUShader(device, color_shader); + SDL_ReleaseGPUShader(device, texture_shader); + SDL_ReleaseGPUShader(device, vert_shader); +} + +RenderInterface_SDL_GPU::RenderInterface_SDL_GPU(SDL_GPUDevice* device, SDL_Window* window) : + device(device), window(window), render_pass(nullptr), copy_pass(nullptr) +{ + CreatePipelines(); + + SDL_GPUSamplerCreateInfo info{}; + info.min_filter = SDL_GPU_FILTER_LINEAR; + info.mag_filter = SDL_GPU_FILTER_LINEAR; + info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; + info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; + info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; + info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; + linear_sampler = SDL_CreateGPUSampler(device, &info); + if (!linear_sampler) + { + Log::Message(Log::LT_ERROR, "Failed to acquire command buffer: %s", SDL_GetError()); + return; + } +} + +void RenderInterface_SDL_GPU::Shutdown() +{ + for (Rml::UniquePtr& command : commands) + { + command->Update(*this); + } + for (Rml::UniquePtr& buffer : buffers) + { + SDL_ReleaseGPUTransferBuffer(device, buffer->transfer_buffer); + SDL_ReleaseGPUBuffer(device, buffer->buffer); + } + SDL_ReleaseGPUSampler(device, linear_sampler); + SDL_ReleaseGPUGraphicsPipeline(device, color_pipeline); + SDL_ReleaseGPUGraphicsPipeline(device, texture_pipeline); +} + +void RenderInterface_SDL_GPU::BeginFrame(SDL_GPUCommandBuffer* command_buffer, SDL_GPUTexture* swapchain_texture, uint32_t width, uint32_t height) +{ + this->command_buffer = command_buffer; + this->swapchain_texture = swapchain_texture; + swapchain_width = width; + swapchain_height = height; + proj = Matrix4f::ProjectOrtho(0.0f, static_cast(width), static_cast(height), 0.0f, -10'000.f, 10'000.f); + SetTransform(nullptr); + EnableScissorRegion(false); +} + +void RenderInterface_SDL_GPU::EndFrame() +{ + for (Rml::UniquePtr& command : commands) + { + command->Update(*this); + } + commands.clear(); + + if (copy_pass) + { + SDL_EndGPUCopyPass(copy_pass); + copy_pass = nullptr; + } + if (render_pass) + { + SDL_EndGPURenderPass(render_pass); + render_pass = nullptr; + } +} + +bool RenderInterface_SDL_GPU::BeginCopyPass() +{ + if (copy_pass) + { + return true; + } + if (render_pass) + { + SDL_EndGPURenderPass(render_pass); + render_pass = nullptr; + } + copy_pass = SDL_BeginGPUCopyPass(command_buffer); + if (!copy_pass) + { + Log::Message(Log::LT_ERROR, "Failed to begin copy pass: %s", SDL_GetError()); + return false; + } + return true; +} + +bool RenderInterface_SDL_GPU::BeginRenderPass() +{ + if (render_pass) + { + return true; + } + if (copy_pass) + { + SDL_EndGPUCopyPass(copy_pass); + copy_pass = nullptr; + } + SDL_GPUColorTargetInfo color_info{}; + color_info.texture = swapchain_texture; + color_info.load_op = SDL_GPU_LOADOP_LOAD; + color_info.store_op = SDL_GPU_STOREOP_STORE; + render_pass = SDL_BeginGPURenderPass(command_buffer, &color_info, 1, nullptr); + if (!render_pass) + { + Log::Message(Log::LT_ERROR, "Failed to begin render pass: %s", SDL_GetError()); + return false; + } + return true; +} + +CompiledGeometryHandle RenderInterface_SDL_GPU::CompileGeometry(Span vertices, Span indices) +{ + if (!BeginCopyPass()) + { + return 0; + } + + uint32_t vertex_size = static_cast(vertices.size() * sizeof(Vertex)); + uint32_t index_size = static_cast(indices.size() * sizeof(int)); + + GeometryView* geometry = new GeometryView(); + geometry->vertex_buffer = RequestBuffer(vertex_size, SDL_GPU_BUFFERUSAGE_VERTEX); + geometry->index_buffer = RequestBuffer(index_size, SDL_GPU_BUFFERUSAGE_INDEX); + if (!geometry->vertex_buffer || !geometry->index_buffer) + { + Log::Message(Log::LT_ERROR, "Failed to request buffer(s)"); + delete geometry; + return 0; + } + + void* vertex_data = SDL_MapGPUTransferBuffer(device, geometry->vertex_buffer->transfer_buffer, true); + void* index_data = SDL_MapGPUTransferBuffer(device, geometry->index_buffer->transfer_buffer, true); + if (!vertex_data || !index_data) + { + Log::Message(Log::LT_ERROR, "Failed to map transfer buffer(s): %s", SDL_GetError()); + delete geometry; + return 0; + } + + std::memcpy(vertex_data, vertices.data(), vertex_size); + std::memcpy(index_data, indices.data(), index_size); + SDL_UnmapGPUTransferBuffer(device, geometry->vertex_buffer->transfer_buffer); + SDL_UnmapGPUTransferBuffer(device, geometry->index_buffer->transfer_buffer); + + SDL_GPUTransferBufferLocation location{}; + SDL_GPUBufferRegion region{}; + + location.transfer_buffer = geometry->vertex_buffer->transfer_buffer; + region.buffer = geometry->vertex_buffer->buffer; + region.size = vertex_size; + SDL_UploadToGPUBuffer(copy_pass, &location, ®ion, false); + + location.transfer_buffer = geometry->index_buffer->transfer_buffer; + region.buffer = geometry->index_buffer->buffer; + region.size = index_size; + SDL_UploadToGPUBuffer(copy_pass, &location, ®ion, false); + + geometry->num_indices = static_cast(indices.size()); + geometry->vertex_buffer->in_use = true; + geometry->index_buffer->in_use = true; + + return reinterpret_cast(geometry); +} + +void RenderInterface_SDL_GPU::ReleaseGeometryCommand::Update(RenderInterface_SDL_GPU& interface) +{ + (void)interface; + GeometryView* geometry = reinterpret_cast(handle); + geometry->vertex_buffer->in_use = false; + geometry->index_buffer->in_use = false; + delete geometry; +} + +void RenderInterface_SDL_GPU::ReleaseGeometry(CompiledGeometryHandle handle) +{ + commands.push_back(Rml::MakeUnique(handle)); +} + +void RenderInterface_SDL_GPU::RenderGeometryCommand::Update(RenderInterface_SDL_GPU& interface) +{ + if (!interface.BeginRenderPass()) + { + return; + } + + GeometryView* geometry = reinterpret_cast(handle); + + if (texture != 0) + { + SDL_BindGPUGraphicsPipeline(interface.render_pass, interface.texture_pipeline); + + SDL_GPUTextureSamplerBinding texture_binding{}; + texture_binding.texture = reinterpret_cast(texture); + texture_binding.sampler = interface.linear_sampler; + SDL_BindGPUFragmentSamplers(interface.render_pass, 0, &texture_binding, 1); + } + else + { + SDL_BindGPUGraphicsPipeline(interface.render_pass, interface.color_pipeline); + } + + SDL_GPUBufferBinding vertex_buffer_binding{}; + SDL_GPUBufferBinding index_buffer_binding{}; + vertex_buffer_binding.buffer = geometry->vertex_buffer->buffer; + index_buffer_binding.buffer = geometry->index_buffer->buffer; + + SDL_BindGPUVertexBuffers(interface.render_pass, 0, &vertex_buffer_binding, 1); + SDL_BindGPUIndexBuffer(interface.render_pass, &index_buffer_binding, SDL_GPU_INDEXELEMENTSIZE_32BIT); + + SDL_SetGPUScissor(interface.render_pass, &interface.scissor); + + SDL_PushGPUVertexUniformData(interface.command_buffer, 0, &interface.transform, sizeof(interface.transform)); + SDL_PushGPUVertexUniformData(interface.command_buffer, 1, &translation, sizeof(translation)); + + SDL_DrawGPUIndexedPrimitives(interface.render_pass, geometry->num_indices, 1, 0, 0, 0); +} + +void RenderInterface_SDL_GPU::RenderGeometry(CompiledGeometryHandle handle, Vector2f translation, TextureHandle texture) +{ + commands.push_back(Rml::MakeUnique(handle, translation, texture)); +} + +void RenderInterface_SDL_GPU::EnableScissorRegionCommand::Update(RenderInterface_SDL_GPU& interface) +{ + if (!enable) + { + interface.scissor.x = 0; + interface.scissor.y = 0; + interface.scissor.w = interface.swapchain_width; + interface.scissor.h = interface.swapchain_height; + } +} + +void RenderInterface_SDL_GPU::EnableScissorRegion(bool enable) +{ + commands.push_back(Rml::MakeUnique(enable)); +} + +void RenderInterface_SDL_GPU::SetScissorRegionCommand::Update(RenderInterface_SDL_GPU& interface) +{ + interface.scissor.x = region.Left(); + interface.scissor.w = region.Width(); + interface.scissor.y = region.Top(); + interface.scissor.h = region.Height(); +} + +void RenderInterface_SDL_GPU::SetScissorRegion(Rectanglei region) +{ + commands.push_back(Rml::MakeUnique(region)); +} + +TextureHandle RenderInterface_SDL_GPU::LoadTexture(Vector2i& texture_dimensions, const String& source) +{ + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + { + return 0; + } + + file_interface->Seek(file_handle, 0, SEEK_END); + size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + using Rml::byte; + Rml::UniquePtr buffer(new byte[buffer_size]); + file_interface->Read(buffer.get(), buffer_size, file_handle); + file_interface->Close(file_handle); + + const size_t i_ext = source.rfind('.'); + Rml::String extension = (i_ext == Rml::String::npos ? Rml::String() : source.substr(i_ext + 1)); + + SDL_Surface* surface = IMG_LoadTyped_IO(SDL_IOFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str()); + if (!surface) + { + return 0; + } + + texture_dimensions = {surface->w, surface->h}; + + if (surface->format != SDL_PIXELFORMAT_RGBA32) + { + SDL_Surface* converted_surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); + SDL_DestroySurface(surface); + if (!converted_surface) + { + return 0; + } + + surface = converted_surface; + } + + // Convert colors to premultiplied alpha, which is necessary for correct alpha compositing. + const size_t pixels_byte_size = surface->w * surface->h * 4; + byte* pixels = static_cast(surface->pixels); + for (size_t i = 0; i < pixels_byte_size; i += 4) + { + const byte alpha = pixels[i + 3]; + for (size_t j = 0; j < 3; ++j) + { + pixels[i + j] = byte(int(pixels[i + j]) * int(alpha) / 255); + } + } + + Span data{static_cast(surface->pixels), static_cast(surface->pitch * surface->h)}; + texture_dimensions = {surface->w, surface->h}; + + TextureHandle handle = GenerateTexture(data, texture_dimensions); + + SDL_DestroySurface(surface); + + return handle; +} + +TextureHandle RenderInterface_SDL_GPU::GenerateTexture(Span source, Vector2i source_dimensions) +{ + SDL_GPUTransferBuffer* transfer_buffer; + { + SDL_GPUTransferBufferCreateInfo info{}; + info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + info.size = source_dimensions.x * source_dimensions.y * 4; + transfer_buffer = SDL_CreateGPUTransferBuffer(device, &info); + if (!transfer_buffer) + { + Log::Message(Log::LT_ERROR, "Failed to create transfer buffer: %s", SDL_GetError()); + return 0; + } + } + + void* dst = SDL_MapGPUTransferBuffer(device, transfer_buffer, false); + if (!dst) + { + SDL_Log("Failed to map transfer buffer: %s", SDL_GetError()); + return 0; + } + + std::memcpy(dst, source.data(), source_dimensions.x * source_dimensions.y * 4); + SDL_UnmapGPUTransferBuffer(device, transfer_buffer); + + SDL_GPUTexture* texture; + { + SDL_GPUTextureCreateInfo info{}; + info.type = SDL_GPU_TEXTURETYPE_2D; + info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; + info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; + info.width = source_dimensions.x; + info.height = source_dimensions.y; + info.layer_count_or_depth = 1; + info.num_levels = 1; + texture = SDL_CreateGPUTexture(device, &info); + if (!texture) + { + Log::Message(Log::LT_ERROR, "Failed to create texture: %s", SDL_GetError()); + return 0; + } + } + + SDL_GPUTextureTransferInfo transfer_info{}; + SDL_GPUTextureRegion region{}; + transfer_info.transfer_buffer = transfer_buffer; + region.texture = texture; + region.w = source_dimensions.x; + region.h = source_dimensions.y; + region.d = 1; + + // We can get calls out outside of Begin/End Frame so always acquire a command buffer here + SDL_GPUCommandBuffer* command_buffer = SDL_AcquireGPUCommandBuffer(device); + if (!command_buffer) + { + Log::Message(Log::LT_ERROR, "Failed to acquire command buffer: %s", SDL_GetError()); + SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); + SDL_ReleaseGPUTexture(device, texture); + return 0; + } + + SDL_GPUCopyPass* copy_pass = SDL_BeginGPUCopyPass(command_buffer); + if (!copy_pass) + { + Log::Message(Log::LT_ERROR, "Failed to begin copy pass: %s", SDL_GetError()); + SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); + SDL_ReleaseGPUTexture(device, texture); + SDL_CancelGPUCommandBuffer(command_buffer); + return 0; + } + + SDL_UploadToGPUTexture(copy_pass, &transfer_info, ®ion, false); + SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); + SDL_EndGPUCopyPass(copy_pass); + SDL_SubmitGPUCommandBuffer(command_buffer); + + return reinterpret_cast(texture); +} + +void RenderInterface_SDL_GPU::ReleaseTextureCommand::Update(RenderInterface_SDL_GPU& interface) +{ + SDL_GPUTexture* texture = reinterpret_cast(handle); + SDL_ReleaseGPUTexture(interface.device, texture); +} + +void RenderInterface_SDL_GPU::ReleaseTexture(TextureHandle texture_handle) +{ + commands.push_back(Rml::MakeUnique(texture_handle)); +} + +RenderInterface_SDL_GPU::SetTransformCommand::SetTransformCommand(const Rml::Matrix4f* new_transform) +{ + if (new_transform) + { + has_transform = true; + transform = *new_transform; + } + else + { + has_transform = false; + } +} + +void RenderInterface_SDL_GPU::SetTransformCommand::Update(RenderInterface_SDL_GPU& interface) +{ + if (has_transform) + { + interface.transform = interface.proj * transform; + } + else + { + interface.transform = interface.proj; + } +} + +void RenderInterface_SDL_GPU::SetTransform(const Rml::Matrix4f* new_transform) +{ + commands.push_back(Rml::MakeUnique(new_transform)); +} + +RenderInterface_SDL_GPU::Buffer* RenderInterface_SDL_GPU::RequestBuffer(int capacity, SDL_GPUBufferUsageFlags usage) +{ + auto it = std::lower_bound(buffers.begin(), buffers.end(), capacity, + [](const Rml::UniquePtr& lhs, int capacity) { return lhs->capacity < capacity; }); + + for (auto tmp_it = it; tmp_it != buffers.end(); ++tmp_it) + { + const auto& buffer = *tmp_it; + if (!buffer->in_use && buffer->usage == usage) + { + // set in_use as false and expect the caller to set it to true themselves + buffer->in_use = false; + return buffer.get(); + } + } + + Rml::UniquePtr buffer = Rml::MakeUnique(); + { + SDL_GPUTransferBufferCreateInfo info{}; + info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + info.size = capacity; + buffer->transfer_buffer = SDL_CreateGPUTransferBuffer(device, &info); + } + { + SDL_GPUBufferCreateInfo info{}; + info.usage = usage; + info.size = capacity; + buffer->buffer = SDL_CreateGPUBuffer(device, &info); + } + if (!buffer->transfer_buffer || !buffer->buffer) + { + Log::Message(Log::LT_ERROR, "Failed to create buffer(s): %s", SDL_GetError()); + SDL_ReleaseGPUTransferBuffer(device, buffer->transfer_buffer); + SDL_ReleaseGPUBuffer(device, buffer->buffer); + return {}; + } + buffer->usage = usage; + buffer->in_use = false; + buffer->capacity = capacity; + auto inserted_it = buffers.insert(it, std::move(buffer)); + return inserted_it->get(); +} diff --git a/vendor/rmlui_backend/RmlUi_Renderer_SDL_GPU.h b/vendor/rmlui_backend/RmlUi_Renderer_SDL_GPU.h new file mode 100644 index 0000000..b1e9e03 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_SDL_GPU.h @@ -0,0 +1,122 @@ +#pragma once + +#include +#include +#include + +class RenderInterface_SDL_GPU : public Rml::RenderInterface { +public: + RenderInterface_SDL_GPU(SDL_GPUDevice* device, SDL_Window* window); + void Shutdown(); + void BeginFrame(SDL_GPUCommandBuffer* command_buffer, SDL_GPUTexture* swapchain_texture, uint32_t width, uint32_t height); + void EndFrame(); + Rml::CompiledGeometryHandle CompileGeometry(Rml::Span vertices, Rml::Span indices) override; + void ReleaseGeometry(Rml::CompiledGeometryHandle geometry) override; + void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override; + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override; + Rml::TextureHandle GenerateTexture(Rml::Span source, Rml::Vector2i source_dimensions) override; + void ReleaseTexture(Rml::TextureHandle texture_handle) override; + void EnableScissorRegion(bool enable) override; + void SetScissorRegion(Rml::Rectanglei region) override; + void SetTransform(const Rml::Matrix4f* new_transform) override; + +private: + SDL_GPUDevice* device; + SDL_Window* window; + SDL_GPUGraphicsPipeline* texture_pipeline; + SDL_GPUGraphicsPipeline* color_pipeline; + SDL_GPUSampler* linear_sampler; + SDL_GPUCommandBuffer* command_buffer; + SDL_GPUTexture* swapchain_texture; + uint32_t swapchain_width; + uint32_t swapchain_height; + SDL_GPURenderPass* render_pass; + SDL_GPUCopyPass* copy_pass; + SDL_Rect scissor; + Rml::Matrix4f transform; + Rml::Matrix4f proj; + + struct Command { + virtual ~Command() = default; + virtual void Update(RenderInterface_SDL_GPU& interface) = 0; + }; + + struct EnableScissorRegionCommand : Command { + EnableScissorRegionCommand(bool enable) : enable(enable) {} + void Update(RenderInterface_SDL_GPU& interface) override; + + bool enable; + }; + + struct SetScissorRegionCommand : Command { + SetScissorRegionCommand(Rml::Rectanglei region) : region(region) {} + void Update(RenderInterface_SDL_GPU& interface) override; + + Rml::Rectanglei region; + }; + + struct RenderGeometryCommand : Command { + RenderGeometryCommand(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) : + handle(handle), translation(translation), texture(texture) + {} + void Update(RenderInterface_SDL_GPU& interface) override; + + Rml::CompiledGeometryHandle handle; + Rml::Vector2f translation; + Rml::TextureHandle texture; + }; + + struct ReleaseGeometryCommand : Command { + ReleaseGeometryCommand(Rml::CompiledGeometryHandle handle) : handle(handle) {} + void Update(RenderInterface_SDL_GPU& interface) override; + + Rml::CompiledGeometryHandle handle; + }; + + struct ReleaseTextureCommand : Command { + ReleaseTextureCommand(Rml::TextureHandle handle) : handle(handle) {} + void Update(RenderInterface_SDL_GPU& interface) override; + + Rml::TextureHandle handle; + }; + + struct SetTransformCommand : Command { + SetTransformCommand(const Rml::Matrix4f* new_transform); + void Update(RenderInterface_SDL_GPU& interface) override; + + Rml::Matrix4f transform; + bool has_transform; + }; + + friend struct EnableScissorRegionCommand; + friend struct SetScissorRegionCommand; + friend struct RenderGeometryCommand; + friend struct ReleaseGeometryCommand; + friend struct ReleaseTextureCommand; + friend struct SetTransformCommand; + + struct Buffer { + SDL_GPUTransferBuffer* transfer_buffer; + SDL_GPUBuffer* buffer; + SDL_GPUBufferUsageFlags usage; + int capacity; + bool in_use; + }; + + struct GeometryView { + Buffer* vertex_buffer; + Buffer* index_buffer; + int num_indices; + }; + + // List of ordered render commands + Rml::Vector> commands; + + // Sorted vertex/index buffers by capacities + Rml::Vector> buffers; + + void CreatePipelines(); + bool BeginCopyPass(); + bool BeginRenderPass(); + Buffer* RequestBuffer(int capacity, SDL_GPUBufferUsageFlags usage); +}; diff --git a/vendor/rmlui_backend/RmlUi_Renderer_VK.cpp b/vendor/rmlui_backend/RmlUi_Renderer_VK.cpp new file mode 100644 index 0000000..bd31daa --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_VK.cpp @@ -0,0 +1,3017 @@ +#include "RmlUi_Renderer_VK.h" +#include "RmlUi_Vulkan/ShadersCompiledSPV.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// AlignUp(314, 256) = 512 +template +static T AlignUp(T val, T alignment) +{ + return (val + alignment - (T)1) & ~(alignment - (T)1); +} + +VkValidationFeaturesEXT debug_validation_features_ext = {}; +VkValidationFeatureEnableEXT debug_validation_features_ext_requested[] = { + VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT, + VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT, + VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT, +}; + +#ifdef RMLUI_VK_DEBUG +static Rml::String FormatByteSize(VkDeviceSize size) noexcept +{ + constexpr VkDeviceSize K = VkDeviceSize(1024); + if (size < K) + return Rml::CreateString("%zu B", size); + else if (size < K * K) + return Rml::CreateString("%g KB", double(size) / double(K)); + return Rml::CreateString("%g MB", double(size) / double(K * K)); +} + +static VKAPI_ATTR VkBool32 VKAPI_CALL MyDebugReportCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severityFlags, + VkDebugUtilsMessageTypeFlagsEXT /*messageTypeFlags*/, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* /*pUserData*/) +{ + if (severityFlags & VkDebugUtilsMessageSeverityFlagBitsEXT::VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) + { + return VK_FALSE; + } + + #ifdef RMLUI_PLATFORM_WIN32 + if (severityFlags & VkDebugUtilsMessageSeverityFlagBitsEXT::VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + { + // some logs are not passed to our UI, because of early calling for explicity I put native log output + OutputDebugString(TEXT("\n")); + OutputDebugStringA(pCallbackData->pMessage); + } + #endif + + Rml::Log::Message(Rml::Log::LT_ERROR, "[Vulkan][VALIDATION] %s ", pCallbackData->pMessage); + + return VK_FALSE; +} +#endif + +RenderInterface_VK::RenderInterface_VK() : + m_is_transform_enabled{false}, m_is_apply_to_regular_geometry_stencil{false}, m_is_use_scissor_specified{false}, m_is_use_stencil_pipeline{false}, + m_width{}, m_height{}, m_queue_index_present{}, m_queue_index_graphics{}, m_queue_index_compute{}, m_semaphore_index{}, + m_semaphore_index_previous{}, m_image_index{}, m_p_instance{}, m_p_device{}, m_p_physical_device{}, m_p_surface{}, m_p_swapchain{}, + m_p_allocator{}, m_p_current_command_buffer{}, m_p_descriptor_set_layout_vertex_transform{}, m_p_descriptor_set_layout_texture{}, + m_p_pipeline_layout{}, m_p_pipeline_with_textures{}, m_p_pipeline_without_textures{}, + m_p_pipeline_stencil_for_region_where_geometry_will_be_drawn{}, m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_with_textures{}, + m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_without_textures{}, m_p_descriptor_set{}, m_p_render_pass{}, + m_p_sampler_linear{}, m_scissor{}, m_scissor_original{}, m_viewport{}, m_p_queue_present{}, m_p_queue_graphics{}, m_p_queue_compute{}, +#ifdef RMLUI_VK_DEBUG + m_debug_messenger{}, +#endif + m_swapchain_format{}, m_texture_depthstencil{}, m_pending_for_deletion_textures_by_frames{} +{} + +RenderInterface_VK::~RenderInterface_VK() {} + +Rml::CompiledGeometryHandle RenderInterface_VK::CompileGeometry(Rml::Span vertices, Rml::Span indices) +{ + RMLUI_ZoneScopedN("Vulkan - CompileGeometry"); + + VkDescriptorSet p_current_descriptor_set = nullptr; + p_current_descriptor_set = m_p_descriptor_set; + + RMLUI_VK_ASSERTMSG(p_current_descriptor_set, + "you can't have here an invalid pointer of VkDescriptorSet. Two reason might be. 1. - you didn't allocate it " + "at all or 2. - Somehing is wrong with allocation and somehow it was corrupted by something."); + + auto* p_geometry_handle = new geometry_handle_t{}; + + uint32_t* pCopyDataToBuffer = nullptr; + const void* pData = reinterpret_cast(vertices.data()); + + bool status = m_memory_pool.Alloc_VertexBuffer((uint32_t)vertices.size(), sizeof(Rml::Vertex), reinterpret_cast(&pCopyDataToBuffer), + &p_geometry_handle->m_p_vertex, &p_geometry_handle->m_p_vertex_allocation); + RMLUI_VK_ASSERTMSG(status, "failed to AllocVertexBuffer"); + + memcpy(pCopyDataToBuffer, pData, sizeof(Rml::Vertex) * vertices.size()); + + status = m_memory_pool.Alloc_IndexBuffer((uint32_t)indices.size(), sizeof(int), reinterpret_cast(&pCopyDataToBuffer), + &p_geometry_handle->m_p_index, &p_geometry_handle->m_p_index_allocation); + RMLUI_VK_ASSERTMSG(status, "failed to AllocIndexBuffer"); + + memcpy(pCopyDataToBuffer, indices.data(), sizeof(int) * indices.size()); + + p_geometry_handle->m_num_indices = (int)indices.size(); + + return Rml::CompiledGeometryHandle(p_geometry_handle); +} + +void RenderInterface_VK::RenderGeometry(Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation, Rml::TextureHandle texture) +{ + RMLUI_ZoneScopedN("Vulkan - RenderCompiledGeometry"); + + if (m_p_current_command_buffer == nullptr) + return; + + RMLUI_VK_ASSERTMSG(m_p_current_command_buffer, "must be valid otherwise you can't render now!!! (can't be)"); + + texture_data_t* p_texture = reinterpret_cast(texture); + + VkDescriptorImageInfo info_descriptor_image = {}; + if (p_texture && p_texture->m_p_vk_descriptor_set == nullptr) + { + VkDescriptorSet p_texture_set = nullptr; + m_manager_descriptors.Alloc_Descriptor(m_p_device, &m_p_descriptor_set_layout_texture, &p_texture_set); + + info_descriptor_image.imageView = p_texture->m_p_vk_image_view; + info_descriptor_image.sampler = p_texture->m_p_vk_sampler; + info_descriptor_image.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet info_write = {}; + + info_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + info_write.dstSet = p_texture_set; + info_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + info_write.dstBinding = 2; + info_write.pImageInfo = &info_descriptor_image; + info_write.descriptorCount = 1; + + vkUpdateDescriptorSets(m_p_device, 1, &info_write, 0, nullptr); + p_texture->m_p_vk_descriptor_set = p_texture_set; + } + + geometry_handle_t* p_casted_compiled_geometry = reinterpret_cast(geometry); + + m_user_data_for_vertex_shader.m_translate = translation; + + VkDescriptorSet p_current_descriptor_set = nullptr; + p_current_descriptor_set = m_p_descriptor_set; + + RMLUI_VK_ASSERTMSG(p_current_descriptor_set, + "you can't have here an invalid pointer of VkDescriptorSet. Two reason might be. 1. - you didn't allocate it " + "at all or 2. - Somehing is wrong with allocation and somehow it was corrupted by something."); + + shader_vertex_user_data_t* p_data = nullptr; + + if (p_casted_compiled_geometry->m_p_shader_allocation == nullptr) + { + // it means it was freed in ReleaseCompiledGeometry method + bool status = m_memory_pool.Alloc_GeneralBuffer(sizeof(m_user_data_for_vertex_shader), reinterpret_cast(&p_data), + &p_casted_compiled_geometry->m_p_shader, &p_casted_compiled_geometry->m_p_shader_allocation); + RMLUI_VK_ASSERTMSG(status, "failed to allocate VkDescriptorBufferInfo for uniform data to shaders"); + } + else + { + // it means our state is dirty and we need to update data, but it is not right in terms of architecture, for real better experience would + // be great to free all "compiled" geometries and "re-build" them in one general way, but here I got only three callings for + // font-face-layer textures (load_document example) and that shit. So better to think how to make it right, if it is fine okay, if it is + // not okay and like we really expect that ReleaseCompiledGeometry for all objects that needs to be rebuilt so better to implement that, + // but still it is a big architectural thing (or at least you need to do something big commits here to implement a such feature), so my + // implementation doesn't break anything what we had, but still it looks strange. If I get callings for releasing maybe I need to use it + // for all objects not separately????? Otherwise it is better to provide method for resizing (or some kind of "resizing" callback) for + // recalculating all geometry IDK, so it means you pass the existed geometry that wasn't pass to ReleaseCompiledGeometry, but from another + // hand you need to re-build compiled geometry again so we have two kinds of geometry one is compiled and never changes and one is dynamic + // and it goes through pipeline InitializationOfProgram...->Compile->Render->Release->Compile->Render->Release... + + m_memory_pool.Free_GeometryHandle_ShaderDataOnly(p_casted_compiled_geometry); + bool status = m_memory_pool.Alloc_GeneralBuffer(sizeof(m_user_data_for_vertex_shader), reinterpret_cast(&p_data), + &p_casted_compiled_geometry->m_p_shader, &p_casted_compiled_geometry->m_p_shader_allocation); + RMLUI_VK_ASSERTMSG(status, "failed to allocate VkDescriptorBufferInfo for uniform data to shaders"); + } + + if (p_data) + { + p_data->m_transform = m_user_data_for_vertex_shader.m_transform; + p_data->m_translate = m_user_data_for_vertex_shader.m_translate; + } + else + { + RMLUI_VK_ASSERTMSG(p_data, "you can't reach this zone, it means something bad"); + } + + const uint32_t pDescriptorOffsets = static_cast(p_casted_compiled_geometry->m_p_shader.offset); + + VkDescriptorSet p_texture_descriptor_set = nullptr; + + if (p_texture) + { + p_texture_descriptor_set = p_texture->m_p_vk_descriptor_set; + } + + VkDescriptorSet p_sets[] = {p_current_descriptor_set, p_texture_descriptor_set}; + int real_size_of_sets = 2; + + if (p_texture == nullptr) + real_size_of_sets = 1; + + vkCmdBindDescriptorSets(m_p_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_pipeline_layout, 0, real_size_of_sets, p_sets, 1, + &pDescriptorOffsets); + + if (m_is_use_stencil_pipeline) + { + vkCmdBindPipeline(m_p_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_pipeline_stencil_for_region_where_geometry_will_be_drawn); + } + else + { + if (p_texture) + { + if (m_is_apply_to_regular_geometry_stencil) + { + vkCmdBindPipeline(m_p_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_with_textures); + } + else + { + vkCmdBindPipeline(m_p_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_pipeline_with_textures); + } + } + else + { + if (m_is_apply_to_regular_geometry_stencil) + { + vkCmdBindPipeline(m_p_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_without_textures); + } + else + { + vkCmdBindPipeline(m_p_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_pipeline_without_textures); + } + } + } + + vkCmdBindVertexBuffers(m_p_current_command_buffer, 0, 1, &p_casted_compiled_geometry->m_p_vertex.buffer, + &p_casted_compiled_geometry->m_p_vertex.offset); + + vkCmdBindIndexBuffer(m_p_current_command_buffer, p_casted_compiled_geometry->m_p_index.buffer, p_casted_compiled_geometry->m_p_index.offset, + VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(m_p_current_command_buffer, p_casted_compiled_geometry->m_num_indices, 1, 0, 0, 0); +} + +void RenderInterface_VK::ReleaseGeometry(Rml::CompiledGeometryHandle geometry) +{ + RMLUI_ZoneScopedN("Vulkan - ReleaseCompiledGeometry"); + + geometry_handle_t* p_casted_geometry = reinterpret_cast(geometry); + + m_pending_for_deletion_geometries.push_back(p_casted_geometry); +} + +void RenderInterface_VK::EnableScissorRegion(bool enable) +{ + if (m_p_current_command_buffer == nullptr) + return; + + if (m_is_transform_enabled) + { + m_is_apply_to_regular_geometry_stencil = true; + } + + m_is_use_scissor_specified = enable; + + if (m_is_use_scissor_specified == false) + { + m_is_apply_to_regular_geometry_stencil = false; + vkCmdSetScissor(m_p_current_command_buffer, 0, 1, &m_scissor_original); + } +} + +void RenderInterface_VK::SetScissorRegion(Rml::Rectanglei region) +{ + if (m_is_use_scissor_specified) + { + if (m_is_transform_enabled) + { + Rml::Vertex vertices[4]; + + vertices[0].position = Rml::Vector2f(region.TopLeft()); + vertices[1].position = Rml::Vector2f(region.TopRight()); + vertices[2].position = Rml::Vector2f(region.BottomRight()); + vertices[3].position = Rml::Vector2f(region.BottomLeft()); + + int indices[6] = {0, 2, 1, 0, 3, 2}; + + m_is_use_stencil_pipeline = true; + +#ifdef RMLUI_DEBUG + VkDebugUtilsLabelEXT info{}; + info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; + info.color[0] = 1.0f; + info.color[1] = 1.0f; + info.color[2] = 0.0f; + info.color[3] = 1.0f; + info.pLabelName = "SetScissorRegion (generated region)"; + + vkCmdInsertDebugUtilsLabelEXT(m_p_current_command_buffer, &info); +#endif + + VkClearDepthStencilValue info_clear_color{}; + + info_clear_color.depth = 1.0f; + info_clear_color.stencil = 0; + + VkClearAttachment clear_attachment = {}; + clear_attachment.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + clear_attachment.clearValue.depthStencil = info_clear_color; + clear_attachment.colorAttachment = 1; + + VkClearRect clear_rect = {}; + clear_rect.layerCount = 1; + clear_rect.rect.extent.width = m_width; + clear_rect.rect.extent.height = m_height; + + vkCmdClearAttachments(m_p_current_command_buffer, 1, &clear_attachment, 1, &clear_rect); + + if (Rml::CompiledGeometryHandle handle = CompileGeometry({vertices, 4}, {indices, 6})) + { + RenderGeometry(handle, {}, {}); + ReleaseGeometry(handle); + } + + m_is_use_stencil_pipeline = false; + + m_is_apply_to_regular_geometry_stencil = true; + } + else + { + m_scissor.extent.width = region.Width(); + m_scissor.extent.height = region.Height(); + m_scissor.offset.x = Rml::Math::Clamp(region.Left(), 0, m_width); + m_scissor.offset.y = Rml::Math::Clamp(region.Top(), 0, m_height); + +#ifdef RMLUI_DEBUG + VkDebugUtilsLabelEXT info{}; + info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; + info.color[0] = 1.0f; + info.color[1] = 0.0f; + info.color[2] = 0.0f; + info.color[3] = 1.0f; + info.pLabelName = "SetScissorRegion (offset)"; + + vkCmdInsertDebugUtilsLabelEXT(m_p_current_command_buffer, &info); +#endif + + vkCmdSetScissor(m_p_current_command_buffer, 0, 1, &m_scissor); + } + } +} + +// Set to byte packing, or the compiler will expand our struct, which means it won't read correctly from file +#pragma pack(1) +struct TGAHeader { + char idLength; + char colourMapType; + char dataType; + short int colourMapOrigin; + short int colourMapLength; + char colourMapDepth; + short int xOrigin; + short int yOrigin; + short int width; + short int height; + char bitsPerPixel; + char imageDescriptor; +}; +// Restore packing +#pragma pack() + +Rml::TextureHandle RenderInterface_VK::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) +{ + Rml::FileInterface* file_interface = Rml::GetFileInterface(); + Rml::FileHandle file_handle = file_interface->Open(source); + if (!file_handle) + { + return false; + } + + file_interface->Seek(file_handle, 0, SEEK_END); + size_t buffer_size = file_interface->Tell(file_handle); + file_interface->Seek(file_handle, 0, SEEK_SET); + + if (buffer_size <= sizeof(TGAHeader)) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Texture file size is smaller than TGAHeader, file is not a valid TGA image."); + file_interface->Close(file_handle); + return false; + } + + using Rml::byte; + Rml::UniquePtr buffer(new byte[buffer_size]); + file_interface->Read(buffer.get(), buffer_size, file_handle); + file_interface->Close(file_handle); + + TGAHeader header; + memcpy(&header, buffer.get(), sizeof(TGAHeader)); + + int color_mode = header.bitsPerPixel / 8; + const size_t image_size = header.width * header.height * 4; // We always make 32bit textures + + if (header.dataType != 2) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24/32bit uncompressed TGAs are supported."); + return false; + } + + // Ensure we have at least 3 colors + if (color_mode < 3) + { + Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24 and 32bit textures are supported."); + return false; + } + + const byte* image_src = buffer.get() + sizeof(TGAHeader); + Rml::UniquePtr image_dest_buffer(new byte[image_size]); + byte* image_dest = image_dest_buffer.get(); + const bool top_to_bottom_order = ((header.imageDescriptor & 32) != 0); + + // Targa is BGR, swap to RGB, flip Y axis as necessary, and convert to premultiplied alpha. + for (long y = 0; y < header.height; y++) + { + long read_index = y * header.width * color_mode; + long write_index = top_to_bottom_order ? (y * header.width * 4) : (header.height - y - 1) * header.width * 4; + for (long x = 0; x < header.width; x++) + { + image_dest[write_index] = image_src[read_index + 2]; + image_dest[write_index + 1] = image_src[read_index + 1]; + image_dest[write_index + 2] = image_src[read_index]; + if (color_mode == 4) + { + const byte alpha = image_src[read_index + 3]; + for (size_t j = 0; j < 3; j++) + image_dest[write_index + j] = byte((image_dest[write_index + j] * alpha) / 255); + image_dest[write_index + 3] = alpha; + } + else + image_dest[write_index + 3] = 255; + + write_index += 4; + read_index += color_mode; + } + } + + texture_dimensions.x = header.width; + texture_dimensions.y = header.height; + + return GenerateTexture({image_dest, image_size}, texture_dimensions); +} + +Rml::TextureHandle RenderInterface_VK::GenerateTexture(Rml::Span source_data, Rml::Vector2i source_dimensions) +{ + RMLUI_ASSERT(source_data.data() && source_data.size() == size_t(source_dimensions.x * source_dimensions.y * 4)); + Rml::String source_name = "generated-texture"; + return CreateTexture(source_data, source_dimensions, source_name); +} + +/* + How vulkan works with textures efficiently? + + You need to create buffer that has CPU memory accessibility it means it uses your RAM memory for storing data and it has only CPU visibility (RAM) + After you create buffer that has GPU memory accessibility it means it uses by your video hardware and it has only VRAM (Video RAM) visibility + + So you copy data to CPU_buffer and after you copy that thing to GPU_buffer, but delete CPU_buffer + + So it means you "uploaded" data to GPU + + Again, you need to "write" data into CPU buffer after you need to copy that data from buffer to GPU buffer and after that buffer go to GPU. + + RAW_POINTER_DATA_BYTES_LITERALLY->COPY_TO->CPU->COPY_TO->GPU->Releasing_CPU <= that's how works uploading textures in Vulkan if you want to have + efficient handling otherwise it is cpu_to_gpu visibility and it means you create only ONE buffer that is accessible for CPU and for GPU, but it + will cause the worst performance... +*/ +Rml::TextureHandle RenderInterface_VK::CreateTexture(Rml::Span source, Rml::Vector2i dimensions, const Rml::String& name) +{ + RMLUI_ZoneScopedN("Vulkan - GenerateTexture"); + + RMLUI_VK_ASSERTMSG(!source.empty(), "you pushed not valid data for copying to buffer"); + RMLUI_VK_ASSERTMSG(m_p_allocator, "you have to initialize Vma Allocator for this method"); + (void)name; + + int width = dimensions.x; + int height = dimensions.y; + + RMLUI_VK_ASSERTMSG(width, "invalid width"); + RMLUI_VK_ASSERTMSG(height, "invalid height"); + + VkDeviceSize image_size = source.size(); + VkFormat format = VkFormat::VK_FORMAT_R8G8B8A8_UNORM; + + buffer_data_t cpu_buffer = CreateResource_StagingBuffer(image_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); + + void* data; + vmaMapMemory(m_p_allocator, cpu_buffer.m_p_vma_allocation, &data); + memcpy(data, source.data(), static_cast(image_size)); + vmaUnmapMemory(m_p_allocator, cpu_buffer.m_p_vma_allocation); + + VkExtent3D extent_image = {}; + extent_image.width = static_cast(width); + extent_image.height = static_cast(height); + extent_image.depth = 1; + + auto* p_texture = new texture_data_t{}; + + VkImageCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.pNext = nullptr; + info.imageType = VK_IMAGE_TYPE_2D; + info.format = format; + info.extent = extent_image; + info.mipLevels = 1; + info.arrayLayers = 1; + info.samples = VK_SAMPLE_COUNT_1_BIT; + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + VmaAllocationCreateInfo info_allocation = {}; + info_allocation.usage = VMA_MEMORY_USAGE_GPU_ONLY; + + VkImage p_image = nullptr; + VmaAllocation p_allocation = nullptr; + + VmaAllocationInfo info_stats = {}; + VkResult status = vmaCreateImage(m_p_allocator, &info, &info_allocation, &p_image, &p_allocation, &info_stats); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vmaCreateImage"); + +#ifdef RMLUI_VK_DEBUG + Rml::Log::Message(Rml::Log::LT_DEBUG, "Created texture '%s' [%dx%d, %s]", name.c_str(), dimensions.x, dimensions.y, + FormatByteSize(info_stats.size).c_str()); +#endif + + p_texture->m_p_vk_image = p_image; + p_texture->m_p_vma_allocation = p_allocation; + +#ifdef RMLUI_VK_DEBUG + vmaSetAllocationName(m_p_allocator, p_allocation, name.c_str()); +#endif + + /* + * So Vulkan works only through VkCommandBuffer, it is for remembering API commands what you want to call from GPU + * So on CPU side you need to create a scope that consists of two things + * vkBeginCommandBuffer + * ... <= here your commands what you want to place into your command buffer and send it to GPU through vkQueueSubmit function + * vkEndCommandBuffer + * + * So commands start to work ONLY when you called the vkQueueSubmit otherwise you just "place" commands into your command buffer but you + * didn't issue any thing in order to start the work on GPU side. ALWAYS remember that just sumbit means execute async mode, so you have to wait + * operations before they exeecute fully otherwise you will get some errors or write/read concurrent state and all other stuff, vulkan validation + * will notify you :) (in most cases) + * + * BUT you need always sync what you have done when you called your vkQueueSubmit function, so it is wait method, but generally you can create + * another queue and isolate all stuff tbh + * + * So understing these principles you understand how to work with API and your GPU + * + * There's nothing hard, but it makes all stuff on programmer side if you remember OpenGL and how it was easy to load texture upload it and create + * buffers and it In OpenGL all stuff is handled by driver and other things, not a programmer definitely + * + * What we do here? We need to change the layout of our image. it means where we want to use it. So in our case we want to see that this image + * will be in shaders Because the initial state of create object is VK_IMAGE_LAYOUT_UNDEFINED means you can't just pass that VkImage handle to + * your functions and wait that it comes to shaders for exmaple No it doesn't work like that you have to have the explicit states of your resource + * and where it goes + * + * In our case we want to see in our pixel shader so we need to change transfer into this flag VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, because we + * want to copy so it means some transfer thing, but after we say it goes to pixel after our copying operation + */ + m_upload_manager.UploadToGPU([p_image, extent_image, cpu_buffer](VkCommandBuffer p_cmd) { + VkImageSubresourceRange range = {}; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.baseArrayLayer = 0; + range.levelCount = 1; + range.layerCount = 1; + + VkImageMemoryBarrier info_barrier = {}; + info_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + info_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + info_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + info_barrier.image = p_image; + info_barrier.subresourceRange = range; + info_barrier.srcAccessMask = 0; + info_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + vkCmdPipelineBarrier(p_cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &info_barrier); + + VkBufferImageCopy region = {}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageExtent = extent_image; + + vkCmdCopyBufferToImage(p_cmd, cpu_buffer.m_p_vk_buffer, p_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + VkImageMemoryBarrier info_barrier_shader_read = {}; + info_barrier_shader_read.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + info_barrier_shader_read.pNext = nullptr; + info_barrier_shader_read.image = p_image; + info_barrier_shader_read.subresourceRange = range; + info_barrier_shader_read.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + info_barrier_shader_read.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + info_barrier_shader_read.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + info_barrier_shader_read.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(p_cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, + &info_barrier_shader_read); + }); + + DestroyResource_StagingBuffer(cpu_buffer); + + VkImageViewCreateInfo info_image_view = {}; + info_image_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info_image_view.pNext = nullptr; + info_image_view.image = p_texture->m_p_vk_image; + info_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; + info_image_view.format = format; + info_image_view.subresourceRange.baseMipLevel = 0; + info_image_view.subresourceRange.levelCount = 1; + info_image_view.subresourceRange.baseArrayLayer = 0; + info_image_view.subresourceRange.layerCount = 1; + info_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + + VkImageView p_image_view = nullptr; + status = vkCreateImageView(m_p_device, &info_image_view, nullptr, &p_image_view); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateImageView"); + + p_texture->m_p_vk_image_view = p_image_view; + p_texture->m_p_vk_sampler = m_p_sampler_linear; + + return reinterpret_cast(p_texture); +} + +void RenderInterface_VK::ReleaseTexture(Rml::TextureHandle texture_handle) +{ + texture_data_t* p_texture = reinterpret_cast(texture_handle); + + if (p_texture) + { + m_pending_for_deletion_textures_by_frames[m_semaphore_index_previous].push_back(p_texture); + } +} + +void RenderInterface_VK::SetTransform(const Rml::Matrix4f* transform) +{ + m_is_transform_enabled = !!(transform); + m_user_data_for_vertex_shader.m_transform = m_projection * (transform ? *transform : Rml::Matrix4f::Identity()); +} + +void RenderInterface_VK::BeginFrame() +{ + Wait(); + + Update_PendingForDeletion_Textures_By_Frames(); + Update_PendingForDeletion_Geometries(); + + m_command_buffer_ring.OnBeginFrame(); + m_p_current_command_buffer = m_command_buffer_ring.GetCommandBufferForActiveFrame(CommandBufferName::Primary); + + VkCommandBufferBeginInfo info = {}; + + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.pInheritanceInfo = nullptr; + info.pNext = nullptr; + info.flags = VkCommandBufferUsageFlagBits::VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + auto status = vkBeginCommandBuffer(m_p_current_command_buffer, &info); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkBeginCommandBuffer"); + + VkClearValue for_filling_back_buffer_color; + VkClearValue for_stencil_depth; + + for_stencil_depth.depthStencil = {1.0f, 0}; + for_filling_back_buffer_color.color = {{0.0f, 0.0f, 0.0f, 1.0f}}; + + const VkClearValue p_color_rt[] = {for_filling_back_buffer_color, for_stencil_depth}; + + VkRenderPassBeginInfo info_pass = {}; + + info_pass.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info_pass.pNext = nullptr; + info_pass.renderPass = m_p_render_pass; + info_pass.framebuffer = m_swapchain_frame_buffers[m_image_index]; + info_pass.pClearValues = p_color_rt; + info_pass.clearValueCount = 2; + info_pass.renderArea.offset.x = 0; + info_pass.renderArea.offset.y = 0; + info_pass.renderArea.extent.width = m_width; + info_pass.renderArea.extent.height = m_height; + + vkCmdBeginRenderPass(m_p_current_command_buffer, &info_pass, VkSubpassContents::VK_SUBPASS_CONTENTS_INLINE); + vkCmdSetViewport(m_p_current_command_buffer, 0, 1, &m_viewport); + + m_is_apply_to_regular_geometry_stencil = false; +} + +void RenderInterface_VK::EndFrame() +{ + if (m_p_current_command_buffer == nullptr) + return; + + vkCmdEndRenderPass(m_p_current_command_buffer); + + auto status = vkEndCommandBuffer(m_p_current_command_buffer); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkEndCommandBuffer"); + + Submit(); + Present(); + + m_p_current_command_buffer = nullptr; +} + +void RenderInterface_VK::SetViewport(int width, int height) +{ + auto status = vkDeviceWaitIdle(m_p_device); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkDeviceWaitIdle"); + + if (width > 0 && height > 0) + { + m_width = width; + m_height = height; + } + + if (m_p_swapchain) + { + Destroy_Swapchain(); + DestroyResourcesDependentOnSize(); + m_p_swapchain = {}; + } + + VkExtent2D window_extent = GetValidSurfaceExtent(); + if (window_extent.width == 0 || window_extent.height == 0) + return; + +#ifdef RMLUI_VK_DEBUG + Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "Rml width: %d height: %d | Vulkan width: %d height: %d", m_width, m_height, window_extent.width, + window_extent.height); +#endif + + // we need to sync the data from Vulkan so we can't use native Rml's data about width and height so be careful otherwise we create framebuffer + // with Rml's width and height but they're different to what Vulkan determines for our window (e.g. device/swapchain) + m_width = window_extent.width; + m_height = window_extent.height; + + Initialize_Swapchain(window_extent); + CreateResourcesDependentOnSize(window_extent); +} + +bool RenderInterface_VK::IsSwapchainValid() +{ + return m_p_swapchain != nullptr; +} + +void RenderInterface_VK::RecreateSwapchain() +{ + SetViewport(m_width, m_height); +} + +bool RenderInterface_VK::Initialize(Rml::Vector required_extensions, CreateSurfaceCallback create_surface_callback) +{ + RMLUI_ZoneScopedN("Vulkan - Initialize"); + + int glad_result = 0; + glad_result = gladLoaderLoadVulkan(VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE); + RMLUI_VK_ASSERTMSG(glad_result != 0, "Vulkan loader failed - Global functions"); + + Initialize_Instance(std::move(required_extensions)); + + VkPhysicalDeviceProperties physical_device_properties = {}; + Initialize_PhysicalDevice(physical_device_properties); + + glad_result = gladLoaderLoadVulkan(m_p_instance, m_p_physical_device, VK_NULL_HANDLE); + RMLUI_VK_ASSERTMSG(glad_result != 0, "Vulkan loader failed - Instance functions"); + + Initialize_Surface(create_surface_callback); + Initialize_QueueIndecies(); + Initialize_Device(); + + glad_result = gladLoaderLoadVulkan(m_p_instance, m_p_physical_device, m_p_device); + RMLUI_VK_ASSERTMSG(glad_result != 0, "Vulkan loader failed - Device functions"); + + Initialize_Queues(); + Initialize_SyncPrimitives(); + Initialize_Allocator(); + Initialize_Resources(physical_device_properties); + + return true; +} + +void RenderInterface_VK::Shutdown() +{ + RMLUI_ZoneScopedN("Vulkan - Shutdown"); + + auto status = vkDeviceWaitIdle(m_p_device); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "you must have a valid status here"); + + DestroyResourcesDependentOnSize(); + Destroy_Resources(); + Destroy_Allocator(); + Destroy_SyncPrimitives(); + Destroy_Swapchain(); + Destroy_Surface(); + Destroy_Device(); + Destroy_ReportDebugCallback(); + Destroy_Instance(); + + gladLoaderUnloadVulkan(); +} + +void RenderInterface_VK::Initialize_Instance(Rml::Vector required_extensions) noexcept +{ + uint32_t required_version = GetRequiredVersionAndValidateMachine(); + + VkApplicationInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + info.pNext = nullptr; + info.pApplicationName = "RmlUi Shell"; + info.applicationVersion = 50; + info.pEngineName = "RmlUi"; + info.apiVersion = required_version; + + Rml::Vector instance_layer_names; + Rml::Vector instance_extension_names = std::move(required_extensions); + CreatePropertiesFor_Instance(instance_layer_names, instance_extension_names); + + VkInstanceCreateInfo info_instance = {}; + info_instance.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + info_instance.pNext = &debug_validation_features_ext; + info_instance.flags = 0; + info_instance.pApplicationInfo = &info; + info_instance.enabledExtensionCount = static_cast(instance_extension_names.size()); + info_instance.ppEnabledExtensionNames = instance_extension_names.data(); + info_instance.enabledLayerCount = static_cast(instance_layer_names.size()); + info_instance.ppEnabledLayerNames = instance_layer_names.data(); + + VkResult status = vkCreateInstance(&info_instance, nullptr, &m_p_instance); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkCreateInstance"); + + CreateReportDebugCallback(); +} + +void RenderInterface_VK::Initialize_Device() noexcept +{ + ExtensionPropertiesList device_extension_properties; + CreatePropertiesFor_Device(device_extension_properties); + + Rml::Vector device_extension_names; + AddExtensionToDevice(device_extension_names, device_extension_properties, VK_KHR_SWAPCHAIN_EXTENSION_NAME); + AddExtensionToDevice(device_extension_names, device_extension_properties, VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME); + +#ifdef RMLUI_DEBUG + AddExtensionToDevice(device_extension_names, device_extension_properties, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); +#endif + + float queue_priorities[1] = {0.0f}; + + VkDeviceQueueCreateInfo info_queue[2] = {}; + + info_queue[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + info_queue[0].pNext = nullptr; + info_queue[0].queueCount = 1; + info_queue[0].pQueuePriorities = queue_priorities; + info_queue[0].queueFamilyIndex = m_queue_index_graphics; + + info_queue[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + info_queue[1].pNext = nullptr; + info_queue[1].queueCount = 1; + info_queue[1].pQueuePriorities = queue_priorities; + info_queue[1].queueFamilyIndex = m_queue_index_compute; + + VkPhysicalDeviceFeatures features_physical_device = {}; + + features_physical_device.fillModeNonSolid = true; + features_physical_device.pipelineStatisticsQuery = true; + features_physical_device.fragmentStoresAndAtomics = true; + features_physical_device.vertexPipelineStoresAndAtomics = true; + features_physical_device.shaderImageGatherExtended = true; + features_physical_device.wideLines = true; + + VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR shader_subgroup_extended_type = {}; + + shader_subgroup_extended_type.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES_KHR; + shader_subgroup_extended_type.pNext = nullptr; + shader_subgroup_extended_type.shaderSubgroupExtendedTypes = VK_TRUE; + + VkPhysicalDeviceFeatures2 features_physical_device2 = {}; + + features_physical_device2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + features_physical_device2.features = features_physical_device; + features_physical_device2.pNext = &shader_subgroup_extended_type; + + VkDeviceCreateInfo info_device = {}; + + info_device.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + info_device.pNext = &features_physical_device2; + info_device.queueCreateInfoCount = m_queue_index_compute != m_queue_index_graphics ? 2 : 1; + info_device.pQueueCreateInfos = info_queue; + info_device.enabledExtensionCount = static_cast(device_extension_names.size()); + info_device.ppEnabledExtensionNames = info_device.enabledExtensionCount ? device_extension_names.data() : nullptr; + info_device.pEnabledFeatures = nullptr; + + VkResult status = vkCreateDevice(m_p_physical_device, &info_device, nullptr, &m_p_device); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkCreateDevice"); +} + +void RenderInterface_VK::Initialize_PhysicalDevice(VkPhysicalDeviceProperties& out_physical_device_properties) noexcept +{ + PhysicalDeviceWrapperList physical_devices; + CollectPhysicalDevices(physical_devices); + + const PhysicalDeviceWrapper* selected_physical_device = + ChoosePhysicalDevice(physical_devices, VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); + + if (!selected_physical_device) + { + Rml::Log::Message(Rml::Log::LT_WARNING, "Failed to pick the discrete gpu, now trying to pick integrated GPU"); + selected_physical_device = ChoosePhysicalDevice(physical_devices, VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU); + + if (!selected_physical_device) + { + Rml::Log::Message(Rml::Log::LT_WARNING, "Failed to pick the integrated gpu, now trying to pick the CPU"); + selected_physical_device = ChoosePhysicalDevice(physical_devices, VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU); + } + } + + RMLUI_VK_ASSERTMSG(selected_physical_device, "there's no suitable physical device for rendering, abort this application"); + + m_p_physical_device = selected_physical_device->m_p_physical_device; + vkGetPhysicalDeviceProperties(m_p_physical_device, &out_physical_device_properties); + +#ifdef RMLUI_VK_DEBUG + const auto& properties = selected_physical_device->m_physical_device_properties; + Rml::Log::Message(Rml::Log::LT_DEBUG, "Picked physical device: %s", properties.deviceName); +#endif +} + +void RenderInterface_VK::Initialize_Swapchain(VkExtent2D window_extent) noexcept +{ + m_swapchain_format = ChooseSwapchainFormat(); + + VkSwapchainCreateInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + info.pNext = nullptr; + info.surface = m_p_surface; + info.imageFormat = m_swapchain_format.format; + info.minImageCount = Choose_SwapchainImageCount(); + info.imageColorSpace = m_swapchain_format.colorSpace; + info.imageExtent = window_extent; + info.preTransform = CreatePretransformSwapchain(); + info.compositeAlpha = ChooseSwapchainCompositeAlpha(); + info.imageArrayLayers = 1; + info.presentMode = GetPresentMode(); + info.oldSwapchain = nullptr; + info.clipped = true; + info.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.queueFamilyIndexCount = 0; + info.pQueueFamilyIndices = nullptr; + + uint32_t queue_family_index_present = m_queue_index_present; + uint32_t queue_family_index_graphics = m_queue_index_graphics; + + if (queue_family_index_graphics != queue_family_index_present) + { + uint32_t p_indecies[2] = {queue_family_index_graphics, queue_family_index_present}; + + info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + info.queueFamilyIndexCount = sizeof(p_indecies) / sizeof(p_indecies[0]); + info.pQueueFamilyIndices = p_indecies; + } + + VkResult status = vkCreateSwapchainKHR(m_p_device, &info, nullptr, &m_p_swapchain); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkCreateSwapchainKHR"); +} + +void RenderInterface_VK::Initialize_Surface(CreateSurfaceCallback create_surface_callback) noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_instance, "you must initialize your VkInstance"); + + bool result = create_surface_callback(m_p_instance, &m_p_surface); + RMLUI_VK_ASSERTMSG(result && m_p_surface, "failed to call create_surface_callback"); +} + +void RenderInterface_VK::Initialize_QueueIndecies() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_physical_device, "you must initialize your physical device"); + RMLUI_VK_ASSERTMSG(m_p_surface, "you must initialize VkSurfaceKHR before calling this method"); + + uint32_t queue_family_count = 0; + + vkGetPhysicalDeviceQueueFamilyProperties(m_p_physical_device, &queue_family_count, nullptr); + + RMLUI_VK_ASSERTMSG(queue_family_count >= 1, "failed to vkGetPhysicalDeviceQueueFamilyProperties (getting count)"); + + Rml::Vector queue_props; + queue_props.resize(queue_family_count); + + vkGetPhysicalDeviceQueueFamilyProperties(m_p_physical_device, &queue_family_count, queue_props.data()); + + RMLUI_VK_ASSERTMSG(queue_family_count >= 1, "failed to vkGetPhysicalDeviceQueueFamilyProperties (filling vector of VkQueueFamilyProperties)"); + + constexpr uint32_t kUint32Undefined = uint32_t(-1); + + m_queue_index_compute = kUint32Undefined; + m_queue_index_graphics = kUint32Undefined; + m_queue_index_present = kUint32Undefined; + + for (uint32_t i = 0; i < queue_family_count; ++i) + { + if ((queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) + { + if (m_queue_index_graphics == kUint32Undefined) + m_queue_index_graphics = i; + + VkBool32 is_support_present; + + vkGetPhysicalDeviceSurfaceSupportKHR(m_p_physical_device, i, m_p_surface, &is_support_present); + + // User's videocard may have same index for two queues like graphics and present + + if (is_support_present == VK_TRUE) + { + m_queue_index_graphics = i; + m_queue_index_present = m_queue_index_graphics; + break; + } + } + } + + if (m_queue_index_present == static_cast(-1)) + { + Rml::Log::Message(Rml::Log::LT_WARNING, "[Vulkan] User doesn't have one index for two queues, so we need to find for present queue index"); + + for (uint32_t i = 0; i < queue_family_count; ++i) + { + VkBool32 is_support_present; + + vkGetPhysicalDeviceSurfaceSupportKHR(m_p_physical_device, i, m_p_surface, &is_support_present); + + if (is_support_present == VK_TRUE) + { + m_queue_index_present = i; + break; + } + } + } + + for (uint32_t i = 0; i < queue_family_count; ++i) + { + if ((queue_props[i].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0) + { + if (m_queue_index_compute == kUint32Undefined) + m_queue_index_compute = i; + + if (i != m_queue_index_graphics) + { + m_queue_index_compute = i; + break; + } + } + } + +#ifdef RMLUI_VK_DEBUG + Rml::Log::Message(Rml::Log::LT_DEBUG, "[Vulkan] User family queues indecies: Graphics[%d] Present[%d] Compute[%d]", m_queue_index_graphics, + m_queue_index_present, m_queue_index_compute); +#endif +} + +void RenderInterface_VK::Initialize_Queues() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must initialize VkDevice before using this method"); + + vkGetDeviceQueue(m_p_device, m_queue_index_graphics, 0, &m_p_queue_graphics); + + if (m_queue_index_graphics == m_queue_index_present) + { + m_p_queue_present = m_p_queue_graphics; + } + else + { + vkGetDeviceQueue(m_p_device, m_queue_index_present, 0, &m_p_queue_present); + } + + constexpr uint32_t kUint32Undefined = uint32_t(-1); + + if (m_queue_index_compute != kUint32Undefined) + { + vkGetDeviceQueue(m_p_device, m_queue_index_compute, 0, &m_p_queue_compute); + } +} + +void RenderInterface_VK::Initialize_SyncPrimitives() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must initialize your device"); + + m_executed_fences.resize(kSwapchainBackBufferCount); + m_semaphores_finished_render.resize(kSwapchainBackBufferCount); + m_semaphores_image_available.resize(kSwapchainBackBufferCount); + + VkResult status = VK_SUCCESS; + + for (uint32_t i = 0; i < kSwapchainBackBufferCount; ++i) + { + VkFenceCreateInfo info_fence = {}; + + info_fence.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + info_fence.pNext = nullptr; + info_fence.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + status = vkCreateFence(m_p_device, &info_fence, nullptr, &m_executed_fences[i]); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkCreateFence"); + + VkSemaphoreCreateInfo info_semaphore = {}; + + info_semaphore.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + info_semaphore.pNext = nullptr; + info_semaphore.flags = 0; + + status = vkCreateSemaphore(m_p_device, &info_semaphore, nullptr, &m_semaphores_image_available[i]); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkCreateSemaphore"); + + status = vkCreateSemaphore(m_p_device, &info_semaphore, nullptr, &m_semaphores_finished_render[i]); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkCreateSemaphore"); + } +} + +void RenderInterface_VK::Initialize_Resources(const VkPhysicalDeviceProperties& physical_device_properties) noexcept +{ + m_command_buffer_ring.Initialize(m_p_device, m_queue_index_graphics); + + const VkDeviceSize min_buffer_alignment = physical_device_properties.limits.minUniformBufferOffsetAlignment; + m_memory_pool.Initialize(kVideoMemoryForAllocation, min_buffer_alignment, m_p_allocator, m_p_device); + + m_upload_manager.Initialize(m_p_device, m_p_queue_graphics, m_queue_index_graphics); + m_manager_descriptors.Initialize(m_p_device, 100, 100, 10, 10); + + CreateShaders(); + CreateDescriptorSetLayout(); + CreatePipelineLayout(); + CreateSamplers(); + CreateDescriptorSets(); +} + +void RenderInterface_VK::Initialize_Allocator() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must have a valid VkDevice here"); + RMLUI_VK_ASSERTMSG(m_p_physical_device, "you must have a valid VkPhysicalDevice here"); + RMLUI_VK_ASSERTMSG(m_p_instance, "you must have a valid VkInstance here"); + + VmaVulkanFunctions vulkanFunctions = {}; + vulkanFunctions.vkGetInstanceProcAddr = vkGetInstanceProcAddr; + vulkanFunctions.vkGetDeviceProcAddr = vkGetDeviceProcAddr; + + VmaAllocatorCreateInfo info = {}; + + info.vulkanApiVersion = RMLUI_VK_API_VERSION; + info.device = m_p_device; + info.instance = m_p_instance; + info.physicalDevice = m_p_physical_device; + info.pVulkanFunctions = &vulkanFunctions; + + auto status = vmaCreateAllocator(&info, &m_p_allocator); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vmaCreateAllocator"); +} + +void RenderInterface_VK::Destroy_Instance() noexcept +{ + vkDestroyInstance(m_p_instance, nullptr); +} + +void RenderInterface_VK::Destroy_Device() noexcept +{ + vkDestroyDevice(m_p_device, nullptr); +} + +void RenderInterface_VK::Destroy_Swapchain() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must initialize device"); + + vkDestroySwapchainKHR(m_p_device, m_p_swapchain, nullptr); +} + +void RenderInterface_VK::Destroy_Surface() noexcept +{ + vkDestroySurfaceKHR(m_p_instance, m_p_surface, nullptr); +} + +void RenderInterface_VK::Destroy_SyncPrimitives() noexcept +{ + for (auto& p_fence : m_executed_fences) + { + vkDestroyFence(m_p_device, p_fence, nullptr); + } + + for (auto& p_semaphore : m_semaphores_image_available) + { + vkDestroySemaphore(m_p_device, p_semaphore, nullptr); + } + + for (auto& p_semaphore : m_semaphores_finished_render) + { + vkDestroySemaphore(m_p_device, p_semaphore, nullptr); + } +} + +void RenderInterface_VK::Destroy_Resources() noexcept +{ + m_command_buffer_ring.Shutdown(); + m_upload_manager.Shutdown(); + + if (m_p_descriptor_set) + { + m_manager_descriptors.Free_Descriptors(m_p_device, &m_p_descriptor_set); + } + + vkDestroyDescriptorSetLayout(m_p_device, m_p_descriptor_set_layout_vertex_transform, nullptr); + vkDestroyDescriptorSetLayout(m_p_device, m_p_descriptor_set_layout_texture, nullptr); + + vkDestroyPipelineLayout(m_p_device, m_p_pipeline_layout, nullptr); + + for (const auto& p_module : m_shaders) + { + vkDestroyShaderModule(m_p_device, p_module, nullptr); + } + + DestroySamplers(); + Destroy_Textures(); + Destroy_Geometries(); + + m_manager_descriptors.Shutdown(m_p_device); +} + +void RenderInterface_VK::Destroy_Allocator() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_allocator, "you must have an initialized allocator for deleting"); + + vmaDestroyAllocator(m_p_allocator); + + m_p_allocator = nullptr; +} + +void RenderInterface_VK::QueryInstanceLayers(LayerPropertiesList& result) noexcept +{ + uint32_t instance_layer_properties_count = 0; + VkResult status = vkEnumerateInstanceLayerProperties(&instance_layer_properties_count, nullptr); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateInstanceLayerProperties (getting count)"); + + if (instance_layer_properties_count) + { + result.resize(instance_layer_properties_count); + status = vkEnumerateInstanceLayerProperties(&instance_layer_properties_count, result.data()); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateInstanceLayerProperties (filling vector of VkLayerProperties)"); + } +} + +void RenderInterface_VK::QueryInstanceExtensions(ExtensionPropertiesList& result, const LayerPropertiesList& instance_layer_properties) noexcept +{ + uint32_t instance_extension_property_count = 0; + VkResult status = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_property_count, nullptr); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateInstanceExtensionProperties (getting count)"); + + if (instance_extension_property_count) + { + result.resize(instance_extension_property_count); + status = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_property_count, result.data()); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateInstanceExtensionProperties (filling vector of VkExtensionProperties)"); + } + + uint32_t count = 0; + + // without first argument in vkEnumerateInstanceExtensionProperties + // it doesn't collect information well so we need brute-force + // and pass through everything what use has + for (const auto& layer_property : instance_layer_properties) + { + status = vkEnumerateInstanceExtensionProperties(layer_property.layerName, &count, nullptr); + + if (status == VK_SUCCESS) + { + if (count) + { + ExtensionPropertiesList props; + props.resize(count); + status = vkEnumerateInstanceExtensionProperties(layer_property.layerName, &count, props.data()); + + if (status == VK_SUCCESS) + { +#ifdef RMLUI_VK_DEBUG + Rml::Log::Message(Rml::Log::LT_DEBUG, "[Vulkan] obtained extensions for layer: %s, count: %zu", layer_property.layerName, + props.size()); +#endif + + for (const auto& extension : props) + { + if (IsExtensionPresent(result, extension.extensionName) == false) + { +#ifdef RMLUI_VK_DEBUG + Rml::Log::Message(Rml::Log::LT_DEBUG, "[Vulkan] new extension is added: %s", extension.extensionName); +#endif + + result.push_back(extension); + } + } + } + } + } + } +} + +bool RenderInterface_VK::AddLayerToInstance(Rml::Vector& result, const LayerPropertiesList& instance_layer_properties, + const char* p_instance_layer_name) noexcept +{ + if (p_instance_layer_name == nullptr) + { + RMLUI_VK_ASSERTMSG(p_instance_layer_name, "you have an invalid layer"); + return false; + } + + if (IsLayerPresent(instance_layer_properties, p_instance_layer_name)) + { + result.push_back(p_instance_layer_name); + return true; + } + + Rml::Log::Message(Rml::Log::LT_WARNING, "[Vulkan] can't add layer %s", p_instance_layer_name); + + return false; +} + +bool RenderInterface_VK::AddExtensionToInstance(Rml::Vector& result, const ExtensionPropertiesList& instance_extension_properties, + const char* p_instance_extension_name) noexcept +{ + if (p_instance_extension_name == nullptr) + { + RMLUI_VK_ASSERTMSG(p_instance_extension_name, "you have an invalid extension"); + return false; + } + + if (IsExtensionPresent(instance_extension_properties, p_instance_extension_name)) + { + result.push_back(p_instance_extension_name); + return true; + } + + Rml::Log::Message(Rml::Log::LT_WARNING, "[Vulkan] can't add extension %s", p_instance_extension_name); + + return false; +} + +void RenderInterface_VK::CreatePropertiesFor_Instance(Rml::Vector& instance_layer_names, + Rml::Vector& instance_extension_names) noexcept +{ + ExtensionPropertiesList instance_extension_properties; + LayerPropertiesList instance_layer_properties; + + QueryInstanceLayers(instance_layer_properties); + QueryInstanceExtensions(instance_extension_properties, instance_layer_properties); + + AddExtensionToInstance(instance_extension_names, instance_extension_properties, "VK_EXT_debug_utils"); + AddExtensionToInstance(instance_extension_names, instance_extension_properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + +#ifdef RMLUI_VK_DEBUG + AddLayerToInstance(instance_layer_names, instance_layer_properties, "VK_LAYER_LUNARG_monitor"); + + bool is_cpu_validation = AddLayerToInstance(instance_layer_names, instance_layer_properties, "VK_LAYER_KHRONOS_validation") && + AddExtensionToInstance(instance_extension_names, instance_extension_properties, VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + + if (is_cpu_validation) + { + Rml::Log::Message(Rml::Log::LT_DEBUG, "[Vulkan] CPU validation is enabled"); + + Rml::Array requested_extensions_for_gpu = {VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME}; + + for (const auto& extension_name : requested_extensions_for_gpu) + { + AddExtensionToInstance(instance_extension_names, instance_extension_properties, extension_name); + } + + debug_validation_features_ext.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; + debug_validation_features_ext.pNext = nullptr; + debug_validation_features_ext.enabledValidationFeatureCount = + sizeof(debug_validation_features_ext_requested) / sizeof(debug_validation_features_ext_requested[0]); + debug_validation_features_ext.pEnabledValidationFeatures = debug_validation_features_ext_requested; + } + +#else + (void)instance_layer_names; + +#endif +} + +bool RenderInterface_VK::IsLayerPresent(const LayerPropertiesList& properties, const char* p_layer_name) noexcept +{ + if (properties.empty()) + return false; + + if (p_layer_name == nullptr) + return false; + + return std::find_if(properties.cbegin(), properties.cend(), + [p_layer_name](const VkLayerProperties& prop) -> bool { return strcmp(prop.layerName, p_layer_name) == 0; }) != properties.cend(); +} + +bool RenderInterface_VK::IsExtensionPresent(const ExtensionPropertiesList& properties, const char* p_extension_name) noexcept +{ + if (properties.empty()) + return false; + + if (p_extension_name == nullptr) + return false; + + return std::find_if(properties.cbegin(), properties.cend(), [p_extension_name](const VkExtensionProperties& prop) -> bool { + return strcmp(prop.extensionName, p_extension_name) == 0; + }) != properties.cend(); +} + +bool RenderInterface_VK::AddExtensionToDevice(Rml::Vector& result, const ExtensionPropertiesList& device_extension_properties, + const char* p_device_extension_name) noexcept +{ + if (IsExtensionPresent(device_extension_properties, p_device_extension_name)) + { + result.push_back(p_device_extension_name); + return true; + } + + return false; +} + +void RenderInterface_VK::CreatePropertiesFor_Device(ExtensionPropertiesList& result) noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_physical_device, "you must initialize your physical device. Call InitializePhysicalDevice first"); + + uint32_t extension_count = 0; + VkResult status = vkEnumerateDeviceExtensionProperties(m_p_physical_device, nullptr, &extension_count, nullptr); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateDeviceExtensionProperties (getting count)"); + + result.resize(extension_count); + status = vkEnumerateDeviceExtensionProperties(m_p_physical_device, nullptr, &extension_count, result.data()); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateDeviceExtensionProperties (filling vector of VkExtensionProperties)"); + + uint32_t instance_layer_property_count = 0; + status = vkEnumerateInstanceLayerProperties(&instance_layer_property_count, nullptr); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateInstanceLayerProperties (getting count)"); + + LayerPropertiesList layers; + layers.resize(instance_layer_property_count); + + // On different OS Vulkan acts strange, so we can't get our extensions to just iterate through default functions + // We need to deeply analyze our layers and get specified extensions which pass user + // So we collect all extensions that are presented in physical device + // And add when they exist to extension_names so we don't pass properties + + if (instance_layer_property_count) + { + status = vkEnumerateInstanceLayerProperties(&instance_layer_property_count, layers.data()); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateInstanceLayerProperties (filling vector of VkLayerProperties)"); + + for (const auto& layer : layers) + { + extension_count = 0; + status = vkEnumerateDeviceExtensionProperties(m_p_physical_device, layer.layerName, &extension_count, nullptr); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateDeviceExtensionProperties (getting count)"); + + if (extension_count) + { + ExtensionPropertiesList new_extensions; + new_extensions.resize(extension_count); + + status = vkEnumerateDeviceExtensionProperties(m_p_physical_device, layer.layerName, &extension_count, new_extensions.data()); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateDeviceExtensionProperties (filling vector of VkExtensionProperties)"); + + for (const auto& extension : new_extensions) + { + if (IsExtensionPresent(result, extension.extensionName) == false) + { +#ifdef RMLUI_VK_DEBUG + Rml::Log::Message(Rml::Log::LT_DEBUG, "[Vulkan] obtained new device extension from layer[%s]: %s", layer.layerName, + extension.extensionName); +#endif + + result.push_back(extension); + } + } + } + } + } +} + +void RenderInterface_VK::CreateReportDebugCallback() noexcept +{ +#ifdef RMLUI_VK_DEBUG + VkDebugUtilsMessengerCreateInfoEXT info = {}; + + info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; + info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + info.pfnUserCallback = MyDebugReportCallback; + + PFN_vkCreateDebugUtilsMessengerEXT p_callback_creation = VK_NULL_HANDLE; + + p_callback_creation = reinterpret_cast(vkGetInstanceProcAddr(m_p_instance, "vkCreateDebugUtilsMessengerEXT")); + VkResult status = p_callback_creation(m_p_instance, &info, nullptr, &m_debug_messenger); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkCreateDebugUtilsMessengerEXT"); +#endif +} + +void RenderInterface_VK::Destroy_ReportDebugCallback() noexcept +{ +#ifdef RMLUI_VK_DEBUG + PFN_vkDestroyDebugUtilsMessengerEXT p_destroy_callback = + reinterpret_cast(vkGetInstanceProcAddr(m_p_instance, "vkDestroyDebugUtilsMessengerEXT")); + + if (m_debug_messenger) + { + p_destroy_callback(m_p_instance, m_debug_messenger, nullptr); + m_debug_messenger = VK_NULL_HANDLE; + } +#endif +} + +uint32_t RenderInterface_VK::GetUserAPIVersion() const noexcept +{ + uint32_t result = RMLUI_VK_API_VERSION; + +#if defined VK_VERSION_1_1 + VkResult status = vkEnumerateInstanceVersion(&result); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumerateInstanceVersion, See Status"); +#endif + + return result; +} + +uint32_t RenderInterface_VK::GetRequiredVersionAndValidateMachine() noexcept +{ + constexpr uint32_t kRequiredVersion = RMLUI_VK_API_VERSION; + const uint32_t user_version = GetUserAPIVersion(); + + RMLUI_VK_ASSERTMSG(kRequiredVersion <= user_version, "Your machine doesn't support Vulkan"); + + return kRequiredVersion; +} + +void RenderInterface_VK::CollectPhysicalDevices(PhysicalDeviceWrapperList& out_physical_devices) noexcept +{ + uint32_t gpu_count = 1; + Rml::Vector temp_devices; + + VkResult status = vkEnumeratePhysicalDevices(m_p_instance, &gpu_count, nullptr); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumeratePhysicalDevices (getting count)"); + + temp_devices.resize(gpu_count); + status = vkEnumeratePhysicalDevices(m_p_instance, &gpu_count, temp_devices.data()); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkEnumeratePhysicalDevices (filling the vector of VkPhysicalDevice)"); + RMLUI_VK_ASSERTMSG(temp_devices.empty() == false, "you must have one videocard at least!"); + + out_physical_devices.resize(temp_devices.size()); + for (size_t i = 0; i < out_physical_devices.size(); i++) + { + out_physical_devices[i].m_p_physical_device = temp_devices[i]; + vkGetPhysicalDeviceProperties(out_physical_devices[i].m_p_physical_device, &out_physical_devices[i].m_physical_device_properties); + } +} + +const RenderInterface_VK::PhysicalDeviceWrapper* RenderInterface_VK::ChoosePhysicalDevice(const PhysicalDeviceWrapperList& physical_devices, + VkPhysicalDeviceType device_type) noexcept +{ + RMLUI_VK_ASSERTMSG(physical_devices.empty() == false, + "you must have one videocard at least or early calling of this method, try call this after CollectPhysicalDevices"); + + for (const auto& device : physical_devices) + { + if (device.m_physical_device_properties.deviceType == device_type) + return &device; + } + + return nullptr; +} + +VkSurfaceFormatKHR RenderInterface_VK::ChooseSwapchainFormat() noexcept +{ + static constexpr VkFormat UNORM_FORMATS[] = { + 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_R8G8_UNORM, + VK_FORMAT_R8G8B8_UNORM, + VK_FORMAT_B8G8R8_UNORM, + VK_FORMAT_R8G8B8A8_UNORM, + VK_FORMAT_B8G8R8A8_UNORM, + VK_FORMAT_A8B8G8R8_UNORM_PACK32, + VK_FORMAT_A2R10G10B10_UNORM_PACK32, + VK_FORMAT_A2B10G10R10_UNORM_PACK32, + VK_FORMAT_R16_UNORM, + VK_FORMAT_R16G16_UNORM, + VK_FORMAT_R16G16B16_UNORM, + VK_FORMAT_R16G16B16A16_UNORM, + VK_FORMAT_D16_UNORM, + VK_FORMAT_X8_D24_UNORM_PACK32, + VK_FORMAT_D16_UNORM_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_BC1_RGB_UNORM_BLOCK, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK, + VK_FORMAT_BC2_UNORM_BLOCK, + VK_FORMAT_BC3_UNORM_BLOCK, + VK_FORMAT_BC4_UNORM_BLOCK, + VK_FORMAT_BC5_UNORM_BLOCK, + VK_FORMAT_BC7_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + VK_FORMAT_EAC_R11_UNORM_BLOCK, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK, + VK_FORMAT_ASTC_5x4_UNORM_BLOCK, + VK_FORMAT_ASTC_5x5_UNORM_BLOCK, + VK_FORMAT_ASTC_6x5_UNORM_BLOCK, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK, + VK_FORMAT_ASTC_8x5_UNORM_BLOCK, + VK_FORMAT_ASTC_8x6_UNORM_BLOCK, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK, + VK_FORMAT_ASTC_10x5_UNORM_BLOCK, + VK_FORMAT_ASTC_10x6_UNORM_BLOCK, + VK_FORMAT_ASTC_10x8_UNORM_BLOCK, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK, + VK_FORMAT_ASTC_12x10_UNORM_BLOCK, + VK_FORMAT_ASTC_12x12_UNORM_BLOCK, + }; + + RMLUI_VK_ASSERTMSG(m_p_physical_device, "you must initialize your physical device, before calling this method"); + RMLUI_VK_ASSERTMSG(m_p_surface, "you must initialize your surface, before calling this method"); + + uint32_t surface_count = 0; + VkResult status = vkGetPhysicalDeviceSurfaceFormatsKHR(m_p_physical_device, m_p_surface, &surface_count, nullptr); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkGetPhysicalDeviceSurfaceFormatsKHR (getting count)"); + + Rml::Vector formats(surface_count); + status = vkGetPhysicalDeviceSurfaceFormatsKHR(m_p_physical_device, m_p_surface, &surface_count, formats.data()); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkGetPhysicalDeviceSurfaceFormatsKHR (filling vector of VkSurfaceFormatKHR)"); + + // Prefer UNORM formats + for (auto& format : formats) + { + for (auto ufmt : UNORM_FORMATS) + { + if (ufmt == format.format) + return format; + } + } + + return formats.front(); +} + +VkExtent2D RenderInterface_VK::GetValidSurfaceExtent() noexcept +{ + VkSurfaceCapabilitiesKHR caps = GetSurfaceCapabilities(); + VkExtent2D result = {(uint32_t)m_width, (uint32_t)m_height}; + + /* + https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSurfaceCapabilitiesKHR.html + */ + if (caps.currentExtent.width == 0xFFFFFFFF) + { + result.width = Rml::Math::Clamp(result.width, caps.minImageExtent.width, caps.maxImageExtent.width); + result.height = Rml::Math::Clamp(result.height, caps.minImageExtent.height, caps.maxImageExtent.height); + } + else + { + result = caps.currentExtent; + } + + return result; +} + +VkSurfaceTransformFlagBitsKHR RenderInterface_VK::CreatePretransformSwapchain() noexcept +{ + auto caps = GetSurfaceCapabilities(); + + VkSurfaceTransformFlagBitsKHR result = + (caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : caps.currentTransform; + + return result; +} + +VkCompositeAlphaFlagBitsKHR RenderInterface_VK::ChooseSwapchainCompositeAlpha() noexcept +{ + auto caps = GetSurfaceCapabilities(); + + VkCompositeAlphaFlagBitsKHR result = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + + VkCompositeAlphaFlagBitsKHR composite_alpha_flags[4] = {VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR}; + + for (uint32_t i = 0; i < sizeof(composite_alpha_flags); ++i) + { + if (caps.supportedCompositeAlpha & composite_alpha_flags[i]) + { + result = composite_alpha_flags[i]; + break; + } + } + + return result; +} + +int RenderInterface_VK::Choose_SwapchainImageCount(uint32_t user_swapchain_count_for_creation, bool if_failed_choose_min) noexcept +{ + auto caps = GetSurfaceCapabilities(); + + // don't worry if you get this assert just ignore it the method will fix the count ;) + RMLUI_VK_ASSERTMSG(user_swapchain_count_for_creation >= caps.minImageCount, + "can't be, you must have a valid count that bounds from minImageCount to maxImageCount! Otherwise you will get a validation error that " + "specifies that you created a swapchain with invalid image count"); + RMLUI_VK_ASSERTMSG(user_swapchain_count_for_creation <= caps.maxImageCount, + "can't be, you must have a valid count that bounds from minImageCount to maxImageCount! Otherwise you will get a validation error that " + "specifies that you created a swapchain with invalid image count"); + + int result = 0; + + if (user_swapchain_count_for_creation < caps.minImageCount || user_swapchain_count_for_creation > caps.maxImageCount) + result = if_failed_choose_min ? caps.minImageCount : caps.maxImageCount; + else + result = user_swapchain_count_for_creation; + + return result; +} + +// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPresentModeKHR.html +// VK_PRESENT_MODE_FIFO_KHR system must support this mode at least so by default we want to use it otherwise user can specify his mode +VkPresentModeKHR RenderInterface_VK::GetPresentMode(VkPresentModeKHR required) noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "[Vulkan] you must initialize your device, before calling this method"); + RMLUI_VK_ASSERTMSG(m_p_physical_device, "[Vulkan] you must initialize your physical device, before calling this method"); + RMLUI_VK_ASSERTMSG(m_p_surface, "[Vulkan] you must initialize your surface, before calling this method"); + + VkPresentModeKHR result = required; + + uint32_t present_modes_count = 0; + VkResult status = vkGetPhysicalDeviceSurfacePresentModesKHR(m_p_physical_device, m_p_surface, &present_modes_count, nullptr); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "[Vulkan] failed to vkGetPhysicalDeviceSurfacePresentModesKHR (getting count)"); + + Rml::Vector present_modes(present_modes_count); + status = vkGetPhysicalDeviceSurfacePresentModesKHR(m_p_physical_device, m_p_surface, &present_modes_count, present_modes.data()); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "[Vulkan] failed to vkGetPhysicalDeviceSurfacePresentModesKHR (filling vector of VkPresentModeKHR)"); + + for (const auto& mode : present_modes) + { + if (mode == required) + return result; + } + + Rml::Log::Message(Rml::Log::LT_WARNING, + "[Vulkan] WARNING system can't detect your type of present mode so we choose the first from vector front"); + + return present_modes.front(); +} + +VkSurfaceCapabilitiesKHR RenderInterface_VK::GetSurfaceCapabilities() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "[Vulkan] you must initialize your device, before calling this method"); + RMLUI_VK_ASSERTMSG(m_p_physical_device, "[Vulkan] you must initialize your physical device, before calling this method"); + RMLUI_VK_ASSERTMSG(m_p_surface, "[Vulkan] you must initialize your surface, before calling this method"); + + VkSurfaceCapabilitiesKHR result; + VkResult status = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_p_physical_device, m_p_surface, &result); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "[Vulkan] failed to vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); + + return result; +} + +void RenderInterface_VK::CreateShaders() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "[Vulkan] you must initialize VkDevice before calling this method"); + + struct shader_data_t { + const uint32_t* m_data; + size_t m_data_size; + VkShaderStageFlagBits m_shader_type; + }; + + const Rml::Vector shaders = { + {reinterpret_cast(shader_vert), sizeof(shader_vert), VK_SHADER_STAGE_VERTEX_BIT}, + {reinterpret_cast(shader_frag_color), sizeof(shader_frag_color), VK_SHADER_STAGE_FRAGMENT_BIT}, + {reinterpret_cast(shader_frag_texture), sizeof(shader_frag_texture), VK_SHADER_STAGE_FRAGMENT_BIT}, + }; + + for (const shader_data_t& shader_data : shaders) + { + VkShaderModuleCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + info.pCode = shader_data.m_data; + info.codeSize = shader_data.m_data_size; + + VkShaderModule p_module = nullptr; + VkResult status = vkCreateShaderModule(m_p_device, &info, nullptr, &p_module); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "[Vulkan] failed to vkCreateShaderModule"); + + m_shaders.push_back(p_module); + } +} + +void RenderInterface_VK::CreateDescriptorSetLayout() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "[Vulkan] you must initialize VkDevice before calling this method"); + RMLUI_VK_ASSERTMSG(!m_p_descriptor_set_layout_vertex_transform && !m_p_descriptor_set_layout_texture, "[Vulkan] Already initialized"); + + { + VkDescriptorSetLayoutBinding binding_for_vertex_transform = {}; + binding_for_vertex_transform.binding = 1; + binding_for_vertex_transform.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + binding_for_vertex_transform.descriptorCount = 1; + binding_for_vertex_transform.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + VkDescriptorSetLayoutCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + info.pBindings = &binding_for_vertex_transform; + info.bindingCount = 1; + + VkResult status = vkCreateDescriptorSetLayout(m_p_device, &info, nullptr, &m_p_descriptor_set_layout_vertex_transform); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "[Vulkan] failed to vkCreateDescriptorSetLayout"); + } + + { + VkDescriptorSetLayoutBinding binding_for_fragment_texture = {}; + binding_for_fragment_texture.binding = 2; + binding_for_fragment_texture.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + binding_for_fragment_texture.descriptorCount = 1; + binding_for_fragment_texture.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + info.pBindings = &binding_for_fragment_texture; + info.bindingCount = 1; + + VkResult status = vkCreateDescriptorSetLayout(m_p_device, &info, nullptr, &m_p_descriptor_set_layout_texture); + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "[Vulkan] failed to vkCreateDescriptorSetLayout"); + } +} + +void RenderInterface_VK::CreatePipelineLayout() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_descriptor_set_layout_vertex_transform, "[Vulkan] You must initialize VkDescriptorSetLayout before calling this method"); + RMLUI_VK_ASSERTMSG(m_p_descriptor_set_layout_texture, + "[Vulkan] you must initialize VkDescriptorSetLayout for textures before calling this method!"); + RMLUI_VK_ASSERTMSG(m_p_device, "[Vulkan] you must initialize VkDevice before calling this method"); + + VkDescriptorSetLayout p_layouts[] = {m_p_descriptor_set_layout_vertex_transform, m_p_descriptor_set_layout_texture}; + + VkPipelineLayoutCreateInfo info = {}; + + info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + info.pNext = nullptr; + info.pSetLayouts = p_layouts; + info.setLayoutCount = 2; + + auto status = vkCreatePipelineLayout(m_p_device, &info, nullptr, &m_p_pipeline_layout); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "[Vulkan] failed to vkCreatePipelineLayout"); +} + +void RenderInterface_VK::CreateDescriptorSets() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "[Vulkan] you have to initialize your VkDevice before calling this method"); + RMLUI_VK_ASSERTMSG(m_p_descriptor_set_layout_vertex_transform, + "[Vulkan] you have to initialize your VkDescriptorSetLayout before calling this method"); + + m_manager_descriptors.Alloc_Descriptor(m_p_device, &m_p_descriptor_set_layout_vertex_transform, &m_p_descriptor_set); + m_memory_pool.SetDescriptorSet(1, sizeof(shader_vertex_user_data_t), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, m_p_descriptor_set); +} + +void RenderInterface_VK::CreateSamplers() noexcept +{ + VkSamplerCreateInfo info = {}; + + info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + info.pNext = nullptr; + info.magFilter = VK_FILTER_LINEAR; + info.minFilter = VK_FILTER_LINEAR; + info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + + vkCreateSampler(m_p_device, &info, nullptr, &m_p_sampler_linear); +} + +void RenderInterface_VK::Create_Pipelines() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_pipeline_layout, "must be initialized"); + RMLUI_VK_ASSERTMSG(m_p_render_pass, "must be initialized"); + + VkPipelineInputAssemblyStateCreateInfo info_assembly_state = {}; + info_assembly_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + info_assembly_state.pNext = nullptr; + info_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + info_assembly_state.primitiveRestartEnable = VK_FALSE; + info_assembly_state.flags = 0; + + VkPipelineRasterizationStateCreateInfo info_raster_state = {}; + info_raster_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + info_raster_state.pNext = nullptr; + info_raster_state.polygonMode = VK_POLYGON_MODE_FILL; + info_raster_state.cullMode = VK_CULL_MODE_NONE; + info_raster_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + info_raster_state.rasterizerDiscardEnable = VK_FALSE; + info_raster_state.depthBiasEnable = VK_FALSE; + info_raster_state.lineWidth = 1.0f; + + VkPipelineColorBlendAttachmentState info_color_blend_att = {}; + info_color_blend_att.colorWriteMask = 0xf; + info_color_blend_att.blendEnable = VK_TRUE; + info_color_blend_att.srcColorBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_ONE; + info_color_blend_att.dstColorBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + info_color_blend_att.colorBlendOp = VkBlendOp::VK_BLEND_OP_ADD; + info_color_blend_att.srcAlphaBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_ONE; + info_color_blend_att.dstAlphaBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + info_color_blend_att.alphaBlendOp = VkBlendOp::VK_BLEND_OP_SUBTRACT; + + VkPipelineColorBlendStateCreateInfo info_color_blend_state = {}; + info_color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + info_color_blend_state.pNext = nullptr; + info_color_blend_state.attachmentCount = 1; + info_color_blend_state.pAttachments = &info_color_blend_att; + + VkPipelineDepthStencilStateCreateInfo info_depth = {}; + info_depth.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + info_depth.pNext = nullptr; + info_depth.depthTestEnable = VK_FALSE; + info_depth.depthWriteEnable = VK_TRUE; + info_depth.depthBoundsTestEnable = VK_FALSE; + info_depth.maxDepthBounds = 1.0f; + + info_depth.depthCompareOp = VK_COMPARE_OP_ALWAYS; + + info_depth.stencilTestEnable = VK_TRUE; + info_depth.back.compareOp = VK_COMPARE_OP_ALWAYS; + info_depth.back.failOp = VK_STENCIL_OP_KEEP; + info_depth.back.depthFailOp = VK_STENCIL_OP_KEEP; + info_depth.back.passOp = VK_STENCIL_OP_KEEP; + info_depth.back.compareMask = 1; + info_depth.back.writeMask = 1; + info_depth.back.reference = 1; + info_depth.front = info_depth.back; + + VkPipelineViewportStateCreateInfo info_viewport = {}; + info_viewport.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + info_viewport.pNext = nullptr; + info_viewport.viewportCount = 1; + info_viewport.scissorCount = 1; + info_viewport.flags = 0; + + VkPipelineMultisampleStateCreateInfo info_multisample = {}; + info_multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + info_multisample.pNext = nullptr; + info_multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + info_multisample.flags = 0; + + Rml::Array dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; + + VkPipelineDynamicStateCreateInfo info_dynamic_state = {}; + info_dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + info_dynamic_state.pNext = nullptr; + info_dynamic_state.pDynamicStates = dynamicStateEnables.data(); + info_dynamic_state.dynamicStateCount = static_cast(dynamicStateEnables.size()); + info_dynamic_state.flags = 0; + + Rml::Array shaders_that_will_be_used_in_pipeline; + + VkPipelineShaderStageCreateInfo info_shader = {}; + info_shader.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + info_shader.pNext = nullptr; + info_shader.pName = "main"; + info_shader.stage = VK_SHADER_STAGE_VERTEX_BIT; + info_shader.module = m_shaders[static_cast(shader_id_t::Vertex)]; + + shaders_that_will_be_used_in_pipeline[0] = info_shader; + + info_shader.module = m_shaders[static_cast(shader_id_t::Fragment_WithTextures)]; + info_shader.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + + shaders_that_will_be_used_in_pipeline[1] = info_shader; + + VkPipelineVertexInputStateCreateInfo info_vertex = {}; + info_vertex.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + info_vertex.pNext = nullptr; + info_vertex.flags = 0; + + Rml::Array info_shader_vertex_attributes; + // describe info about our vertex and what is used in vertex shader as "layout(location = X) in" + + VkVertexInputBindingDescription info_vertex_input_binding = {}; + info_vertex_input_binding.binding = 0; + info_vertex_input_binding.stride = sizeof(Rml::Vertex); + info_vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + info_shader_vertex_attributes[0].binding = 0; + info_shader_vertex_attributes[0].location = 0; + info_shader_vertex_attributes[0].format = VK_FORMAT_R32G32_SFLOAT; + info_shader_vertex_attributes[0].offset = offsetof(Rml::Vertex, position); + + info_shader_vertex_attributes[1].binding = 0; + info_shader_vertex_attributes[1].location = 1; + info_shader_vertex_attributes[1].format = VK_FORMAT_R8G8B8A8_UNORM; + info_shader_vertex_attributes[1].offset = offsetof(Rml::Vertex, colour); + + info_shader_vertex_attributes[2].binding = 0; + info_shader_vertex_attributes[2].location = 2; + info_shader_vertex_attributes[2].format = VK_FORMAT_R32G32_SFLOAT; + info_shader_vertex_attributes[2].offset = offsetof(Rml::Vertex, tex_coord); + + info_vertex.pVertexAttributeDescriptions = info_shader_vertex_attributes.data(); + info_vertex.vertexAttributeDescriptionCount = static_cast(info_shader_vertex_attributes.size()); + info_vertex.pVertexBindingDescriptions = &info_vertex_input_binding; + info_vertex.vertexBindingDescriptionCount = 1; + + VkGraphicsPipelineCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + info.pNext = nullptr; + info.pInputAssemblyState = &info_assembly_state; + info.pRasterizationState = &info_raster_state; + info.pColorBlendState = &info_color_blend_state; + info.pMultisampleState = &info_multisample; + info.pViewportState = &info_viewport; + info.pDepthStencilState = &info_depth; + info.pDynamicState = &info_dynamic_state; + info.stageCount = static_cast(shaders_that_will_be_used_in_pipeline.size()); + info.pStages = shaders_that_will_be_used_in_pipeline.data(); + info.pVertexInputState = &info_vertex; + info.layout = m_p_pipeline_layout; + info.renderPass = m_p_render_pass; + info.subpass = 0; + + auto status = vkCreateGraphicsPipelines(m_p_device, nullptr, 1, &info, nullptr, &m_p_pipeline_with_textures); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateGraphicsPipelines"); + + info_depth.back.passOp = VK_STENCIL_OP_KEEP; + info_depth.back.failOp = VK_STENCIL_OP_KEEP; + info_depth.back.depthFailOp = VK_STENCIL_OP_KEEP; + info_depth.back.compareOp = VK_COMPARE_OP_EQUAL; + info_depth.back.compareMask = 1; + info_depth.back.writeMask = 1; + info_depth.back.reference = 1; + info_depth.front = info_depth.back; + + status = vkCreateGraphicsPipelines(m_p_device, nullptr, 1, &info, nullptr, + &m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_with_textures); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateGraphicsPipelines"); + + info_shader.module = m_shaders[static_cast(shader_id_t::Fragment_WithoutTextures)]; + shaders_that_will_be_used_in_pipeline[1] = info_shader; + info_depth.back.compareOp = VK_COMPARE_OP_ALWAYS; + info_depth.back.failOp = VK_STENCIL_OP_KEEP; + info_depth.back.depthFailOp = VK_STENCIL_OP_KEEP; + info_depth.back.passOp = VK_STENCIL_OP_KEEP; + info_depth.back.compareMask = 1; + info_depth.back.writeMask = 1; + info_depth.back.reference = 1; + info_depth.front = info_depth.back; + + status = vkCreateGraphicsPipelines(m_p_device, nullptr, 1, &info, nullptr, &m_p_pipeline_without_textures); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateGraphicsPipelines"); + + info_depth.back.passOp = VK_STENCIL_OP_KEEP; + info_depth.back.failOp = VK_STENCIL_OP_KEEP; + info_depth.back.depthFailOp = VK_STENCIL_OP_KEEP; + info_depth.back.compareOp = VK_COMPARE_OP_EQUAL; + info_depth.back.compareMask = 1; + info_depth.back.writeMask = 1; + info_depth.back.reference = 1; + info_depth.front = info_depth.back; + + status = vkCreateGraphicsPipelines(m_p_device, nullptr, 1, &info, nullptr, + &m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_without_textures); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateGraphicsPipelines"); + + info_color_blend_att.colorWriteMask = 0x0; + info_depth.back.passOp = VK_STENCIL_OP_REPLACE; + info_depth.back.failOp = VK_STENCIL_OP_KEEP; + info_depth.back.depthFailOp = VK_STENCIL_OP_KEEP; + info_depth.back.compareOp = VK_COMPARE_OP_ALWAYS; + info_depth.back.compareMask = 1; + info_depth.back.writeMask = 1; + info_depth.back.reference = 1; + info_depth.front = info_depth.back; + + status = vkCreateGraphicsPipelines(m_p_device, nullptr, 1, &info, nullptr, &m_p_pipeline_stencil_for_region_where_geometry_will_be_drawn); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateGraphicsPipelines"); + +#ifdef RMLUI_DEBUG + VkDebugUtilsObjectNameInfoEXT info_debug = {}; + + info_debug.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; + info_debug.pObjectName = "pipeline_stencil for region where geometry will be drawn"; + info_debug.objectType = VkObjectType::VK_OBJECT_TYPE_PIPELINE; + info_debug.objectHandle = (uint64_t)m_p_pipeline_stencil_for_region_where_geometry_will_be_drawn; + + vkSetDebugUtilsObjectNameEXT(m_p_device, &info_debug); + + info_debug.pObjectName = "pipeline_stencil_for_regular_geometry_that_applied_to_region_without_textures"; + info_debug.objectHandle = (uint64_t)m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_without_textures; + + vkSetDebugUtilsObjectNameEXT(m_p_device, &info_debug); + + info_debug.pObjectName = "pipeline_without_textures"; + info_debug.objectHandle = (uint64_t)m_p_pipeline_without_textures; + + vkSetDebugUtilsObjectNameEXT(m_p_device, &info_debug); + + info_debug.pObjectName = "pipeline_stencil_for_regular_geometry_that_applied_to_region_with_textures"; + info_debug.objectHandle = (uint64_t)m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_with_textures; + + vkSetDebugUtilsObjectNameEXT(m_p_device, &info_debug); + + info_debug.pObjectName = "pipeline_with_textures"; + info_debug.objectHandle = (uint64_t)m_p_pipeline_with_textures; + + vkSetDebugUtilsObjectNameEXT(m_p_device, &info_debug); +#endif +} + +void RenderInterface_VK::CreateSwapchainFrameBuffers(const VkExtent2D& real_render_image_size) noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_render_pass, "you must create a VkRenderPass before calling this method"); + RMLUI_VK_ASSERTMSG(m_p_device, "you must have a valid VkDevice here"); + + CreateSwapchainImageViews(); + Create_DepthStencilImage(); + Create_DepthStencilImageViews(); + + m_swapchain_frame_buffers.resize(m_swapchain_image_views.size()); + + Rml::Array attachments; + + VkFramebufferCreateInfo info = {}; + info.sType = VkStructureType::VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + info.pNext = nullptr; + info.renderPass = m_p_render_pass; + info.attachmentCount = static_cast(attachments.size()); + info.pAttachments = attachments.data(); + info.width = real_render_image_size.width; + info.height = real_render_image_size.height; + info.layers = 1; + + int index = 0; + VkResult status = VkResult::VK_SUCCESS; + + attachments[1] = m_texture_depthstencil.m_p_vk_image_view; + + for (auto p_view : m_swapchain_image_views) + { + attachments[0] = p_view; + + status = vkCreateFramebuffer(m_p_device, &info, nullptr, &m_swapchain_frame_buffers[index]); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateFramebuffer"); + + ++index; + } +} + +void RenderInterface_VK::CreateSwapchainImages() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "[Vulkan] you must initialize VkDevice before calling this method"); + RMLUI_VK_ASSERTMSG(m_p_swapchain, "[Vulkan] you must initialize VkSwapchainKHR before calling this method"); + + uint32_t count = 0; + auto status = vkGetSwapchainImagesKHR(m_p_device, m_p_swapchain, &count, nullptr); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "[Vulkan] failed to vkGetSwapchainImagesKHR (get count)"); + + m_swapchain_images.resize(count); + + status = vkGetSwapchainImagesKHR(m_p_device, m_p_swapchain, &count, m_swapchain_images.data()); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "[Vulkan] failed to vkGetSwapchainImagesKHR (filling vector)"); +} + +void RenderInterface_VK::CreateSwapchainImageViews() noexcept +{ + CreateSwapchainImages(); + + m_swapchain_image_views.resize(m_swapchain_images.size()); + + uint32_t index = 0; + VkImageViewCreateInfo info = {}; + VkResult status = VkResult::VK_SUCCESS; + + for (auto p_image : m_swapchain_images) + { + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.pNext = nullptr; + info.format = m_swapchain_format.format; + info.components.r = VK_COMPONENT_SWIZZLE_R; + info.components.g = VK_COMPONENT_SWIZZLE_G; + info.components.b = VK_COMPONENT_SWIZZLE_B; + info.components.a = VK_COMPONENT_SWIZZLE_A; + info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + info.subresourceRange.baseMipLevel = 0; + info.subresourceRange.levelCount = 1; + info.subresourceRange.baseArrayLayer = 0; + info.subresourceRange.layerCount = 1; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.flags = 0; + info.image = p_image; + + status = vkCreateImageView(m_p_device, &info, nullptr, &m_swapchain_image_views[index]); + ++index; + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "[Vulkan] failed to vkCreateImageView (creating swapchain views)"); + } +} + +void RenderInterface_VK::Create_DepthStencilImage() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must initialize your VkDevice here"); + RMLUI_VK_ASSERTMSG(m_p_allocator, "you must initialize your VMA allcator"); + RMLUI_VK_ASSERTMSG(m_texture_depthstencil.m_p_vk_image == nullptr, "you should delete texture before create it"); + + VkImageCreateInfo info = {}; + + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.imageType = VK_IMAGE_TYPE_2D; + info.format = Get_SupportedDepthFormat(); + info.extent = {static_cast(m_width), static_cast(m_height), 1}; + info.mipLevels = 1; + info.arrayLayers = 1; + info.samples = VK_SAMPLE_COUNT_1_BIT; + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + VmaAllocation p_allocation = {}; + VkImage p_image = {}; + + VmaAllocationCreateInfo info_alloc = {}; + auto p_commentary = "our depth stencil image"; + + info_alloc.usage = VMA_MEMORY_USAGE_GPU_ONLY; + info_alloc.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT; + info_alloc.pUserData = const_cast(p_commentary); + + VkResult status = vmaCreateImage(m_p_allocator, &info, &info_alloc, &p_image, &p_allocation, nullptr); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vmaCreateImage"); + + m_texture_depthstencil.m_p_vk_image = p_image; + m_texture_depthstencil.m_p_vma_allocation = p_allocation; +} + +void RenderInterface_VK::Create_DepthStencilImageViews() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must initialize your VkDevice here"); + RMLUI_VK_ASSERTMSG(m_texture_depthstencil.m_p_vk_image_view == nullptr, "you should delete it before creating"); + RMLUI_VK_ASSERTMSG(m_texture_depthstencil.m_p_vk_image, "you must initialize VkImage before create this"); + + VkImageViewCreateInfo info = {}; + + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.image = m_texture_depthstencil.m_p_vk_image; + info.format = Get_SupportedDepthFormat(); + info.subresourceRange.baseMipLevel = 0; + info.subresourceRange.levelCount = 1; + info.subresourceRange.baseArrayLayer = 0; + info.subresourceRange.layerCount = 1; + info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + + if (Get_SupportedDepthFormat() >= VK_FORMAT_D16_UNORM_S8_UINT) + { + info.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + + VkImageView p_image_view = {}; + + VkResult status = vkCreateImageView(m_p_device, &info, nullptr, &p_image_view); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateImageView"); + + m_texture_depthstencil.m_p_vk_image_view = p_image_view; +} + +void RenderInterface_VK::CreateResourcesDependentOnSize(const VkExtent2D& real_render_image_size) noexcept +{ + m_viewport.height = static_cast(real_render_image_size.height); + m_viewport.width = static_cast(real_render_image_size.width); + m_viewport.minDepth = 0.0f; + m_viewport.maxDepth = 1.0f; + m_viewport.x = 0.0f; + m_viewport.y = 0.0f; + + m_scissor.extent.width = real_render_image_size.width; + m_scissor.extent.height = real_render_image_size.height; + m_scissor.offset.x = 0; + m_scissor.offset.y = 0; + + m_scissor_original = m_scissor; + + m_projection = Rml::Matrix4f::ProjectOrtho(0.0f, static_cast(m_width), static_cast(m_height), 0.0f, -10000, 10000); + + // https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/ + Rml::Matrix4f correction_matrix; + correction_matrix.SetColumns(Rml::Vector4f(1.0f, 0.0f, 0.0f, 0.0f), Rml::Vector4f(0.0f, -1.0f, 0.0f, 0.0f), Rml::Vector4f(0.0f, 0.0f, 0.5f, 0.0f), + Rml::Vector4f(0.0f, 0.0f, 0.5f, 1.0f)); + + m_projection = correction_matrix * m_projection; + + SetTransform(nullptr); + + CreateRenderPass(); + CreateSwapchainFrameBuffers(real_render_image_size); + Create_Pipelines(); +} + +RenderInterface_VK::buffer_data_t RenderInterface_VK::CreateResource_StagingBuffer(VkDeviceSize size, VkBufferUsageFlags flags) noexcept +{ + VkBufferCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + info.pNext = nullptr; + info.size = size; + info.usage = flags; + + VmaAllocationCreateInfo info_allocation = {}; + info_allocation.usage = VMA_MEMORY_USAGE_CPU_ONLY; + + VkBuffer p_buffer = nullptr; + VmaAllocation p_allocation = nullptr; + VmaAllocationInfo info_stats = {}; + + VkResult status = vmaCreateBuffer(m_p_allocator, &info, &info_allocation, &p_buffer, &p_allocation, &info_stats); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vmaCreateBuffer"); + +#ifdef RMLUI_VK_DEBUG + Rml::Log::Message(Rml::Log::LT_DEBUG, "Allocated buffer [%s]", FormatByteSize(info_stats.size).c_str()); +#endif + + buffer_data_t result = {}; + result.m_p_vk_buffer = p_buffer; + result.m_p_vma_allocation = p_allocation; + + return result; +} + +void RenderInterface_VK::DestroyResource_StagingBuffer(const buffer_data_t& data) noexcept +{ + if (m_p_allocator) + { + if (data.m_p_vk_buffer && data.m_p_vma_allocation) + { + vmaDestroyBuffer(m_p_allocator, data.m_p_vk_buffer, data.m_p_vma_allocation); + } + } +} + +void RenderInterface_VK::Destroy_Textures() noexcept +{ + for (auto& textures : m_pending_for_deletion_textures_by_frames) + { + for (texture_data_t* p_data : textures) + { + Destroy_Texture(*p_data); + delete p_data; + } + + textures.clear(); + } +} + +void RenderInterface_VK::Destroy_Geometries() noexcept +{ + Update_PendingForDeletion_Geometries(); + m_memory_pool.Shutdown(); +} + +void RenderInterface_VK::Destroy_Texture(const texture_data_t& texture) noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_allocator, "you must have initialized VmaAllocator"); + RMLUI_VK_ASSERTMSG(m_p_device, "you must have initialized VkDevice"); + + if (texture.m_p_vma_allocation) + { + vmaDestroyImage(m_p_allocator, texture.m_p_vk_image, texture.m_p_vma_allocation); + vkDestroyImageView(m_p_device, texture.m_p_vk_image_view, nullptr); + + VkDescriptorSet p_set = texture.m_p_vk_descriptor_set; + + if (p_set) + { + m_manager_descriptors.Free_Descriptors(m_p_device, &p_set); + } + } +} + +void RenderInterface_VK::DestroyResourcesDependentOnSize() noexcept +{ + Destroy_Pipelines(); + DestroySwapchainFrameBuffers(); + DestroyRenderPass(); +} + +void RenderInterface_VK::DestroySwapchainImageViews() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "[Vulkan] you must initialize VkDevice before calling this method"); + + m_swapchain_images.clear(); + + for (auto p_view : m_swapchain_image_views) + { + vkDestroyImageView(m_p_device, p_view, nullptr); + } + + m_swapchain_image_views.clear(); +} + +void RenderInterface_VK::DestroySwapchainFrameBuffers() noexcept +{ + DestroySwapchainImageViews(); + + Destroy_Texture(m_texture_depthstencil); + m_texture_depthstencil.m_p_vk_image = nullptr; + m_texture_depthstencil.m_p_vk_image_view = nullptr; + + for (auto p_frame_buffer : m_swapchain_frame_buffers) + { + vkDestroyFramebuffer(m_p_device, p_frame_buffer, nullptr); + } + + m_swapchain_frame_buffers.clear(); +} + +void RenderInterface_VK::DestroyRenderPass() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must have a valid VkDevice here"); + + if (m_p_render_pass) + { + vkDestroyRenderPass(m_p_device, m_p_render_pass, nullptr); + m_p_render_pass = nullptr; + } +} + +void RenderInterface_VK::Destroy_Pipelines() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "must exist here"); + + vkDestroyPipeline(m_p_device, m_p_pipeline_with_textures, nullptr); + vkDestroyPipeline(m_p_device, m_p_pipeline_without_textures, nullptr); + vkDestroyPipeline(m_p_device, m_p_pipeline_stencil_for_region_where_geometry_will_be_drawn, nullptr); + vkDestroyPipeline(m_p_device, m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_with_textures, nullptr); + vkDestroyPipeline(m_p_device, m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_without_textures, nullptr); +} + +void RenderInterface_VK::DestroyDescriptorSets() noexcept {} + +void RenderInterface_VK::DestroyPipelineLayout() noexcept {} + +void RenderInterface_VK::DestroySamplers() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "must exist here"); + vkDestroySampler(m_p_device, m_p_sampler_linear, nullptr); +} + +void RenderInterface_VK::CreateRenderPass() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must have a valid VkDevice here"); + + Rml::Array attachments = {}; + + attachments[0].format = m_swapchain_format.format; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + attachments[1].format = Get_SupportedDepthFormat(); + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + RMLUI_VK_ASSERTMSG(attachments[1].format != VkFormat::VK_FORMAT_UNDEFINED, + "can't obtain depth format, your device doesn't support depth/stencil operations"); + + Rml::Array color_references; + + // swapchain + color_references[0].attachment = 0; + color_references[0].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + // depth stencil + color_references[1].attachment = 1; + color_references[1].layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + VkSubpassDescription subpass = {}; + + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = nullptr; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_references[0]; + subpass.pResolveAttachments = nullptr; + subpass.pDepthStencilAttachment = &color_references[1]; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = nullptr; + + Rml::Array dependencies = {}; + + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcAccessMask = 0; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].dstSubpass = 0; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependencies[1].srcAccessMask = 0; + dependencies[1].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo info = {}; + + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + info.pNext = nullptr; + info.attachmentCount = static_cast(attachments.size()); + info.pAttachments = attachments.data(); + info.subpassCount = 1; + info.pSubpasses = &subpass; + info.dependencyCount = static_cast(dependencies.size()); + info.pDependencies = dependencies.data(); + + VkResult status = vkCreateRenderPass(m_p_device, &info, nullptr, &m_p_render_pass); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkCreateRenderPass"); +} + +void RenderInterface_VK::Wait() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must initialize device"); + RMLUI_VK_ASSERTMSG(m_p_swapchain, "you must initialize swapchain"); + + constexpr uint64_t kMaxUint64 = std::numeric_limits::max(); + + auto status = + vkAcquireNextImageKHR(m_p_device, m_p_swapchain, kMaxUint64, m_semaphores_image_available[m_semaphore_index], nullptr, &m_image_index); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkAcquireNextImageKHR (see status)"); + + m_semaphore_index_previous = m_semaphore_index; + m_semaphore_index = ((m_semaphore_index + 1) % kSwapchainBackBufferCount); + + status = vkWaitForFences(m_p_device, 1, &m_executed_fences[m_semaphore_index_previous], VK_TRUE, kMaxUint64); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkWaitForFences (see status)"); + + status = vkResetFences(m_p_device, 1, &m_executed_fences[m_semaphore_index_previous]); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkResetFences (see status)"); +} + +void RenderInterface_VK::Update_PendingForDeletion_Textures_By_Frames() noexcept +{ + auto& textures_for_previous_frame = m_pending_for_deletion_textures_by_frames[m_semaphore_index_previous]; + + for (texture_data_t* p_data : textures_for_previous_frame) + { + Destroy_Texture(*p_data); + delete p_data; + } + + textures_for_previous_frame.clear(); +} + +void RenderInterface_VK::Update_PendingForDeletion_Geometries() noexcept +{ + for (geometry_handle_t* p_geometry_handle : m_pending_for_deletion_geometries) + { + m_memory_pool.Free_GeometryHandle(p_geometry_handle); + delete p_geometry_handle; + } + + m_pending_for_deletion_geometries.clear(); +} + +void RenderInterface_VK::Submit() noexcept +{ + const VkSemaphore p_semaphores_wait[] = {m_semaphores_image_available[m_semaphore_index_previous]}; + const VkSemaphore p_semaphores_signal[] = {m_semaphores_finished_render[m_semaphore_index]}; + + VkFence p_fence = m_executed_fences[m_semaphore_index]; + + VkPipelineStageFlags submit_wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + + VkSubmitInfo info = {}; + + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.pNext = nullptr; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = p_semaphores_wait; + info.pWaitDstStageMask = &submit_wait_stage; + info.signalSemaphoreCount = 1; + info.pSignalSemaphores = p_semaphores_signal; + info.commandBufferCount = 1; + info.pCommandBuffers = &m_p_current_command_buffer; + + VkResult status = vkQueueSubmit(m_p_queue_graphics, 1, &info, p_fence); + + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkQueueSubmit"); +} + +void RenderInterface_VK::Present() noexcept +{ + VkPresentInfoKHR info = {}; + + info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + info.pNext = nullptr; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &(m_semaphores_finished_render[m_semaphore_index]); + info.swapchainCount = 1; + info.pSwapchains = &m_p_swapchain; + info.pImageIndices = &m_image_index; + info.pResults = nullptr; + + VkResult status = vkQueuePresentKHR(m_p_queue_present, &info); + + if (status != VK_SUCCESS) + { + if (status == VK_ERROR_OUT_OF_DATE_KHR || status == VK_SUBOPTIMAL_KHR) + { + RecreateSwapchain(); + } + else + { + RMLUI_VK_ASSERTMSG(status == VK_SUCCESS, "failed to vkQueuePresentKHR"); + } + } +} + +VkFormat RenderInterface_VK::Get_SupportedDepthFormat() +{ + RMLUI_VK_ASSERTMSG(m_p_physical_device, "you must initialize and pick physical device for your renderer"); + + Rml::Array formats = {VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, + VK_FORMAT_D16_UNORM}; + + VkFormatProperties properties; + for (const auto& format : formats) + { + vkGetPhysicalDeviceFormatProperties(m_p_physical_device, format, &properties); + + if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) + { + return format; + } + } + + return VkFormat::VK_FORMAT_UNDEFINED; +} + +RenderInterface_VK::CommandBufferRing::CommandBufferRing() : m_p_device{}, m_frame_index{}, m_p_current_frame{}, m_frames{} {} + +void RenderInterface_VK::CommandBufferRing::Initialize(VkDevice p_device, uint32_t queue_index_graphics) noexcept +{ + RMLUI_VK_ASSERTMSG(p_device, "you can't pass an invalid VkDevice here"); + RMLUI_VK_ASSERTMSG(!m_p_device, "already initialized"); + + m_p_device = p_device; + + for (CommandBuffersPerFrame& current_buffer : m_frames) + { + for (uint32_t command_buffer_index = 0; command_buffer_index < kNumCommandBuffersPerFrame; ++command_buffer_index) + { + VkCommandPoolCreateInfo info_pool = {}; + info_pool.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + info_pool.pNext = nullptr; + info_pool.queueFamilyIndex = queue_index_graphics; + info_pool.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + + VkCommandPool p_pool = nullptr; + auto status = vkCreateCommandPool(p_device, &info_pool, nullptr, &p_pool); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "can't create command pool"); + + current_buffer.m_command_pools[command_buffer_index] = p_pool; + + VkCommandBufferAllocateInfo info_buffer = {}; + info_buffer.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info_buffer.pNext = nullptr; + info_buffer.commandPool = p_pool; + info_buffer.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + info_buffer.commandBufferCount = 1; + + VkCommandBuffer p_buffer = nullptr; + status = vkAllocateCommandBuffers(p_device, &info_buffer, &p_buffer); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to fill command buffers"); + + current_buffer.m_command_buffers[command_buffer_index] = p_buffer; + } + } + + m_frame_index = 0; + m_p_current_frame = &m_frames[m_frame_index]; +} + +void RenderInterface_VK::CommandBufferRing::Shutdown() +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you can't have an uninitialized VkDevice"); + + for (CommandBuffersPerFrame& current_buffer : m_frames) + { + for (uint32_t i = 0; i < kNumCommandBuffersPerFrame; ++i) + { + vkFreeCommandBuffers(m_p_device, current_buffer.m_command_pools[i], 1, ¤t_buffer.m_command_buffers[i]); + vkDestroyCommandPool(m_p_device, current_buffer.m_command_pools[i], nullptr); + } + } +} + +void RenderInterface_VK::CommandBufferRing::OnBeginFrame() +{ + m_frame_index = ((m_frame_index + 1) % kNumFramesToBuffer); + m_p_current_frame = &m_frames[m_frame_index]; + + // Reset all command pools of the current frame. + for (VkCommandPool command_pool : m_p_current_frame->m_command_pools) + { + auto status = vkResetCommandPool(m_p_device, command_pool, 0); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkResetCommandPool"); + } +} + +VkCommandBuffer RenderInterface_VK::CommandBufferRing::GetCommandBufferForActiveFrame(CommandBufferName named_command_buffer) +{ + RMLUI_VK_ASSERTMSG(m_p_current_frame, "must be valid"); + RMLUI_VK_ASSERTMSG(m_p_device, "you must initialize your VkDevice field with valid pointer or it's uninitialized field"); + RMLUI_VK_ASSERTMSG((int)named_command_buffer < (int)CommandBufferName::Count, "overflow, please use one of the named command lists"); + + const uint32_t list_index = static_cast(named_command_buffer); + + VkCommandBuffer result = m_p_current_frame->m_command_buffers[list_index]; + RMLUI_VK_ASSERTMSG(result, "your VkCommandBuffer must be valid otherwise debug your command list class for frame"); + + return result; +} + +RenderInterface_VK::MemoryPool::MemoryPool() : + m_memory_total_size{}, m_device_min_uniform_alignment{}, m_p_data{}, m_p_buffer{}, m_p_buffer_alloc{}, m_p_device{}, m_p_vk_allocator{}, + m_p_block{} +{} + +RenderInterface_VK::MemoryPool::~MemoryPool() {} + +void RenderInterface_VK::MemoryPool::Initialize(VkDeviceSize byte_size, VkDeviceSize device_min_uniform_alignment, VmaAllocator p_allocator, + VkDevice p_device) noexcept +{ + RMLUI_VK_ASSERTMSG(byte_size > 0, "size must be valid"); + RMLUI_VK_ASSERTMSG(device_min_uniform_alignment > 0, "uniform alignment must be valid"); + RMLUI_VK_ASSERTMSG(p_device, "you must pass a valid VkDevice"); + RMLUI_VK_ASSERTMSG(p_allocator, "you must pass a valid VmaAllocator"); + + m_p_device = p_device; + m_p_vk_allocator = p_allocator; + m_device_min_uniform_alignment = device_min_uniform_alignment; + +#ifdef RMLUI_VK_DEBUG + Rml::Log::Message(Rml::Log::LT_DEBUG, "[Vulkan][Debug] the alignment for uniform buffer is: %zu", m_device_min_uniform_alignment); +#endif + + m_memory_total_size = AlignUp(static_cast(byte_size), m_device_min_uniform_alignment); + + VkBufferCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + info.size = m_memory_total_size; + + VmaAllocationCreateInfo info_alloc = {}; + + auto p_commentary = "our pool buffer that manages all memory in vulkan (dynamic)"; + + info_alloc.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; + info_alloc.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT; + info_alloc.pUserData = const_cast(p_commentary); + + VmaAllocationInfo info_stats = {}; + + auto status = vmaCreateBuffer(m_p_vk_allocator, &info, &info_alloc, &m_p_buffer, &m_p_buffer_alloc, &info_stats); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vmaCreateBuffer"); + + VmaVirtualBlockCreateInfo info_virtual_block = {}; + info_virtual_block.size = m_memory_total_size; + + status = vmaCreateVirtualBlock(&info_virtual_block, &m_p_block); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vmaCreateVirtualBlock"); + +#ifdef RMLUI_VK_DEBUG + Rml::Log::Message(Rml::Log::LT_DEBUG, "[Vulkan][Debug] Allocated memory pool [%s]", FormatByteSize(info_stats.size).c_str()); +#endif + + status = vmaMapMemory(m_p_vk_allocator, m_p_buffer_alloc, (void**)&m_p_data); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vmaMapMemory"); +} + +void RenderInterface_VK::MemoryPool::Shutdown() noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_vk_allocator, "you must have a valid VmaAllocator"); + RMLUI_VK_ASSERTMSG(m_p_buffer, "you must allocate VkBuffer for deleting"); + RMLUI_VK_ASSERTMSG(m_p_buffer_alloc, "you must allocate VmaAllocation for deleting"); + +#ifdef RMLUI_VK_DEBUG + Rml::Log::Message(Rml::Log::LT_DEBUG, "[Vulkan][Debug] Destroyed memory pool [%s]", FormatByteSize(m_memory_total_size).c_str()); +#endif + + vmaUnmapMemory(m_p_vk_allocator, m_p_buffer_alloc); + vmaDestroyVirtualBlock(m_p_block); + vmaDestroyBuffer(m_p_vk_allocator, m_p_buffer, m_p_buffer_alloc); +} + +bool RenderInterface_VK::MemoryPool::Alloc_GeneralBuffer(VkDeviceSize size, void** p_data, VkDescriptorBufferInfo* p_out, + VmaVirtualAllocation* p_alloc) noexcept +{ + RMLUI_VK_ASSERTMSG(p_out, "you must pass a valid pointer"); + RMLUI_VK_ASSERTMSG(m_p_buffer, "you must have a valid VkBuffer"); + + RMLUI_VK_ASSERTMSG(*p_alloc == nullptr, + "you can't pass a VALID object, because it is for initialization. So it means you passed the already allocated " + "VmaVirtualAllocation and it means you did something wrong, like you wanted to allocate into the same object..."); + + size = AlignUp(static_cast(size), m_device_min_uniform_alignment); + + VkDeviceSize offset_memory{}; + + VmaVirtualAllocationCreateInfo info = {}; + info.size = size; + info.alignment = m_device_min_uniform_alignment; + + auto status = vmaVirtualAllocate(m_p_block, &info, p_alloc, &offset_memory); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vmaVirtualAllocate"); + + *p_data = (void*)(m_p_data + offset_memory); + + p_out->buffer = m_p_buffer; + p_out->offset = offset_memory; + p_out->range = size; + + return true; +} + +bool RenderInterface_VK::MemoryPool::Alloc_VertexBuffer(uint32_t number_of_elements, uint32_t stride_in_bytes, void** p_data, + VkDescriptorBufferInfo* p_out, VmaVirtualAllocation* p_alloc) noexcept +{ + return Alloc_GeneralBuffer(number_of_elements * stride_in_bytes, p_data, p_out, p_alloc); +} + +bool RenderInterface_VK::MemoryPool::Alloc_IndexBuffer(uint32_t number_of_elements, uint32_t stride_in_bytes, void** p_data, + VkDescriptorBufferInfo* p_out, VmaVirtualAllocation* p_alloc) noexcept +{ + return Alloc_GeneralBuffer(number_of_elements * stride_in_bytes, p_data, p_out, p_alloc); +} + +void RenderInterface_VK::MemoryPool::SetDescriptorSet(uint32_t binding_index, uint32_t size, VkDescriptorType descriptor_type, + VkDescriptorSet p_set) noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must have a valid VkDevice here"); + RMLUI_VK_ASSERTMSG(p_set, "you must have a valid VkDescriptorSet here"); + RMLUI_VK_ASSERTMSG(m_p_buffer, "you must have a valid VkBuffer here"); + + VkDescriptorBufferInfo info = {}; + + info.buffer = m_p_buffer; + info.offset = 0; + info.range = size; + + VkWriteDescriptorSet info_write = {}; + + info_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + info_write.pNext = nullptr; + info_write.dstSet = p_set; + info_write.descriptorCount = 1; + info_write.descriptorType = descriptor_type; + info_write.dstArrayElement = 0; + info_write.dstBinding = binding_index; + info_write.pBufferInfo = &info; + + vkUpdateDescriptorSets(m_p_device, 1, &info_write, 0, nullptr); +} + +void RenderInterface_VK::MemoryPool::SetDescriptorSet(uint32_t binding_index, VkDescriptorBufferInfo* p_info, VkDescriptorType descriptor_type, + VkDescriptorSet p_set) noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must have a valid VkDevice here"); + RMLUI_VK_ASSERTMSG(p_set, "you must have a valid VkDescriptorSet here"); + RMLUI_VK_ASSERTMSG(m_p_buffer, "you must have a valid VkBuffer here"); + RMLUI_VK_ASSERTMSG(p_info, "must be valid pointer"); + + VkWriteDescriptorSet info_write = {}; + + info_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + info_write.pNext = nullptr; + info_write.dstSet = p_set; + info_write.descriptorCount = 1; + info_write.descriptorType = descriptor_type; + info_write.dstArrayElement = 0; + info_write.dstBinding = binding_index; + info_write.pBufferInfo = p_info; + + vkUpdateDescriptorSets(m_p_device, 1, &info_write, 0, nullptr); +} + +void RenderInterface_VK::MemoryPool::SetDescriptorSet(uint32_t binding_index, VkSampler p_sampler, VkImageLayout layout, VkImageView p_view, + VkDescriptorType descriptor_type, VkDescriptorSet p_set) noexcept +{ + RMLUI_VK_ASSERTMSG(m_p_device, "you must have a valid VkDevice here"); + RMLUI_VK_ASSERTMSG(p_set, "you must have a valid VkDescriptorSet here"); + RMLUI_VK_ASSERTMSG(m_p_buffer, "you must have a valid VkBuffer here"); + RMLUI_VK_ASSERTMSG(p_view, "you must have a valid VkImageView"); + RMLUI_VK_ASSERTMSG(p_sampler, "you must have a valid VkSampler here"); + + VkDescriptorImageInfo info = {}; + + info.imageLayout = layout; + info.imageView = p_view; + info.sampler = p_sampler; + + VkWriteDescriptorSet info_write = {}; + + info_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + info_write.pNext = nullptr; + info_write.dstSet = p_set; + info_write.descriptorCount = 1; + info_write.descriptorType = descriptor_type; + info_write.dstArrayElement = 0; + info_write.dstBinding = binding_index; + info_write.pImageInfo = &info; + + vkUpdateDescriptorSets(m_p_device, 1, &info_write, 0, nullptr); +} + +void RenderInterface_VK::MemoryPool::Free_GeometryHandle(geometry_handle_t* p_valid_geometry_handle) noexcept +{ + RMLUI_VK_ASSERTMSG(p_valid_geometry_handle, + "you must pass a VALID pointer to geometry_handle_t, otherwise something is wrong and debug your code"); + RMLUI_VK_ASSERTMSG(p_valid_geometry_handle->m_p_vertex_allocation, "you must have a VALID pointer of VmaAllocation for vertex buffer"); + RMLUI_VK_ASSERTMSG(p_valid_geometry_handle->m_p_index_allocation, "you must have a VALID pointer of VmaAllocation for index buffer"); + + // TODO: The following assertion is disabled for now. The shader allocation pointer is only set once the geometry + // handle is rendered with. However, currently the Vulkan renderer does not handle all draw calls from RmlUi, so + // this pointer may never be set if the geometry was only used in a unsupported draw calls. This can then trigger + // the following assertion. The free call below gracefully handles zero pointers so this should be safe regardless. + // RMLUI_VK_ASSERTMSG(p_valid_geometry_handle->m_p_shader_allocation, + // "you must have a VALID pointer of VmaAllocation for shader operations (like uniforms and etc)"); + + RMLUI_VK_ASSERTMSG(m_p_block, "you have to allocate the virtual block before do this operation..."); + + vmaVirtualFree(m_p_block, p_valid_geometry_handle->m_p_vertex_allocation); + vmaVirtualFree(m_p_block, p_valid_geometry_handle->m_p_index_allocation); + vmaVirtualFree(m_p_block, p_valid_geometry_handle->m_p_shader_allocation); + + p_valid_geometry_handle->m_p_vertex_allocation = nullptr; + p_valid_geometry_handle->m_p_shader_allocation = nullptr; + p_valid_geometry_handle->m_p_index_allocation = nullptr; + p_valid_geometry_handle->m_num_indices = 0; +} + +void RenderInterface_VK::MemoryPool::Free_GeometryHandle_ShaderDataOnly(geometry_handle_t* p_valid_geometry_handle) noexcept +{ + RMLUI_VK_ASSERTMSG(p_valid_geometry_handle, + "you must pass a VALID pointer to geometry_handle_t, otherwise something is wrong and debug your code"); + RMLUI_VK_ASSERTMSG(p_valid_geometry_handle->m_p_vertex_allocation, "you must have a VALID pointer of VmaAllocation for vertex buffer"); + RMLUI_VK_ASSERTMSG(p_valid_geometry_handle->m_p_index_allocation, "you must have a VALID pointer of VmaAllocation for index buffer"); + RMLUI_VK_ASSERTMSG(p_valid_geometry_handle->m_p_shader_allocation, + "you must have a VALID pointer of VmaAllocation for shader operations (like uniforms and etc)"); + RMLUI_VK_ASSERTMSG(m_p_block, "you have to allocate the virtual block before do this operation..."); + + vmaVirtualFree(m_p_block, p_valid_geometry_handle->m_p_shader_allocation); + p_valid_geometry_handle->m_p_shader_allocation = nullptr; +} + +#define GLAD_VULKAN_IMPLEMENTATION +#define VMA_IMPLEMENTATION +#include "RmlUi_Include_Vulkan.h" diff --git a/vendor/rmlui_backend/RmlUi_Renderer_VK.h b/vendor/rmlui_backend/RmlUi_Renderer_VK.h new file mode 100644 index 0000000..dd348b7 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Renderer_VK.h @@ -0,0 +1,563 @@ +#pragma once + +#include + +#ifdef RMLUI_PLATFORM_WIN32 + #include "RmlUi_Include_Windows.h" + #define VK_USE_PLATFORM_WIN32_KHR +#endif + +#include "RmlUi_Include_Vulkan.h" + +#ifdef RMLUI_DEBUG + #define RMLUI_VK_ASSERTMSG(statement, msg) RMLUI_ASSERTMSG(statement, msg) + +// Uncomment the following line to enable additional Vulkan debugging. +// #define RMLUI_VK_DEBUG +#else + #define RMLUI_VK_ASSERTMSG(statement, msg) static_cast(statement) +#endif + +// Your specified API version. Ideally, this will be dynamic in the future. +#define RMLUI_VK_API_VERSION VK_API_VERSION_1_0 + +class RenderInterface_VK : public Rml::RenderInterface { +public: + static constexpr uint32_t kSwapchainBackBufferCount = 3; + static constexpr VkDeviceSize kVideoMemoryForAllocation = 4 * 1024 * 1024; // [bytes] + + RenderInterface_VK(); + ~RenderInterface_VK(); + + using CreateSurfaceCallback = bool (*)(VkInstance instance, VkSurfaceKHR* out_surface); + + bool Initialize(Rml::Vector required_extensions, CreateSurfaceCallback create_surface_callback); + void Shutdown(); + + void BeginFrame(); + void EndFrame(); + + void SetViewport(int width, int height); + bool IsSwapchainValid(); + void RecreateSwapchain(); + + // -- Inherited from Rml::RenderInterface -- + + /// Called by RmlUi when it wants to compile geometry it believes will be static for the forseeable future. + Rml::CompiledGeometryHandle CompileGeometry(Rml::Span vertices, Rml::Span indices) override; + /// Called by RmlUi when it wants to render application-compiled geometry. + void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override; + /// Called by RmlUi when it wants to release application-compiled geometry. + void ReleaseGeometry(Rml::CompiledGeometryHandle geometry) override; + + /// Called by RmlUi when a texture is required by the library. + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override; + /// Called by RmlUi when a texture is required to be built from an internally-generated sequence of pixels. + Rml::TextureHandle GenerateTexture(Rml::Span source_data, Rml::Vector2i source_dimensions) override; + /// Called by RmlUi when a loaded texture is no longer required. + void ReleaseTexture(Rml::TextureHandle texture_handle) override; + + /// Called by RmlUi when it wants to enable or disable scissoring to clip content. + void EnableScissorRegion(bool enable) override; + /// Called by RmlUi when it wants to change the scissor region. + void SetScissorRegion(Rml::Rectanglei region) override; + + /// Called by RmlUi when it wants to set the current transform matrix to a new matrix. + void SetTransform(const Rml::Matrix4f* transform) override; + +private: + enum class shader_type_t : int { Vertex, Fragment, Unknown = -1 }; + enum class shader_id_t : int { Vertex, Fragment_WithoutTextures, Fragment_WithTextures }; + + struct shader_vertex_user_data_t { + // Member objects are order-sensitive to match shader. + Rml::Matrix4f m_transform; + Rml::Vector2f m_translate; + }; + + struct texture_data_t { + VkImage m_p_vk_image; + VkImageView m_p_vk_image_view; + VkSampler m_p_vk_sampler; + VkDescriptorSet m_p_vk_descriptor_set; + VmaAllocation m_p_vma_allocation; + }; + + struct geometry_handle_t { + int m_num_indices; + + VkDescriptorBufferInfo m_p_vertex; + VkDescriptorBufferInfo m_p_index; + VkDescriptorBufferInfo m_p_shader; + + // @ this is for freeing our logical blocks for VMA + // see https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/virtual_allocator.html + VmaVirtualAllocation m_p_vertex_allocation; + VmaVirtualAllocation m_p_index_allocation; + VmaVirtualAllocation m_p_shader_allocation; + }; + + struct buffer_data_t { + VkBuffer m_p_vk_buffer; + VmaAllocation m_p_vma_allocation; + }; + + class UploadResourceManager { + public: + UploadResourceManager() : m_p_device{}, m_p_fence{}, m_p_command_buffer{}, m_p_command_pool{}, m_p_graphics_queue{} {} + ~UploadResourceManager() {} + + void Initialize(VkDevice p_device, VkQueue p_queue, uint32_t queue_family_index) + { + RMLUI_VK_ASSERTMSG(p_queue, "you have to pass a valid VkQueue"); + RMLUI_VK_ASSERTMSG(p_device, "you have to pass a valid VkDevice for creation resources"); + + m_p_device = p_device; + m_p_graphics_queue = p_queue; + + Create_All(queue_family_index); + } + + void Shutdown() + { + vkDestroyFence(m_p_device, m_p_fence, nullptr); + vkDestroyCommandPool(m_p_device, m_p_command_pool, nullptr); + } + + template + void UploadToGPU(Func&& p_user_commands) noexcept + { + RMLUI_VK_ASSERTMSG(m_p_command_buffer, "you didn't initialize VkCommandBuffer"); + + VkCommandBufferBeginInfo info_command = {}; + + info_command.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info_command.pNext = nullptr; + info_command.pInheritanceInfo = nullptr; + info_command.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + VkResult status = vkBeginCommandBuffer(m_p_command_buffer, &info_command); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkBeginCommandBuffer"); + + p_user_commands(m_p_command_buffer); + + status = vkEndCommandBuffer(m_p_command_buffer); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "faield to vkEndCommandBuffer"); + + Submit(); + Wait(); + } + + private: + void Create_Fence() noexcept + { + VkFenceCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + info.pNext = nullptr; + info.flags = 0; + + VkResult status = vkCreateFence(m_p_device, &info, nullptr, &m_p_fence); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateFence"); + } + + void Create_CommandBuffer() noexcept + { + RMLUI_VK_ASSERTMSG(m_p_command_pool, "you have to initialize VkCommandPool before calling this method!"); + + VkCommandBufferAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.pNext = nullptr; + info.commandPool = m_p_command_pool; + info.commandBufferCount = 1; + info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + + VkResult status = vkAllocateCommandBuffers(m_p_device, &info, &m_p_command_buffer); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkAllocateCommandBuffers"); + } + + void Create_CommandPool(uint32_t queue_family_index) noexcept + { + VkCommandPoolCreateInfo info = {}; + + info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + info.pNext = nullptr; + info.queueFamilyIndex = queue_family_index; + info.flags = 0; + + VkResult status = vkCreateCommandPool(m_p_device, &info, nullptr, &m_p_command_pool); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateCommandPool"); + } + + void Create_All(uint32_t queue_family_index) noexcept + { + Create_Fence(); + Create_CommandPool(queue_family_index); + Create_CommandBuffer(); + } + + void Wait() noexcept + { + RMLUI_VK_ASSERTMSG(m_p_fence, "you must initialize your VkFence"); + + vkWaitForFences(m_p_device, 1, &m_p_fence, VK_TRUE, UINT64_MAX); + vkResetFences(m_p_device, 1, &m_p_fence); + vkResetCommandPool(m_p_device, m_p_command_pool, 0); + } + + void Submit() noexcept + { + VkSubmitInfo info = {}; + + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.pNext = nullptr; + info.waitSemaphoreCount = 0; + info.signalSemaphoreCount = 0; + info.pSignalSemaphores = nullptr; + info.pWaitSemaphores = nullptr; + info.pWaitDstStageMask = nullptr; + info.pCommandBuffers = &m_p_command_buffer; + info.commandBufferCount = 1; + + auto status = vkQueueSubmit(m_p_graphics_queue, 1, &info, m_p_fence); + + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkQueueSubmit"); + } + + private: + VkDevice m_p_device; + VkFence m_p_fence; + VkCommandBuffer m_p_command_buffer; + VkCommandPool m_p_command_pool; + VkQueue m_p_graphics_queue; + }; + + // @ main manager for "allocating" vertex, index, uniform stuff + class MemoryPool { + public: + MemoryPool(); + ~MemoryPool(); + + void Initialize(VkDeviceSize byte_size, VkDeviceSize device_min_uniform_alignment, VmaAllocator p_allocator, VkDevice p_device) noexcept; + void Shutdown() noexcept; + + bool Alloc_GeneralBuffer(VkDeviceSize size, void** p_data, VkDescriptorBufferInfo* p_out, VmaVirtualAllocation* p_alloc) noexcept; + bool Alloc_VertexBuffer(uint32_t number_of_elements, uint32_t stride_in_bytes, void** p_data, VkDescriptorBufferInfo* p_out, + VmaVirtualAllocation* p_alloc) noexcept; + bool Alloc_IndexBuffer(uint32_t number_of_elements, uint32_t stride_in_bytes, void** p_data, VkDescriptorBufferInfo* p_out, + VmaVirtualAllocation* p_alloc) noexcept; + + void SetDescriptorSet(uint32_t binding_index, uint32_t size, VkDescriptorType descriptor_type, VkDescriptorSet p_set) noexcept; + void SetDescriptorSet(uint32_t binding_index, VkDescriptorBufferInfo* p_info, VkDescriptorType descriptor_type, + VkDescriptorSet p_set) noexcept; + void SetDescriptorSet(uint32_t binding_index, VkSampler p_sampler, VkImageLayout layout, VkImageView p_view, VkDescriptorType descriptor_type, + VkDescriptorSet p_set) noexcept; + + void Free_GeometryHandle(geometry_handle_t* p_valid_geometry_handle) noexcept; + void Free_GeometryHandle_ShaderDataOnly(geometry_handle_t* p_valid_geometry_handle) noexcept; + + private: + VkDeviceSize m_memory_total_size; + VkDeviceSize m_device_min_uniform_alignment; + char* m_p_data; + VkBuffer m_p_buffer; + VmaAllocation m_p_buffer_alloc; + VkDevice m_p_device; + VmaAllocator m_p_vk_allocator; + VmaVirtualBlock m_p_block; + }; + + // If we need additional command buffers, we can add them to this list and retrieve them from the ring. + enum class CommandBufferName { Primary, Count }; + + // The command buffer ring stores a unique set of named command buffers for each bufferd frame. + // Explanation of how to use Vulkan efficiently: https://vkguide.dev/docs/chapter-4/double_buffering/ + class CommandBufferRing { + public: + static constexpr uint32_t kNumFramesToBuffer = kSwapchainBackBufferCount; + static constexpr uint32_t kNumCommandBuffersPerFrame = static_cast(CommandBufferName::Count); + + CommandBufferRing(); + + void Initialize(VkDevice p_device, uint32_t queue_index_graphics) noexcept; + void Shutdown(); + + void OnBeginFrame(); + VkCommandBuffer GetCommandBufferForActiveFrame(CommandBufferName named_command_buffer); + + private: + struct CommandBuffersPerFrame { + Rml::Array m_command_pools; + Rml::Array m_command_buffers; + }; + + VkDevice m_p_device; + uint32_t m_frame_index; + CommandBuffersPerFrame* m_p_current_frame; + Rml::Array m_frames; + }; + + class DescriptorPoolManager { + public: + DescriptorPoolManager() : m_allocated_descriptor_count{}, m_p_descriptor_pool{} {} + ~DescriptorPoolManager() + { + RMLUI_VK_ASSERTMSG(m_allocated_descriptor_count <= 0, "something is wrong. You didn't free some VkDescriptorSet"); + } + + void Initialize(VkDevice p_device, uint32_t count_uniform_buffer, uint32_t count_image_sampler, uint32_t count_sampler, + uint32_t count_storage_buffer) noexcept + { + RMLUI_VK_ASSERTMSG(p_device, "you can't pass an invalid VkDevice here"); + + Rml::Array sizes; + sizes[0] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, count_uniform_buffer}; + sizes[1] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, count_uniform_buffer}; + sizes[2] = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, count_image_sampler}; + sizes[3] = {VK_DESCRIPTOR_TYPE_SAMPLER, count_sampler}; + sizes[4] = {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, count_storage_buffer}; + + VkDescriptorPoolCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + info.pNext = nullptr; + info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + info.maxSets = 1000; + info.poolSizeCount = static_cast(sizes.size()); + info.pPoolSizes = sizes.data(); + + auto status = vkCreateDescriptorPool(p_device, &info, nullptr, &m_p_descriptor_pool); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateDescriptorPool"); + } + + void Shutdown(VkDevice p_device) + { + RMLUI_VK_ASSERTMSG(p_device, "you can't pass an invalid VkDevice here"); + + vkDestroyDescriptorPool(p_device, m_p_descriptor_pool, nullptr); + } + + uint32_t Get_AllocatedDescriptorCount() const noexcept { return m_allocated_descriptor_count; } + + bool Alloc_Descriptor(VkDevice p_device, VkDescriptorSetLayout* p_layouts, VkDescriptorSet* p_sets, + uint32_t descriptor_count_for_creation = 1) noexcept + { + RMLUI_VK_ASSERTMSG(p_layouts, "you have to pass a valid and initialized VkDescriptorSetLayout (probably you must create it)"); + RMLUI_VK_ASSERTMSG(p_device, "you must pass a valid VkDevice here"); + + VkDescriptorSetAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + info.pNext = nullptr; + info.descriptorPool = m_p_descriptor_pool; + info.descriptorSetCount = descriptor_count_for_creation; + info.pSetLayouts = p_layouts; + + auto status = vkAllocateDescriptorSets(p_device, &info, p_sets); + RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkAllocateDescriptorSets"); + + m_allocated_descriptor_count += descriptor_count_for_creation; + + return status == VkResult::VK_SUCCESS; + } + + void Free_Descriptors(VkDevice p_device, VkDescriptorSet* p_sets, uint32_t descriptor_count = 1) noexcept + { + RMLUI_VK_ASSERTMSG(p_device, "you must pass a valid VkDevice here"); + + if (p_sets) + { + m_allocated_descriptor_count -= descriptor_count; + vkFreeDescriptorSets(p_device, m_p_descriptor_pool, descriptor_count, p_sets); + } + } + + private: + int m_allocated_descriptor_count; + VkDescriptorPool m_p_descriptor_pool; + }; + + struct PhysicalDeviceWrapper { + VkPhysicalDevice m_p_physical_device; + VkPhysicalDeviceProperties m_physical_device_properties; + }; + + using PhysicalDeviceWrapperList = Rml::Vector; + using LayerPropertiesList = Rml::Vector; + using ExtensionPropertiesList = Rml::Vector; + +private: + Rml::TextureHandle CreateTexture(Rml::Span source, Rml::Vector2i dimensions, const Rml::String& name); + + void Initialize_Instance(Rml::Vector required_extensions) noexcept; + void Initialize_Device() noexcept; + void Initialize_PhysicalDevice(VkPhysicalDeviceProperties& out_physical_device_properties) noexcept; + void Initialize_Swapchain(VkExtent2D window_extent) noexcept; + void Initialize_Surface(CreateSurfaceCallback create_surface_callback) noexcept; + void Initialize_QueueIndecies() noexcept; + void Initialize_Queues() noexcept; + void Initialize_SyncPrimitives() noexcept; + void Initialize_Resources(const VkPhysicalDeviceProperties& physical_device_properties) noexcept; + void Initialize_Allocator() noexcept; + + void Destroy_Instance() noexcept; + void Destroy_Device() noexcept; + void Destroy_Swapchain() noexcept; + void Destroy_Surface() noexcept; + void Destroy_SyncPrimitives() noexcept; + void Destroy_Resources() noexcept; + void Destroy_Allocator() noexcept; + + void QueryInstanceLayers(LayerPropertiesList& result) noexcept; + void QueryInstanceExtensions(ExtensionPropertiesList& result, const LayerPropertiesList& instance_layer_properties) noexcept; + bool AddLayerToInstance(Rml::Vector& result, const LayerPropertiesList& instance_layer_properties, + const char* p_instance_layer_name) noexcept; + bool AddExtensionToInstance(Rml::Vector& result, const ExtensionPropertiesList& instance_extension_properties, + const char* p_instance_extension_name) noexcept; + void CreatePropertiesFor_Instance(Rml::Vector& instance_layer_names, Rml::Vector& instance_extension_names) noexcept; + + bool IsLayerPresent(const LayerPropertiesList& properties, const char* p_layer_name) noexcept; + bool IsExtensionPresent(const ExtensionPropertiesList& properties, const char* p_extension_name) noexcept; + + bool AddExtensionToDevice(Rml::Vector& result, const ExtensionPropertiesList& device_extension_properties, + const char* p_device_extension_name) noexcept; + void CreatePropertiesFor_Device(ExtensionPropertiesList& result) noexcept; + + void CreateReportDebugCallback() noexcept; + void Destroy_ReportDebugCallback() noexcept; + + uint32_t GetUserAPIVersion() const noexcept; + uint32_t GetRequiredVersionAndValidateMachine() noexcept; + + void CollectPhysicalDevices(PhysicalDeviceWrapperList& out_physical_devices) noexcept; + const PhysicalDeviceWrapper* ChoosePhysicalDevice(const PhysicalDeviceWrapperList& physical_devices, VkPhysicalDeviceType device_type) noexcept; + + VkSurfaceFormatKHR ChooseSwapchainFormat() noexcept; + VkSurfaceTransformFlagBitsKHR CreatePretransformSwapchain() noexcept; + VkCompositeAlphaFlagBitsKHR ChooseSwapchainCompositeAlpha() noexcept; + int Choose_SwapchainImageCount(uint32_t user_swapchain_count_for_creation = kSwapchainBackBufferCount, bool if_failed_choose_min = true) noexcept; + VkPresentModeKHR GetPresentMode(VkPresentModeKHR type = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR) noexcept; + VkSurfaceCapabilitiesKHR GetSurfaceCapabilities() noexcept; + + VkExtent2D GetValidSurfaceExtent() noexcept; + + void CreateShaders() noexcept; + void CreateDescriptorSetLayout() noexcept; + void CreatePipelineLayout() noexcept; + void CreateDescriptorSets() noexcept; + void CreateSamplers() noexcept; + void Create_Pipelines() noexcept; + void CreateRenderPass() noexcept; + + void CreateSwapchainFrameBuffers(const VkExtent2D& real_render_image_size) noexcept; + + // This method is called in Views, so don't call it manually + void CreateSwapchainImages() noexcept; + void CreateSwapchainImageViews() noexcept; + + void Create_DepthStencilImage() noexcept; + void Create_DepthStencilImageViews() noexcept; + + void CreateResourcesDependentOnSize(const VkExtent2D& real_render_image_size) noexcept; + + buffer_data_t CreateResource_StagingBuffer(VkDeviceSize size, VkBufferUsageFlags flags) noexcept; + void DestroyResource_StagingBuffer(const buffer_data_t& data) noexcept; + + void Destroy_Textures() noexcept; + void Destroy_Geometries() noexcept; + + void Destroy_Texture(const texture_data_t& p_texture) noexcept; + + void DestroyResourcesDependentOnSize() noexcept; + void DestroySwapchainImageViews() noexcept; + void DestroySwapchainFrameBuffers() noexcept; + void DestroyRenderPass() noexcept; + void Destroy_Pipelines() noexcept; + void DestroyDescriptorSets() noexcept; + void DestroyPipelineLayout() noexcept; + void DestroySamplers() noexcept; + + void Wait() noexcept; + + void Update_PendingForDeletion_Textures_By_Frames() noexcept; + void Update_PendingForDeletion_Geometries() noexcept; + + void Submit() noexcept; + void Present() noexcept; + + VkFormat Get_SupportedDepthFormat(); + +private: + bool m_is_transform_enabled; + bool m_is_apply_to_regular_geometry_stencil; + bool m_is_use_scissor_specified; + bool m_is_use_stencil_pipeline; + + int m_width; + int m_height; + + uint32_t m_queue_index_present; + uint32_t m_queue_index_graphics; + uint32_t m_queue_index_compute; + uint32_t m_semaphore_index; + uint32_t m_semaphore_index_previous; + uint32_t m_image_index; + + VkInstance m_p_instance; + VkDevice m_p_device; + VkPhysicalDevice m_p_physical_device; + VkSurfaceKHR m_p_surface; + VkSwapchainKHR m_p_swapchain; + VmaAllocator m_p_allocator; + // @ obtained from command list see PrepareRenderBuffer method + VkCommandBuffer m_p_current_command_buffer; + + VkDescriptorSetLayout m_p_descriptor_set_layout_vertex_transform; + VkDescriptorSetLayout m_p_descriptor_set_layout_texture; + VkPipelineLayout m_p_pipeline_layout; + VkPipeline m_p_pipeline_with_textures; + VkPipeline m_p_pipeline_without_textures; + VkPipeline m_p_pipeline_stencil_for_region_where_geometry_will_be_drawn; + VkPipeline m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_with_textures; + VkPipeline m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_without_textures; + VkDescriptorSet m_p_descriptor_set; + VkRenderPass m_p_render_pass; + VkSampler m_p_sampler_linear; + VkRect2D m_scissor; + + // @ means it captures the window size full width and full height, offset equals both x and y to 0 + VkRect2D m_scissor_original; + VkViewport m_viewport; + + VkQueue m_p_queue_present; + VkQueue m_p_queue_graphics; + VkQueue m_p_queue_compute; + +#ifdef RMLUI_VK_DEBUG + VkDebugUtilsMessengerEXT m_debug_messenger; +#endif + + VkSurfaceFormatKHR m_swapchain_format; + shader_vertex_user_data_t m_user_data_for_vertex_shader; + texture_data_t m_texture_depthstencil; + + Rml::Matrix4f m_projection; + Rml::Vector m_executed_fences; + Rml::Vector m_semaphores_image_available; + Rml::Vector m_semaphores_finished_render; + Rml::Vector m_swapchain_frame_buffers; + Rml::Vector m_swapchain_images; + Rml::Vector m_swapchain_image_views; + Rml::Vector m_shaders; + Rml::Array, kSwapchainBackBufferCount> m_pending_for_deletion_textures_by_frames; + + // vma handles that thing, so there's no need for frame splitting + Rml::Vector m_pending_for_deletion_geometries; + + CommandBufferRing m_command_buffer_ring; + MemoryPool m_memory_pool; + UploadResourceManager m_upload_manager; + DescriptorPoolManager m_manager_descriptors; +}; diff --git a/vendor/rmlui_backend/RmlUi_SDL_GPU/SDL_shadercross/LICENSE.txt b/vendor/rmlui_backend/RmlUi_SDL_GPU/SDL_shadercross/LICENSE.txt new file mode 100644 index 0000000..c12ae1f --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_SDL_GPU/SDL_shadercross/LICENSE.txt @@ -0,0 +1,28 @@ +The SDL GPU backend for RmlUi uses the following third-party tool to generate +the included shader binaries: + +SDL_shadercross +https://github.com/libsdl-org/SDL_shadercross + +This tool is licensed separately from Rmlui. The license of this dependency +is recited below. + +---- + +Copyright (C) 2024 Sam Lantinga + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/vendor/rmlui_backend/RmlUi_SDL_GPU/ShadersCompiledSPV.h b/vendor/rmlui_backend/RmlUi_SDL_GPU/ShadersCompiledSPV.h new file mode 100644 index 0000000..e2e4df7 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_SDL_GPU/ShadersCompiledSPV.h @@ -0,0 +1,817 @@ +// RmlUi SDL GPU shaders compiled using command: 'python compile_shaders.py'. Do not edit manually. + +#include + +alignas(uint32_t) static const unsigned char shader_frag_color_spirv[] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x00,0x00,0x0E,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0F,0x00,0x07,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x03,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x58,0x02,0x00,0x00,0x05,0x00,0x07,0x00,0x02,0x00,0x00,0x00, + 0x69,0x6E,0x2E,0x76,0x61,0x72,0x2E,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x30,0x00,0x00,0x00,0x00, + 0x05,0x00,0x07,0x00,0x03,0x00,0x00,0x00,0x6F,0x75,0x74,0x2E,0x76,0x61,0x72,0x2E,0x53,0x56,0x5F,0x54, + 0x61,0x72,0x67,0x65,0x74,0x30,0x00,0x00,0x05,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x02,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x03,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x04,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x13,0x00,0x02,0x00, + 0x08,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x0A,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x05,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x03,0x00,0x00,0x00, + 0x0B,0x00,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; + +alignas(uint32_t) static const unsigned char shader_frag_color_msl[] = { + 0x23,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x6D,0x65,0x74,0x61,0x6C,0x5F,0x73,0x74,0x64,0x6C, + 0x69,0x62,0x3E,0x0A,0x23,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x73,0x69,0x6D,0x64,0x2F,0x73, + 0x69,0x6D,0x64,0x2E,0x68,0x3E,0x0A,0x0A,0x75,0x73,0x69,0x6E,0x67,0x20,0x6E,0x61,0x6D,0x65,0x73,0x70, + 0x61,0x63,0x65,0x20,0x6D,0x65,0x74,0x61,0x6C,0x3B,0x0A,0x0A,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6D, + 0x61,0x69,0x6E,0x30,0x5F,0x6F,0x75,0x74,0x0A,0x7B,0x0A,0x20,0x20,0x20,0x20,0x66,0x6C,0x6F,0x61,0x74, + 0x34,0x20,0x6F,0x75,0x74,0x5F,0x76,0x61,0x72,0x5F,0x53,0x56,0x5F,0x54,0x61,0x72,0x67,0x65,0x74,0x30, + 0x20,0x5B,0x5B,0x63,0x6F,0x6C,0x6F,0x72,0x28,0x30,0x29,0x5D,0x5D,0x3B,0x0A,0x7D,0x3B,0x0A,0x0A,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6D,0x61,0x69,0x6E,0x30,0x5F,0x69,0x6E,0x0A,0x7B,0x0A,0x20,0x20,0x20, + 0x20,0x66,0x6C,0x6F,0x61,0x74,0x34,0x20,0x69,0x6E,0x5F,0x76,0x61,0x72,0x5F,0x54,0x45,0x58,0x43,0x4F, + 0x4F,0x52,0x44,0x30,0x20,0x5B,0x5B,0x75,0x73,0x65,0x72,0x28,0x6C,0x6F,0x63,0x6E,0x30,0x29,0x5D,0x5D, + 0x3B,0x0A,0x7D,0x3B,0x0A,0x0A,0x66,0x72,0x61,0x67,0x6D,0x65,0x6E,0x74,0x20,0x6D,0x61,0x69,0x6E,0x30, + 0x5F,0x6F,0x75,0x74,0x20,0x6D,0x61,0x69,0x6E,0x30,0x28,0x6D,0x61,0x69,0x6E,0x30,0x5F,0x69,0x6E,0x20, + 0x69,0x6E,0x20,0x5B,0x5B,0x73,0x74,0x61,0x67,0x65,0x5F,0x69,0x6E,0x5D,0x5D,0x29,0x0A,0x7B,0x0A,0x20, + 0x20,0x20,0x20,0x6D,0x61,0x69,0x6E,0x30,0x5F,0x6F,0x75,0x74,0x20,0x6F,0x75,0x74,0x20,0x3D,0x20,0x7B, + 0x7D,0x3B,0x0A,0x20,0x20,0x20,0x20,0x6F,0x75,0x74,0x2E,0x6F,0x75,0x74,0x5F,0x76,0x61,0x72,0x5F,0x53, + 0x56,0x5F,0x54,0x61,0x72,0x67,0x65,0x74,0x30,0x20,0x3D,0x20,0x69,0x6E,0x2E,0x69,0x6E,0x5F,0x76,0x61, + 0x72,0x5F,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x30,0x3B,0x0A,0x20,0x20,0x20,0x20,0x72,0x65,0x74, + 0x75,0x72,0x6E,0x20,0x6F,0x75,0x74,0x3B,0x0A,0x7D,0x0A,0x0A, +}; + +alignas(uint32_t) static const unsigned char shader_frag_color_dxil[] = { + 0x44,0x58,0x42,0x43,0x2E,0x2E,0xA9,0x96,0x45,0xE1,0xC0,0xAA,0xE2,0x19,0x19,0xF0,0x13,0x89,0x7B,0xCC, + 0x01,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x4C,0x00,0x00,0x00, + 0x88,0x00,0x00,0x00,0xC4,0x00,0x00,0x00,0x58,0x01,0x00,0x00,0x08,0x06,0x00,0x00,0x24,0x06,0x00,0x00, + 0x53,0x46,0x49,0x30,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x53,0x47,0x31, + 0x34,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x00,0x00, + 0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x00,0x00,0x00,0x00,0x4F,0x53,0x47,0x31, + 0x34,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x53,0x56,0x5F,0x54,0x61,0x72,0x67,0x65,0x74,0x00,0x00,0x00,0x50,0x53,0x56,0x30, + 0x8C,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x01, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x00,0x6D,0x61, + 0x69,0x6E,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x44,0x00,0x03,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x44,0x10,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x53,0x54,0x41,0x54,0xA8,0x04,0x00,0x00,0x60,0x00,0x00,0x00,0x2A,0x01,0x00,0x00, + 0x44,0x58,0x49,0x4C,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x90,0x04,0x00,0x00,0x42,0x43,0xC0,0xDE, + 0x21,0x0C,0x00,0x00,0x21,0x01,0x00,0x00,0x0B,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x07,0x81,0x23,0x91,0x41,0xC8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0C,0x25,0x05,0x08,0x19, + 0x1E,0x04,0x8B,0x62,0x80,0x10,0x45,0x02,0x42,0x92,0x0B,0x42,0x84,0x10,0x32,0x14,0x38,0x08,0x18,0x4B, + 0x0A,0x32,0x42,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xA5,0x00,0x19,0x32,0x42,0xE4,0x48,0x0E,0x90, + 0x11,0x22,0xC4,0x50,0x41,0x51,0x81,0x8C,0xE1,0x83,0xE5,0x8A,0x04,0x21,0x46,0x06,0x51,0x18,0x00,0x00, + 0x06,0x00,0x00,0x00,0x1B,0x8C,0xE0,0xFF,0xFF,0xFF,0xFF,0x07,0x40,0x02,0xA8,0x0D,0x84,0xF0,0xFF,0xFF, + 0xFF,0xFF,0x03,0x20,0x01,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x82,0x60,0x42, + 0x20,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x0F,0x00,0x00,0x00,0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04, + 0x13,0x22,0xA4,0x84,0x04,0x13,0x22,0xE3,0x84,0xA1,0x90,0x14,0x12,0x4C,0x88,0x8C,0x0B,0x84,0x84,0x4C, + 0x10,0x30,0x23,0x00,0x25,0x00,0x8A,0x19,0x80,0x39,0x02,0x30,0x98,0x23,0x40,0x8A,0x31,0x44,0x54,0x44, + 0x56,0x0C,0x20,0xA2,0x1A,0xC2,0x81,0x80,0x34,0x20,0x00,0x00,0x13,0x14,0x72,0xC0,0x87,0x74,0x60,0x87, + 0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xC0,0x87,0x0D,0xAF,0x50,0x0E,0x6D,0xD0,0x0E,0x7A,0x50,0x0E,0x6D, + 0x00,0x0F,0x7A,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x71,0xA0,0x07,0x73,0x20,0x07, + 0x6D,0x90,0x0E,0x78,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x71,0x60,0x07,0x7A,0x30,0x07,0x72,0xD0, + 0x06,0xE9,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x76,0x40,0x07,0x7A,0x60,0x07,0x74, + 0xD0,0x06,0xE6,0x10,0x07,0x76,0xA0,0x07,0x73,0x20,0x07,0x6D,0x60,0x0E,0x73,0x20,0x07,0x7A,0x30,0x07, + 0x72,0xD0,0x06,0xE6,0x60,0x07,0x74,0xA0,0x07,0x76,0x40,0x07,0x6D,0xE0,0x0E,0x78,0xA0,0x07,0x71,0x60, + 0x07,0x7A,0x30,0x07,0x72,0xA0,0x07,0x76,0x40,0x07,0x43,0x9E,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x86,0x3C,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x79,0x10,0x20, + 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC8,0x02,0x01,0x0B,0x00,0x00,0x00,0x32,0x1E,0x98,0x14, + 0x19,0x11,0x4C,0x90,0x8C,0x09,0x26,0x47,0xC6,0x04,0x43,0xA2,0x12,0x18,0x01,0x28,0x86,0x32,0x28,0x8F, + 0x92,0x28,0x04,0xAA,0x92,0x18,0x01,0x28,0x82,0x42,0x28,0x10,0xDA,0xB1,0x0C,0x82,0x08,0x04,0x02,0x01, + 0x79,0x18,0x00,0x00,0x57,0x00,0x00,0x00,0x1A,0x03,0x4C,0x90,0x46,0x02,0x13,0xC4,0x8E,0x0C,0x6F,0xEC, + 0xED,0x4D,0x0C,0x24,0xC6,0x05,0xC7,0x45,0xA6,0x06,0x46,0xC6,0x05,0x07,0x04,0x45,0x8C,0xE6,0x26,0x26, + 0x06,0x67,0x26,0xA7,0x2C,0x65,0x43,0x10,0x4C,0x10,0x88,0x61,0x82,0x40,0x10,0x1B,0x84,0x81,0xD8,0x20, + 0x10,0x04,0x05,0xB8,0xB9,0x09,0x02,0x51,0x6C,0x18,0x0E,0x84,0x98,0x20,0x08,0xC0,0x06,0x60,0xC3,0x40, + 0x2C,0xCB,0x86,0x80,0xD9,0x30,0x0C,0x4A,0x33,0x41,0x58,0x9E,0x0D,0xC1,0x43,0xA2,0x2D,0x2C,0xCD,0x8D, + 0x08,0x55,0x11,0xD6,0xD0,0xD3,0x93,0x14,0xD1,0x04,0xA1,0x50,0x26,0x08,0xC5,0xB2,0x21,0x20,0x26,0x08, + 0x05,0x33,0x41,0x28,0x9A,0x09,0x02,0x61,0x4C,0x10,0x88,0x63,0x83,0x80,0x65,0x1B,0x16,0x42,0x9A,0xA8, + 0xCA,0x1A,0x2E,0x82,0xD2,0x36,0x04,0x1B,0x93,0x29,0xAB,0x2F,0xAA,0x30,0xB9,0xB3,0x32,0xBA,0x09,0x42, + 0xE1,0x6C,0x58,0x88,0x6E,0xF2,0x2A,0x6A,0xB8,0x08,0x4A,0xDB,0x10,0x7C,0x1B,0x06,0x0E,0x0C,0x80,0x0D, + 0x85,0x12,0x85,0x01,0x00,0xB0,0x48,0x73,0x9B,0xA3,0x9B,0x9B,0x20,0x10,0x08,0x8D,0xB9,0xB4,0xB3,0x2F, + 0x36,0xB2,0x09,0x02,0x91,0xD0,0x98,0x4B,0x3B,0xFB,0x9A,0xA3,0xDB,0x60,0x8C,0x01,0x19,0x94,0x81,0x19, + 0x9C,0x81,0x19,0x54,0x61,0x63,0xB3,0x6B,0x73,0x49,0x23,0x2B,0x73,0xA3,0x9B,0x12,0x04,0x55,0xC8,0xF0, + 0x5C,0xEC,0xCA,0xE4,0xE6,0xD2,0xDE,0xDC,0xA6,0x04,0x44,0x13,0x32,0x3C,0x17,0xBB,0x30,0x36,0xBB,0x32, + 0xB9,0x29,0x41,0x51,0x87,0x0C,0xCF,0x65,0x0E,0x2D,0x8C,0xAC,0x4C,0xAE,0xE9,0x8D,0xAC,0x8C,0x6D,0x4A, + 0x80,0x54,0x22,0xC3,0x73,0xA1,0xCB,0x83,0x2B,0x0B,0x72,0x73,0x7B,0xA3,0x0B,0xA3,0x4B,0x7B,0x73,0x9B, + 0x9B,0x12,0x34,0x75,0xC8,0xF0,0x5C,0xEC,0xD2,0xCA,0xEE,0x92,0xC8,0xA6,0xE8,0xC2,0xE8,0xCA,0xA6,0x04, + 0x4F,0x1D,0x32,0x3C,0x97,0x32,0x37,0x3A,0xB9,0x3C,0xA8,0xB7,0x34,0x37,0xBA,0xB9,0x29,0x41,0x18,0x74, + 0x21,0xC3,0x73,0x19,0x7B,0xAB,0x73,0xA3,0x2B,0x93,0x9B,0x9B,0x12,0x9C,0x01,0x00,0x79,0x18,0x00,0x00, + 0x4C,0x00,0x00,0x00,0x33,0x08,0x80,0x1C,0xC4,0xE1,0x1C,0x66,0x14,0x01,0x3D,0x88,0x43,0x38,0x84,0xC3, + 0x8C,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0C,0xE6,0x00,0x0F,0xED,0x10,0x0E,0xF4,0x80,0x0E, + 0x33,0x0C,0x42,0x1E,0xC2,0xC1,0x1D,0xCE,0xA1,0x1C,0x66,0x30,0x05,0x3D,0x88,0x43,0x38,0x84,0x83,0x1B, + 0xCC,0x03,0x3D,0xC8,0x43,0x3D,0x8C,0x03,0x3D,0xCC,0x78,0x8C,0x74,0x70,0x07,0x7B,0x08,0x07,0x79,0x48, + 0x87,0x70,0x70,0x07,0x7A,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xCC,0x11,0x0E,0xEC,0x90,0x0E, + 0xE1,0x30,0x0F,0x6E,0x30,0x0F,0xE3,0xF0,0x0E,0xF0,0x50,0x0E,0x33,0x10,0xC4,0x1D,0xDE,0x21,0x1C,0xD8, + 0x21,0x1D,0xC2,0x61,0x1E,0x66,0x30,0x89,0x3B,0xBC,0x83,0x3B,0xD0,0x43,0x39,0xB4,0x03,0x3C,0xBC,0x83, + 0x3C,0x84,0x03,0x3B,0xCC,0xF0,0x14,0x76,0x60,0x07,0x7B,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37, + 0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xF8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, + 0x5F,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2C,0xEE,0xF0,0x0E,0xEE,0xE0,0x0E,0xF5, + 0xC0,0x0E,0xEC,0x30,0x03,0x62,0xC8,0xA1,0x1C,0xE4,0xA1,0x1C,0xCC,0xA1,0x1C,0xE4,0xA1,0x1C,0xDC,0x61, + 0x1C,0xCA,0x21,0x1C,0xC4,0x81,0x1D,0xCA,0x61,0x06,0xD6,0x90,0x43,0x39,0xC8,0x43,0x39,0x98,0x43,0x39, + 0xC8,0x43,0x39,0xB8,0xC3,0x38,0x94,0x43,0x38,0x88,0x03,0x3B,0x94,0xC3,0x2F,0xBC,0x83,0x3C,0xFC,0x82, + 0x3B,0xD4,0x03,0x3B,0xB0,0xC3,0x8C,0xC8,0x21,0x07,0x7C,0x70,0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7B, + 0x08,0x07,0x79,0x60,0x87,0x70,0xC8,0x87,0x77,0xA8,0x07,0x7A,0x98,0x81,0x3C,0xE4,0x80,0x0F,0x6E,0x40, + 0x0F,0xE5,0xD0,0x0E,0xF0,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x0B,0x00,0x00,0x00,0x16,0x30,0x0D,0x97, + 0xEF,0x3C,0xFE,0xE2,0x00,0x83,0xD8,0x3C,0xD4,0xE4,0x17,0xB7,0x6D,0x02,0xD5,0x70,0xF9,0xCE,0xE3,0x4B, + 0x93,0x13,0x11,0x28,0x35,0x3D,0xD4,0xE4,0x17,0xB7,0x6D,0x00,0x04,0x03,0x20,0x0D,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x41,0x53,0x48,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x65,0xE3,0x7F,0x22, + 0xB6,0x5B,0x10,0xF4,0xA4,0x0F,0xBE,0x64,0x48,0x54,0x5A,0xC1,0x44,0x58,0x49,0x4C,0xD4,0x04,0x00,0x00, + 0x60,0x00,0x00,0x00,0x35,0x01,0x00,0x00,0x44,0x58,0x49,0x4C,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00, + 0xBC,0x04,0x00,0x00,0x42,0x43,0xC0,0xDE,0x21,0x0C,0x00,0x00,0x2C,0x01,0x00,0x00,0x0B,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xC8,0x04,0x49,0x06,0x10,0x32,0x39, + 0x92,0x01,0x84,0x0C,0x25,0x05,0x08,0x19,0x1E,0x04,0x8B,0x62,0x80,0x10,0x45,0x02,0x42,0x92,0x0B,0x42, + 0x84,0x10,0x32,0x14,0x38,0x08,0x18,0x4B,0x0A,0x32,0x42,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xA5, + 0x00,0x19,0x32,0x42,0xE4,0x48,0x0E,0x90,0x11,0x22,0xC4,0x50,0x41,0x51,0x81,0x8C,0xE1,0x83,0xE5,0x8A, + 0x04,0x21,0x46,0x06,0x51,0x18,0x00,0x00,0x06,0x00,0x00,0x00,0x1B,0x8C,0xE0,0xFF,0xFF,0xFF,0xFF,0x07, + 0x40,0x02,0xA8,0x0D,0x84,0xF0,0xFF,0xFF,0xFF,0xFF,0x03,0x20,0x01,0x00,0x00,0x00,0x49,0x18,0x00,0x00, + 0x02,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x0F,0x00,0x00,0x00, + 0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04,0x13,0x22,0xA4,0x84,0x04,0x13,0x22,0xE3,0x84,0xA1,0x90,0x14, + 0x12,0x4C,0x88,0x8C,0x0B,0x84,0x84,0x4C,0x10,0x30,0x23,0x00,0x25,0x00,0x8A,0x19,0x80,0x39,0x02,0x30, + 0x98,0x23,0x40,0x8A,0x31,0x44,0x54,0x44,0x56,0x0C,0x20,0xA2,0x1A,0xC2,0x81,0x80,0x34,0x20,0x00,0x00, + 0x13,0x14,0x72,0xC0,0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xC0,0x87,0x0D,0xAF,0x50, + 0x0E,0x6D,0xD0,0x0E,0x7A,0x50,0x0E,0x6D,0x00,0x0F,0x7A,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D, + 0x90,0x0E,0x71,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x78,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E, + 0x71,0x60,0x07,0x7A,0x30,0x07,0x72,0xD0,0x06,0xE9,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90, + 0x0E,0x76,0x40,0x07,0x7A,0x60,0x07,0x74,0xD0,0x06,0xE6,0x10,0x07,0x76,0xA0,0x07,0x73,0x20,0x07,0x6D, + 0x60,0x0E,0x73,0x20,0x07,0x7A,0x30,0x07,0x72,0xD0,0x06,0xE6,0x60,0x07,0x74,0xA0,0x07,0x76,0x40,0x07, + 0x6D,0xE0,0x0E,0x78,0xA0,0x07,0x71,0x60,0x07,0x7A,0x30,0x07,0x72,0xA0,0x07,0x76,0x40,0x07,0x43,0x9E, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x3C,0x06,0x10,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0C,0x79,0x10,0x20,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC8,0x02,0x01, + 0x0B,0x00,0x00,0x00,0x32,0x1E,0x98,0x10,0x19,0x11,0x4C,0x90,0x8C,0x09,0x26,0x47,0xC6,0x04,0x43,0xA2, + 0x12,0x18,0x01,0x28,0x88,0x62,0x28,0x83,0xF2,0xA0,0x2A,0x89,0x11,0x80,0x22,0x28,0x84,0x02,0xA1,0x1D, + 0xCB,0x20,0x88,0x40,0x20,0x10,0x00,0x00,0x79,0x18,0x00,0x00,0x42,0x00,0x00,0x00,0x1A,0x03,0x4C,0x90, + 0x46,0x02,0x13,0xC4,0x8E,0x0C,0x6F,0xEC,0xED,0x4D,0x0C,0x24,0xC6,0x05,0xC7,0x45,0xA6,0x06,0x46,0xC6, + 0x05,0x07,0x04,0x45,0x8C,0xE6,0x26,0x26,0x06,0x67,0x26,0xA7,0x2C,0x65,0x43,0x10,0x4C,0x10,0x88,0x61, + 0x82,0x40,0x10,0x1B,0x84,0x81,0x98,0x20,0x10,0xC5,0x06,0x61,0x30,0x28,0xC0,0xCD,0x4D,0x10,0x08,0x63, + 0xC3,0x80,0x24,0xC4,0x04,0x61,0x71,0x36,0x04,0xCB,0x04,0x41,0x00,0x48,0xB4,0x85,0xA5,0xB9,0x11,0xA1, + 0x2A,0xC2,0x1A,0x7A,0x7A,0x92,0x22,0x9A,0x20,0x14,0xC9,0x04,0xA1,0x50,0x36,0x04,0xC4,0x04,0xA1,0x58, + 0x26,0x08,0x05,0x33,0x41,0x20,0x8E,0x09,0x02,0x81,0x6C,0x10,0x2A,0x6B,0xC3,0x42,0x3C,0x50,0x24,0x4D, + 0x03,0x45,0x44,0xD7,0x86,0x00,0x63,0x32,0x65,0xF5,0x45,0x15,0x26,0x77,0x56,0x46,0x37,0x41,0x28,0x9A, + 0x0D,0x0B,0xA1,0x41,0x9B,0x14,0x0D,0x14,0x11,0x5D,0x1B,0x02,0x6E,0xC3,0x90,0x75,0xC0,0x86,0xA2,0x71, + 0x3C,0x00,0xA8,0xC2,0xC6,0x66,0xD7,0xE6,0x92,0x46,0x56,0xE6,0x46,0x37,0x25,0x08,0xAA,0x90,0xE1,0xB9, + 0xD8,0x95,0xC9,0xCD,0xA5,0xBD,0xB9,0x4D,0x09,0x88,0x26,0x64,0x78,0x2E,0x76,0x61,0x6C,0x76,0x65,0x72, + 0x53,0x02,0xA3,0x0E,0x19,0x9E,0xCB,0x1C,0x5A,0x18,0x59,0x99,0x5C,0xD3,0x1B,0x59,0x19,0xDB,0x94,0x20, + 0xA9,0x43,0x86,0xE7,0x62,0x97,0x56,0x76,0x97,0x44,0x36,0x45,0x17,0x46,0x57,0x36,0x25,0x58,0xEA,0x90, + 0xE1,0xB9,0x94,0xB9,0xD1,0xC9,0xE5,0x41,0xBD,0xA5,0xB9,0xD1,0xCD,0x4D,0x09,0x3C,0x00,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0x4C,0x00,0x00,0x00,0x33,0x08,0x80,0x1C,0xC4,0xE1,0x1C,0x66,0x14,0x01,0x3D,0x88, + 0x43,0x38,0x84,0xC3,0x8C,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0C,0xE6,0x00,0x0F,0xED,0x10, + 0x0E,0xF4,0x80,0x0E,0x33,0x0C,0x42,0x1E,0xC2,0xC1,0x1D,0xCE,0xA1,0x1C,0x66,0x30,0x05,0x3D,0x88,0x43, + 0x38,0x84,0x83,0x1B,0xCC,0x03,0x3D,0xC8,0x43,0x3D,0x8C,0x03,0x3D,0xCC,0x78,0x8C,0x74,0x70,0x07,0x7B, + 0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7A,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xCC,0x11, + 0x0E,0xEC,0x90,0x0E,0xE1,0x30,0x0F,0x6E,0x30,0x0F,0xE3,0xF0,0x0E,0xF0,0x50,0x0E,0x33,0x10,0xC4,0x1D, + 0xDE,0x21,0x1C,0xD8,0x21,0x1D,0xC2,0x61,0x1E,0x66,0x30,0x89,0x3B,0xBC,0x83,0x3B,0xD0,0x43,0x39,0xB4, + 0x03,0x3C,0xBC,0x83,0x3C,0x84,0x03,0x3B,0xCC,0xF0,0x14,0x76,0x60,0x07,0x7B,0x68,0x07,0x37,0x68,0x87, + 0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xF8,0x05,0x76,0x78, + 0x87,0x77,0x80,0x87,0x5F,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2C,0xEE,0xF0,0x0E, + 0xEE,0xE0,0x0E,0xF5,0xC0,0x0E,0xEC,0x30,0x03,0x62,0xC8,0xA1,0x1C,0xE4,0xA1,0x1C,0xCC,0xA1,0x1C,0xE4, + 0xA1,0x1C,0xDC,0x61,0x1C,0xCA,0x21,0x1C,0xC4,0x81,0x1D,0xCA,0x61,0x06,0xD6,0x90,0x43,0x39,0xC8,0x43, + 0x39,0x98,0x43,0x39,0xC8,0x43,0x39,0xB8,0xC3,0x38,0x94,0x43,0x38,0x88,0x03,0x3B,0x94,0xC3,0x2F,0xBC, + 0x83,0x3C,0xFC,0x82,0x3B,0xD4,0x03,0x3B,0xB0,0xC3,0x8C,0xC8,0x21,0x07,0x7C,0x70,0x03,0x72,0x10,0x87, + 0x73,0x70,0x03,0x7B,0x08,0x07,0x79,0x60,0x87,0x70,0xC8,0x87,0x77,0xA8,0x07,0x7A,0x98,0x81,0x3C,0xE4, + 0x80,0x0F,0x6E,0x40,0x0F,0xE5,0xD0,0x0E,0xF0,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x0B,0x00,0x00,0x00, + 0x16,0x30,0x0D,0x97,0xEF,0x3C,0xFE,0xE2,0x00,0x83,0xD8,0x3C,0xD4,0xE4,0x17,0xB7,0x6D,0x02,0xD5,0x70, + 0xF9,0xCE,0xE3,0x4B,0x93,0x13,0x11,0x28,0x35,0x3D,0xD4,0xE4,0x17,0xB7,0x6D,0x00,0x04,0x03,0x20,0x0D, + 0x00,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x1E,0x00,0x00,0x00,0x13,0x04,0x41,0x2C,0x10,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x44,0x85,0x30,0x03,0x50,0x0A,0x54,0x25,0x50,0x06,0x00,0x00,0x23,0x06,0x09,0x00, + 0x82,0x60,0x60,0x4C,0x05,0x04,0x29,0xC4,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0x94,0x11,0x45,0x43,0x31, + 0x62,0x90,0x00,0x20,0x08,0x06,0x46,0x75,0x48,0xD2,0x62,0x8C,0x18,0x24,0x00,0x08,0x82,0x81,0x61,0x21, + 0xD3,0x44,0x1C,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0x58,0x07,0x45,0x39,0xC4,0x88,0x41,0x02,0x80,0x20, + 0x18,0x20,0xD6,0x41,0x51,0xC6,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x88,0x75,0x50,0x54,0x23,0x8C,0x18, + 0x24,0x00,0x08,0x82,0x01,0x62,0x1D,0x14,0x55,0x04,0x08,0x00,0x00,0x00,0x00,0x00, +}; + +alignas(uint32_t) static const unsigned char shader_frag_texture_spirv[] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x00,0x00,0x0E,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0F,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x58,0x02,0x00,0x00,0x05,0x00,0x06,0x00, + 0x05,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2E,0x32,0x64,0x2E,0x69,0x6D,0x61,0x67,0x65,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x00,0x05,0x00,0x06,0x00, + 0x07,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2E,0x73,0x61,0x6D,0x70,0x6C,0x65,0x72,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x53,0x61,0x6D,0x70,0x6C,0x65,0x72,0x00,0x05,0x00,0x07,0x00, + 0x02,0x00,0x00,0x00,0x69,0x6E,0x2E,0x76,0x61,0x72,0x2E,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x30, + 0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x03,0x00,0x00,0x00,0x69,0x6E,0x2E,0x76,0x61,0x72,0x2E,0x54, + 0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x31,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x04,0x00,0x00,0x00, + 0x6F,0x75,0x74,0x2E,0x76,0x61,0x72,0x2E,0x53,0x56,0x5F,0x54,0x61,0x72,0x67,0x65,0x74,0x30,0x00,0x00, + 0x05,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x09,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2E,0x73,0x61,0x6D,0x70,0x6C,0x65,0x64,0x2E,0x69,0x6D,0x61, + 0x67,0x65,0x00,0x00,0x47,0x00,0x04,0x00,0x02,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x03,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x04,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x0A,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x05,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x1A,0x00,0x02,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x0D,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x0F,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x0D,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x12,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x13,0x00,0x00,0x00, + 0x12,0x00,0x00,0x00,0x1B,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x0B,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0C,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x14,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0D,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x05,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x56,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x57,0x00,0x06,0x00,0x0D,0x00,0x00,0x00,0x1A,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x0D,0x00,0x00,0x00, + 0x1B,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x1B,0x00,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; + +alignas(uint32_t) static const unsigned char shader_frag_texture_msl[] = { + 0x23,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x6D,0x65,0x74,0x61,0x6C,0x5F,0x73,0x74,0x64,0x6C, + 0x69,0x62,0x3E,0x0A,0x23,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x73,0x69,0x6D,0x64,0x2F,0x73, + 0x69,0x6D,0x64,0x2E,0x68,0x3E,0x0A,0x0A,0x75,0x73,0x69,0x6E,0x67,0x20,0x6E,0x61,0x6D,0x65,0x73,0x70, + 0x61,0x63,0x65,0x20,0x6D,0x65,0x74,0x61,0x6C,0x3B,0x0A,0x0A,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6D, + 0x61,0x69,0x6E,0x30,0x5F,0x6F,0x75,0x74,0x0A,0x7B,0x0A,0x20,0x20,0x20,0x20,0x66,0x6C,0x6F,0x61,0x74, + 0x34,0x20,0x6F,0x75,0x74,0x5F,0x76,0x61,0x72,0x5F,0x53,0x56,0x5F,0x54,0x61,0x72,0x67,0x65,0x74,0x30, + 0x20,0x5B,0x5B,0x63,0x6F,0x6C,0x6F,0x72,0x28,0x30,0x29,0x5D,0x5D,0x3B,0x0A,0x7D,0x3B,0x0A,0x0A,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6D,0x61,0x69,0x6E,0x30,0x5F,0x69,0x6E,0x0A,0x7B,0x0A,0x20,0x20,0x20, + 0x20,0x66,0x6C,0x6F,0x61,0x74,0x34,0x20,0x69,0x6E,0x5F,0x76,0x61,0x72,0x5F,0x54,0x45,0x58,0x43,0x4F, + 0x4F,0x52,0x44,0x30,0x20,0x5B,0x5B,0x75,0x73,0x65,0x72,0x28,0x6C,0x6F,0x63,0x6E,0x30,0x29,0x5D,0x5D, + 0x3B,0x0A,0x20,0x20,0x20,0x20,0x66,0x6C,0x6F,0x61,0x74,0x32,0x20,0x69,0x6E,0x5F,0x76,0x61,0x72,0x5F, + 0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x31,0x20,0x5B,0x5B,0x75,0x73,0x65,0x72,0x28,0x6C,0x6F,0x63, + 0x6E,0x31,0x29,0x5D,0x5D,0x3B,0x0A,0x7D,0x3B,0x0A,0x0A,0x66,0x72,0x61,0x67,0x6D,0x65,0x6E,0x74,0x20, + 0x6D,0x61,0x69,0x6E,0x30,0x5F,0x6F,0x75,0x74,0x20,0x6D,0x61,0x69,0x6E,0x30,0x28,0x6D,0x61,0x69,0x6E, + 0x30,0x5F,0x69,0x6E,0x20,0x69,0x6E,0x20,0x5B,0x5B,0x73,0x74,0x61,0x67,0x65,0x5F,0x69,0x6E,0x5D,0x5D, + 0x2C,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x64,0x3C,0x66,0x6C,0x6F,0x61,0x74,0x3E,0x20,0x54, + 0x65,0x78,0x74,0x75,0x72,0x65,0x20,0x5B,0x5B,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5D, + 0x5D,0x2C,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x72,0x20,0x53,0x61,0x6D,0x70,0x6C,0x65,0x72,0x20,0x5B, + 0x5B,0x73,0x61,0x6D,0x70,0x6C,0x65,0x72,0x28,0x30,0x29,0x5D,0x5D,0x29,0x0A,0x7B,0x0A,0x20,0x20,0x20, + 0x20,0x6D,0x61,0x69,0x6E,0x30,0x5F,0x6F,0x75,0x74,0x20,0x6F,0x75,0x74,0x20,0x3D,0x20,0x7B,0x7D,0x3B, + 0x0A,0x20,0x20,0x20,0x20,0x6F,0x75,0x74,0x2E,0x6F,0x75,0x74,0x5F,0x76,0x61,0x72,0x5F,0x53,0x56,0x5F, + 0x54,0x61,0x72,0x67,0x65,0x74,0x30,0x20,0x3D,0x20,0x69,0x6E,0x2E,0x69,0x6E,0x5F,0x76,0x61,0x72,0x5F, + 0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x30,0x20,0x2A,0x20,0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x2E, + 0x73,0x61,0x6D,0x70,0x6C,0x65,0x28,0x53,0x61,0x6D,0x70,0x6C,0x65,0x72,0x2C,0x20,0x69,0x6E,0x2E,0x69, + 0x6E,0x5F,0x76,0x61,0x72,0x5F,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x31,0x29,0x3B,0x0A,0x20,0x20, + 0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6E,0x20,0x6F,0x75,0x74,0x3B,0x0A,0x7D,0x0A,0x0A, +}; + +alignas(uint32_t) static const unsigned char shader_frag_texture_dxil[] = { + 0x44,0x58,0x42,0x43,0xD6,0xEC,0x87,0x84,0xAB,0xDB,0x76,0x44,0x3D,0x9B,0x09,0x21,0x5C,0x7F,0x90,0x2E, + 0x01,0x00,0x00,0x00,0x14,0x0F,0x00,0x00,0x07,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x4C,0x00,0x00,0x00, + 0xA8,0x00,0x00,0x00,0xE4,0x00,0x00,0x00,0xD8,0x01,0x00,0x00,0x30,0x08,0x00,0x00,0x4C,0x08,0x00,0x00, + 0x53,0x46,0x49,0x30,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x53,0x47,0x31, + 0x54,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4F,0x4F,0x52,0x44,0x00,0x00,0x00,0x00,0x4F,0x53,0x47,0x31,0x34,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x56,0x5F,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0x00,0x00,0x50,0x53,0x56,0x30,0xEC,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x02,0x01,0x00,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52, + 0x44,0x00,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x00,0x6D,0x61,0x69,0x6E,0x00,0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x44,0x00,0x03,0x02,0x00,0x00,0x0A,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x01,0x42,0x00, + 0x03,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x44,0x10,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, + 0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x54,0x41,0x54,0x50,0x06,0x00,0x00, + 0x60,0x00,0x00,0x00,0x94,0x01,0x00,0x00,0x44,0x58,0x49,0x4C,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00, + 0x38,0x06,0x00,0x00,0x42,0x43,0xC0,0xDE,0x21,0x0C,0x00,0x00,0x8B,0x01,0x00,0x00,0x0B,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xC8,0x04,0x49,0x06,0x10,0x32,0x39, + 0x92,0x01,0x84,0x0C,0x25,0x05,0x08,0x19,0x1E,0x04,0x8B,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0B,0x42, + 0xA4,0x10,0x32,0x14,0x38,0x08,0x18,0x4B,0x0A,0x32,0x52,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xA5, + 0x00,0x19,0x32,0x42,0xE4,0x48,0x0E,0x90,0x91,0x22,0xC4,0x50,0x41,0x51,0x81,0x8C,0xE1,0x83,0xE5,0x8A, + 0x04,0x29,0x46,0x06,0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1B,0x8C,0xE0,0xFF,0xFF,0xFF,0xFF,0x07, + 0x40,0x02,0xA8,0x0D,0x84,0xF0,0xFF,0xFF,0xFF,0xFF,0x03,0x20,0x6D,0x30,0x86,0xFF,0xFF,0xFF,0xFF,0x1F, + 0x00,0x09,0xA8,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x4C,0x08,0x06, + 0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x43,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04, + 0x93,0x22,0xA4,0x84,0x04,0x93,0x22,0xE3,0x84,0xA1,0x90,0x14,0x12,0x4C,0x8A,0x8C,0x0B,0x84,0xA4,0x4C, + 0x10,0x68,0x23,0x00,0x25,0x00,0x14,0x66,0x00,0xE6,0x08,0xC0,0x60,0x8E,0x00,0x29,0xC6,0x20,0x84,0x14, + 0x42,0xA6,0x18,0x80,0x10,0x52,0x06,0xA1,0x9B,0x86,0xCB,0x9F,0xB0,0x87,0x90,0xFC,0x95,0x90,0x56,0x62, + 0xF2,0x8B,0xDB,0x46,0xC5,0x18,0x63,0x10,0x2A,0xF7,0x0C,0x97,0x3F,0x61,0x0F,0x21,0xF9,0x21,0xD0,0x0C, + 0x0B,0x81,0x82,0x55,0x18,0x45,0x18,0x1B,0x63,0x0C,0x42,0xC8,0xA0,0x36,0x47,0x10,0x14,0x83,0x91,0x42, + 0xC8,0x23,0x38,0x10,0x30,0x8C,0x40,0x0C,0x33,0xB5,0xC1,0x38,0xB0,0x43,0x38,0xCC,0xC3,0x3C,0xB8,0x01, + 0x2D,0x94,0x03,0x3E,0xD0,0x43,0x3D,0xC8,0x43,0x39,0xC8,0x01,0x29,0xF0,0x81,0x3D,0x94,0xC3,0x38,0xD0, + 0xC3,0x3B,0xC8,0x03,0x1F,0x98,0x03,0x3B,0xBC,0x43,0x38,0xD0,0x03,0x1B,0x80,0x01,0x1D,0xF8,0x01,0x18, + 0xF8,0x81,0x1E,0xE8,0x41,0x3B,0xA4,0x03,0x3C,0xCC,0xC3,0x2F,0xD0,0x43,0x3E,0xC0,0x43,0x39,0xA0,0x80, + 0xCC,0x24,0x06,0xE3,0xC0,0x0E,0xE1,0x30,0x0F,0xF3,0xE0,0x06,0xB4,0x50,0x0E,0xF8,0x40,0x0F,0xF5,0x20, + 0x0F,0xE5,0x20,0x07,0xA4,0xC0,0x07,0xF6,0x50,0x0E,0xE3,0x40,0x0F,0xEF,0x20,0x0F,0x7C,0x60,0x0E,0xEC, + 0xF0,0x0E,0xE1,0x40,0x0F,0x6C,0x00,0x06,0x74,0xE0,0x07,0x60,0xE0,0x07,0x48,0x98,0x94,0xEA,0x4D,0xD2, + 0x14,0x51,0xC2,0xE4,0xB3,0x00,0xF3,0x2C,0x44,0xC4,0x4E,0xC0,0x44,0xA0,0x80,0xD0,0x4D,0x04,0x02,0x00, + 0x13,0x14,0x72,0xC0,0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xC0,0x87,0x0D,0xAF,0x50, + 0x0E,0x6D,0xD0,0x0E,0x7A,0x50,0x0E,0x6D,0x00,0x0F,0x7A,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D, + 0x90,0x0E,0x71,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x78,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E, + 0x71,0x60,0x07,0x7A,0x30,0x07,0x72,0xD0,0x06,0xE9,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90, + 0x0E,0x76,0x40,0x07,0x7A,0x60,0x07,0x74,0xD0,0x06,0xE6,0x10,0x07,0x76,0xA0,0x07,0x73,0x20,0x07,0x6D, + 0x60,0x0E,0x73,0x20,0x07,0x7A,0x30,0x07,0x72,0xD0,0x06,0xE6,0x60,0x07,0x74,0xA0,0x07,0x76,0x40,0x07, + 0x6D,0xE0,0x0E,0x78,0xA0,0x07,0x71,0x60,0x07,0x7A,0x30,0x07,0x72,0xA0,0x07,0x76,0x40,0x07,0x43,0x9E, + 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x3C,0x06,0x10,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0C,0x79,0x10,0x20,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xF2,0x34, + 0x40,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xE4,0x81,0x80,0x00,0x18,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x20,0x0B,0x04,0x00,0x00,0x0E,0x00,0x00,0x00,0x32,0x1E,0x98,0x14,0x19,0x11,0x4C,0x90, + 0x8C,0x09,0x26,0x47,0xC6,0x04,0x43,0x22,0x25,0x30,0x02,0x50,0x0C,0x45,0x50,0x12,0x65,0x50,0x1E,0x85, + 0x50,0x2C,0x54,0x4A,0x62,0x04,0xA0,0x08,0x0A,0xA1,0x40,0xC8,0xCE,0x00,0x10,0x9E,0x01,0xA0,0x3C,0x16, + 0x62,0x10,0x81,0x40,0x20,0xCF,0x03,0x00,0x79,0x18,0x00,0x00,0x79,0x00,0x00,0x00,0x1A,0x03,0x4C,0x90, + 0x46,0x02,0x13,0xC4,0x8E,0x0C,0x6F,0xEC,0xED,0x4D,0x0C,0x24,0xC6,0x05,0xC7,0x45,0xA6,0x06,0x46,0xC6, + 0x05,0x07,0x04,0x45,0x8C,0xE6,0x26,0x26,0x06,0x67,0x26,0xA7,0x2C,0x65,0x43,0x10,0x4C,0x10,0x88,0x62, + 0x82,0x40,0x18,0x1B,0x84,0x81,0xD8,0x20,0x10,0x04,0x05,0xB8,0xB9,0x09,0x02,0x71,0x6C,0x18,0x0E,0x84, + 0x98,0x20,0x58,0x13,0x0F,0xAA,0x32,0x3C,0xBA,0x3A,0xB9,0xB2,0x09,0x02,0x81,0x4C,0x10,0x88,0x64,0x83, + 0x40,0x34,0x1B,0x12,0x42,0x59,0x18,0x62,0x60,0x08,0x67,0x43,0xF0,0x4C,0x10,0x30,0x8A,0xC7,0x54,0x58, + 0x1B,0x1C,0x5B,0x99,0xDC,0x06,0x84,0x88,0x24,0x86,0x18,0x08,0x60,0x43,0x30,0x6D,0x20,0x20,0x00,0xA0, + 0x26,0x08,0x02,0xB0,0x01,0xD8,0x30,0x10,0xD7,0xB5,0x21,0xC0,0x36,0x0C,0x83,0x95,0x4D,0x10,0xB2,0x6A, + 0x43,0xB0,0x91,0x68,0x0B,0x4B,0x73,0x23,0x42,0x55,0x84,0x35,0xF4,0xF4,0x24,0x45,0x34,0x41,0x28,0x9C, + 0x09,0x42,0xF1,0x6C,0x08,0x88,0x09,0x42,0x01,0x4D,0x10,0x8A,0x68,0x82,0x40,0x28,0x13,0x04,0x62,0xD9, + 0x20,0x90,0x41,0x19,0x6C,0x58,0x08,0xEF,0x03,0x83,0x30,0x10,0x83,0x61,0x0C,0x08,0x30,0x30,0x83,0x0D, + 0xC1,0xB0,0x41,0x20,0x03,0x32,0xD8,0xB0,0x0C,0xDE,0x07,0x06,0x68,0x20,0x06,0x83,0x18,0x0C,0x60,0x90, + 0x06,0x1B,0x84,0x33,0x50,0x03,0x26,0x53,0x56,0x5F,0x54,0x61,0x72,0x67,0x65,0x74,0x13,0x84,0x42,0xDA, + 0xB0,0x10,0x6C,0xF0,0xB5,0x41,0x18,0x80,0xC1,0x30,0x06,0x04,0x18,0x98,0xC1,0x86,0xC0,0x0D,0x36,0x0C, + 0x6B,0xF0,0x06,0xC0,0x86,0xC2,0xEA,0xE0,0xA0,0x02,0x68,0x98,0xB1,0xBD,0x85,0xD1,0xCD,0x4D,0x10,0x08, + 0x86,0x45,0x9A,0xDB,0x1C,0xDD,0xDC,0x04,0x81,0x68,0x68,0xCC,0xA5,0x9D,0x7D,0xB1,0x91,0xD1,0x98,0x4B, + 0x3B,0xFB,0x9A,0xA3,0x23,0x42,0x57,0x86,0xF7,0xE5,0xF6,0x26,0xD7,0xB6,0x41,0x91,0x83,0x39,0xA0,0x83, + 0x3A,0xB0,0x03,0xE4,0x0E,0xE6,0x00,0x0F,0x86,0x2A,0x6C,0x6C,0x76,0x6D,0x2E,0x69,0x64,0x65,0x6E,0x74, + 0x53,0x82,0xA0,0x0A,0x19,0x9E,0x8B,0x5D,0x99,0xDC,0x5C,0xDA,0x9B,0xDB,0x94,0x80,0x68,0x42,0x86,0xE7, + 0x62,0x17,0xC6,0x66,0x57,0x26,0x37,0x25,0x28,0xEA,0x90,0xE1,0xB9,0xCC,0xA1,0x85,0x91,0x95,0xC9,0x35, + 0xBD,0x91,0x95,0xB1,0x4D,0x09,0x90,0x32,0x64,0x78,0x2E,0x72,0x65,0x73,0x6F,0x75,0x72,0x63,0x65,0x73, + 0x53,0x02,0xAA,0x12,0x19,0x9E,0x0B,0x5D,0x1E,0x5C,0x59,0x90,0x9B,0xDB,0x1B,0x5D,0x18,0x5D,0xDA,0x9B, + 0xDB,0xDC,0x94,0x20,0xAB,0x43,0x86,0xE7,0x62,0x97,0x56,0x76,0x97,0x44,0x36,0x45,0x17,0x46,0x57,0x36, + 0x25,0xD8,0xEA,0x90,0xE1,0xB9,0x94,0xB9,0xD1,0xC9,0xE5,0x41,0xBD,0xA5,0xB9,0xD1,0xCD,0x4D,0x09,0xE0, + 0xA0,0x0B,0x19,0x9E,0xCB,0xD8,0x5B,0x9D,0x1B,0x5D,0x99,0xDC,0xDC,0x94,0x00,0x0F,0x00,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0x4C,0x00,0x00,0x00,0x33,0x08,0x80,0x1C,0xC4,0xE1,0x1C,0x66,0x14,0x01,0x3D,0x88, + 0x43,0x38,0x84,0xC3,0x8C,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0C,0xE6,0x00,0x0F,0xED,0x10, + 0x0E,0xF4,0x80,0x0E,0x33,0x0C,0x42,0x1E,0xC2,0xC1,0x1D,0xCE,0xA1,0x1C,0x66,0x30,0x05,0x3D,0x88,0x43, + 0x38,0x84,0x83,0x1B,0xCC,0x03,0x3D,0xC8,0x43,0x3D,0x8C,0x03,0x3D,0xCC,0x78,0x8C,0x74,0x70,0x07,0x7B, + 0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7A,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xCC,0x11, + 0x0E,0xEC,0x90,0x0E,0xE1,0x30,0x0F,0x6E,0x30,0x0F,0xE3,0xF0,0x0E,0xF0,0x50,0x0E,0x33,0x10,0xC4,0x1D, + 0xDE,0x21,0x1C,0xD8,0x21,0x1D,0xC2,0x61,0x1E,0x66,0x30,0x89,0x3B,0xBC,0x83,0x3B,0xD0,0x43,0x39,0xB4, + 0x03,0x3C,0xBC,0x83,0x3C,0x84,0x03,0x3B,0xCC,0xF0,0x14,0x76,0x60,0x07,0x7B,0x68,0x07,0x37,0x68,0x87, + 0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xF8,0x05,0x76,0x78, + 0x87,0x77,0x80,0x87,0x5F,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2C,0xEE,0xF0,0x0E, + 0xEE,0xE0,0x0E,0xF5,0xC0,0x0E,0xEC,0x30,0x03,0x62,0xC8,0xA1,0x1C,0xE4,0xA1,0x1C,0xCC,0xA1,0x1C,0xE4, + 0xA1,0x1C,0xDC,0x61,0x1C,0xCA,0x21,0x1C,0xC4,0x81,0x1D,0xCA,0x61,0x06,0xD6,0x90,0x43,0x39,0xC8,0x43, + 0x39,0x98,0x43,0x39,0xC8,0x43,0x39,0xB8,0xC3,0x38,0x94,0x43,0x38,0x88,0x03,0x3B,0x94,0xC3,0x2F,0xBC, + 0x83,0x3C,0xFC,0x82,0x3B,0xD4,0x03,0x3B,0xB0,0xC3,0x8C,0xC8,0x21,0x07,0x7C,0x70,0x03,0x72,0x10,0x87, + 0x73,0x70,0x03,0x7B,0x08,0x07,0x79,0x60,0x87,0x70,0xC8,0x87,0x77,0xA8,0x07,0x7A,0x98,0x81,0x3C,0xE4, + 0x80,0x0F,0x6E,0x40,0x0F,0xE5,0xD0,0x0E,0xF0,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x12,0x00,0x00,0x00, + 0x46,0x20,0x0D,0x97,0xEF,0x3C,0xBE,0x10,0x11,0xC0,0x44,0x84,0x40,0x33,0x2C,0x84,0x05,0x4C,0xC3,0xE5, + 0x3B,0x8F,0xBF,0x38,0xC0,0x20,0x36,0x0F,0x35,0xF9,0xC5,0x6D,0xDB,0x00,0x34,0x5C,0xBE,0xF3,0xF8,0x12, + 0xC0,0x3C,0x0B,0xE1,0x17,0xB7,0x6D,0x02,0xD5,0x70,0xF9,0xCE,0xE3,0x4B,0x93,0x13,0x11,0x28,0x35,0x3D, + 0xD4,0xE4,0x17,0xB7,0x6D,0x00,0x04,0x03,0x20,0x0D,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x41,0x53,0x48, + 0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x40,0x62,0x49,0x8C,0x9B,0x86,0xEE,0x5C,0xCE,0x55,0xED, + 0xA7,0x1A,0xB9,0x7D,0x44,0x58,0x49,0x4C,0xC0,0x06,0x00,0x00,0x60,0x00,0x00,0x00,0xB0,0x01,0x00,0x00, + 0x44,0x58,0x49,0x4C,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0xA8,0x06,0x00,0x00,0x42,0x43,0xC0,0xDE, + 0x21,0x0C,0x00,0x00,0xA7,0x01,0x00,0x00,0x0B,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x07,0x81,0x23,0x91,0x41,0xC8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0C,0x25,0x05,0x08,0x19, + 0x1E,0x04,0x8B,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0B,0x42,0xA4,0x10,0x32,0x14,0x38,0x08,0x18,0x4B, + 0x0A,0x32,0x52,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xA5,0x00,0x19,0x32,0x42,0xE4,0x48,0x0E,0x90, + 0x91,0x22,0xC4,0x50,0x41,0x51,0x81,0x8C,0xE1,0x83,0xE5,0x8A,0x04,0x29,0x46,0x06,0x51,0x18,0x00,0x00, + 0x08,0x00,0x00,0x00,0x1B,0x8C,0xE0,0xFF,0xFF,0xFF,0xFF,0x07,0x40,0x02,0xA8,0x0D,0x84,0xF0,0xFF,0xFF, + 0xFF,0xFF,0x03,0x20,0x6D,0x30,0x86,0xFF,0xFF,0xFF,0xFF,0x1F,0x00,0x09,0xA8,0x00,0x49,0x18,0x00,0x00, + 0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x4C,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00, + 0x43,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xA4,0x84,0x04,0x93,0x22,0xE3, + 0x84,0xA1,0x90,0x14,0x12,0x4C,0x8A,0x8C,0x0B,0x84,0xA4,0x4C,0x10,0x68,0x23,0x00,0x25,0x00,0x14,0x66, + 0x00,0xE6,0x08,0xC0,0x60,0x8E,0x00,0x29,0xC6,0x20,0x84,0x14,0x42,0xA6,0x18,0x80,0x10,0x52,0x06,0xA1, + 0x9B,0x86,0xCB,0x9F,0xB0,0x87,0x90,0xFC,0x95,0x90,0x56,0x62,0xF2,0x8B,0xDB,0x46,0xC5,0x18,0x63,0x10, + 0x2A,0xF7,0x0C,0x97,0x3F,0x61,0x0F,0x21,0xF9,0x21,0xD0,0x0C,0x0B,0x81,0x82,0x55,0x18,0x45,0x18,0x1B, + 0x63,0x0C,0x42,0xC8,0xA0,0x36,0x47,0x10,0x14,0x83,0x91,0x42,0xC8,0x23,0x38,0x10,0x30,0x8C,0x40,0x0C, + 0x33,0xB5,0xC1,0x38,0xB0,0x43,0x38,0xCC,0xC3,0x3C,0xB8,0x01,0x2D,0x94,0x03,0x3E,0xD0,0x43,0x3D,0xC8, + 0x43,0x39,0xC8,0x01,0x29,0xF0,0x81,0x3D,0x94,0xC3,0x38,0xD0,0xC3,0x3B,0xC8,0x03,0x1F,0x98,0x03,0x3B, + 0xBC,0x43,0x38,0xD0,0x03,0x1B,0x80,0x01,0x1D,0xF8,0x01,0x18,0xF8,0x81,0x1E,0xE8,0x41,0x3B,0xA4,0x03, + 0x3C,0xCC,0xC3,0x2F,0xD0,0x43,0x3E,0xC0,0x43,0x39,0xA0,0x80,0xCC,0x24,0x06,0xE3,0xC0,0x0E,0xE1,0x30, + 0x0F,0xF3,0xE0,0x06,0xB4,0x50,0x0E,0xF8,0x40,0x0F,0xF5,0x20,0x0F,0xE5,0x20,0x07,0xA4,0xC0,0x07,0xF6, + 0x50,0x0E,0xE3,0x40,0x0F,0xEF,0x20,0x0F,0x7C,0x60,0x0E,0xEC,0xF0,0x0E,0xE1,0x40,0x0F,0x6C,0x00,0x06, + 0x74,0xE0,0x07,0x60,0xE0,0x07,0x48,0x98,0x94,0xEA,0x4D,0xD2,0x14,0x51,0xC2,0xE4,0xB3,0x00,0xF3,0x2C, + 0x44,0xC4,0x4E,0xC0,0x44,0xA0,0x80,0xD0,0x4D,0x04,0x02,0x00,0x13,0x14,0x72,0xC0,0x87,0x74,0x60,0x87, + 0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xC0,0x87,0x0D,0xAF,0x50,0x0E,0x6D,0xD0,0x0E,0x7A,0x50,0x0E,0x6D, + 0x00,0x0F,0x7A,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x71,0xA0,0x07,0x73,0x20,0x07, + 0x6D,0x90,0x0E,0x78,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x71,0x60,0x07,0x7A,0x30,0x07,0x72,0xD0, + 0x06,0xE9,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x76,0x40,0x07,0x7A,0x60,0x07,0x74, + 0xD0,0x06,0xE6,0x10,0x07,0x76,0xA0,0x07,0x73,0x20,0x07,0x6D,0x60,0x0E,0x73,0x20,0x07,0x7A,0x30,0x07, + 0x72,0xD0,0x06,0xE6,0x60,0x07,0x74,0xA0,0x07,0x76,0x40,0x07,0x6D,0xE0,0x0E,0x78,0xA0,0x07,0x71,0x60, + 0x07,0x7A,0x30,0x07,0x72,0xA0,0x07,0x76,0x40,0x07,0x43,0x9E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x86,0x3C,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x79,0x10,0x20, + 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xF2,0x34,0x40,0x00,0x0C,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x30,0xE4,0x81,0x80,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x0B,0x04,0x00,0x00, + 0x0E,0x00,0x00,0x00,0x32,0x1E,0x98,0x14,0x19,0x11,0x4C,0x90,0x8C,0x09,0x26,0x47,0xC6,0x04,0x43,0x22, + 0x25,0x30,0x02,0x50,0x10,0xC5,0x50,0x04,0x25,0x51,0x06,0xE5,0x41,0xA5,0x24,0x46,0x00,0x8A,0xA0,0x10, + 0x0A,0x84,0xEC,0x0C,0x00,0xE1,0x19,0x00,0xCA,0x63,0x21,0x06,0x11,0x08,0x04,0xF2,0x3C,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0x58,0x00,0x00,0x00,0x1A,0x03,0x4C,0x90,0x46,0x02,0x13,0xC4,0x8E,0x0C,0x6F,0xEC, + 0xED,0x4D,0x0C,0x24,0xC6,0x05,0xC7,0x45,0xA6,0x06,0x46,0xC6,0x05,0x07,0x04,0x45,0x8C,0xE6,0x26,0x26, + 0x06,0x67,0x26,0xA7,0x2C,0x65,0x43,0x10,0x4C,0x10,0x88,0x62,0x82,0x40,0x18,0x1B,0x84,0x81,0x98,0x20, + 0x10,0xC7,0x06,0x61,0x30,0x28,0xC0,0xCD,0x4D,0x10,0x08,0x64,0xC3,0x80,0x24,0xC4,0x04,0xC1,0x92,0x08, + 0x4C,0x10,0x88,0x64,0x82,0x40,0x28,0x1B,0x04,0xC2,0xD9,0x90,0x10,0x0B,0xD3,0x10,0x43,0x43,0x3C,0x1B, + 0x02,0x68,0x82,0x80,0x4D,0x1B,0x10,0x42,0x62,0x1A,0x62,0x20,0x80,0x0D,0xC1,0xB4,0x81,0x88,0x00,0x80, + 0x9A,0x20,0x64,0xD4,0x86,0xC0,0x9A,0x20,0x08,0x00,0x89,0xB6,0xB0,0x34,0x37,0x22,0x54,0x45,0x58,0x43, + 0x4F,0x4F,0x52,0x44,0x13,0x84,0xA2,0x99,0x20,0x14,0xCE,0x86,0x80,0x98,0x20,0x14,0xCF,0x04,0xA1,0x80, + 0x26,0x08,0xC4,0x32,0x41,0x20,0x98,0x0D,0x02,0x18,0x84,0xC1,0x86,0x85,0xD0,0x36,0xAE,0xF3,0x86,0x8F, + 0xE0,0xC4,0x60,0x43,0x30,0x6C,0x10,0xC0,0x00,0x0C,0x36,0x2C,0x83,0xB6,0x71,0x64,0xE0,0x0D,0xDE,0xC0, + 0x95,0xC1,0x06,0x61,0x0C,0xCC,0x80,0xC9,0x94,0xD5,0x17,0x55,0x98,0xDC,0x59,0x19,0xDD,0x04,0xA1,0x88, + 0x36,0x2C,0x04,0x1A,0x6C,0x69,0xD0,0x71,0xC3,0x47,0x70,0x62,0xB0,0x21,0x50,0x83,0x0D,0xC3,0x19,0xAC, + 0x01,0xB0,0xA1,0xC0,0x32,0x36,0xA8,0x80,0x2A,0x6C,0x6C,0x76,0x6D,0x2E,0x69,0x64,0x65,0x6E,0x74,0x53, + 0x82,0xA0,0x0A,0x19,0x9E,0x8B,0x5D,0x99,0xDC,0x5C,0xDA,0x9B,0xDB,0x94,0x80,0x68,0x42,0x86,0xE7,0x62, + 0x17,0xC6,0x66,0x57,0x26,0x37,0x25,0x30,0xEA,0x90,0xE1,0xB9,0xCC,0xA1,0x85,0x91,0x95,0xC9,0x35,0xBD, + 0x91,0x95,0xB1,0x4D,0x09,0x92,0x32,0x64,0x78,0x2E,0x72,0x65,0x73,0x6F,0x75,0x72,0x63,0x65,0x73,0x53, + 0x02,0xAA,0x0E,0x19,0x9E,0x8B,0x5D,0x5A,0xD9,0x5D,0x12,0xD9,0x14,0x5D,0x18,0x5D,0xD9,0x94,0xC0,0xAA, + 0x43,0x86,0xE7,0x52,0xE6,0x46,0x27,0x97,0x07,0xF5,0x96,0xE6,0x46,0x37,0x37,0x25,0x60,0x03,0x00,0x00, + 0x79,0x18,0x00,0x00,0x4C,0x00,0x00,0x00,0x33,0x08,0x80,0x1C,0xC4,0xE1,0x1C,0x66,0x14,0x01,0x3D,0x88, + 0x43,0x38,0x84,0xC3,0x8C,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0C,0xE6,0x00,0x0F,0xED,0x10, + 0x0E,0xF4,0x80,0x0E,0x33,0x0C,0x42,0x1E,0xC2,0xC1,0x1D,0xCE,0xA1,0x1C,0x66,0x30,0x05,0x3D,0x88,0x43, + 0x38,0x84,0x83,0x1B,0xCC,0x03,0x3D,0xC8,0x43,0x3D,0x8C,0x03,0x3D,0xCC,0x78,0x8C,0x74,0x70,0x07,0x7B, + 0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7A,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xCC,0x11, + 0x0E,0xEC,0x90,0x0E,0xE1,0x30,0x0F,0x6E,0x30,0x0F,0xE3,0xF0,0x0E,0xF0,0x50,0x0E,0x33,0x10,0xC4,0x1D, + 0xDE,0x21,0x1C,0xD8,0x21,0x1D,0xC2,0x61,0x1E,0x66,0x30,0x89,0x3B,0xBC,0x83,0x3B,0xD0,0x43,0x39,0xB4, + 0x03,0x3C,0xBC,0x83,0x3C,0x84,0x03,0x3B,0xCC,0xF0,0x14,0x76,0x60,0x07,0x7B,0x68,0x07,0x37,0x68,0x87, + 0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xF8,0x05,0x76,0x78, + 0x87,0x77,0x80,0x87,0x5F,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2C,0xEE,0xF0,0x0E, + 0xEE,0xE0,0x0E,0xF5,0xC0,0x0E,0xEC,0x30,0x03,0x62,0xC8,0xA1,0x1C,0xE4,0xA1,0x1C,0xCC,0xA1,0x1C,0xE4, + 0xA1,0x1C,0xDC,0x61,0x1C,0xCA,0x21,0x1C,0xC4,0x81,0x1D,0xCA,0x61,0x06,0xD6,0x90,0x43,0x39,0xC8,0x43, + 0x39,0x98,0x43,0x39,0xC8,0x43,0x39,0xB8,0xC3,0x38,0x94,0x43,0x38,0x88,0x03,0x3B,0x94,0xC3,0x2F,0xBC, + 0x83,0x3C,0xFC,0x82,0x3B,0xD4,0x03,0x3B,0xB0,0xC3,0x8C,0xC8,0x21,0x07,0x7C,0x70,0x03,0x72,0x10,0x87, + 0x73,0x70,0x03,0x7B,0x08,0x07,0x79,0x60,0x87,0x70,0xC8,0x87,0x77,0xA8,0x07,0x7A,0x98,0x81,0x3C,0xE4, + 0x80,0x0F,0x6E,0x40,0x0F,0xE5,0xD0,0x0E,0xF0,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x12,0x00,0x00,0x00, + 0x46,0x20,0x0D,0x97,0xEF,0x3C,0xBE,0x10,0x11,0xC0,0x44,0x84,0x40,0x33,0x2C,0x84,0x05,0x4C,0xC3,0xE5, + 0x3B,0x8F,0xBF,0x38,0xC0,0x20,0x36,0x0F,0x35,0xF9,0xC5,0x6D,0xDB,0x00,0x34,0x5C,0xBE,0xF3,0xF8,0x12, + 0xC0,0x3C,0x0B,0xE1,0x17,0xB7,0x6D,0x02,0xD5,0x70,0xF9,0xCE,0xE3,0x4B,0x93,0x13,0x11,0x28,0x35,0x3D, + 0xD4,0xE4,0x17,0xB7,0x6D,0x00,0x04,0x03,0x20,0x0D,0x00,0x00,0x61,0x20,0x00,0x00,0x3B,0x00,0x00,0x00, + 0x13,0x04,0x41,0x2C,0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0xF4,0x46,0x00,0x88,0xCC,0x00,0x14,0x42, + 0x29,0x94,0x5C,0xE1,0x51,0x29,0x83,0x12,0xA0,0x31,0x03,0x00,0x23,0x06,0x09,0x00,0x82,0x60,0x00,0x69, + 0x05,0x84,0x61,0xC9,0x88,0x41,0x02,0x80,0x20,0x18,0x40,0x9B,0x41,0x64,0x99,0x32,0x62,0x90,0x00,0x20, + 0x08,0x06,0xC6,0x97,0x6C,0x9A,0xA4,0x8C,0x18,0x24,0x00,0x08,0x82,0x81,0x01,0x06,0x0A,0xB7,0x15,0xCB, + 0x88,0x41,0x02,0x80,0x20,0x18,0x18,0x61,0xB0,0x70,0x1C,0xC5,0x8C,0x18,0x24,0x00,0x08,0x82,0x81,0x21, + 0x06,0x4C,0xD7,0x1D,0xCD,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0x63,0xD0,0x78,0x5E,0xE5,0x8C,0x18,0x24, + 0x00,0x08,0x82,0x81,0x41,0x06,0xCE,0xF7,0x29,0xCF,0x88,0xC1,0x03,0x80,0x20,0x18,0x34,0x63,0xC0,0x20, + 0x87,0x51,0x24,0x09,0x18,0x80,0x01,0x94,0x8C,0x26,0x04,0xC0,0x68,0x82,0x10,0x8C,0x26,0x0C,0xC2,0x68, + 0x02,0x31,0x18,0x91,0xC8,0xC7,0x88,0x44,0x3E,0x46,0x24,0xF2,0x31,0x22,0x91,0xCF,0x88,0x41,0x02,0x80, + 0x20,0x18,0x20,0x6D,0x70,0xA5,0x41,0x1A,0x84,0x01,0x31,0x62,0x90,0x00,0x20,0x08,0x06,0x48,0x1B,0x5C, + 0x69,0x90,0x06,0xD3,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x48,0x1B,0x5C,0x69,0x90,0x06,0x60,0x20,0x8C, + 0x18,0x24,0x00,0x08,0x82,0x01,0xD2,0x06,0x57,0x1A,0xA4,0x01,0x15,0x20,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +alignas(uint32_t) static const unsigned char shader_vert_spirv[] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x00,0x00,0x0E,0x00,0x2B,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0F,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x58,0x02,0x00,0x00,0x05,0x00,0x09,0x00, + 0x08,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2E,0x55,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x42,0x6C,0x6F,0x63, + 0x6B,0x54,0x72,0x61,0x6E,0x73,0x66,0x6F,0x72,0x6D,0x00,0x00,0x06,0x00,0x06,0x00,0x08,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x54,0x72,0x61,0x6E,0x73,0x66,0x6F,0x72,0x6D,0x00,0x00,0x00,0x05,0x00,0x08,0x00, + 0x09,0x00,0x00,0x00,0x55,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x42,0x6C,0x6F,0x63,0x6B,0x54,0x72,0x61,0x6E, + 0x73,0x66,0x6F,0x72,0x6D,0x00,0x00,0x00,0x05,0x00,0x09,0x00,0x0A,0x00,0x00,0x00,0x74,0x79,0x70,0x65, + 0x2E,0x55,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x42,0x6C,0x6F,0x63,0x6B,0x54,0x72,0x61,0x6E,0x73,0x6C,0x61, + 0x74,0x65,0x00,0x00,0x06,0x00,0x06,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x72,0x61,0x6E, + 0x73,0x6C,0x61,0x74,0x65,0x00,0x00,0x00,0x05,0x00,0x08,0x00,0x0B,0x00,0x00,0x00,0x55,0x6E,0x69,0x66, + 0x6F,0x72,0x6D,0x42,0x6C,0x6F,0x63,0x6B,0x54,0x72,0x61,0x6E,0x73,0x6C,0x61,0x74,0x65,0x00,0x00,0x00, + 0x05,0x00,0x07,0x00,0x02,0x00,0x00,0x00,0x69,0x6E,0x2E,0x76,0x61,0x72,0x2E,0x54,0x45,0x58,0x43,0x4F, + 0x4F,0x52,0x44,0x30,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x03,0x00,0x00,0x00,0x69,0x6E,0x2E,0x76, + 0x61,0x72,0x2E,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x31,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x04,0x00,0x00,0x00,0x69,0x6E,0x2E,0x76,0x61,0x72,0x2E,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x32, + 0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x05,0x00,0x00,0x00,0x6F,0x75,0x74,0x2E,0x76,0x61,0x72,0x2E, + 0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x30,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x06,0x00,0x00,0x00, + 0x6F,0x75,0x74,0x2E,0x76,0x61,0x72,0x2E,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x31,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x02,0x00,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x03,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0B,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0B,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x08,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x0A,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x0C,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x0C,0x00,0x00,0x00, + 0x0D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x0E,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x0E,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x80,0x3F,0x17,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x0E,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x1E,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x14,0x00,0x00,0x00, + 0x0E,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1E,0x00,0x03,0x00,0x0A,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x13,0x00,0x02,0x00,0x1A,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x1B,0x00,0x00,0x00,0x1A,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x1C,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x1D,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x13,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x16,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x18,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x1A,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x1E,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x1F,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x1C,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x14,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x14,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x0E,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x0E,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x11,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x1D,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0D,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x90,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0x2A,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x05,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x2A,0x00,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; + +alignas(uint32_t) static const unsigned char shader_vert_msl[] = { + 0x23,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x6D,0x65,0x74,0x61,0x6C,0x5F,0x73,0x74,0x64,0x6C, + 0x69,0x62,0x3E,0x0A,0x23,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x73,0x69,0x6D,0x64,0x2F,0x73, + 0x69,0x6D,0x64,0x2E,0x68,0x3E,0x0A,0x0A,0x75,0x73,0x69,0x6E,0x67,0x20,0x6E,0x61,0x6D,0x65,0x73,0x70, + 0x61,0x63,0x65,0x20,0x6D,0x65,0x74,0x61,0x6C,0x3B,0x0A,0x0A,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, + 0x79,0x70,0x65,0x5F,0x55,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x42,0x6C,0x6F,0x63,0x6B,0x54,0x72,0x61,0x6E, + 0x73,0x66,0x6F,0x72,0x6D,0x0A,0x7B,0x0A,0x20,0x20,0x20,0x20,0x66,0x6C,0x6F,0x61,0x74,0x34,0x78,0x34, + 0x20,0x54,0x72,0x61,0x6E,0x73,0x66,0x6F,0x72,0x6D,0x3B,0x0A,0x7D,0x3B,0x0A,0x0A,0x73,0x74,0x72,0x75, + 0x63,0x74,0x20,0x74,0x79,0x70,0x65,0x5F,0x55,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x42,0x6C,0x6F,0x63,0x6B, + 0x54,0x72,0x61,0x6E,0x73,0x6C,0x61,0x74,0x65,0x0A,0x7B,0x0A,0x20,0x20,0x20,0x20,0x66,0x6C,0x6F,0x61, + 0x74,0x32,0x20,0x54,0x72,0x61,0x6E,0x73,0x6C,0x61,0x74,0x65,0x3B,0x0A,0x7D,0x3B,0x0A,0x0A,0x73,0x74, + 0x72,0x75,0x63,0x74,0x20,0x6D,0x61,0x69,0x6E,0x30,0x5F,0x6F,0x75,0x74,0x0A,0x7B,0x0A,0x20,0x20,0x20, + 0x20,0x66,0x6C,0x6F,0x61,0x74,0x34,0x20,0x6F,0x75,0x74,0x5F,0x76,0x61,0x72,0x5F,0x54,0x45,0x58,0x43, + 0x4F,0x4F,0x52,0x44,0x30,0x20,0x5B,0x5B,0x75,0x73,0x65,0x72,0x28,0x6C,0x6F,0x63,0x6E,0x30,0x29,0x5D, + 0x5D,0x3B,0x0A,0x20,0x20,0x20,0x20,0x66,0x6C,0x6F,0x61,0x74,0x32,0x20,0x6F,0x75,0x74,0x5F,0x76,0x61, + 0x72,0x5F,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x31,0x20,0x5B,0x5B,0x75,0x73,0x65,0x72,0x28,0x6C, + 0x6F,0x63,0x6E,0x31,0x29,0x5D,0x5D,0x3B,0x0A,0x20,0x20,0x20,0x20,0x66,0x6C,0x6F,0x61,0x74,0x34,0x20, + 0x67,0x6C,0x5F,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x20,0x5B,0x5B,0x70,0x6F,0x73,0x69,0x74,0x69, + 0x6F,0x6E,0x5D,0x5D,0x3B,0x0A,0x7D,0x3B,0x0A,0x0A,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6D,0x61,0x69, + 0x6E,0x30,0x5F,0x69,0x6E,0x0A,0x7B,0x0A,0x20,0x20,0x20,0x20,0x66,0x6C,0x6F,0x61,0x74,0x32,0x20,0x69, + 0x6E,0x5F,0x76,0x61,0x72,0x5F,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x30,0x20,0x5B,0x5B,0x61,0x74, + 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x30,0x29,0x5D,0x5D,0x3B,0x0A,0x20,0x20,0x20,0x20,0x66,0x6C, + 0x6F,0x61,0x74,0x34,0x20,0x69,0x6E,0x5F,0x76,0x61,0x72,0x5F,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44, + 0x31,0x20,0x5B,0x5B,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5D,0x5D,0x3B,0x0A, + 0x20,0x20,0x20,0x20,0x66,0x6C,0x6F,0x61,0x74,0x32,0x20,0x69,0x6E,0x5F,0x76,0x61,0x72,0x5F,0x54,0x45, + 0x58,0x43,0x4F,0x4F,0x52,0x44,0x32,0x20,0x5B,0x5B,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28, + 0x32,0x29,0x5D,0x5D,0x3B,0x0A,0x7D,0x3B,0x0A,0x0A,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x6D,0x61,0x69, + 0x6E,0x30,0x5F,0x6F,0x75,0x74,0x20,0x6D,0x61,0x69,0x6E,0x30,0x28,0x6D,0x61,0x69,0x6E,0x30,0x5F,0x69, + 0x6E,0x20,0x69,0x6E,0x20,0x5B,0x5B,0x73,0x74,0x61,0x67,0x65,0x5F,0x69,0x6E,0x5D,0x5D,0x2C,0x20,0x63, + 0x6F,0x6E,0x73,0x74,0x61,0x6E,0x74,0x20,0x74,0x79,0x70,0x65,0x5F,0x55,0x6E,0x69,0x66,0x6F,0x72,0x6D, + 0x42,0x6C,0x6F,0x63,0x6B,0x54,0x72,0x61,0x6E,0x73,0x66,0x6F,0x72,0x6D,0x26,0x20,0x55,0x6E,0x69,0x66, + 0x6F,0x72,0x6D,0x42,0x6C,0x6F,0x63,0x6B,0x54,0x72,0x61,0x6E,0x73,0x66,0x6F,0x72,0x6D,0x20,0x5B,0x5B, + 0x62,0x75,0x66,0x66,0x65,0x72,0x28,0x30,0x29,0x5D,0x5D,0x2C,0x20,0x63,0x6F,0x6E,0x73,0x74,0x61,0x6E, + 0x74,0x20,0x74,0x79,0x70,0x65,0x5F,0x55,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x42,0x6C,0x6F,0x63,0x6B,0x54, + 0x72,0x61,0x6E,0x73,0x6C,0x61,0x74,0x65,0x26,0x20,0x55,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x42,0x6C,0x6F, + 0x63,0x6B,0x54,0x72,0x61,0x6E,0x73,0x6C,0x61,0x74,0x65,0x20,0x5B,0x5B,0x62,0x75,0x66,0x66,0x65,0x72, + 0x28,0x31,0x29,0x5D,0x5D,0x29,0x0A,0x7B,0x0A,0x20,0x20,0x20,0x20,0x6D,0x61,0x69,0x6E,0x30,0x5F,0x6F, + 0x75,0x74,0x20,0x6F,0x75,0x74,0x20,0x3D,0x20,0x7B,0x7D,0x3B,0x0A,0x20,0x20,0x20,0x20,0x6F,0x75,0x74, + 0x2E,0x6F,0x75,0x74,0x5F,0x76,0x61,0x72,0x5F,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x30,0x20,0x3D, + 0x20,0x69,0x6E,0x2E,0x69,0x6E,0x5F,0x76,0x61,0x72,0x5F,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x31, + 0x3B,0x0A,0x20,0x20,0x20,0x20,0x6F,0x75,0x74,0x2E,0x6F,0x75,0x74,0x5F,0x76,0x61,0x72,0x5F,0x54,0x45, + 0x58,0x43,0x4F,0x4F,0x52,0x44,0x31,0x20,0x3D,0x20,0x69,0x6E,0x2E,0x69,0x6E,0x5F,0x76,0x61,0x72,0x5F, + 0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x32,0x3B,0x0A,0x20,0x20,0x20,0x20,0x6F,0x75,0x74,0x2E,0x67, + 0x6C,0x5F,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x20,0x3D,0x20,0x55,0x6E,0x69,0x66,0x6F,0x72,0x6D, + 0x42,0x6C,0x6F,0x63,0x6B,0x54,0x72,0x61,0x6E,0x73,0x66,0x6F,0x72,0x6D,0x2E,0x54,0x72,0x61,0x6E,0x73, + 0x66,0x6F,0x72,0x6D,0x20,0x2A,0x20,0x66,0x6C,0x6F,0x61,0x74,0x34,0x28,0x69,0x6E,0x2E,0x69,0x6E,0x5F, + 0x76,0x61,0x72,0x5F,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x30,0x20,0x2B,0x20,0x55,0x6E,0x69,0x66, + 0x6F,0x72,0x6D,0x42,0x6C,0x6F,0x63,0x6B,0x54,0x72,0x61,0x6E,0x73,0x6C,0x61,0x74,0x65,0x2E,0x54,0x72, + 0x61,0x6E,0x73,0x6C,0x61,0x74,0x65,0x2C,0x20,0x30,0x2E,0x30,0x2C,0x20,0x31,0x2E,0x30,0x29,0x3B,0x0A, + 0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6E,0x20,0x6F,0x75,0x74,0x3B,0x0A,0x7D,0x0A,0x0A, +}; + +alignas(uint32_t) static const unsigned char shader_vert_dxil[] = { + 0x44,0x58,0x42,0x43,0x41,0x89,0x95,0x46,0xEE,0xBB,0xC2,0x51,0x0B,0xA5,0x2F,0x16,0x3D,0x58,0x9E,0xDD, + 0x01,0x00,0x00,0x00,0x9C,0x11,0x00,0x00,0x07,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x4C,0x00,0x00,0x00, + 0xC8,0x00,0x00,0x00,0x50,0x01,0x00,0x00,0xA4,0x02,0x00,0x00,0xB4,0x09,0x00,0x00,0xD0,0x09,0x00,0x00, + 0x53,0x46,0x49,0x30,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x53,0x47,0x31, + 0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x00,0x00,0x00,0x00, + 0x4F,0x53,0x47,0x31,0x80,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x0C,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44, + 0x00,0x53,0x56,0x5F,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,0x00,0x00,0x00,0x50,0x53,0x56,0x30, + 0x4C,0x01,0x00,0x00,0x34,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x00,0x00,0x03,0x03,0x00,0x03, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2E,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x00,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x00,0x54, + 0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4F,0x4F,0x52,0x44,0x00,0x54,0x45,0x58, + 0x43,0x4F,0x4F,0x52,0x44,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x42,0x00,0x03,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x01,0x44,0x00, + 0x03,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x02,0x42,0x00,0x03,0x00,0x00,0x00, + 0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x44,0x00,0x03,0x02,0x00,0x00,0x25,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x01,0x42,0x00,0x03,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x44,0x03,0x03,0x04,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x54,0x41,0x54, + 0x08,0x07,0x00,0x00,0x60,0x00,0x01,0x00,0xC2,0x01,0x00,0x00,0x44,0x58,0x49,0x4C,0x00,0x01,0x00,0x00, + 0x10,0x00,0x00,0x00,0xF0,0x06,0x00,0x00,0x42,0x43,0xC0,0xDE,0x21,0x0C,0x00,0x00,0xB9,0x01,0x00,0x00, + 0x0B,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xC8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0C,0x25,0x05,0x08,0x19,0x1E,0x04,0x8B,0x62,0x80,0x14,0x45,0x02, + 0x42,0x92,0x0B,0x42,0xA4,0x10,0x32,0x14,0x38,0x08,0x18,0x4B,0x0A,0x32,0x52,0x88,0x48,0x90,0x14,0x20, + 0x43,0x46,0x88,0xA5,0x00,0x19,0x32,0x42,0xE4,0x48,0x0E,0x90,0x91,0x22,0xC4,0x50,0x41,0x51,0x81,0x8C, + 0xE1,0x83,0xE5,0x8A,0x04,0x29,0x46,0x06,0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1B,0x8C,0xE0,0xFF, + 0xFF,0xFF,0xFF,0x07,0x40,0x02,0xA8,0x0D,0x84,0xF0,0xFF,0xFF,0xFF,0xFF,0x03,0x20,0x6D,0x30,0x86,0xFF, + 0xFF,0xFF,0xFF,0x1F,0x00,0x09,0xA8,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42, + 0x20,0x4C,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x2E,0x00,0x00,0x00,0x32,0x22,0x48,0x09, + 0x20,0x64,0x85,0x04,0x93,0x22,0xA4,0x84,0x04,0x93,0x22,0xE3,0x84,0xA1,0x90,0x14,0x12,0x4C,0x8A,0x8C, + 0x0B,0x84,0xA4,0x4C,0x10,0x74,0x23,0x00,0x25,0x00,0x14,0x66,0x00,0xE6,0x08,0xC0,0x60,0x8E,0x00,0x29, + 0xC6,0x20,0x84,0x14,0x42,0xA6,0x18,0x80,0x10,0x52,0x06,0xA1,0xA3,0x86,0xCB,0x9F,0xB0,0x87,0x90,0x7C, + 0x6E,0xA3,0x8A,0x95,0x98,0xFC,0xE2,0xB6,0x11,0x31,0xC6,0x18,0x54,0xEE,0x19,0x2E,0x7F,0xC2,0x1E,0x42, + 0xF2,0x43,0xA0,0x19,0x16,0x02,0x05,0xAB,0x10,0x8A,0x30,0x42,0xAD,0x14,0x83,0x8C,0x31,0xE8,0xCD,0x11, + 0x04,0xC5,0x60,0xA4,0x10,0x12,0x49,0x0E,0x04,0x0C,0x23,0x10,0x43,0x12,0xD4,0x2B,0x83,0xC3,0x91,0xA6, + 0x05,0xC0,0x1C,0x6A,0xF2,0x27,0xEC,0x21,0x7E,0xB7,0x41,0x0A,0x27,0x62,0xB6,0xC5,0x11,0x94,0x36,0x02, + 0x1A,0xA9,0x70,0x22,0x06,0x05,0x96,0xEE,0x30,0x82,0x30,0x9C,0x36,0x61,0x0F,0xF1,0xBB,0x0D,0x52,0x38, + 0x11,0xB3,0x2D,0x8E,0xA0,0xB4,0x11,0xD0,0x48,0x0B,0x30,0x11,0x28,0xC8,0xA4,0x93,0x81,0x00,0x00,0x00, + 0x13,0x14,0x72,0xC0,0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xC0,0x87,0x0D,0xAF,0x50, + 0x0E,0x6D,0xD0,0x0E,0x7A,0x50,0x0E,0x6D,0x00,0x0F,0x7A,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D, + 0x90,0x0E,0x71,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x78,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E, + 0x71,0x60,0x07,0x7A,0x30,0x07,0x72,0xD0,0x06,0xE9,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90, + 0x0E,0x76,0x40,0x07,0x7A,0x60,0x07,0x74,0xD0,0x06,0xE6,0x10,0x07,0x76,0xA0,0x07,0x73,0x20,0x07,0x6D, + 0x60,0x0E,0x73,0x20,0x07,0x7A,0x30,0x07,0x72,0xD0,0x06,0xE6,0x60,0x07,0x74,0xA0,0x07,0x76,0x40,0x07, + 0x6D,0xE0,0x0E,0x78,0xA0,0x07,0x71,0x60,0x07,0x7A,0x30,0x07,0x72,0xA0,0x07,0x76,0x40,0x07,0x43,0x9E, + 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x3C,0x06,0x10,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0C,0x79,0x10,0x20,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xF2,0x34, + 0x40,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xE4,0x79,0x80,0x00,0x08,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x60,0xC8,0x23,0x01,0x01,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x16,0x08,0x00, + 0x14,0x00,0x00,0x00,0x32,0x1E,0x98,0x14,0x19,0x11,0x4C,0x90,0x8C,0x09,0x26,0x47,0xC6,0x04,0x43,0x22, + 0x25,0x30,0x02,0x50,0x0C,0x05,0x18,0x50,0x10,0x65,0x50,0x0E,0x25,0x51,0x1A,0x45,0x50,0x08,0xE5,0x51, + 0x14,0xA5,0x46,0xA5,0x24,0x46,0x00,0x8A,0xA0,0x10,0xCA,0x80,0xEE,0x0C,0x00,0xE1,0x19,0x00,0xD2,0x33, + 0x00,0xB4,0x67,0x00,0x88,0x8F,0xC5,0x28,0x0C,0x88,0x0F,0x20,0x3E,0x00,0x40,0x20,0x10,0x08,0x04,0x06, + 0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xAD,0x00,0x00,0x00,0x1A,0x03,0x4C,0x90,0x46,0x02,0x13,0xC4, + 0x8E,0x0C,0x6F,0xEC,0xED,0x4D,0x0C,0x24,0xC6,0x05,0xC7,0x45,0xA6,0x06,0x46,0xC6,0x05,0x07,0x04,0x45, + 0x8C,0xE6,0x26,0x26,0x06,0x67,0x26,0xA7,0x2C,0x65,0x43,0x10,0x4C,0x10,0x08,0x63,0x82,0x40,0x1C,0x1B, + 0x84,0x81,0xD8,0x20,0x10,0x04,0x05,0xBB,0xB9,0x09,0x02,0x81,0x6C,0x18,0x0E,0x84,0x98,0x20,0x60,0x1A, + 0x35,0xBA,0x3C,0xB8,0xB2,0xAF,0x2A,0xB7,0x34,0xB3,0x37,0xB9,0x36,0x21,0xB6,0xB7,0xB1,0x35,0x2A,0xB9, + 0x30,0xB7,0x39,0xB3,0x37,0xB9,0xB6,0x09,0x02,0x91,0x6C,0x40,0x08,0x65,0x19,0x88,0x81,0x01,0x26,0x08, + 0x1B,0x47,0x8D,0x2E,0x0F,0xAE,0xEC,0xAB,0xCA,0x2D,0xCD,0xEC,0x4D,0xAE,0x4D,0x88,0xED,0x6D,0x6C,0x8D, + 0x4A,0x2E,0xCC,0x6D,0x8E,0x2D,0x8C,0xAE,0x6C,0x82,0x40,0x28,0x1B,0x90,0xC1,0x79,0x86,0x61,0x80,0x80, + 0x0D,0x42,0x13,0x6D,0x20,0x00,0x40,0x02,0x26,0x08,0xDA,0xC6,0xAF,0xCA,0x2D,0xCD,0xEC,0x4D,0xAE,0x4D, + 0x88,0xED,0x6D,0x6C,0x8D,0x4A,0x2E,0xCC,0x6D,0x8E,0x2D,0x8C,0xAE,0xEC,0x8B,0x4A,0x2E,0xCC,0x6D,0x8E, + 0x2D,0x8C,0xAE,0x6C,0x82,0x40,0x2C,0x13,0x04,0x82,0x99,0x20,0x10,0xCD,0x04,0x81,0x70,0x26,0x08,0xC4, + 0xB3,0x01,0x41,0x2A,0x8B,0xB8,0xB0,0x4C,0xDB,0x20,0x40,0xDB,0x04,0xE1,0xCA,0xF8,0x55,0xB9,0xA5,0x99, + 0xBD,0xC9,0xB5,0x09,0xB1,0xBD,0x8D,0xAD,0x51,0xC9,0x85,0xB9,0xCD,0x99,0xBD,0xC9,0xB5,0x7D,0x51,0xC9, + 0x85,0xB9,0xCD,0x99,0xBD,0xC9,0xB5,0x4D,0x10,0x08,0x68,0xC3,0xF0,0x7D,0xDA,0x06,0x04,0xF1,0x34,0x30, + 0xB0,0x88,0x0B,0xDB,0x20,0x30,0x61,0xB0,0xA1,0x20,0x28,0xAE,0x13,0x83,0x09,0x82,0x00,0x6C,0x00,0x36, + 0x0C,0x44,0x19,0x94,0xC1,0x86,0xC0,0x0C,0x36,0x0C,0x03,0x19,0x9C,0xC1,0x04,0x81,0xEB,0x36,0x04,0x69, + 0x40,0xA2,0x2D,0x2C,0xCD,0x8D,0x08,0x55,0x11,0xD6,0xD0,0xD3,0x93,0x14,0xD1,0x04,0xA1,0xA0,0x26,0x08, + 0x45,0xB5,0x21,0x20,0x26,0x08,0x85,0xB5,0x41,0xB0,0xAC,0x0D,0x0B,0xC1,0x06,0x6D,0xE0,0x06,0x6F,0xE0, + 0x06,0x03,0x1C,0x10,0x6E,0x10,0x07,0x1B,0x82,0x61,0x82,0x50,0x5C,0x13,0x04,0x22,0xDA,0x20,0x58,0x75, + 0xB0,0x61,0x19,0xD8,0xA0,0x0D,0xDC,0x60,0x0E,0xDC,0x60,0xA0,0x83,0xC1,0x0D,0xEC,0x60,0x43,0xA0,0x6D, + 0x58,0x34,0x36,0x68,0x03,0x37,0xC0,0x03,0x37,0x18,0xE0,0x40,0x73,0x83,0x38,0xD8,0x30,0xC8,0xC1,0x1D, + 0xE4,0xC1,0x86,0x85,0x60,0x83,0x36,0x70,0x83,0x37,0x80,0x83,0x81,0x0E,0x08,0x37,0xB0,0x83,0x0D,0xCB, + 0xC0,0x06,0x6D,0xE0,0x06,0x73,0x00,0x07,0x03,0x1C,0x0C,0x6E,0x10,0x07,0x5C,0xA6,0xAC,0xBE,0xA0,0xDE, + 0xE6,0xD2,0xE8,0xD2,0xDE,0xDC,0x26,0x08,0x05,0xB6,0x61,0xD1,0xFA,0xA0,0x0D,0xFC,0xE0,0x0D,0xE8,0x60, + 0xA0,0x03,0xCD,0x0D,0xEC,0x60,0xC3,0xB0,0x07,0x7C,0xF0,0x07,0x1B,0x06,0x3D,0x00,0x05,0x60,0x43,0x41, + 0x06,0x6B,0x10,0x0A,0x13,0x40,0xC3,0x8C,0xED,0x2D,0x8C,0x6E,0x6E,0x82,0x40,0x48,0x2C,0xD2,0xDC,0xE6, + 0xE8,0xE6,0x26,0x08,0xC4,0x44,0x63,0x2E,0xED,0xEC,0x8B,0x8D,0x8C,0xC6,0x5C,0xDA,0xD9,0xD7,0x1C,0xDD, + 0x06,0x64,0x14,0x48,0xA1,0x14,0x4C,0xE1,0x14,0x20,0x54,0x20,0x85,0x2A,0x6C,0x6C,0x76,0x6D,0x2E,0x69, + 0x64,0x65,0x6E,0x74,0x53,0x82,0xA0,0x0A,0x19,0x9E,0x8B,0x5D,0x99,0xDC,0x5C,0xDA,0x9B,0xDB,0x94,0x80, + 0x68,0x42,0x86,0xE7,0x62,0x17,0xC6,0x66,0x57,0x26,0x37,0x25,0x28,0xEA,0x90,0xE1,0xB9,0xCC,0xA1,0x85, + 0x91,0x95,0xC9,0x35,0xBD,0x91,0x95,0xB1,0x4D,0x09,0x90,0x32,0x64,0x78,0x2E,0x72,0x65,0x73,0x6F,0x75, + 0x72,0x63,0x65,0x73,0x53,0x02,0xA9,0x12,0x19,0x9E,0x0B,0x5D,0x1E,0x5C,0x59,0x90,0x9B,0xDB,0x1B,0x5D, + 0x18,0x5D,0xDA,0x9B,0xDB,0xDC,0x14,0x41,0x0C,0xCE,0xA0,0x0E,0x19,0x9E,0x8B,0x5D,0x5A,0xD9,0x5D,0x12, + 0xD9,0x14,0x5D,0x18,0x5D,0xD9,0x94,0x20,0x0D,0xEA,0x90,0xE1,0xB9,0x94,0xB9,0xD1,0xC9,0xE5,0x41,0xBD, + 0xA5,0xB9,0xD1,0xCD,0x4D,0x09,0x42,0xA1,0x0B,0x19,0x9E,0xCB,0xD8,0x5B,0x9D,0x1B,0x5D,0x99,0xDC,0xDC, + 0x94,0x00,0x15,0x00,0x79,0x18,0x00,0x00,0x4C,0x00,0x00,0x00,0x33,0x08,0x80,0x1C,0xC4,0xE1,0x1C,0x66, + 0x14,0x01,0x3D,0x88,0x43,0x38,0x84,0xC3,0x8C,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0C,0xE6, + 0x00,0x0F,0xED,0x10,0x0E,0xF4,0x80,0x0E,0x33,0x0C,0x42,0x1E,0xC2,0xC1,0x1D,0xCE,0xA1,0x1C,0x66,0x30, + 0x05,0x3D,0x88,0x43,0x38,0x84,0x83,0x1B,0xCC,0x03,0x3D,0xC8,0x43,0x3D,0x8C,0x03,0x3D,0xCC,0x78,0x8C, + 0x74,0x70,0x07,0x7B,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7A,0x70,0x03,0x76,0x78,0x87,0x70,0x20, + 0x87,0x19,0xCC,0x11,0x0E,0xEC,0x90,0x0E,0xE1,0x30,0x0F,0x6E,0x30,0x0F,0xE3,0xF0,0x0E,0xF0,0x50,0x0E, + 0x33,0x10,0xC4,0x1D,0xDE,0x21,0x1C,0xD8,0x21,0x1D,0xC2,0x61,0x1E,0x66,0x30,0x89,0x3B,0xBC,0x83,0x3B, + 0xD0,0x43,0x39,0xB4,0x03,0x3C,0xBC,0x83,0x3C,0x84,0x03,0x3B,0xCC,0xF0,0x14,0x76,0x60,0x07,0x7B,0x68, + 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76, + 0xF8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5F,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81, + 0x2C,0xEE,0xF0,0x0E,0xEE,0xE0,0x0E,0xF5,0xC0,0x0E,0xEC,0x30,0x03,0x62,0xC8,0xA1,0x1C,0xE4,0xA1,0x1C, + 0xCC,0xA1,0x1C,0xE4,0xA1,0x1C,0xDC,0x61,0x1C,0xCA,0x21,0x1C,0xC4,0x81,0x1D,0xCA,0x61,0x06,0xD6,0x90, + 0x43,0x39,0xC8,0x43,0x39,0x98,0x43,0x39,0xC8,0x43,0x39,0xB8,0xC3,0x38,0x94,0x43,0x38,0x88,0x03,0x3B, + 0x94,0xC3,0x2F,0xBC,0x83,0x3C,0xFC,0x82,0x3B,0xD4,0x03,0x3B,0xB0,0xC3,0x8C,0xC8,0x21,0x07,0x7C,0x70, + 0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7B,0x08,0x07,0x79,0x60,0x87,0x70,0xC8,0x87,0x77,0xA8,0x07,0x7A, + 0x98,0x81,0x3C,0xE4,0x80,0x0F,0x6E,0x40,0x0F,0xE5,0xD0,0x0E,0xF0,0x00,0x00,0x00,0x71,0x20,0x00,0x00, + 0x18,0x00,0x00,0x00,0x36,0xB0,0x0D,0x97,0xEF,0x3C,0xBE,0x10,0x50,0x45,0x41,0x44,0xA5,0x03,0x0C,0x25, + 0x61,0x00,0x02,0xE6,0x17,0xB7,0x6D,0x05,0xD2,0x70,0xF9,0xCE,0xE3,0x0B,0x11,0x01,0x4C,0x44,0x08,0x34, + 0xC3,0x42,0x58,0xC0,0x34,0x5C,0xBE,0xF3,0xF8,0x8B,0x03,0x0C,0x62,0xF3,0x50,0x93,0x5F,0xDC,0xB6,0x09, + 0x54,0xC3,0xE5,0x3B,0x8F,0x2F,0x4D,0x4E,0x44,0xA0,0xD4,0xF4,0x50,0x93,0x5F,0xDC,0xB6,0x11,0x48,0xC3, + 0xE5,0x3B,0x8F,0x3F,0x11,0xD1,0x84,0x00,0x11,0xE6,0x17,0xB7,0x6D,0x00,0x04,0x03,0x20,0x0D,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x41,0x53,0x48,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0xC5,0xB4,0xCB, + 0x0D,0xAD,0xA7,0x7C,0x41,0x76,0x11,0xF5,0xDD,0x93,0xF6,0x75,0x44,0x58,0x49,0x4C,0xC4,0x07,0x00,0x00, + 0x60,0x00,0x01,0x00,0xF1,0x01,0x00,0x00,0x44,0x58,0x49,0x4C,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00, + 0xAC,0x07,0x00,0x00,0x42,0x43,0xC0,0xDE,0x21,0x0C,0x00,0x00,0xE8,0x01,0x00,0x00,0x0B,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xC8,0x04,0x49,0x06,0x10,0x32,0x39, + 0x92,0x01,0x84,0x0C,0x25,0x05,0x08,0x19,0x1E,0x04,0x8B,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0B,0x42, + 0xA4,0x10,0x32,0x14,0x38,0x08,0x18,0x4B,0x0A,0x32,0x52,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xA5, + 0x00,0x19,0x32,0x42,0xE4,0x48,0x0E,0x90,0x91,0x22,0xC4,0x50,0x41,0x51,0x81,0x8C,0xE1,0x83,0xE5,0x8A, + 0x04,0x29,0x46,0x06,0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1B,0x8C,0xE0,0xFF,0xFF,0xFF,0xFF,0x07, + 0x40,0x02,0xA8,0x0D,0x84,0xF0,0xFF,0xFF,0xFF,0xFF,0x03,0x20,0x6D,0x30,0x86,0xFF,0xFF,0xFF,0xFF,0x1F, + 0x00,0x09,0xA8,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x4C,0x08,0x06, + 0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x2E,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04, + 0x93,0x22,0xA4,0x84,0x04,0x93,0x22,0xE3,0x84,0xA1,0x90,0x14,0x12,0x4C,0x8A,0x8C,0x0B,0x84,0xA4,0x4C, + 0x10,0x74,0x23,0x00,0x25,0x00,0x14,0x66,0x00,0xE6,0x08,0xC0,0x60,0x8E,0x00,0x29,0xC6,0x20,0x84,0x14, + 0x42,0xA6,0x18,0x80,0x10,0x52,0x06,0xA1,0xA3,0x86,0xCB,0x9F,0xB0,0x87,0x90,0x7C,0x6E,0xA3,0x8A,0x95, + 0x98,0xFC,0xE2,0xB6,0x11,0x31,0xC6,0x18,0x54,0xEE,0x19,0x2E,0x7F,0xC2,0x1E,0x42,0xF2,0x43,0xA0,0x19, + 0x16,0x02,0x05,0xAB,0x10,0x8A,0x30,0x42,0xAD,0x14,0x83,0x8C,0x31,0xE8,0xCD,0x11,0x04,0xC5,0x60,0xA4, + 0x10,0x12,0x49,0x0E,0x04,0x0C,0x23,0x10,0x43,0x12,0xD4,0x2B,0x83,0xC3,0x91,0xA6,0x05,0xC0,0x1C,0x6A, + 0xF2,0x27,0xEC,0x21,0x7E,0xB7,0x41,0x0A,0x27,0x62,0xB6,0xC5,0x11,0x94,0x36,0x02,0x1A,0xA9,0x70,0x22, + 0x06,0x05,0x96,0xEE,0x30,0x82,0x30,0x9C,0x36,0x61,0x0F,0xF1,0xBB,0x0D,0x52,0x38,0x11,0xB3,0x2D,0x8E, + 0xA0,0xB4,0x11,0xD0,0x48,0x0B,0x30,0x11,0x28,0xC8,0xA4,0x93,0x81,0x00,0x00,0x00,0x13,0x14,0x72,0xC0, + 0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xC0,0x87,0x0D,0xAF,0x50,0x0E,0x6D,0xD0,0x0E, + 0x7A,0x50,0x0E,0x6D,0x00,0x0F,0x7A,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x71,0xA0, + 0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x78,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x71,0x60,0x07,0x7A, + 0x30,0x07,0x72,0xD0,0x06,0xE9,0x30,0x07,0x72,0xA0,0x07,0x73,0x20,0x07,0x6D,0x90,0x0E,0x76,0x40,0x07, + 0x7A,0x60,0x07,0x74,0xD0,0x06,0xE6,0x10,0x07,0x76,0xA0,0x07,0x73,0x20,0x07,0x6D,0x60,0x0E,0x73,0x20, + 0x07,0x7A,0x30,0x07,0x72,0xD0,0x06,0xE6,0x60,0x07,0x74,0xA0,0x07,0x76,0x40,0x07,0x6D,0xE0,0x0E,0x78, + 0xA0,0x07,0x71,0x60,0x07,0x7A,0x30,0x07,0x72,0xA0,0x07,0x76,0x40,0x07,0x43,0x9E,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x3C,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0C,0x79,0x10,0x20,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xF2,0x34,0x40,0x00,0x0C,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xE4,0x79,0x80,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60, + 0xC8,0x23,0x01,0x01,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x10,0x00,0x00,0x00, + 0x32,0x1E,0x98,0x14,0x19,0x11,0x4C,0x90,0x8C,0x09,0x26,0x47,0xC6,0x04,0x43,0x22,0x25,0x30,0x02,0x50, + 0x10,0xC5,0x50,0x80,0x01,0x65,0x50,0x1E,0x45,0x40,0xA5,0x24,0x46,0x00,0x8A,0xA0,0x10,0xCA,0x80,0xF0, + 0x0C,0x00,0xED,0x19,0x00,0xE2,0x63,0x31,0x0A,0x03,0xE2,0x03,0x88,0x0F,0x00,0x10,0x08,0x04,0x02,0x81, + 0x01,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x61,0x00,0x00,0x00,0x1A,0x03,0x4C,0x90,0x46,0x02,0x13,0xC4, + 0x8E,0x0C,0x6F,0xEC,0xED,0x4D,0x0C,0x24,0xC6,0x05,0xC7,0x45,0xA6,0x06,0x46,0xC6,0x05,0x07,0x04,0x45, + 0x8C,0xE6,0x26,0x26,0x06,0x67,0x26,0xA7,0x2C,0x65,0x43,0x10,0x4C,0x10,0x08,0x63,0x82,0x40,0x1C,0x1B, + 0x84,0x81,0x98,0x20,0x10,0xC8,0x06,0x61,0x30,0x28,0xD8,0xCD,0x4D,0x10,0x88,0x64,0xC3,0x80,0x24,0xC4, + 0x04,0x01,0x9B,0x08,0x4C,0x10,0x08,0x65,0x03,0x42,0x2C,0xCC,0x40,0x0C,0x0D,0x30,0x41,0xD8,0xA8,0x0D, + 0xC8,0xF0,0x30,0xC3,0x30,0x18,0xC0,0x06,0xC1,0x81,0x36,0x10,0x00,0x10,0x01,0x13,0x04,0xAE,0xDA,0x10, + 0x4C,0x13,0x04,0x01,0x20,0xD1,0x16,0x96,0xE6,0x46,0x84,0xAA,0x08,0x6B,0xE8,0xE9,0x49,0x8A,0x68,0x82, + 0x50,0x38,0x13,0x84,0xE2,0xD9,0x10,0x10,0x13,0x84,0x02,0x9A,0x20,0x10,0xCB,0x06,0x81,0xE3,0x36,0x2C, + 0xC4,0x85,0x65,0x5A,0x36,0x6C,0x44,0xD6,0x6D,0x08,0x86,0x09,0x42,0x11,0x4D,0x10,0x08,0x66,0x83,0xC0, + 0x85,0xC1,0x86,0x65,0xB8,0xB0,0xEC,0xCB,0x06,0x30,0x18,0x32,0x31,0x98,0x20,0x10,0xCD,0x86,0x80,0x0C, + 0x36,0x2C,0x64,0x70,0x61,0x59,0x19,0x64,0xC3,0x46,0x06,0x59,0xB7,0x61,0xF0,0xC6,0xC0,0x0C,0x36,0x2C, + 0xC4,0x85,0x65,0xDA,0x36,0x80,0x01,0x91,0x89,0xC1,0x86,0x65,0xB8,0xB0,0xEC,0xDB,0x86,0x6D,0xC8,0x3A, + 0x2E,0x53,0x56,0x5F,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x13,0x84,0x42,0xDA,0xB0,0x90,0x81,0x1A, + 0x60,0x6B,0xA0,0x81,0xC1,0x00,0x06,0x64,0x90,0x89,0xC1,0x86,0x01,0x0D,0xD2,0x80,0x0D,0x36,0x0C,0x67, + 0xD0,0x06,0xC0,0x86,0xA2,0xB2,0xDC,0x40,0x02,0xAA,0xB0,0xB1,0xD9,0xB5,0xB9,0xA4,0x91,0x95,0xB9,0xD1, + 0x4D,0x09,0x82,0x2A,0x64,0x78,0x2E,0x76,0x65,0x72,0x73,0x69,0x6F,0x6E,0x53,0x02,0xA2,0x09,0x19,0x9E, + 0x8B,0x5D,0x18,0x9B,0x5D,0x99,0xDC,0x94,0xC0,0xA8,0x43,0x86,0xE7,0x32,0x87,0x16,0x46,0x56,0x26,0xD7, + 0xF4,0x46,0x56,0xC6,0x36,0x25,0x48,0xCA,0x90,0xE1,0xB9,0xC8,0x95,0xCD,0xBD,0xD5,0xC9,0x8D,0x95,0xCD, + 0x4D,0x09,0xA2,0x3A,0x64,0x78,0x2E,0x76,0x69,0x65,0x77,0x49,0x64,0x53,0x74,0x61,0x74,0x65,0x53,0x82, + 0xA9,0x0E,0x19,0x9E,0x4B,0x99,0x1B,0x9D,0x5C,0x1E,0xD4,0x5B,0x9A,0x1B,0xDD,0xDC,0x94,0xC0,0x0D,0x00, + 0x79,0x18,0x00,0x00,0x4C,0x00,0x00,0x00,0x33,0x08,0x80,0x1C,0xC4,0xE1,0x1C,0x66,0x14,0x01,0x3D,0x88, + 0x43,0x38,0x84,0xC3,0x8C,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0C,0xE6,0x00,0x0F,0xED,0x10, + 0x0E,0xF4,0x80,0x0E,0x33,0x0C,0x42,0x1E,0xC2,0xC1,0x1D,0xCE,0xA1,0x1C,0x66,0x30,0x05,0x3D,0x88,0x43, + 0x38,0x84,0x83,0x1B,0xCC,0x03,0x3D,0xC8,0x43,0x3D,0x8C,0x03,0x3D,0xCC,0x78,0x8C,0x74,0x70,0x07,0x7B, + 0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7A,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xCC,0x11, + 0x0E,0xEC,0x90,0x0E,0xE1,0x30,0x0F,0x6E,0x30,0x0F,0xE3,0xF0,0x0E,0xF0,0x50,0x0E,0x33,0x10,0xC4,0x1D, + 0xDE,0x21,0x1C,0xD8,0x21,0x1D,0xC2,0x61,0x1E,0x66,0x30,0x89,0x3B,0xBC,0x83,0x3B,0xD0,0x43,0x39,0xB4, + 0x03,0x3C,0xBC,0x83,0x3C,0x84,0x03,0x3B,0xCC,0xF0,0x14,0x76,0x60,0x07,0x7B,0x68,0x07,0x37,0x68,0x87, + 0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xF8,0x05,0x76,0x78, + 0x87,0x77,0x80,0x87,0x5F,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2C,0xEE,0xF0,0x0E, + 0xEE,0xE0,0x0E,0xF5,0xC0,0x0E,0xEC,0x30,0x03,0x62,0xC8,0xA1,0x1C,0xE4,0xA1,0x1C,0xCC,0xA1,0x1C,0xE4, + 0xA1,0x1C,0xDC,0x61,0x1C,0xCA,0x21,0x1C,0xC4,0x81,0x1D,0xCA,0x61,0x06,0xD6,0x90,0x43,0x39,0xC8,0x43, + 0x39,0x98,0x43,0x39,0xC8,0x43,0x39,0xB8,0xC3,0x38,0x94,0x43,0x38,0x88,0x03,0x3B,0x94,0xC3,0x2F,0xBC, + 0x83,0x3C,0xFC,0x82,0x3B,0xD4,0x03,0x3B,0xB0,0xC3,0x8C,0xC8,0x21,0x07,0x7C,0x70,0x03,0x72,0x10,0x87, + 0x73,0x70,0x03,0x7B,0x08,0x07,0x79,0x60,0x87,0x70,0xC8,0x87,0x77,0xA8,0x07,0x7A,0x98,0x81,0x3C,0xE4, + 0x80,0x0F,0x6E,0x40,0x0F,0xE5,0xD0,0x0E,0xF0,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x18,0x00,0x00,0x00, + 0x36,0xB0,0x0D,0x97,0xEF,0x3C,0xBE,0x10,0x50,0x45,0x41,0x44,0xA5,0x03,0x0C,0x25,0x61,0x00,0x02,0xE6, + 0x17,0xB7,0x6D,0x05,0xD2,0x70,0xF9,0xCE,0xE3,0x0B,0x11,0x01,0x4C,0x44,0x08,0x34,0xC3,0x42,0x58,0xC0, + 0x34,0x5C,0xBE,0xF3,0xF8,0x8B,0x03,0x0C,0x62,0xF3,0x50,0x93,0x5F,0xDC,0xB6,0x09,0x54,0xC3,0xE5,0x3B, + 0x8F,0x2F,0x4D,0x4E,0x44,0xA0,0xD4,0xF4,0x50,0x93,0x5F,0xDC,0xB6,0x11,0x48,0xC3,0xE5,0x3B,0x8F,0x3F, + 0x11,0xD1,0x84,0x00,0x11,0xE6,0x17,0xB7,0x6D,0x00,0x04,0x03,0x20,0x0D,0x00,0x00,0x61,0x20,0x00,0x00, + 0x7D,0x00,0x00,0x00,0x13,0x04,0x41,0x2C,0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x44,0x4A,0xA1,0x10, + 0x66,0x00,0xCA,0xAE,0xB8,0x4A,0x8E,0x4A,0x09,0x50,0x1C,0x01,0x00,0x00,0x00,0x00,0x23,0x06,0x09,0x00, + 0x82,0x60,0x20,0x65,0x83,0x83,0x61,0xC1,0x88,0x41,0x02,0x80,0x20,0x18,0x48,0x1A,0xF1,0x60,0x98,0x30, + 0x62,0x90,0x00,0x20,0x08,0x06,0xC6,0x97,0x4C,0x59,0x84,0x8C,0x18,0x24,0x00,0x08,0x82,0x81,0x01,0x06, + 0x0A,0xA5,0x15,0xC9,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0x61,0xB0,0x70,0xDB,0xA4,0x8C,0x18,0x24,0x00, + 0x08,0x82,0x81,0x21,0x06,0x4C,0xC7,0x1D,0xCB,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0x63,0xD0,0x78,0x1D, + 0xC5,0x8C,0x18,0x24,0x00,0x08,0x82,0x81,0x41,0x06,0xCE,0xE7,0x4D,0xCD,0x88,0x41,0x02,0x80,0x20,0x18, + 0x18,0x65,0xF0,0x7C,0xDF,0xE5,0x8C,0x18,0x24,0x00,0x08,0x82,0x81,0x61,0x06,0x10,0x18,0x80,0xC1,0xF2, + 0x8C,0x18,0x1C,0x00,0x08,0x82,0x41,0x53,0x06,0x8F,0x12,0x06,0xA3,0x09,0x01,0x30,0x9A,0x20,0x04,0x26, + 0x14,0xF0,0x31,0xA1,0x80,0xCF,0x88,0xC1,0x01,0x80,0x20,0x18,0x34,0x6A,0x40,0x39,0x66,0x30,0x9A,0x10, + 0x00,0xA3,0x09,0x42,0x30,0x9A,0x30,0x08,0xA3,0x09,0xC4,0x30,0x62,0x70,0x00,0x20,0x08,0x06,0xCD,0x1B, + 0x64,0x13,0x1B,0x8C,0x26,0x04,0xC0,0x68,0x82,0x10,0x8C,0x26,0x0C,0xC2,0x68,0x02,0x31,0x8C,0x18,0x1C, + 0x00,0x08,0x82,0x41,0x43,0x07,0x1E,0xC6,0x06,0xA3,0x09,0x01,0x30,0x9A,0x20,0x04,0xA3,0x09,0x83,0x30, + 0x9A,0x40,0x0C,0xE6,0x44,0xF2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x27,0x0F,0xC6,0x40,0x89,0x02,0x33, + 0x02,0xE8,0x18,0x44,0xC9,0x67,0xC4,0x00,0x01,0x40,0x10,0x0C,0x1E,0x3E,0x30,0x03,0x86,0x0A,0x2C,0x40, + 0xA0,0x63,0xD2,0x25,0x9F,0x11,0x03,0x04,0x00,0x41,0x30,0x78,0xFE,0x20,0x0D,0x9C,0x2B,0xB0,0x40,0x81, + 0x8E,0x51,0x9A,0x7C,0x46,0x0C,0x10,0x00,0x04,0xC1,0xE0,0x11,0x05,0x36,0x80,0xB4,0xC0,0x02,0x06,0x3A, + 0x23,0x06,0x09,0x00,0x82,0x60,0x80,0x98,0x82,0x1C,0x84,0x42,0x28,0xE4,0x81,0x19,0x8C,0x18,0x24,0x00, + 0x08,0x82,0x01,0x62,0x0A,0x72,0x10,0x0A,0xA1,0xC0,0x06,0x65,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x88, + 0x29,0xC8,0x41,0x28,0x84,0x02,0x1E,0x90,0xC1,0x88,0x41,0x02,0x80,0x20,0x18,0x20,0xA6,0x20,0x07,0xA1, + 0x10,0x0A,0x76,0x30,0x06,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0x98,0x82,0x1C,0x88,0x42,0x28,0xE4,0x01, + 0x1A,0x8C,0x18,0x24,0x00,0x08,0x82,0x01,0x62,0x0A,0x72,0x20,0x0A,0xA1,0xC0,0x06,0x67,0x30,0x62,0x90, + 0x00,0x20,0x08,0x06,0x88,0x29,0xC8,0xC1,0x1E,0x84,0x42,0x1E,0x28,0x23,0x06,0x09,0x00,0x82,0x60,0x80, + 0x98,0x82,0x1C,0xEC,0x41,0x28,0xB0,0xC1,0x31,0x62,0x90,0x00,0x20,0x08,0x06,0x88,0x29,0xC8,0xC1,0x1E, + 0x84,0x02,0x1E,0x10,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0x98,0x82,0x1C,0xEC,0x41,0x28,0xD8,0x41,0x80, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; diff --git a/vendor/rmlui_backend/RmlUi_SDL_GPU/compile_shaders.py b/vendor/rmlui_backend/RmlUi_SDL_GPU/compile_shaders.py new file mode 100644 index 0000000..90fc1dd --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_SDL_GPU/compile_shaders.py @@ -0,0 +1,58 @@ +import sys +import os +import subprocess + +# Compiles all .frag and .vert files in this directory to SPIR-V, MSL, and DXIL binary C character arrays. +# +# Requires the SDL_shadercross tool. Binaries can be found here: https://github.com/libsdl-org/SDL_shadercross/actions +# Click on the latest workflow and download them for the platform of your choice. + +if len(sys.argv) < 2: + print("Usage: python compile_shaders.py ") + sys.exit(1) + +out_file = "ShadersCompiledSPV.h" + +current_dir = os.path.dirname(os.path.realpath(__file__)); +shadercross_path = os.path.realpath(sys.argv[1]) + +if not os.path.isfile(shadercross_path): + print(f"Error: The specified shadercross path '{shadercross_path}' does not exist.") + sys.exit(1) + +out_path = os.path.join(current_dir, out_file) + +with open(out_path,'w') as result_file: + result_file.write('// RmlUi SDL GPU shaders compiled using command: \'python compile_shaders.py\'. Do not edit manually.\n\n#include \n') + + for file in os.listdir(current_dir): + if file.endswith(".vert") or file.endswith(".frag"): + shader_path = os.path.join(current_dir, file) + + def compile(target): + print("Compiling '{}' to '{}' using SDL_shadercross.".format(file, target)) + + variable_name = "{}_{}".format(os.path.splitext(file)[0], target) + temp_path = os.path.join(current_dir, ".temp.{}".format(target)) + + print(temp_path) + + subprocess.run([shadercross_path, "-s", "hlsl", shader_path, "-o", temp_path, "-d", target], check = True) + + print("Success, writing output to variable '{}' in {}".format(variable_name, out_file)) + + i = 0 + result_file.write('\nalignas(uint32_t) static const unsigned char {}[] = {{'.format(variable_name)) + for b in open(temp_path, 'rb').read(): + if i % 20 == 0: + result_file.write('\n\t') + result_file.write('0x%02X,' % b) + i += 1 + + result_file.write('\n};\n') + + os.remove(temp_path) + + compile("spirv") + compile("msl") + compile("dxil") diff --git a/vendor/rmlui_backend/RmlUi_SDL_GPU/shader_frag_color.frag b/vendor/rmlui_backend/RmlUi_SDL_GPU/shader_frag_color.frag new file mode 100644 index 0000000..3a6643a --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_SDL_GPU/shader_frag_color.frag @@ -0,0 +1,3 @@ +float4 main(float4 Color : TEXCOORD0) : SV_Target0 { + return Color; +} \ No newline at end of file diff --git a/vendor/rmlui_backend/RmlUi_SDL_GPU/shader_frag_texture.frag b/vendor/rmlui_backend/RmlUi_SDL_GPU/shader_frag_texture.frag new file mode 100644 index 0000000..5577809 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_SDL_GPU/shader_frag_texture.frag @@ -0,0 +1,8 @@ +Texture2D Texture : register(t0, space2); +SamplerState Sampler : register(s0, space2); + +// NOTE: Seems like TEXCOORD0 is getting optimized out and TEXCOORD1 is taking its spot +// Might need to fix when later versions of SDL_shadercross preserve unused bindings +float4 main(float4 Color : TEXCOORD0, float2 TexCoord : TEXCOORD1) : SV_Target0 { + return Color * Texture.Sample(Sampler, TexCoord); +} \ No newline at end of file diff --git a/vendor/rmlui_backend/RmlUi_SDL_GPU/shader_vert.vert b/vendor/rmlui_backend/RmlUi_SDL_GPU/shader_vert.vert new file mode 100644 index 0000000..cad0764 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_SDL_GPU/shader_vert.vert @@ -0,0 +1,28 @@ +cbuffer UniformBlockTransform : register(b0, space1) { + float4x4 Transform : packoffset(c0); +}; + +cbuffer UniformBlockTranslate : register(b1, space1) { + float2 Translate : packoffset(c0); +}; + +struct Input { + float2 Position : TEXCOORD0; + float4 InColor : TEXCOORD1; + float2 TexCoord : TEXCOORD2; +}; + +struct Output { + float4 Color : TEXCOORD0; + float2 TexCoord : TEXCOORD1; + float4 Position : SV_Position; +}; + +Output main(Input input) { + Output output; + output.TexCoord = input.TexCoord; + output.Color = input.InColor; + float4 position = float4(input.Position + Translate, 0, 1); + output.Position = mul(Transform, position); + return output; +} \ No newline at end of file diff --git a/vendor/rmlui_backend/RmlUi_Vulkan/.clang-format b/vendor/rmlui_backend/RmlUi_Vulkan/.clang-format new file mode 100644 index 0000000..47a38a9 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Vulkan/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: Never diff --git a/vendor/rmlui_backend/RmlUi_Vulkan/LICENSE.txt b/vendor/rmlui_backend/RmlUi_Vulkan/LICENSE.txt new file mode 100644 index 0000000..678ebee --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Vulkan/LICENSE.txt @@ -0,0 +1,29 @@ +The Vulkan backend for RmlUi includes the third-party dependency: + +Vulkan Memory Allocator +https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator + +This dependency is located in the file 'vk_mem_alloc.h' and is licensed +separately from Rmlui. The license of this dependency is recited below. + +---- + +Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved. + +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. diff --git a/vendor/rmlui_backend/RmlUi_Vulkan/ShadersCompiledSPV.h b/vendor/rmlui_backend/RmlUi_Vulkan/ShadersCompiledSPV.h new file mode 100644 index 0000000..4e86f0a --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Vulkan/ShadersCompiledSPV.h @@ -0,0 +1,180 @@ +// RmlUi SPIR-V Vulkan shaders compiled using command: 'python compile_shaders.py'. Do not edit manually. + +#include + +alignas(uint32_t) static const unsigned char shader_frag_color[] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x0A,0x00,0x0D,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x47,0x4C,0x53,0x4C, + 0x2E,0x73,0x74,0x64,0x2E,0x34,0x35,0x30,0x00,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0F,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, + 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x10,0x00,0x03,0x00, + 0x04,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0x4A,0x01,0x00,0x00, + 0x04,0x00,0x09,0x00,0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x65,0x5F, + 0x73,0x68,0x61,0x64,0x65,0x72,0x5F,0x6F,0x62,0x6A,0x65,0x63,0x74,0x73,0x00,0x00,0x04,0x00,0x09,0x00, + 0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69,0x6E,0x67,0x5F,0x6C,0x61,0x6E,0x67,0x75, + 0x61,0x67,0x65,0x5F,0x34,0x32,0x30,0x70,0x61,0x63,0x6B,0x00,0x04,0x00,0x0A,0x00,0x47,0x4C,0x5F,0x47, + 0x4F,0x4F,0x47,0x4C,0x45,0x5F,0x63,0x70,0x70,0x5F,0x73,0x74,0x79,0x6C,0x65,0x5F,0x6C,0x69,0x6E,0x65, + 0x5F,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x76,0x65,0x00,0x00,0x04,0x00,0x08,0x00,0x47,0x4C,0x5F,0x47, + 0x4F,0x4F,0x47,0x4C,0x45,0x5F,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x5F,0x64,0x69,0x72,0x65,0x63,0x74, + 0x69,0x76,0x65,0x00,0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x66,0x69,0x6E,0x61,0x6C,0x43,0x6F,0x6C,0x6F,0x72,0x00,0x00, + 0x05,0x00,0x05,0x00,0x0B,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x43,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x0F,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x54,0x65,0x78,0x43,0x6F,0x6F,0x72,0x64, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0B,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x0F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x0D,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x0E,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x05,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x09,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; + +alignas(uint32_t) static const unsigned char shader_frag_texture[] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x0A,0x00,0x0D,0x00,0x1B,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x47,0x4C,0x53,0x4C, + 0x2E,0x73,0x74,0x64,0x2E,0x34,0x35,0x30,0x00,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0F,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, + 0x00,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x10,0x00,0x03,0x00, + 0x04,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0x4A,0x01,0x00,0x00, + 0x04,0x00,0x09,0x00,0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x65,0x5F, + 0x73,0x68,0x61,0x64,0x65,0x72,0x5F,0x6F,0x62,0x6A,0x65,0x63,0x74,0x73,0x00,0x00,0x04,0x00,0x09,0x00, + 0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69,0x6E,0x67,0x5F,0x6C,0x61,0x6E,0x67,0x75, + 0x61,0x67,0x65,0x5F,0x34,0x32,0x30,0x70,0x61,0x63,0x6B,0x00,0x04,0x00,0x0A,0x00,0x47,0x4C,0x5F,0x47, + 0x4F,0x4F,0x47,0x4C,0x45,0x5F,0x63,0x70,0x70,0x5F,0x73,0x74,0x79,0x6C,0x65,0x5F,0x6C,0x69,0x6E,0x65, + 0x5F,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x76,0x65,0x00,0x00,0x04,0x00,0x08,0x00,0x47,0x4C,0x5F,0x47, + 0x4F,0x4F,0x47,0x4C,0x45,0x5F,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x5F,0x64,0x69,0x72,0x65,0x63,0x74, + 0x69,0x76,0x65,0x00,0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x74,0x65,0x78,0x43,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x0D,0x00,0x00,0x00,0x5F,0x74,0x65,0x78,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x11,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x54,0x65,0x78,0x43,0x6F,0x6F,0x72,0x64,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x66,0x69,0x6E,0x61,0x6C,0x43,0x6F,0x6C,0x6F,0x72,0x00,0x00, + 0x05,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x43,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0D,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x0D,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x0A,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x1B,0x00,0x03,0x00,0x0B,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0C,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0C,0x00,0x00,0x00,0x0D,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x14,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x15,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x05,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x0B,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x0F,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x57,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x09,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x85,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x15,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; + +alignas(uint32_t) static const unsigned char shader_vert[] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x0A,0x00,0x0D,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x47,0x4C,0x53,0x4C, + 0x2E,0x73,0x74,0x64,0x2E,0x34,0x35,0x30,0x00,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0F,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, + 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0x4A,0x01,0x00,0x00, + 0x04,0x00,0x09,0x00,0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x65,0x5F, + 0x73,0x68,0x61,0x64,0x65,0x72,0x5F,0x6F,0x62,0x6A,0x65,0x63,0x74,0x73,0x00,0x00,0x04,0x00,0x09,0x00, + 0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69,0x6E,0x67,0x5F,0x6C,0x61,0x6E,0x67,0x75, + 0x61,0x67,0x65,0x5F,0x34,0x32,0x30,0x70,0x61,0x63,0x6B,0x00,0x04,0x00,0x0A,0x00,0x47,0x4C,0x5F,0x47, + 0x4F,0x4F,0x47,0x4C,0x45,0x5F,0x63,0x70,0x70,0x5F,0x73,0x74,0x79,0x6C,0x65,0x5F,0x6C,0x69,0x6E,0x65, + 0x5F,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x76,0x65,0x00,0x00,0x04,0x00,0x08,0x00,0x47,0x4C,0x5F,0x47, + 0x4F,0x4F,0x47,0x4C,0x45,0x5F,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x5F,0x64,0x69,0x72,0x65,0x63,0x74, + 0x69,0x76,0x65,0x00,0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x09,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x54,0x65,0x78,0x43,0x6F,0x6F,0x72,0x64, + 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0B,0x00,0x00,0x00,0x69,0x6E,0x54,0x65,0x78,0x43,0x6F,0x6F, + 0x72,0x64,0x30,0x00,0x05,0x00,0x05,0x00,0x0F,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x43,0x6F,0x6C,0x6F, + 0x72,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x69,0x6E,0x43,0x6F,0x6C,0x6F,0x72,0x30, + 0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x14,0x00,0x00,0x00,0x74,0x72,0x61,0x6E,0x73,0x6C,0x61,0x74, + 0x65,0x64,0x50,0x6F,0x73,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x69,0x6E,0x50,0x6F, + 0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,0x00,0x05,0x00,0x05,0x00,0x18,0x00,0x00,0x00,0x55,0x73,0x65,0x72, + 0x44,0x61,0x74,0x61,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6D,0x5F,0x74,0x72,0x61,0x6E,0x73,0x66,0x6F,0x72,0x6D,0x00,0x06,0x00,0x06,0x00,0x18,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x6D,0x5F,0x74,0x72,0x61,0x6E,0x73,0x6C,0x61,0x74,0x65,0x00,0x05,0x00,0x05,0x00, + 0x1A,0x00,0x00,0x00,0x75,0x73,0x65,0x72,0x64,0x61,0x74,0x61,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x22,0x00,0x00,0x00,0x6F,0x75,0x74,0x50,0x6F,0x73,0x00,0x00,0x05,0x00,0x06,0x00,0x31,0x00,0x00,0x00, + 0x67,0x6C,0x5F,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00, + 0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x67,0x6C,0x5F,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00, + 0x06,0x00,0x07,0x00,0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6C,0x5F,0x50,0x6F,0x69,0x6E,0x74, + 0x53,0x69,0x7A,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x31,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x67,0x6C,0x5F,0x43,0x6C,0x69,0x70,0x44,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x00,0x05,0x00,0x03,0x00, + 0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0B,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x18,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x1A,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1A,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0B,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x31,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0B,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x31,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0A,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x0A,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x0D,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x0D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x13,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00,0x15,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x1E,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x19,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x19,0x00,0x00,0x00, + 0x1A,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x1B,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x1B,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x1D,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x21,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x1B,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x24,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x00,0x00,0x80,0x3F,0x15,0x00,0x04,0x00, + 0x2E,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x2E,0x00,0x00,0x00, + 0x2F,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1C,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x2F,0x00,0x00,0x00,0x1E,0x00,0x05,0x00,0x31,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x30,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x31,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x05,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x09,0x00,0x00,0x00, + 0x0C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0D,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x0F,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x1D,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x1A,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x1F,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x14,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x24,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x17,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2A,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x0D,0x00,0x00,0x00,0x2C,0x00,0x00,0x00, + 0x2A,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x91,0x00,0x05,0x00, + 0x0D,0x00,0x00,0x00,0x2D,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x22,0x00,0x00,0x00,0x2D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0D,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x0E,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x33,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x35,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0xFD,0x00,0x01,0x00, + 0x38,0x00,0x01,0x00, +}; diff --git a/vendor/rmlui_backend/RmlUi_Vulkan/compile_shaders.py b/vendor/rmlui_backend/RmlUi_Vulkan/compile_shaders.py new file mode 100644 index 0000000..47e1410 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Vulkan/compile_shaders.py @@ -0,0 +1,38 @@ +import sys +import os +import subprocess + +# Compiles all .frag and .vert files in this directory to SPIR-V binary C character arrays. Requires 'glslc' installed and available system-wide. + +out_file = "ShadersCompiledSPV.h" + +current_dir = os.path.dirname(os.path.realpath(__file__)); + +temp_spirv_path = os.path.join(current_dir, ".temp.spv") +out_path = os.path.join(current_dir, out_file) + +with open(out_path,'w') as result_file: + result_file.write('// RmlUi SPIR-V Vulkan shaders compiled using command: \'python compile_shaders.py\'. Do not edit manually.\n\n#include \n') + + for file in os.listdir(current_dir): + if file.endswith(".vert") or file.endswith(".frag"): + shader_path = os.path.join(current_dir, file) + variable_name = os.path.splitext(file)[0] + + print("Compiling '{}' to SPIR-V using glslc.".format(file)) + + subprocess.run(["glslc", shader_path, "-o", temp_spirv_path], check = True) + + print("Success, writing output to variable '{}' in {}".format(variable_name, out_file)) + + i = 0 + result_file.write('\nalignas(uint32_t) static const unsigned char {}[] = {{'.format(variable_name)) + for b in open(temp_spirv_path, 'rb').read(): + if i % 20 == 0: + result_file.write('\n\t') + result_file.write('0x%02X,' % b) + i += 1 + + result_file.write('\n};\n') + + os.remove(temp_spirv_path) diff --git a/vendor/rmlui_backend/RmlUi_Vulkan/shader_frag_color.frag b/vendor/rmlui_backend/RmlUi_Vulkan/shader_frag_color.frag new file mode 100644 index 0000000..dfd3d0b --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Vulkan/shader_frag_color.frag @@ -0,0 +1,12 @@ +#version 330 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec2 fragTexCoord; +layout (location = 1) in vec4 fragColor; +layout (location = 0) out vec4 finalColor; + +void main() { + finalColor = fragColor; +} \ No newline at end of file diff --git a/vendor/rmlui_backend/RmlUi_Vulkan/shader_frag_texture.frag b/vendor/rmlui_backend/RmlUi_Vulkan/shader_frag_texture.frag new file mode 100644 index 0000000..3997bbc --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Vulkan/shader_frag_texture.frag @@ -0,0 +1,15 @@ +#version 330 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout(set=1, binding=2) uniform sampler2D _tex; +layout (location = 0) in vec2 fragTexCoord; +layout (location = 1) in vec4 fragColor; + +layout (location = 0) out vec4 finalColor; + +void main() { + vec4 texColor = texture(_tex, fragTexCoord); + finalColor = fragColor * texColor; +} \ No newline at end of file diff --git a/vendor/rmlui_backend/RmlUi_Vulkan/shader_vert.vert b/vendor/rmlui_backend/RmlUi_Vulkan/shader_vert.vert new file mode 100644 index 0000000..f5e8475 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Vulkan/shader_vert.vert @@ -0,0 +1,25 @@ +#version 330 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout(set=0, binding=1) uniform UserData +{ + mat4 m_transform; + vec2 m_translate; +} userdata; + +layout (location = 0) in vec2 inPosition; +layout (location = 1) in vec4 inColor0; +layout (location = 2) in vec2 inTexCoord0; + +layout (location = 0) out vec2 fragTexCoord; +layout (location = 1) out vec4 fragColor; + +void main() { + fragTexCoord = inTexCoord0; + fragColor = inColor0; + vec2 translatedPos = inPosition + userdata.m_translate.xy; + vec4 outPos = userdata.m_transform * vec4(translatedPos, 0, 1); + gl_Position = outPos; +} \ No newline at end of file diff --git a/vendor/rmlui_backend/RmlUi_Vulkan/vk_mem_alloc.h b/vendor/rmlui_backend/RmlUi_Vulkan/vk_mem_alloc.h new file mode 100644 index 0000000..7b04e54 --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Vulkan/vk_mem_alloc.h @@ -0,0 +1,19558 @@ +// +// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved. +// +// 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 AMD_VULKAN_MEMORY_ALLOCATOR_H +#define AMD_VULKAN_MEMORY_ALLOCATOR_H + +/** \mainpage Vulkan Memory Allocator + +Version 3.0.1 (2022-05-26) + +Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved. \n +License: MIT + +API documentation divided into groups: [Modules](modules.html) + +\section main_table_of_contents Table of contents + +- User guide + - \subpage quick_start + - [Project setup](@ref quick_start_project_setup) + - [Initialization](@ref quick_start_initialization) + - [Resource allocation](@ref quick_start_resource_allocation) + - \subpage choosing_memory_type + - [Usage](@ref choosing_memory_type_usage) + - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags) + - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types) + - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools) + - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations) + - \subpage memory_mapping + - [Mapping functions](@ref memory_mapping_mapping_functions) + - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory) + - [Cache flush and invalidate](@ref memory_mapping_cache_control) + - \subpage staying_within_budget + - [Querying for budget](@ref staying_within_budget_querying_for_budget) + - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage) + - \subpage resource_aliasing + - \subpage custom_memory_pools + - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex) + - [Linear allocation algorithm](@ref linear_algorithm) + - [Free-at-once](@ref linear_algorithm_free_at_once) + - [Stack](@ref linear_algorithm_stack) + - [Double stack](@ref linear_algorithm_double_stack) + - [Ring buffer](@ref linear_algorithm_ring_buffer) + - \subpage defragmentation + - \subpage statistics + - [Numeric statistics](@ref statistics_numeric_statistics) + - [JSON dump](@ref statistics_json_dump) + - \subpage allocation_annotation + - [Allocation user data](@ref allocation_user_data) + - [Allocation names](@ref allocation_names) + - \subpage virtual_allocator + - \subpage debugging_memory_usage + - [Memory initialization](@ref debugging_memory_usage_initialization) + - [Margins](@ref debugging_memory_usage_margins) + - [Corruption detection](@ref debugging_memory_usage_corruption_detection) + - \subpage opengl_interop +- \subpage usage_patterns + - [GPU-only resource](@ref usage_patterns_gpu_only) + - [Staging copy for upload](@ref usage_patterns_staging_copy_upload) + - [Readback](@ref usage_patterns_readback) + - [Advanced data uploading](@ref usage_patterns_advanced_data_uploading) + - [Other use cases](@ref usage_patterns_other_use_cases) +- \subpage configuration + - [Pointers to Vulkan functions](@ref config_Vulkan_functions) + - [Custom host memory allocator](@ref custom_memory_allocator) + - [Device memory allocation callbacks](@ref allocation_callbacks) + - [Device heap memory limit](@ref heap_memory_limit) +- Extension support + - \subpage vk_khr_dedicated_allocation + - \subpage enabling_buffer_device_address + - \subpage vk_ext_memory_priority + - \subpage vk_amd_device_coherent_memory +- \subpage general_considerations + - [Thread safety](@ref general_considerations_thread_safety) + - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility) + - [Validation layer warnings](@ref general_considerations_validation_layer_warnings) + - [Allocation algorithm](@ref general_considerations_allocation_algorithm) + - [Features not supported](@ref general_considerations_features_not_supported) + +\section main_see_also See also + +- [**Product page on GPUOpen**](https://gpuopen.com/gaming-product/vulkan-memory-allocator/) +- [**Source repository on GitHub**](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) + +\defgroup group_init Library initialization + +\brief API elements related to the initialization and management of the entire library, especially #VmaAllocator object. + +\defgroup group_alloc Memory allocation + +\brief API elements related to the allocation, deallocation, and management of Vulkan memory, buffers, images. +Most basic ones being: vmaCreateBuffer(), vmaCreateImage(). + +\defgroup group_virtual Virtual allocator + +\brief API elements related to the mechanism of \ref virtual_allocator - using the core allocation algorithm +for user-defined purpose without allocating any real GPU memory. + +\defgroup group_stats Statistics + +\brief API elements that query current status of the allocator, from memory usage, budget, to full dump of the internal state in JSON format. +See documentation chapter: \ref statistics. +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef VULKAN_H_ + #include +#endif + +// Define this macro to declare maximum supported Vulkan version in format AAABBBCCC, +// where AAA = major, BBB = minor, CCC = patch. +// If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion. +#if !defined(VMA_VULKAN_VERSION) + #if defined(VK_VERSION_1_3) + #define VMA_VULKAN_VERSION 1003000 + #elif defined(VK_VERSION_1_2) + #define VMA_VULKAN_VERSION 1002000 + #elif defined(VK_VERSION_1_1) + #define VMA_VULKAN_VERSION 1001000 + #else + #define VMA_VULKAN_VERSION 1000000 + #endif +#endif + +#if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS + extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; + extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; + extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; + extern PFN_vkAllocateMemory vkAllocateMemory; + extern PFN_vkFreeMemory vkFreeMemory; + extern PFN_vkMapMemory vkMapMemory; + extern PFN_vkUnmapMemory vkUnmapMemory; + extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges; + extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges; + extern PFN_vkBindBufferMemory vkBindBufferMemory; + extern PFN_vkBindImageMemory vkBindImageMemory; + extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; + extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; + extern PFN_vkCreateBuffer vkCreateBuffer; + extern PFN_vkDestroyBuffer vkDestroyBuffer; + extern PFN_vkCreateImage vkCreateImage; + extern PFN_vkDestroyImage vkDestroyImage; + extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer; + #if VMA_VULKAN_VERSION >= 1001000 + extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2; + extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2; + extern PFN_vkBindBufferMemory2 vkBindBufferMemory2; + extern PFN_vkBindImageMemory2 vkBindImageMemory2; + extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2; + #endif // #if VMA_VULKAN_VERSION >= 1001000 +#endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES + +#if !defined(VMA_DEDICATED_ALLOCATION) + #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation + #define VMA_DEDICATED_ALLOCATION 1 + #else + #define VMA_DEDICATED_ALLOCATION 0 + #endif +#endif + +#if !defined(VMA_BIND_MEMORY2) + #if VK_KHR_bind_memory2 + #define VMA_BIND_MEMORY2 1 + #else + #define VMA_BIND_MEMORY2 0 + #endif +#endif + +#if !defined(VMA_MEMORY_BUDGET) + #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000) + #define VMA_MEMORY_BUDGET 1 + #else + #define VMA_MEMORY_BUDGET 0 + #endif +#endif + +// Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers. +#if !defined(VMA_BUFFER_DEVICE_ADDRESS) + #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000 + #define VMA_BUFFER_DEVICE_ADDRESS 1 + #else + #define VMA_BUFFER_DEVICE_ADDRESS 0 + #endif +#endif + +// Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers. +#if !defined(VMA_MEMORY_PRIORITY) + #if VK_EXT_memory_priority + #define VMA_MEMORY_PRIORITY 1 + #else + #define VMA_MEMORY_PRIORITY 0 + #endif +#endif + +// Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers. +#if !defined(VMA_EXTERNAL_MEMORY) + #if VK_KHR_external_memory + #define VMA_EXTERNAL_MEMORY 1 + #else + #define VMA_EXTERNAL_MEMORY 0 + #endif +#endif + +// Define these macros to decorate all public functions with additional code, +// before and after returned type, appropriately. This may be useful for +// exporting the functions when compiling VMA as a separate library. Example: +// #define VMA_CALL_PRE __declspec(dllexport) +// #define VMA_CALL_POST __cdecl +#ifndef VMA_CALL_PRE + #define VMA_CALL_PRE +#endif +#ifndef VMA_CALL_POST + #define VMA_CALL_POST +#endif + +// Define this macro to decorate pointers with an attribute specifying the +// length of the array they point to if they are not null. +// +// The length may be one of +// - The name of another parameter in the argument list where the pointer is declared +// - The name of another member in the struct where the pointer is declared +// - The name of a member of a struct type, meaning the value of that member in +// the context of the call. For example +// VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"), +// this means the number of memory heaps available in the device associated +// with the VmaAllocator being dealt with. +#ifndef VMA_LEN_IF_NOT_NULL + #define VMA_LEN_IF_NOT_NULL(len) +#endif + +// The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang. +// see: https://clang.llvm.org/docs/AttributeReference.html#nullable +#ifndef VMA_NULLABLE + #ifdef __clang__ + #define VMA_NULLABLE _Nullable + #else + #define VMA_NULLABLE + #endif +#endif + +// The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang. +// see: https://clang.llvm.org/docs/AttributeReference.html#nonnull +#ifndef VMA_NOT_NULL + #ifdef __clang__ + #define VMA_NOT_NULL _Nonnull + #else + #define VMA_NOT_NULL + #endif +#endif + +// If non-dispatchable handles are represented as pointers then we can give +// then nullability annotations +#ifndef VMA_NOT_NULL_NON_DISPATCHABLE + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL + #else + #define VMA_NOT_NULL_NON_DISPATCHABLE + #endif +#endif + +#ifndef VMA_NULLABLE_NON_DISPATCHABLE + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE + #else + #define VMA_NULLABLE_NON_DISPATCHABLE + #endif +#endif + +#ifndef VMA_STATS_STRING_ENABLED + #define VMA_STATS_STRING_ENABLED 1 +#endif + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// INTERFACE +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// Sections for managing code placement in file, only for development purposes e.g. for convenient folding inside an IDE. +#ifndef _VMA_ENUM_DECLARATIONS + +/** +\addtogroup group_init +@{ +*/ + +/// Flags for created #VmaAllocator. +typedef enum VmaAllocatorCreateFlagBits +{ + /** \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you. + + Using this flag may increase performance because internal mutexes are not used. + */ + VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001, + /** \brief Enables usage of VK_KHR_dedicated_allocation extension. + + The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`. + When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1. + + Using this extension will automatically allocate dedicated blocks of memory for + some buffers and images instead of suballocating place for them out of bigger + memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT + flag) when it is recommended by the driver. It may improve performance on some + GPUs. + + You may set this flag only if you found out that following device extensions are + supported, you enabled them while creating Vulkan device passed as + VmaAllocatorCreateInfo::device, and you want them to be used internally by this + library: + + - VK_KHR_get_memory_requirements2 (device extension) + - VK_KHR_dedicated_allocation (device extension) + + When this flag is set, you can experience following warnings reported by Vulkan + validation layer. You can ignore them. + + > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer. + */ + VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002, + /** + Enables usage of VK_KHR_bind_memory2 extension. + + The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`. + When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1. + + You may set this flag only if you found out that this device extension is supported, + you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + and you want it to be used internally by this library. + + The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`, + which allow to pass a chain of `pNext` structures while binding. + This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2(). + */ + VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004, + /** + Enables usage of VK_EXT_memory_budget extension. + + You may set this flag only if you found out that this device extension is supported, + you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + and you want it to be used internally by this library, along with another instance extension + VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted). + + The extension provides query for current memory usage and budget, which will probably + be more accurate than an estimation used by the library otherwise. + */ + VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008, + /** + Enables usage of VK_AMD_device_coherent_memory extension. + + You may set this flag only if you: + + - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device, + - want it to be used internally by this library. + + The extension and accompanying device feature provide access to memory types with + `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags. + They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR. + + When the extension is not enabled, such memory types are still enumerated, but their usage is illegal. + To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type, + returning `VK_ERROR_FEATURE_NOT_PRESENT`. + */ + VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010, + /** + Enables usage of "buffer device address" feature, which allows you to use function + `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader. + + You may set this flag only if you: + + 1. (For Vulkan version < 1.2) Found as available and enabled device extension + VK_KHR_buffer_device_address. + This extension is promoted to core Vulkan 1.2. + 2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`. + + When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA. + The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to + allocated memory blocks wherever it might be needed. + + For more information, see documentation chapter \ref enabling_buffer_device_address. + */ + VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020, + /** + Enables usage of VK_EXT_memory_priority extension in the library. + + You may set this flag only if you found available and enabled this device extension, + along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`, + while creating Vulkan device passed as VmaAllocatorCreateInfo::device. + + When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority + are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored. + + A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations. + Larger values are higher priority. The granularity of the priorities is implementation-dependent. + It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`. + The value to be used for default priority is 0.5. + For more details, see the documentation of the VK_EXT_memory_priority extension. + */ + VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040, + + VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaAllocatorCreateFlagBits; +/// See #VmaAllocatorCreateFlagBits. +typedef VkFlags VmaAllocatorCreateFlags; + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/// \brief Intended usage of the allocated memory. +typedef enum VmaMemoryUsage +{ + /** No intended memory usage specified. + Use other members of VmaAllocationCreateInfo to specify your requirements. + */ + VMA_MEMORY_USAGE_UNKNOWN = 0, + /** + \deprecated Obsolete, preserved for backward compatibility. + Prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + */ + VMA_MEMORY_USAGE_GPU_ONLY = 1, + /** + \deprecated Obsolete, preserved for backward compatibility. + Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`. + */ + VMA_MEMORY_USAGE_CPU_ONLY = 2, + /** + \deprecated Obsolete, preserved for backward compatibility. + Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + */ + VMA_MEMORY_USAGE_CPU_TO_GPU = 3, + /** + \deprecated Obsolete, preserved for backward compatibility. + Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`. + */ + VMA_MEMORY_USAGE_GPU_TO_CPU = 4, + /** + \deprecated Obsolete, preserved for backward compatibility. + Prefers not `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + */ + VMA_MEMORY_USAGE_CPU_COPY = 5, + /** + Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`. + Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation. + + Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`. + + Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + */ + VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6, + /** + Selects best memory type automatically. + This flag is recommended for most common use cases. + + When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), + you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT + in VmaAllocationCreateInfo::flags. + + It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. + vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() + and not with generic memory allocation functions. + */ + VMA_MEMORY_USAGE_AUTO = 7, + /** + Selects best memory type automatically with preference for GPU (device) memory. + + When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), + you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT + in VmaAllocationCreateInfo::flags. + + It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. + vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() + and not with generic memory allocation functions. + */ + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE = 8, + /** + Selects best memory type automatically with preference for CPU (host) memory. + + When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), + you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT + in VmaAllocationCreateInfo::flags. + + It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. + vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() + and not with generic memory allocation functions. + */ + VMA_MEMORY_USAGE_AUTO_PREFER_HOST = 9, + + VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF +} VmaMemoryUsage; + +/// Flags to be passed as VmaAllocationCreateInfo::flags. +typedef enum VmaAllocationCreateFlagBits +{ + /** \brief Set this flag if the allocation should have its own memory block. + + Use it for special, big resources, like fullscreen images used as attachments. + */ + VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001, + + /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block. + + If new allocation cannot be placed in any of the existing blocks, allocation + fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error. + + You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and + #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense. + */ + VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002, + /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it. + + Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData. + + It is valid to use this flag for allocation made from memory type that is not + `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is + useful if you need an allocation that is efficient to use on GPU + (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that + support it (e.g. Intel GPU). + */ + VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004, + /** \deprecated Preserved for backward compatibility. Consider using vmaSetAllocationName() instead. + + Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a + null-terminated string. Instead of copying pointer value, a local copy of the + string is made and stored in allocation's `pName`. The string is automatically + freed together with the allocation. It is also used in vmaBuildStatsString(). + */ + VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020, + /** Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag. + */ + VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040, + /** Create both buffer/image and allocation, but don't bind them together. + It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions. + The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage(). + Otherwise it is ignored. + + If you want to make sure the new buffer/image is not tied to the new memory allocation + through `VkMemoryDedicatedAllocateInfoKHR` structure in case the allocation ends up in its own memory block, + use also flag #VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT. + */ + VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080, + /** Create allocation only if additional device memory required for it, if any, won't exceed + memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + */ + VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100, + /** \brief Set this flag if the allocated memory will have aliasing resources. + + Usage of this flag prevents supplying `VkMemoryDedicatedAllocateInfoKHR` when #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT is specified. + Otherwise created dedicated memory will not be suitable for aliasing resources, resulting in Vulkan Validation Layer errors. + */ + VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT = 0x00000200, + /** + Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT). + + - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value, + you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect. + - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`. + This includes allocations created in \ref custom_memory_pools. + + Declares that mapped memory will only be written sequentially, e.g. using `memcpy()` or a loop writing number-by-number, + never read or accessed randomly, so a memory type can be selected that is uncached and write-combined. + + \warning Violating this declaration may work correctly, but will likely be very slow. + Watch out for implicit reads introduced by doing e.g. `pMappedData[i] += x;` + Better prepare your data in a local variable and `memcpy()` it to the mapped pointer all at once. + */ + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT = 0x00000400, + /** + Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT). + + - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value, + you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect. + - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`. + This includes allocations created in \ref custom_memory_pools. + + Declares that mapped memory can be read, written, and accessed in random order, + so a `HOST_CACHED` memory type is required. + */ + VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT = 0x00000800, + /** + Together with #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, + it says that despite request for host access, a not-`HOST_VISIBLE` memory type can be selected + if it may improve performance. + + By using this flag, you declare that you will check if the allocation ended up in a `HOST_VISIBLE` memory type + (e.g. using vmaGetAllocationMemoryProperties()) and if not, you will create some "staging" buffer and + issue an explicit transfer to write/read your data. + To prepare for this possibility, don't forget to add appropriate flags like + `VK_BUFFER_USAGE_TRANSFER_DST_BIT`, `VK_BUFFER_USAGE_TRANSFER_SRC_BIT` to the parameters of created buffer or image. + */ + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT = 0x00001000, + /** Allocation strategy that chooses smallest possible free range for the allocation + to minimize memory usage and fragmentation, possibly at the expense of allocation time. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = 0x00010000, + /** Allocation strategy that chooses first suitable free range for the allocation - + not necessarily in terms of the smallest offset but the one that is easiest and fastest to find + to minimize allocation time, possibly at the expense of allocation quality. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = 0x00020000, + /** Allocation strategy that chooses always the lowest offset in available space. + This is not the most efficient strategy but achieves highly packed data. + Used internally by defragmentation, not recomended in typical usage. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = 0x00040000, + /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT. + */ + VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT, + /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT. + */ + VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT, + /** A bit mask to extract only `STRATEGY` bits from entire set of flags. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MASK = + VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT | + VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT | + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + + VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaAllocationCreateFlagBits; +/// See #VmaAllocationCreateFlagBits. +typedef VkFlags VmaAllocationCreateFlags; + +/// Flags to be passed as VmaPoolCreateInfo::flags. +typedef enum VmaPoolCreateFlagBits +{ + /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored. + + This is an optional optimization flag. + + If you always allocate using vmaCreateBuffer(), vmaCreateImage(), + vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator + knows exact type of your allocations so it can handle Buffer-Image Granularity + in the optimal way. + + If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(), + exact type of such allocations is not known, so allocator must be conservative + in handling Buffer-Image Granularity, which can lead to suboptimal allocation + (wasted memory). In that case, if you can make sure you always allocate only + buffers and linear images or only optimal images out of this pool, use this flag + to make allocator disregard Buffer-Image Granularity and so make allocations + faster and more optimal. + */ + VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002, + + /** \brief Enables alternative, linear allocation algorithm in this pool. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. + For details, see documentation chapter \ref linear_algorithm. + */ + VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004, + + /** Bit mask to extract only `ALGORITHM` bits from entire set of flags. + */ + VMA_POOL_CREATE_ALGORITHM_MASK = + VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT, + + VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaPoolCreateFlagBits; +/// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits. +typedef VkFlags VmaPoolCreateFlags; + +/// Flags to be passed as VmaDefragmentationInfo::flags. +typedef enum VmaDefragmentationFlagBits +{ + /* \brief Use simple but fast algorithm for defragmentation. + May not achieve best results but will require least time to compute and least allocations to copy. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT = 0x1, + /* \brief Default defragmentation algorithm, applied also when no `ALGORITHM` flag is specified. + Offers a balance between defragmentation quality and the amount of allocations and bytes that need to be moved. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT = 0x2, + /* \brief Perform full defragmentation of memory. + Can result in notably more time to compute and allocations to copy, but will achieve best memory packing. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT = 0x4, + /** \brief Use the most roboust algorithm at the cost of time to compute and number of copies to make. + Only available when bufferImageGranularity is greater than 1, since it aims to reduce + alignment issues between different types of resources. + Otherwise falls back to same behavior as #VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT = 0x8, + + /// A bit mask to extract only `ALGORITHM` bits from entire set of flags. + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK = + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT | + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT | + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT | + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT, + + VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaDefragmentationFlagBits; +/// See #VmaDefragmentationFlagBits. +typedef VkFlags VmaDefragmentationFlags; + +/// Operation performed on single defragmentation move. See structure #VmaDefragmentationMove. +typedef enum VmaDefragmentationMoveOperation +{ + /// Buffer/image has been recreated at `dstTmpAllocation`, data has been copied, old buffer/image has been destroyed. `srcAllocation` should be changed to point to the new place. This is the default value set by vmaBeginDefragmentationPass(). + VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY = 0, + /// Set this value if you cannot move the allocation. New place reserved at `dstTmpAllocation` will be freed. `srcAllocation` will remain unchanged. + VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE = 1, + /// Set this value if you decide to abandon the allocation and you destroyed the buffer/image. New place reserved at `dstTmpAllocation` will be freed, along with `srcAllocation`, which will be destroyed. + VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY = 2, +} VmaDefragmentationMoveOperation; + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. +typedef enum VmaVirtualBlockCreateFlagBits +{ + /** \brief Enables alternative, linear allocation algorithm in this virtual block. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. + For details, see documentation chapter \ref linear_algorithm. + */ + VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001, + + /** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags. + */ + VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK = + VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT, + + VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaVirtualBlockCreateFlagBits; +/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits. +typedef VkFlags VmaVirtualBlockCreateFlags; + +/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. +typedef enum VmaVirtualAllocationCreateFlagBits +{ + /** \brief Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT, + /** \brief Allocation strategy that tries to minimize memory usage. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT, + /** \brief Allocation strategy that tries to minimize allocation time. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT, + /** Allocation strategy that chooses always the lowest offset in available space. + This is not the most efficient strategy but achieves highly packed data. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags. + + These strategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK, + + VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaVirtualAllocationCreateFlagBits; +/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits. +typedef VkFlags VmaVirtualAllocationCreateFlags; + +/** @} */ + +#endif // _VMA_ENUM_DECLARATIONS + +#ifndef _VMA_DATA_TYPES_DECLARATIONS + +/** +\addtogroup group_init +@{ */ + +/** \struct VmaAllocator +\brief Represents main object of this library initialized. + +Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it. +Call function vmaDestroyAllocator() to destroy it. + +It is recommended to create just one object of this type per `VkDevice` object, +right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed. +*/ +VK_DEFINE_HANDLE(VmaAllocator) + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** \struct VmaPool +\brief Represents custom memory pool + +Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it. +Call function vmaDestroyPool() to destroy it. + +For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools). +*/ +VK_DEFINE_HANDLE(VmaPool) + +/** \struct VmaAllocation +\brief Represents single memory allocation. + +It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type +plus unique offset. + +There are multiple ways to create such object. +You need to fill structure VmaAllocationCreateInfo. +For more information see [Choosing memory type](@ref choosing_memory_type). + +Although the library provides convenience functions that create Vulkan buffer or image, +allocate memory for it and bind them together, +binding of the allocation to a buffer or an image is out of scope of the allocation itself. +Allocation object can exist without buffer/image bound, +binding can be done manually by the user, and destruction of it can be done +independently of destruction of the allocation. + +The object also remembers its size and some other information. +To retrieve this information, use function vmaGetAllocationInfo() and inspect +returned structure VmaAllocationInfo. +*/ +VK_DEFINE_HANDLE(VmaAllocation) + +/** \struct VmaDefragmentationContext +\brief An opaque object that represents started defragmentation process. + +Fill structure #VmaDefragmentationInfo and call function vmaBeginDefragmentation() to create it. +Call function vmaEndDefragmentation() to destroy it. +*/ +VK_DEFINE_HANDLE(VmaDefragmentationContext) + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \struct VmaVirtualAllocation +\brief Represents single memory allocation done inside VmaVirtualBlock. + +Use it as a unique identifier to virtual allocation within the single block. + +Use value `VK_NULL_HANDLE` to represent a null/invalid allocation. +*/ +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaVirtualAllocation); + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \struct VmaVirtualBlock +\brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory. + +Fill in #VmaVirtualBlockCreateInfo structure and use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it. +For more information, see documentation chapter \ref virtual_allocator. + +This object is not thread-safe - should not be used from multiple threads simultaneously, must be synchronized externally. +*/ +VK_DEFINE_HANDLE(VmaVirtualBlock) + +/** @} */ + +/** +\addtogroup group_init +@{ +*/ + +/// Callback function called after successful vkAllocateMemory. +typedef void (VKAPI_PTR* PFN_vmaAllocateDeviceMemoryFunction)( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryType, + VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory, + VkDeviceSize size, + void* VMA_NULLABLE pUserData); + +/// Callback function called before vkFreeMemory. +typedef void (VKAPI_PTR* PFN_vmaFreeDeviceMemoryFunction)( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryType, + VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory, + VkDeviceSize size, + void* VMA_NULLABLE pUserData); + +/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`. + +Provided for informative purpose, e.g. to gather statistics about number of +allocations or total amount of memory allocated in Vulkan. + +Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. +*/ +typedef struct VmaDeviceMemoryCallbacks +{ + /// Optional, can be null. + PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate; + /// Optional, can be null. + PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree; + /// Optional, can be null. + void* VMA_NULLABLE pUserData; +} VmaDeviceMemoryCallbacks; + +/** \brief Pointers to some Vulkan functions - a subset used by the library. + +Used in VmaAllocatorCreateInfo::pVulkanFunctions. +*/ +typedef struct VmaVulkanFunctions +{ + /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS. + PFN_vkGetInstanceProcAddr VMA_NULLABLE vkGetInstanceProcAddr; + /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS. + PFN_vkGetDeviceProcAddr VMA_NULLABLE vkGetDeviceProcAddr; + PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties; + PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties; + PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory; + PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory; + PFN_vkMapMemory VMA_NULLABLE vkMapMemory; + PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory; + PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges; + PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges; + PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory; + PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory; + PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements; + PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements; + PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer; + PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer; + PFN_vkCreateImage VMA_NULLABLE vkCreateImage; + PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage; + PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer; +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + /// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension. + PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR; + /// Fetch "vkGetImageMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension. + PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR; +#endif +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + /// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension. + PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR; + /// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension. + PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR; +#endif +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR; +#endif +#if VMA_VULKAN_VERSION >= 1003000 + /// Fetch from "vkGetDeviceBufferMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceBufferMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4. + PFN_vkGetDeviceBufferMemoryRequirements VMA_NULLABLE vkGetDeviceBufferMemoryRequirements; + /// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4. + PFN_vkGetDeviceImageMemoryRequirements VMA_NULLABLE vkGetDeviceImageMemoryRequirements; +#endif +} VmaVulkanFunctions; + +/// Description of a Allocator to be created. +typedef struct VmaAllocatorCreateInfo +{ + /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum. + VmaAllocatorCreateFlags flags; + /// Vulkan physical device. + /** It must be valid throughout whole lifetime of created allocator. */ + VkPhysicalDevice VMA_NOT_NULL physicalDevice; + /// Vulkan device. + /** It must be valid throughout whole lifetime of created allocator. */ + VkDevice VMA_NOT_NULL device; + /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional. + /** Set to 0 to use default, which is currently 256 MiB. */ + VkDeviceSize preferredLargeHeapBlockSize; + /// Custom CPU memory allocation callbacks. Optional. + /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */ + const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks; + /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional. + /** Optional, can be null. */ + const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks; + /** \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap. + + If not NULL, it must be a pointer to an array of + `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on + maximum number of bytes that can be allocated out of particular Vulkan memory + heap. + + Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that + heap. This is also the default in case of `pHeapSizeLimit` = NULL. + + If there is a limit defined for a heap: + + - If user tries to allocate more memory from that heap using this allocator, + the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the + value of this limit will be reported instead when using vmaGetMemoryProperties(). + + Warning! Using this feature may not be equivalent to installing a GPU with + smaller amount of memory, because graphics driver doesn't necessary fail new + allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is + exceeded. It may return success and just silently migrate some device memory + blocks to system RAM. This driver behavior can also be controlled using + VK_AMD_memory_overallocation_behavior extension. + */ + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit; + + /** \brief Pointers to Vulkan functions. Can be null. + + For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions). + */ + const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions; + /** \brief Handle to Vulkan instance object. + + Starting from version 3.0.0 this member is no longer optional, it must be set! + */ + VkInstance VMA_NOT_NULL instance; + /** \brief Optional. The highest version of Vulkan that the application is designed to use. + + It must be a value in the format as created by macro `VK_MAKE_VERSION` or a constant like: `VK_API_VERSION_1_1`, `VK_API_VERSION_1_0`. + The patch version number specified is ignored. Only the major and minor versions are considered. + It must be less or equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`. + Only versions 1.0, 1.1, 1.2, 1.3 are supported by the current implementation. + Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`. + */ + uint32_t vulkanApiVersion; +#if VMA_EXTERNAL_MEMORY + /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type. + + If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount` + elements, defining external memory handle types of particular Vulkan memory type, + to be passed using `VkExportMemoryAllocateInfoKHR`. + + Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type. + This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL. + */ + const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes; +#endif // #if VMA_EXTERNAL_MEMORY +} VmaAllocatorCreateInfo; + +/// Information about existing #VmaAllocator object. +typedef struct VmaAllocatorInfo +{ + /** \brief Handle to Vulkan instance object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::instance. + */ + VkInstance VMA_NOT_NULL instance; + /** \brief Handle to Vulkan physical device object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice. + */ + VkPhysicalDevice VMA_NOT_NULL physicalDevice; + /** \brief Handle to Vulkan device object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::device. + */ + VkDevice VMA_NOT_NULL device; +} VmaAllocatorInfo; + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Calculated statistics of memory usage e.g. in a specific memory type, heap, custom pool, or total. + +These are fast to calculate. +See functions: vmaGetHeapBudgets(), vmaGetPoolStatistics(). +*/ +typedef struct VmaStatistics +{ + /** \brief Number of `VkDeviceMemory` objects - Vulkan memory blocks allocated. + */ + uint32_t blockCount; + /** \brief Number of #VmaAllocation objects allocated. + + Dedicated allocations have their own blocks, so each one adds 1 to `allocationCount` as well as `blockCount`. + */ + uint32_t allocationCount; + /** \brief Number of bytes allocated in `VkDeviceMemory` blocks. + + \note To avoid confusion, please be aware that what Vulkan calls an "allocation" - a whole `VkDeviceMemory` object + (e.g. as in `VkPhysicalDeviceLimits::maxMemoryAllocationCount`) is called a "block" in VMA, while VMA calls + "allocation" a #VmaAllocation object that represents a memory region sub-allocated from such block, usually for a single buffer or image. + */ + VkDeviceSize blockBytes; + /** \brief Total number of bytes occupied by all #VmaAllocation objects. + + Always less or equal than `blockBytes`. + Difference `(blockBytes - allocationBytes)` is the amount of memory allocated from Vulkan + but unused by any #VmaAllocation. + */ + VkDeviceSize allocationBytes; +} VmaStatistics; + +/** \brief More detailed statistics than #VmaStatistics. + +These are slower to calculate. Use for debugging purposes. +See functions: vmaCalculateStatistics(), vmaCalculatePoolStatistics(). + +Previous version of the statistics API provided averages, but they have been removed +because they can be easily calculated as: + +\code +VkDeviceSize allocationSizeAvg = detailedStats.statistics.allocationBytes / detailedStats.statistics.allocationCount; +VkDeviceSize unusedBytes = detailedStats.statistics.blockBytes - detailedStats.statistics.allocationBytes; +VkDeviceSize unusedRangeSizeAvg = unusedBytes / detailedStats.unusedRangeCount; +\endcode +*/ +typedef struct VmaDetailedStatistics +{ + /// Basic statistics. + VmaStatistics statistics; + /// Number of free ranges of memory between allocations. + uint32_t unusedRangeCount; + /// Smallest allocation size. `VK_WHOLE_SIZE` if there are 0 allocations. + VkDeviceSize allocationSizeMin; + /// Largest allocation size. 0 if there are 0 allocations. + VkDeviceSize allocationSizeMax; + /// Smallest empty range size. `VK_WHOLE_SIZE` if there are 0 empty ranges. + VkDeviceSize unusedRangeSizeMin; + /// Largest empty range size. 0 if there are 0 empty ranges. + VkDeviceSize unusedRangeSizeMax; +} VmaDetailedStatistics; + +/** \brief General statistics from current state of the Allocator - +total memory usage across all memory heaps and types. + +These are slower to calculate. Use for debugging purposes. +See function vmaCalculateStatistics(). +*/ +typedef struct VmaTotalStatistics +{ + VmaDetailedStatistics memoryType[VK_MAX_MEMORY_TYPES]; + VmaDetailedStatistics memoryHeap[VK_MAX_MEMORY_HEAPS]; + VmaDetailedStatistics total; +} VmaTotalStatistics; + +/** \brief Statistics of current memory usage and available budget for a specific memory heap. + +These are fast to calculate. +See function vmaGetHeapBudgets(). +*/ +typedef struct VmaBudget +{ + /** \brief Statistics fetched from the library. + */ + VmaStatistics statistics; + /** \brief Estimated current memory usage of the program, in bytes. + + Fetched from system using VK_EXT_memory_budget extension if enabled. + + It might be different than `statistics.blockBytes` (usually higher) due to additional implicit objects + also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or + `VkDeviceMemory` blocks allocated outside of this library, if any. + */ + VkDeviceSize usage; + /** \brief Estimated amount of memory available to the program, in bytes. + + Fetched from system using VK_EXT_memory_budget extension if enabled. + + It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors + external to the program, decided by the operating system. + Difference `budget - usage` is the amount of additional memory that can probably + be allocated without problems. Exceeding the budget may result in various problems. + */ + VkDeviceSize budget; +} VmaBudget; + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** \brief Parameters of new #VmaAllocation. + +To be used with functions like vmaCreateBuffer(), vmaCreateImage(), and many others. +*/ +typedef struct VmaAllocationCreateInfo +{ + /// Use #VmaAllocationCreateFlagBits enum. + VmaAllocationCreateFlags flags; + /** \brief Intended usage of memory. + + You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n + If `pool` is not null, this member is ignored. + */ + VmaMemoryUsage usage; + /** \brief Flags that must be set in a Memory Type chosen for an allocation. + + Leave 0 if you specify memory requirements in other way. \n + If `pool` is not null, this member is ignored.*/ + VkMemoryPropertyFlags requiredFlags; + /** \brief Flags that preferably should be set in a memory type chosen for an allocation. + + Set to 0 if no additional flags are preferred. \n + If `pool` is not null, this member is ignored. */ + VkMemoryPropertyFlags preferredFlags; + /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation. + + Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if + it meets other requirements specified by this structure, with no further + restrictions on memory type index. \n + If `pool` is not null, this member is ignored. + */ + uint32_t memoryTypeBits; + /** \brief Pool that this allocation should be created in. + + Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members: + `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored. + */ + VmaPool VMA_NULLABLE pool; + /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData(). + + If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either + null or pointer to a null-terminated string. The string will be then copied to + internal buffer, so it doesn't need to be valid after allocation call. + */ + void* VMA_NULLABLE pUserData; + /** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations. + + It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object + and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + Otherwise, it has the priority of a memory block where it is placed and this variable is ignored. + */ + float priority; +} VmaAllocationCreateInfo; + +/// Describes parameter of created #VmaPool. +typedef struct VmaPoolCreateInfo +{ + /** \brief Vulkan memory type index to allocate this pool from. + */ + uint32_t memoryTypeIndex; + /** \brief Use combination of #VmaPoolCreateFlagBits. + */ + VmaPoolCreateFlags flags; + /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional. + + Specify nonzero to set explicit, constant size of memory blocks used by this + pool. + + Leave 0 to use default and let the library manage block sizes automatically. + Sizes of particular blocks may vary. + In this case, the pool will also support dedicated allocations. + */ + VkDeviceSize blockSize; + /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty. + + Set to 0 to have no preallocated blocks and allow the pool be completely empty. + */ + size_t minBlockCount; + /** \brief Maximum number of blocks that can be allocated in this pool. Optional. + + Set to 0 to use default, which is `SIZE_MAX`, which means no limit. + + Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated + throughout whole lifetime of this pool. + */ + size_t maxBlockCount; + /** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations. + + It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object. + Otherwise, this variable is ignored. + */ + float priority; + /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0. + + Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two. + It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough, + e.g. when doing interop with OpenGL. + */ + VkDeviceSize minAllocationAlignment; + /** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional. + + Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`. + It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`. + Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool. + + Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`, + can be attached automatically by this library when using other, more convenient of its features. + */ + void* VMA_NULLABLE pMemoryAllocateNext; +} VmaPoolCreateInfo; + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/// Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo(). +typedef struct VmaAllocationInfo +{ + /** \brief Memory type index that this allocation was allocated from. + + It never changes. + */ + uint32_t memoryType; + /** \brief Handle to Vulkan memory object. + + Same memory object can be shared by multiple allocations. + + It can change after the allocation is moved during \ref defragmentation. + */ + VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory; + /** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation. + + You usually don't need to use this offset. If you create a buffer or an image together with the allocation using e.g. function + vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image, + not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation + and apply this offset automatically. + + It can change after the allocation is moved during \ref defragmentation. + */ + VkDeviceSize offset; + /** \brief Size of this allocation, in bytes. + + It never changes. + + \note Allocation size returned in this variable may be greater than the size + requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the + allocation is accessible for operations on memory e.g. using a pointer after + mapping with vmaMapMemory(), but operations on the resource e.g. using + `vkCmdCopyBuffer` must be limited to the size of the resource. + */ + VkDeviceSize size; + /** \brief Pointer to the beginning of this allocation as mapped data. + + If the allocation hasn't been mapped using vmaMapMemory() and hasn't been + created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null. + + It can change after call to vmaMapMemory(), vmaUnmapMemory(). + It can also change after the allocation is moved during \ref defragmentation. + */ + void* VMA_NULLABLE pMappedData; + /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData(). + + It can change after call to vmaSetAllocationUserData() for this allocation. + */ + void* VMA_NULLABLE pUserData; + /** \brief Custom allocation name that was set with vmaSetAllocationName(). + + It can change after call to vmaSetAllocationName() for this allocation. + + Another way to set custom name is to pass it in VmaAllocationCreateInfo::pUserData with + additional flag #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT set [DEPRECATED]. + */ + const char* VMA_NULLABLE pName; +} VmaAllocationInfo; + +/** \brief Parameters for defragmentation. + +To be used with function vmaBeginDefragmentation(). +*/ +typedef struct VmaDefragmentationInfo +{ + /// \brief Use combination of #VmaDefragmentationFlagBits. + VmaDefragmentationFlags flags; + /** \brief Custom pool to be defragmented. + + If null then default pools will undergo defragmentation process. + */ + VmaPool VMA_NULLABLE pool; + /** \brief Maximum numbers of bytes that can be copied during single pass, while moving allocations to different places. + + `0` means no limit. + */ + VkDeviceSize maxBytesPerPass; + /** \brief Maximum number of allocations that can be moved during single pass to a different place. + + `0` means no limit. + */ + uint32_t maxAllocationsPerPass; +} VmaDefragmentationInfo; + +/// Single move of an allocation to be done for defragmentation. +typedef struct VmaDefragmentationMove +{ + /// Operation to be performed on the allocation by vmaEndDefragmentationPass(). Default value is #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY. You can modify it. + VmaDefragmentationMoveOperation operation; + /// Allocation that should be moved. + VmaAllocation VMA_NOT_NULL srcAllocation; + /** \brief Temporary allocation pointing to destination memory that will replace `srcAllocation`. + + \warning Do not store this allocation in your data structures! It exists only temporarily, for the duration of the defragmentation pass, + to be used for binding new buffer/image to the destination memory using e.g. vmaBindBufferMemory(). + vmaEndDefragmentationPass() will destroy it and make `srcAllocation` point to this memory. + */ + VmaAllocation VMA_NOT_NULL dstTmpAllocation; +} VmaDefragmentationMove; + +/** \brief Parameters for incremental defragmentation steps. + +To be used with function vmaBeginDefragmentationPass(). +*/ +typedef struct VmaDefragmentationPassMoveInfo +{ + /// Number of elements in the `pMoves` array. + uint32_t moveCount; + /** \brief Array of moves to be performed by the user in the current defragmentation pass. + + Pointer to an array of `moveCount` elements, owned by VMA, created in vmaBeginDefragmentationPass(), destroyed in vmaEndDefragmentationPass(). + + For each element, you should: + + 1. Create a new buffer/image in the place pointed by VmaDefragmentationMove::dstMemory + VmaDefragmentationMove::dstOffset. + 2. Copy data from the VmaDefragmentationMove::srcAllocation e.g. using `vkCmdCopyBuffer`, `vkCmdCopyImage`. + 3. Make sure these commands finished executing on the GPU. + 4. Destroy the old buffer/image. + + Only then you can finish defragmentation pass by calling vmaEndDefragmentationPass(). + After this call, the allocation will point to the new place in memory. + + Alternatively, if you cannot move specific allocation, you can set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. + + Alternatively, if you decide you want to completely remove the allocation: + + 1. Destroy its buffer/image. + 2. Set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY. + + Then, after vmaEndDefragmentationPass() the allocation will be freed. + */ + VmaDefragmentationMove* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(moveCount) pMoves; +} VmaDefragmentationPassMoveInfo; + +/// Statistics returned for defragmentation process in function vmaEndDefragmentation(). +typedef struct VmaDefragmentationStats +{ + /// Total number of bytes that have been copied while moving allocations to different places. + VkDeviceSize bytesMoved; + /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects. + VkDeviceSize bytesFreed; + /// Number of allocations that have been moved to different places. + uint32_t allocationsMoved; + /// Number of empty `VkDeviceMemory` objects that have been released to the system. + uint32_t deviceMemoryBlocksFreed; +} VmaDefragmentationStats; + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock(). +typedef struct VmaVirtualBlockCreateInfo +{ + /** \brief Total size of the virtual block. + + Sizes can be expressed in bytes or any units you want as long as you are consistent in using them. + For example, if you allocate from some array of structures, 1 can mean single instance of entire structure. + */ + VkDeviceSize size; + + /** \brief Use combination of #VmaVirtualBlockCreateFlagBits. + */ + VmaVirtualBlockCreateFlags flags; + + /** \brief Custom CPU memory allocation callbacks. Optional. + + Optional, can be null. When specified, they will be used for all CPU-side memory allocations. + */ + const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks; +} VmaVirtualBlockCreateInfo; + +/// Parameters of created virtual allocation to be passed to vmaVirtualAllocate(). +typedef struct VmaVirtualAllocationCreateInfo +{ + /** \brief Size of the allocation. + + Cannot be zero. + */ + VkDeviceSize size; + /** \brief Required alignment of the allocation. Optional. + + Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset. + */ + VkDeviceSize alignment; + /** \brief Use combination of #VmaVirtualAllocationCreateFlagBits. + */ + VmaVirtualAllocationCreateFlags flags; + /** \brief Custom pointer to be associated with the allocation. Optional. + + It can be any value and can be used for user-defined purposes. It can be fetched or changed later. + */ + void* VMA_NULLABLE pUserData; +} VmaVirtualAllocationCreateInfo; + +/// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo(). +typedef struct VmaVirtualAllocationInfo +{ + /** \brief Offset of the allocation. + + Offset at which the allocation was made. + */ + VkDeviceSize offset; + /** \brief Size of the allocation. + + Same value as passed in VmaVirtualAllocationCreateInfo::size. + */ + VkDeviceSize size; + /** \brief Custom pointer associated with the allocation. + + Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData(). + */ + void* VMA_NULLABLE pUserData; +} VmaVirtualAllocationInfo; + +/** @} */ + +#endif // _VMA_DATA_TYPES_DECLARATIONS + +#ifndef _VMA_FUNCTION_HEADERS + +/** +\addtogroup group_init +@{ +*/ + +/// Creates #VmaAllocator object. +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator( + const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocator VMA_NULLABLE* VMA_NOT_NULL pAllocator); + +/// Destroys allocator object. +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator( + VmaAllocator VMA_NULLABLE allocator); + +/** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc. + +It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to +`VkPhysicalDevice`, `VkDevice` etc. every time using this function. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo); + +/** +PhysicalDeviceProperties are fetched from physicalDevice by the allocator. +You can access it here, without fetching it again on your own. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties( + VmaAllocator VMA_NOT_NULL allocator, + const VkPhysicalDeviceProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceProperties); + +/** +PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator. +You can access it here, without fetching it again on your own. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceMemoryProperties); + +/** +\brief Given Memory Type Index, returns Property Flags of this memory type. + +This is just a convenience function. Same information can be obtained using +vmaGetMemoryProperties(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeIndex, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags); + +/** \brief Sets index of the current frame. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t frameIndex); + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Retrieves statistics from current state of the Allocator. + +This function is called "calculate" not "get" because it has to traverse all +internal data structures, so it may be quite slow. Use it for debugging purposes. +For faster but more brief statistics suitable to be called every frame or every allocation, +use vmaGetHeapBudgets(). + +Note that when using allocator from multiple threads, returned information may immediately +become outdated. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics( + VmaAllocator VMA_NOT_NULL allocator, + VmaTotalStatistics* VMA_NOT_NULL pStats); + +/** \brief Retrieves information about current memory usage and budget for all memory heaps. + +\param allocator +\param[out] pBudgets Must point to array with number of elements at least equal to number of memory heaps in physical device used. + +This function is called "get" not "calculate" because it is very fast, suitable to be called +every frame or every allocation. For more detailed statistics use vmaCalculateStatistics(). + +Note that when using allocator from multiple threads, returned information may immediately +become outdated. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets( + VmaAllocator VMA_NOT_NULL allocator, + VmaBudget* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pBudgets); + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** +\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo. + +This algorithm tries to find a memory type that: + +- Is allowed by memoryTypeBits. +- Contains all the flags from pAllocationCreateInfo->requiredFlags. +- Matches intended usage. +- Has as many flags from pAllocationCreateInfo->preferredFlags as possible. + +\return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result +from this function or any other allocating function probably means that your +device doesn't support any memory type with requested features for the specific +type of resource you want to use it for. Please check parameters of your +resource, like image layout (OPTIMAL versus LINEAR) or mip level count. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); + +/** +\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo. + +It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. +It internally creates a temporary, dummy buffer that never has memory bound. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); + +/** +\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo. + +It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. +It internally creates a temporary, dummy image that never has memory bound. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo( + VmaAllocator VMA_NOT_NULL allocator, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); + +/** \brief Allocates Vulkan device memory and creates #VmaPool object. + +\param allocator Allocator object. +\param pCreateInfo Parameters of pool to create. +\param[out] pPool Handle to created pool. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool( + VmaAllocator VMA_NOT_NULL allocator, + const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaPool VMA_NULLABLE* VMA_NOT_NULL pPool); + +/** \brief Destroys #VmaPool object and frees Vulkan device memory. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NULLABLE pool); + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Retrieves statistics of existing #VmaPool object. + +\param allocator Allocator object. +\param pool Pool object. +\param[out] pPoolStats Statistics of specified pool. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + VmaStatistics* VMA_NOT_NULL pPoolStats); + +/** \brief Retrieves detailed statistics of existing #VmaPool object. + +\param allocator Allocator object. +\param pool Pool object. +\param[out] pPoolStats Statistics of specified pool. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + VmaDetailedStatistics* VMA_NOT_NULL pPoolStats); + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions. + +Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, +`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is +`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). + +Possible return values: + +- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool. +- `VK_SUCCESS` - corruption detection has been performed and succeeded. +- `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations. + `VMA_ASSERT` is also fired in that case. +- Other value: Error returned by Vulkan, e.g. memory mapping failure. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool); + +/** \brief Retrieves name of a custom pool. + +After the call `ppName` is either null or points to an internally-owned null-terminated string +containing name of the pool that was previously set. The pointer becomes invalid when the pool is +destroyed or its name is changed using vmaSetPoolName(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + const char* VMA_NULLABLE* VMA_NOT_NULL ppName); + +/** \brief Sets name of a custom pool. + +`pName` can be either null or pointer to a null-terminated string with new name for the pool. +Function makes internal copy of the string, so it can be changed or freed immediately after this call. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + const char* VMA_NULLABLE pName); + +/** \brief General purpose memory allocation. + +\param allocator +\param pVkMemoryRequirements +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). + +It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(), +vmaCreateBuffer(), vmaCreateImage() instead whenever possible. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory( + VmaAllocator VMA_NOT_NULL allocator, + const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief General purpose memory allocation for multiple allocation objects at once. + +\param allocator Allocator object. +\param pVkMemoryRequirements Memory requirements for each allocation. +\param pCreateInfo Creation parameters for each allocation. +\param allocationCount Number of allocations to make. +\param[out] pAllocations Pointer to array that will be filled with handles to created allocations. +\param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations. + +You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). + +Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding. +It is just a general purpose allocation function able to make multiple allocations at once. +It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times. + +All allocations are made using same parameters. All of them are created out of the same memory pool and type. +If any allocation fails, all allocations already made within this function call are also freed, so that when +returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages( + VmaAllocator VMA_NOT_NULL allocator, + const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements, + const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo, + size_t allocationCount, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations, + VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo); + +/** \brief Allocates memory suitable for given `VkBuffer`. + +\param allocator +\param buffer +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindBufferMemory(). + +This is a special-purpose function. In most cases you should use vmaCreateBuffer(). + +You must free the allocation using vmaFreeMemory() when no longer needed. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Allocates memory suitable for given `VkImage`. + +\param allocator +\param image +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindImageMemory(). + +This is a special-purpose function. In most cases you should use vmaCreateImage(). + +You must free the allocation using vmaFreeMemory() when no longer needed. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). + +Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory( + VmaAllocator VMA_NOT_NULL allocator, + const VmaAllocation VMA_NULLABLE allocation); + +/** \brief Frees memory and destroys multiple allocations. + +Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding. +It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(), +vmaAllocateMemoryPages() and other functions. +It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times. + +Allocations in `pAllocations` array can come from any memory pools and types. +Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages( + VmaAllocator VMA_NOT_NULL allocator, + size_t allocationCount, + const VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations); + +/** \brief Returns current information about specified allocation. + +Current paramteres of given allocation are returned in `pAllocationInfo`. + +Although this function doesn't lock any mutex, so it should be quite efficient, +you should avoid calling it too often. +You can retrieve same VmaAllocationInfo structure while creating your resource, from function +vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change +(e.g. due to defragmentation). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo); + +/** \brief Sets pUserData in given allocation to new value. + +The value of pointer `pUserData` is copied to allocation's `pUserData`. +It is opaque, so you can use it however you want - e.g. +as a pointer, ordinal number or some handle to you own data. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + void* VMA_NULLABLE pUserData); + +/** \brief Sets pName in given allocation to new value. + +`pName` must be either null, or pointer to a null-terminated string. The function +makes local copy of the string and sets it as allocation's `pName`. String +passed as pName doesn't need to be valid for whole lifetime of the allocation - +you can free it after this call. String previously pointed by allocation's +`pName` is freed from memory. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const char* VMA_NULLABLE pName); + +/** +\brief Given an allocation, returns Property Flags of its memory type. + +This is just a convenience function. Same information can be obtained using +vmaGetAllocationInfo() + vmaGetMemoryProperties(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags); + +/** \brief Maps memory represented by given allocation and returns pointer to it. + +Maps memory represented by given allocation to make it accessible to CPU code. +When succeeded, `*ppData` contains pointer to first byte of this memory. + +\warning +If the allocation is part of a bigger `VkDeviceMemory` block, returned pointer is +correctly offsetted to the beginning of region assigned to this particular allocation. +Unlike the result of `vkMapMemory`, it points to the allocation, not to the beginning of the whole block. +You should not add VmaAllocationInfo::offset to it! + +Mapping is internally reference-counted and synchronized, so despite raw Vulkan +function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory` +multiple times simultaneously, it is safe to call this function on allocations +assigned to the same memory block. Actual Vulkan memory will be mapped on first +mapping and unmapped on last unmapping. + +If the function succeeded, you must call vmaUnmapMemory() to unmap the +allocation when mapping is no longer needed or before freeing the allocation, at +the latest. + +It also safe to call this function multiple times on the same allocation. You +must call vmaUnmapMemory() same number of times as you called vmaMapMemory(). + +It is also safe to call this function on allocation created with +#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time. +You must still call vmaUnmapMemory() same number of times as you called +vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the +"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. + +This function fails when used on allocation made in memory type that is not +`HOST_VISIBLE`. + +This function doesn't automatically flush or invalidate caches. +If the allocation is made from a memory types that is not `HOST_COHERENT`, +you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + void* VMA_NULLABLE* VMA_NOT_NULL ppData); + +/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory(). + +For details, see description of vmaMapMemory(). + +This function doesn't automatically flush or invalidate caches. +If the allocation is made from a memory types that is not `HOST_COHERENT`, +you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation); + +/** \brief Flushes memory of given allocation. + +Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation. +It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`. +Unmap operation doesn't do that automatically. + +- `offset` must be relative to the beginning of allocation. +- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. +- `offset` and `size` don't have to be aligned. + They are internally rounded down/up to multiply of `nonCoherentAtomSize`. +- If `size` is 0, this call is ignored. +- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, + this call is ignored. + +Warning! `offset` and `size` are relative to the contents of given `allocation`. +If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively. +Do not pass allocation's offset as `offset`!!! + +This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize offset, + VkDeviceSize size); + +/** \brief Invalidates memory of given allocation. + +Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation. +It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`. +Map operation doesn't do that automatically. + +- `offset` must be relative to the beginning of allocation. +- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. +- `offset` and `size` don't have to be aligned. + They are internally rounded down/up to multiply of `nonCoherentAtomSize`. +- If `size` is 0, this call is ignored. +- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, + this call is ignored. + +Warning! `offset` and `size` are relative to the contents of given `allocation`. +If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively. +Do not pass allocation's offset as `offset`!!! + +This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if +it is called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize offset, + VkDeviceSize size); + +/** \brief Flushes memory of given set of allocations. + +Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations. +For more information, see documentation of vmaFlushAllocation(). + +\param allocator +\param allocationCount +\param allocations +\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero. +\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations. + +This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t allocationCount, + const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes); + +/** \brief Invalidates memory of given set of allocations. + +Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations. +For more information, see documentation of vmaInvalidateAllocation(). + +\param allocator +\param allocationCount +\param allocations +\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero. +\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations. + +This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t allocationCount, + const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes); + +/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions. + +\param allocator +\param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked. + +Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, +`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are +`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). + +Possible return values: + +- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types. +- `VK_SUCCESS` - corruption detection has been performed and succeeded. +- `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations. + `VMA_ASSERT` is also fired in that case. +- Other value: Error returned by Vulkan, e.g. memory mapping failure. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeBits); + +/** \brief Begins defragmentation process. + +\param allocator Allocator object. +\param pInfo Structure filled with parameters of defragmentation. +\param[out] pContext Context object that must be passed to vmaEndDefragmentation() to finish defragmentation. +\returns +- `VK_SUCCESS` if defragmentation can begin. +- `VK_ERROR_FEATURE_NOT_PRESENT` if defragmentation is not supported. + +For more information about defragmentation, see documentation chapter: +[Defragmentation](@ref defragmentation). +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation( + VmaAllocator VMA_NOT_NULL allocator, + const VmaDefragmentationInfo* VMA_NOT_NULL pInfo, + VmaDefragmentationContext VMA_NULLABLE* VMA_NOT_NULL pContext); + +/** \brief Ends defragmentation process. + +\param allocator Allocator object. +\param context Context object that has been created by vmaBeginDefragmentation(). +\param[out] pStats Optional stats for the defragmentation. Can be null. + +Use this function to finish defragmentation started by vmaBeginDefragmentation(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationStats* VMA_NULLABLE pStats); + +/** \brief Starts single defragmentation pass. + +\param allocator Allocator object. +\param context Context object that has been created by vmaBeginDefragmentation(). +\param[out] pPassInfo Computed informations for current pass. +\returns +- `VK_SUCCESS` if no more moves are possible. Then you can omit call to vmaEndDefragmentationPass() and simply end whole defragmentation. +- `VK_INCOMPLETE` if there are pending moves returned in `pPassInfo`. You need to perform them, call vmaEndDefragmentationPass(), + and then preferably try another pass with vmaBeginDefragmentationPass(). +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo); + +/** \brief Ends single defragmentation pass. + +\param allocator Allocator object. +\param context Context object that has been created by vmaBeginDefragmentation(). +\param pPassInfo Computed informations for current pass filled by vmaBeginDefragmentationPass() and possibly modified by you. + +Returns `VK_SUCCESS` if no more moves are possible or `VK_INCOMPLETE` if more defragmentations are possible. + +Ends incremental defragmentation pass and commits all defragmentation moves from `pPassInfo`. +After this call: + +- Allocations at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY + (which is the default) will be pointing to the new destination place. +- Allocation at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY + will be freed. + +If no more moves are possible you can end whole defragmentation. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo); + +/** \brief Binds buffer to allocation. + +Binds specified buffer to region of memory represented by specified allocation. +Gets `VkDeviceMemory` handle and offset from the allocation. +If you want to create a buffer, allocate memory for it and bind them together separately, +you should use this function for binding instead of standard `vkBindBufferMemory()`, +because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple +allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously +(which is illegal in Vulkan). + +It is recommended to use function vmaCreateBuffer() instead of this one. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer); + +/** \brief Binds buffer to allocation with additional parameters. + +\param allocator +\param allocation +\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0. +\param buffer +\param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null. + +This function is similar to vmaBindBufferMemory(), but it provides additional parameters. + +If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag +or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer, + const void* VMA_NULLABLE pNext); + +/** \brief Binds image to allocation. + +Binds specified image to region of memory represented by specified allocation. +Gets `VkDeviceMemory` handle and offset from the allocation. +If you want to create an image, allocate memory for it and bind them together separately, +you should use this function for binding instead of standard `vkBindImageMemory()`, +because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple +allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously +(which is illegal in Vulkan). + +It is recommended to use function vmaCreateImage() instead of this one. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image); + +/** \brief Binds image to allocation with additional parameters. + +\param allocator +\param allocation +\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0. +\param image +\param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null. + +This function is similar to vmaBindImageMemory(), but it provides additional parameters. + +If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag +or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image, + const void* VMA_NULLABLE pNext); + +/** \brief Creates a new `VkBuffer`, allocates and binds memory for it. + +\param allocator +\param pBufferCreateInfo +\param pAllocationCreateInfo +\param[out] pBuffer Buffer that was created. +\param[out] pAllocation Allocation that was created. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +This function automatically: + +-# Creates buffer. +-# Allocates appropriate memory for it. +-# Binds the buffer with the memory. + +If any of these operations fail, buffer and allocation are not created, +returned value is negative error code, `*pBuffer` and `*pAllocation` are null. + +If the function succeeded, you must destroy both buffer and allocation when you +no longer need them using either convenience function vmaDestroyBuffer() or +separately, using `vkDestroyBuffer()` and vmaFreeMemory(). + +If #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used, +VK_KHR_dedicated_allocation extension is used internally to query driver whether +it requires or prefers the new buffer to have dedicated allocation. If yes, +and if dedicated allocation is possible +(#VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated +allocation for this buffer, just like when using +#VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + +\note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer, +although recommended as a good practice, is out of scope of this library and could be implemented +by the user as a higher-level logic on top of VMA. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Creates a buffer with additional minimum alignment. + +Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom, +minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g. +for interop with OpenGL. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkDeviceSize minAlignment, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Creates a new `VkBuffer`, binds already created memory for it. + +\param allocator +\param allocation Allocation that provides memory to be used for binding new buffer to it. +\param pBufferCreateInfo +\param[out] pBuffer Buffer that was created. + +This function automatically: + +-# Creates buffer. +-# Binds the buffer with the supplied memory. + +If any of these operations fail, buffer is not created, +returned value is negative error code and `*pBuffer` is null. + +If the function succeeded, you must destroy the buffer when you +no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding +allocation you can use convenience function vmaDestroyBuffer(). +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer); + +/** \brief Destroys Vulkan buffer and frees allocated memory. + +This is just a convenience function equivalent to: + +\code +vkDestroyBuffer(device, buffer, allocationCallbacks); +vmaFreeMemory(allocator, allocation); +\endcode + +It it safe to pass null as buffer and/or allocation. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer, + VmaAllocation VMA_NULLABLE allocation); + +/// Function similar to vmaCreateBuffer(). +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage( + VmaAllocator VMA_NOT_NULL allocator, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/// Function similar to vmaCreateAliasingBuffer(). +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage); + +/** \brief Destroys Vulkan image and frees allocated memory. + +This is just a convenience function equivalent to: + +\code +vkDestroyImage(device, image, allocationCallbacks); +vmaFreeMemory(allocator, allocation); +\endcode + +It it safe to pass null as image and/or allocation. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NULLABLE_NON_DISPATCHABLE image, + VmaAllocation VMA_NULLABLE allocation); + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \brief Creates new #VmaVirtualBlock object. + +\param pCreateInfo Parameters for creation. +\param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock( + const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualBlock VMA_NULLABLE* VMA_NOT_NULL pVirtualBlock); + +/** \brief Destroys #VmaVirtualBlock object. + +Please note that you should consciously handle virtual allocations that could remain unfreed in the block. +You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock() +if you are sure this is what you want. If you do neither, an assert is called. + +If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`, +don't forget to free them. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock( + VmaVirtualBlock VMA_NULLABLE virtualBlock); + +/** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations. +*/ +VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty( + VmaVirtualBlock VMA_NOT_NULL virtualBlock); + +/** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo); + +/** \brief Allocates new virtual allocation inside given #VmaVirtualBlock. + +If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned +(despite the function doesn't ever allocate actual GPU memory). +`pAllocation` is then set to `VK_NULL_HANDLE` and `pOffset`, if not null, it set to `UINT64_MAX`. + +\param virtualBlock Virtual block +\param pCreateInfo Parameters for the allocation +\param[out] pAllocation Returned handle of the new allocation +\param[out] pOffset Returned offset of the new allocation. Optional, can be null. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation, + VkDeviceSize* VMA_NULLABLE pOffset); + +/** \brief Frees virtual allocation inside given #VmaVirtualBlock. + +It is correct to call this function with `allocation == VK_NULL_HANDLE` - it does nothing. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation); + +/** \brief Frees all virtual allocations inside given #VmaVirtualBlock. + +You must either call this function or free each virtual allocation individually with vmaVirtualFree() +before destroying a virtual block. Otherwise, an assert is called. + +If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`, +don't forget to free it as well. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock( + VmaVirtualBlock VMA_NOT_NULL virtualBlock); + +/** \brief Changes custom pointer associated with given virtual allocation. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, + void* VMA_NULLABLE pUserData); + +/** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock. + +This function is fast to call. For more detailed statistics, see vmaCalculateVirtualBlockStatistics(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaStatistics* VMA_NOT_NULL pStats); + +/** \brief Calculates and returns detailed statistics about virtual allocations and memory usage in given #VmaVirtualBlock. + +This function is slow to call. Use for debugging purposes. +For less detailed statistics, see vmaGetVirtualBlockStatistics(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaDetailedStatistics* VMA_NOT_NULL pStats); + +/** @} */ + +#if VMA_STATS_STRING_ENABLED +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock. +\param virtualBlock Virtual block. +\param[out] ppStatsString Returned string. +\param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStatistics(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces. + +Returned string must be freed using vmaFreeVirtualBlockStatsString(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString, + VkBool32 detailedMap); + +/// Frees a string returned by vmaBuildVirtualBlockStatsString(). +VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE pStatsString); + +/** \brief Builds and returns statistics as a null-terminated string in JSON format. +\param allocator +\param[out] ppStatsString Must be freed using vmaFreeStatsString() function. +\param detailedMap +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString( + VmaAllocator VMA_NOT_NULL allocator, + char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString, + VkBool32 detailedMap); + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString( + VmaAllocator VMA_NOT_NULL allocator, + char* VMA_NULLABLE pStatsString); + +/** @} */ + +#endif // VMA_STATS_STRING_ENABLED + +#endif // _VMA_FUNCTION_HEADERS + +#ifdef __cplusplus +} +#endif + +#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// For Visual Studio IntelliSense. +#if defined(__cplusplus) && defined(__INTELLISENSE__) +#define VMA_IMPLEMENTATION +#endif + +#ifdef VMA_IMPLEMENTATION +#undef VMA_IMPLEMENTATION + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + #include // For functions like __popcnt, _BitScanForward etc. +#endif +#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20 + #include // For std::popcount +#endif + +/******************************************************************************* +CONFIGURATION SECTION + +Define some of these macros before each #include of this header or change them +here if you need other then default behavior depending on your environment. +*/ +#ifndef _VMA_CONFIGURATION + +/* +Define this macro to 1 to make the library fetch pointers to Vulkan functions +internally, like: + + vulkanFunctions.vkAllocateMemory = &vkAllocateMemory; +*/ +#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES) + #define VMA_STATIC_VULKAN_FUNCTIONS 1 +#endif + +/* +Define this macro to 1 to make the library fetch pointers to Vulkan functions +internally, like: + + vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(device, "vkAllocateMemory"); + +To use this feature in new versions of VMA you now have to pass +VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as +VmaAllocatorCreateInfo::pVulkanFunctions. Other members can be null. +*/ +#if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS) + #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 +#endif + +#ifndef VMA_USE_STL_SHARED_MUTEX + // Compiler conforms to C++17. + #if __cplusplus >= 201703L + #define VMA_USE_STL_SHARED_MUTEX 1 + // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus + // Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2. + #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L + #define VMA_USE_STL_SHARED_MUTEX 1 + #else + #define VMA_USE_STL_SHARED_MUTEX 0 + #endif +#endif + +/* +Define this macro to include custom header files without having to edit this file directly, e.g.: + + // Inside of "my_vma_configuration_user_includes.h": + + #include "my_custom_assert.h" // for MY_CUSTOM_ASSERT + #include "my_custom_min.h" // for my_custom_min + #include + #include + + // Inside a different file, which includes "vk_mem_alloc.h": + + #define VMA_CONFIGURATION_USER_INCLUDES_H "my_vma_configuration_user_includes.h" + #define VMA_ASSERT(expr) MY_CUSTOM_ASSERT(expr) + #define VMA_MIN(v1, v2) (my_custom_min(v1, v2)) + #include "vk_mem_alloc.h" + ... + +The following headers are used in this CONFIGURATION section only, so feel free to +remove them if not needed. +*/ +#if !defined(VMA_CONFIGURATION_USER_INCLUDES_H) + #include // for assert + #include // for min, max + #include +#else + #include VMA_CONFIGURATION_USER_INCLUDES_H +#endif + +#ifndef VMA_NULL + // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0. + #define VMA_NULL nullptr +#endif + +#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16) +#include +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + return memalign(alignment, size); +} +#elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC)) +#include + +#if defined(__APPLE__) +#include +#endif + +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + // Unfortunately, aligned_alloc causes VMA to crash due to it returning null pointers. (At least under 11.4) + // Therefore, for now disable this specific exception until a proper solution is found. + //#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0)) + //#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0 + // // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only + // // with the MacOSX11.0 SDK in Xcode 12 (which is what adds + // // MAC_OS_X_VERSION_10_16), even though the function is marked + // // availabe for 10.15. That is why the preprocessor checks for 10.16 but + // // the __builtin_available checks for 10.15. + // // People who use C++17 could call aligned_alloc with the 10.15 SDK already. + // if (__builtin_available(macOS 10.15, iOS 13, *)) + // return aligned_alloc(alignment, size); + //#endif + //#endif + + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + void *pointer; + if(posix_memalign(&pointer, alignment, size) == 0) + return pointer; + return VMA_NULL; +} +#elif defined(_WIN32) +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + return _aligned_malloc(size, alignment); +} +#else +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} +#endif + +#if defined(_WIN32) +static void vma_aligned_free(void* ptr) +{ + _aligned_free(ptr); +} +#else +static void vma_aligned_free(void* VMA_NULLABLE ptr) +{ + free(ptr); +} +#endif + +// If your compiler is not compatible with C++11 and definition of +// aligned_alloc() function is missing, uncommeting following line may help: + +//#include + +// Normal assert to check for programmer's errors, especially in Debug configuration. +#ifndef VMA_ASSERT + #ifdef NDEBUG + #define VMA_ASSERT(expr) + #else + #define VMA_ASSERT(expr) assert(expr) + #endif +#endif + +// Assert that will be called very often, like inside data structures e.g. operator[]. +// Making it non-empty can make program slow. +#ifndef VMA_HEAVY_ASSERT + #ifdef NDEBUG + #define VMA_HEAVY_ASSERT(expr) + #else + #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr) + #endif +#endif + +#ifndef VMA_ALIGN_OF + #define VMA_ALIGN_OF(type) (__alignof(type)) +#endif + +#ifndef VMA_SYSTEM_ALIGNED_MALLOC + #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size)) +#endif + +#ifndef VMA_SYSTEM_ALIGNED_FREE + // VMA_SYSTEM_FREE is the old name, but might have been defined by the user + #if defined(VMA_SYSTEM_FREE) + #define VMA_SYSTEM_ALIGNED_FREE(ptr) VMA_SYSTEM_FREE(ptr) + #else + #define VMA_SYSTEM_ALIGNED_FREE(ptr) vma_aligned_free(ptr) + #endif +#endif + +#ifndef VMA_COUNT_BITS_SET + // Returns number of bits set to 1 in (v) + #define VMA_COUNT_BITS_SET(v) VmaCountBitsSet(v) +#endif + +#ifndef VMA_BITSCAN_LSB + // Scans integer for index of first nonzero value from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX + #define VMA_BITSCAN_LSB(mask) VmaBitScanLSB(mask) +#endif + +#ifndef VMA_BITSCAN_MSB + // Scans integer for index of first nonzero value from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX + #define VMA_BITSCAN_MSB(mask) VmaBitScanMSB(mask) +#endif + +#ifndef VMA_MIN + #define VMA_MIN(v1, v2) ((std::min)((v1), (v2))) +#endif + +#ifndef VMA_MAX + #define VMA_MAX(v1, v2) ((std::max)((v1), (v2))) +#endif + +#ifndef VMA_SWAP + #define VMA_SWAP(v1, v2) std::swap((v1), (v2)) +#endif + +#ifndef VMA_SORT + #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp) +#endif + +#ifndef VMA_DEBUG_LOG + #define VMA_DEBUG_LOG(format, ...) + /* + #define VMA_DEBUG_LOG(format, ...) do { \ + printf(format, __VA_ARGS__); \ + printf("\n"); \ + } while(false) + */ +#endif + +// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString. +#if VMA_STATS_STRING_ENABLED + static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num) + { + snprintf(outStr, strLen, "%u", static_cast(num)); + } + static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num) + { + snprintf(outStr, strLen, "%llu", static_cast(num)); + } + static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr) + { + snprintf(outStr, strLen, "%p", ptr); + } +#endif + +#ifndef VMA_MUTEX + class VmaMutex + { + public: + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + bool TryLock() { return m_Mutex.try_lock(); } + private: + std::mutex m_Mutex; + }; + #define VMA_MUTEX VmaMutex +#endif + +// Read-write mutex, where "read" is shared access, "write" is exclusive access. +#ifndef VMA_RW_MUTEX + #if VMA_USE_STL_SHARED_MUTEX + // Use std::shared_mutex from C++17. + #include + class VmaRWMutex + { + public: + void LockRead() { m_Mutex.lock_shared(); } + void UnlockRead() { m_Mutex.unlock_shared(); } + bool TryLockRead() { return m_Mutex.try_lock_shared(); } + void LockWrite() { m_Mutex.lock(); } + void UnlockWrite() { m_Mutex.unlock(); } + bool TryLockWrite() { return m_Mutex.try_lock(); } + private: + std::shared_mutex m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex + #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600 + // Use SRWLOCK from WinAPI. + // Minimum supported client = Windows Vista, server = Windows Server 2008. + class VmaRWMutex + { + public: + VmaRWMutex() { InitializeSRWLock(&m_Lock); } + void LockRead() { AcquireSRWLockShared(&m_Lock); } + void UnlockRead() { ReleaseSRWLockShared(&m_Lock); } + bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; } + void LockWrite() { AcquireSRWLockExclusive(&m_Lock); } + void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); } + bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; } + private: + SRWLOCK m_Lock; + }; + #define VMA_RW_MUTEX VmaRWMutex + #else + // Less efficient fallback: Use normal mutex. + class VmaRWMutex + { + public: + void LockRead() { m_Mutex.Lock(); } + void UnlockRead() { m_Mutex.Unlock(); } + bool TryLockRead() { return m_Mutex.TryLock(); } + void LockWrite() { m_Mutex.Lock(); } + void UnlockWrite() { m_Mutex.Unlock(); } + bool TryLockWrite() { return m_Mutex.TryLock(); } + private: + VMA_MUTEX m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex + #endif // #if VMA_USE_STL_SHARED_MUTEX +#endif // #ifndef VMA_RW_MUTEX + +/* +If providing your own implementation, you need to implement a subset of std::atomic. +*/ +#ifndef VMA_ATOMIC_UINT32 + #include + #define VMA_ATOMIC_UINT32 std::atomic +#endif + +#ifndef VMA_ATOMIC_UINT64 + #include + #define VMA_ATOMIC_UINT64 std::atomic +#endif + +#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY + /** + Every allocation will have its own memory block. + Define to 1 for debugging purposes only. + */ + #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0) +#endif + +#ifndef VMA_MIN_ALIGNMENT + /** + Minimum alignment of all allocations, in bytes. + Set to more than 1 for debugging purposes. Must be power of two. + */ + #ifdef VMA_DEBUG_ALIGNMENT // Old name + #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT + #else + #define VMA_MIN_ALIGNMENT (1) + #endif +#endif + +#ifndef VMA_DEBUG_MARGIN + /** + Minimum margin after every allocation, in bytes. + Set nonzero for debugging purposes only. + */ + #define VMA_DEBUG_MARGIN (0) +#endif + +#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS + /** + Define this macro to 1 to automatically fill new allocations and destroyed + allocations with some bit pattern. + */ + #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0) +#endif + +#ifndef VMA_DEBUG_DETECT_CORRUPTION + /** + Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to + enable writing magic value to the margin after every allocation and + validating it, so that memory corruptions (out-of-bounds writes) are detected. + */ + #define VMA_DEBUG_DETECT_CORRUPTION (0) +#endif + +#ifndef VMA_DEBUG_GLOBAL_MUTEX + /** + Set this to 1 for debugging purposes only, to enable single mutex protecting all + entry calls to the library. Can be useful for debugging multithreading issues. + */ + #define VMA_DEBUG_GLOBAL_MUTEX (0) +#endif + +#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY + /** + Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity. + Set to more than 1 for debugging purposes only. Must be power of two. + */ + #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1) +#endif + +#ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT + /* + Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount + and return error instead of leaving up to Vulkan implementation what to do in such cases. + */ + #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0) +#endif + +#ifndef VMA_SMALL_HEAP_MAX_SIZE + /// Maximum size of a memory heap in Vulkan to consider it "small". + #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024) +#endif + +#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE + /// Default size of a block allocated as single VkDeviceMemory from a "large" heap. + #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024) +#endif + +/* +Mapping hysteresis is a logic that launches when vmaMapMemory/vmaUnmapMemory is called +or a persistently mapped allocation is created and destroyed several times in a row. +It keeps additional +1 mapping of a device memory block to prevent calling actual +vkMapMemory/vkUnmapMemory too many times, which may improve performance and help +tools like RenderDOc. +*/ +#ifndef VMA_MAPPING_HYSTERESIS_ENABLED + #define VMA_MAPPING_HYSTERESIS_ENABLED 1 +#endif + +#ifndef VMA_CLASS_NO_COPY + #define VMA_CLASS_NO_COPY(className) \ + private: \ + className(const className&) = delete; \ + className& operator=(const className&) = delete; +#endif + +#define VMA_VALIDATE(cond) do { if(!(cond)) { \ + VMA_ASSERT(0 && "Validation failed: " #cond); \ + return false; \ + } } while(false) + +/******************************************************************************* +END OF CONFIGURATION +*/ +#endif // _VMA_CONFIGURATION + + +static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC; +static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF; +// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F. +static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666; + +// Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants. +static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040; +static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080; +static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000; +static const uint32_t VK_IMAGE_CREATE_DISJOINT_BIT_COPY = 0x00000200; +static const int32_t VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY = 1000158000; +static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u; +static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32; +static const uint32_t VMA_VENDOR_ID_AMD = 4098; + +// This one is tricky. Vulkan specification defines this code as available since +// Vulkan 1.0, but doesn't actually define it in Vulkan SDK earlier than 1.2.131. +// See pull request #207. +#define VK_ERROR_UNKNOWN_COPY ((VkResult)-13) + + +#if VMA_STATS_STRING_ENABLED +// Correspond to values of enum VmaSuballocationType. +static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = +{ + "FREE", + "UNKNOWN", + "BUFFER", + "IMAGE_UNKNOWN", + "IMAGE_LINEAR", + "IMAGE_OPTIMAL", +}; +#endif + +static VkAllocationCallbacks VmaEmptyAllocationCallbacks = + { VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL }; + + +#ifndef _VMA_ENUM_DECLARATIONS + +enum VmaSuballocationType +{ + VMA_SUBALLOCATION_TYPE_FREE = 0, + VMA_SUBALLOCATION_TYPE_UNKNOWN = 1, + VMA_SUBALLOCATION_TYPE_BUFFER = 2, + VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3, + VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4, + VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5, + VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF +}; + +enum VMA_CACHE_OPERATION +{ + VMA_CACHE_FLUSH, + VMA_CACHE_INVALIDATE +}; + +enum class VmaAllocationRequestType +{ + Normal, + TLSF, + // Used by "Linear" algorithm. + UpperAddress, + EndOf1st, + EndOf2nd, +}; + +#endif // _VMA_ENUM_DECLARATIONS + +#ifndef _VMA_FORWARD_DECLARATIONS +// Opaque handle used by allocation algorithms to identify single allocation in any conforming way. +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaAllocHandle); + +struct VmaMutexLock; +struct VmaMutexLockRead; +struct VmaMutexLockWrite; + +template +struct AtomicTransactionalIncrement; + +template +struct VmaStlAllocator; + +template +class VmaVector; + +template +class VmaSmallVector; + +template +class VmaPoolAllocator; + +template +struct VmaListItem; + +template +class VmaRawList; + +template +class VmaList; + +template +class VmaIntrusiveLinkedList; + +// Unused in this version +#if 0 +template +struct VmaPair; +template +struct VmaPairFirstLess; + +template +class VmaMap; +#endif + +#if VMA_STATS_STRING_ENABLED +class VmaStringBuilder; +class VmaJsonWriter; +#endif + +class VmaDeviceMemoryBlock; + +struct VmaDedicatedAllocationListItemTraits; +class VmaDedicatedAllocationList; + +struct VmaSuballocation; +struct VmaSuballocationOffsetLess; +struct VmaSuballocationOffsetGreater; +struct VmaSuballocationItemSizeLess; + +typedef VmaList> VmaSuballocationList; + +struct VmaAllocationRequest; + +class VmaBlockMetadata; +class VmaBlockMetadata_Linear; +class VmaBlockMetadata_TLSF; + +class VmaBlockVector; + +struct VmaPoolListItemTraits; + +struct VmaCurrentBudgetData; + +class VmaAllocationObjectAllocator; + +#endif // _VMA_FORWARD_DECLARATIONS + + +#ifndef _VMA_FUNCTIONS + +/* +Returns number of bits set to 1 in (v). + +On specific platforms and compilers you can use instrinsics like: + +Visual Studio: + return __popcnt(v); +GCC, Clang: + return static_cast(__builtin_popcount(v)); + +Define macro VMA_COUNT_BITS_SET to provide your optimized implementation. +But you need to check in runtime whether user's CPU supports these, as some old processors don't. +*/ +static inline uint32_t VmaCountBitsSet(uint32_t v) +{ +#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20 + return std::popcount(v); +#else + uint32_t c = v - ((v >> 1) & 0x55555555); + c = ((c >> 2) & 0x33333333) + (c & 0x33333333); + c = ((c >> 4) + c) & 0x0F0F0F0F; + c = ((c >> 8) + c) & 0x00FF00FF; + c = ((c >> 16) + c) & 0x0000FFFF; + return c; +#endif +} + +static inline uint8_t VmaBitScanLSB(uint64_t mask) +{ +#if defined(_MSC_VER) && defined(_WIN64) + unsigned long pos; + if (_BitScanForward64(&pos, mask)) + return static_cast(pos); + return UINT8_MAX; +#elif defined __GNUC__ || defined __clang__ + return static_cast(__builtin_ffsll(mask)) - 1U; +#else + uint8_t pos = 0; + uint64_t bit = 1; + do + { + if (mask & bit) + return pos; + bit <<= 1; + } while (pos++ < 63); + return UINT8_MAX; +#endif +} + +static inline uint8_t VmaBitScanLSB(uint32_t mask) +{ +#ifdef _MSC_VER + unsigned long pos; + if (_BitScanForward(&pos, mask)) + return static_cast(pos); + return UINT8_MAX; +#elif defined __GNUC__ || defined __clang__ + return static_cast(__builtin_ffs(mask)) - 1U; +#else + uint8_t pos = 0; + uint32_t bit = 1; + do + { + if (mask & bit) + return pos; + bit <<= 1; + } while (pos++ < 31); + return UINT8_MAX; +#endif +} + +static inline uint8_t VmaBitScanMSB(uint64_t mask) +{ +#if defined(_MSC_VER) && defined(_WIN64) + unsigned long pos; + if (_BitScanReverse64(&pos, mask)) + return static_cast(pos); +#elif defined __GNUC__ || defined __clang__ + if (mask) + return 63 - static_cast(__builtin_clzll(mask)); +#else + uint8_t pos = 63; + uint64_t bit = 1ULL << 63; + do + { + if (mask & bit) + return pos; + bit >>= 1; + } while (pos-- > 0); +#endif + return UINT8_MAX; +} + +static inline uint8_t VmaBitScanMSB(uint32_t mask) +{ +#ifdef _MSC_VER + unsigned long pos; + if (_BitScanReverse(&pos, mask)) + return static_cast(pos); +#elif defined __GNUC__ || defined __clang__ + if (mask) + return 31 - static_cast(__builtin_clz(mask)); +#else + uint8_t pos = 31; + uint32_t bit = 1UL << 31; + do + { + if (mask & bit) + return pos; + bit >>= 1; + } while (pos-- > 0); +#endif + return UINT8_MAX; +} + +/* +Returns true if given number is a power of two. +T must be unsigned integer number or signed integer but always nonnegative. +For 0 returns true. +*/ +template +inline bool VmaIsPow2(T x) +{ + return (x & (x - 1)) == 0; +} + +// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16. +// Use types like uint32_t, uint64_t as T. +template +static inline T VmaAlignUp(T val, T alignment) +{ + VMA_HEAVY_ASSERT(VmaIsPow2(alignment)); + return (val + alignment - 1) & ~(alignment - 1); +} + +// Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8. +// Use types like uint32_t, uint64_t as T. +template +static inline T VmaAlignDown(T val, T alignment) +{ + VMA_HEAVY_ASSERT(VmaIsPow2(alignment)); + return val & ~(alignment - 1); +} + +// Division with mathematical rounding to nearest number. +template +static inline T VmaRoundDiv(T x, T y) +{ + return (x + (y / (T)2)) / y; +} + +// Divide by 'y' and round up to nearest integer. +template +static inline T VmaDivideRoundingUp(T x, T y) +{ + return (x + y - (T)1) / y; +} + +// Returns smallest power of 2 greater or equal to v. +static inline uint32_t VmaNextPow2(uint32_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +static inline uint64_t VmaNextPow2(uint64_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + return v; +} + +// Returns largest power of 2 less or equal to v. +static inline uint32_t VmaPrevPow2(uint32_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v = v ^ (v >> 1); + return v; +} + +static inline uint64_t VmaPrevPow2(uint64_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v = v ^ (v >> 1); + return v; +} + +static inline bool VmaStrIsEmpty(const char* pStr) +{ + return pStr == VMA_NULL || *pStr == '\0'; +} + +/* +Returns true if two memory blocks occupy overlapping pages. +ResourceA must be in less memory offset than ResourceB. + +Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)" +chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity". +*/ +static inline bool VmaBlocksOnSamePage( + VkDeviceSize resourceAOffset, + VkDeviceSize resourceASize, + VkDeviceSize resourceBOffset, + VkDeviceSize pageSize) +{ + VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0); + VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1; + VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1); + VkDeviceSize resourceBStart = resourceBOffset; + VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1); + return resourceAEndPage == resourceBStartPage; +} + +/* +Returns true if given suballocation types could conflict and must respect +VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer +or linear image and another one is optimal image. If type is unknown, behave +conservatively. +*/ +static inline bool VmaIsBufferImageGranularityConflict( + VmaSuballocationType suballocType1, + VmaSuballocationType suballocType2) +{ + if (suballocType1 > suballocType2) + { + VMA_SWAP(suballocType1, suballocType2); + } + + switch (suballocType1) + { + case VMA_SUBALLOCATION_TYPE_FREE: + return false; + case VMA_SUBALLOCATION_TYPE_UNKNOWN: + return true; + case VMA_SUBALLOCATION_TYPE_BUFFER: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL: + return false; + default: + VMA_ASSERT(0); + return true; + } +} + +static void VmaWriteMagicValue(void* pData, VkDeviceSize offset) +{ +#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION + uint32_t* pDst = (uint32_t*)((char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); + for (size_t i = 0; i < numberCount; ++i, ++pDst) + { + *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE; + } +#else + // no-op +#endif +} + +static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset) +{ +#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION + const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); + for (size_t i = 0; i < numberCount; ++i, ++pSrc) + { + if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE) + { + return false; + } + } +#endif + return true; +} + +/* +Fills structure with parameters of an example buffer to be used for transfers +during GPU memory defragmentation. +*/ +static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo) +{ + memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo)); + outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size. +} + + +/* +Performs binary search and returns iterator to first element that is greater or +equal to (key), according to comparison (cmp). + +Cmp should return true if first argument is less than second argument. + +Returned value is the found element, if present in the collection or place where +new element with value (key) should be inserted. +*/ +template +static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp) +{ + size_t down = 0, up = (end - beg); + while (down < up) + { + const size_t mid = down + (up - down) / 2; // Overflow-safe midpoint calculation + if (cmp(*(beg + mid), key)) + { + down = mid + 1; + } + else + { + up = mid; + } + } + return beg + down; +} + +template +IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp) +{ + IterT it = VmaBinaryFindFirstNotLess( + beg, end, value, cmp); + if (it == end || + (!cmp(*it, value) && !cmp(value, *it))) + { + return it; + } + return end; +} + +/* +Returns true if all pointers in the array are not-null and unique. +Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT. +T must be pointer type, e.g. VmaAllocation, VmaPool. +*/ +template +static bool VmaValidatePointerArray(uint32_t count, const T* arr) +{ + for (uint32_t i = 0; i < count; ++i) + { + const T iPtr = arr[i]; + if (iPtr == VMA_NULL) + { + return false; + } + for (uint32_t j = i + 1; j < count; ++j) + { + if (iPtr == arr[j]) + { + return false; + } + } + } + return true; +} + +template +static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct) +{ + newStruct->pNext = mainStruct->pNext; + mainStruct->pNext = newStruct; +} + +// This is the main algorithm that guides the selection of a memory type best for an allocation - +// converts usage to required/preferred/not preferred flags. +static bool FindMemoryPreferences( + bool isIntegratedGPU, + const VmaAllocationCreateInfo& allocCreateInfo, + VkFlags bufImgUsage, // VkBufferCreateInfo::usage or VkImageCreateInfo::usage. UINT32_MAX if unknown. + VkMemoryPropertyFlags& outRequiredFlags, + VkMemoryPropertyFlags& outPreferredFlags, + VkMemoryPropertyFlags& outNotPreferredFlags) +{ + outRequiredFlags = allocCreateInfo.requiredFlags; + outPreferredFlags = allocCreateInfo.preferredFlags; + outNotPreferredFlags = 0; + + switch(allocCreateInfo.usage) + { + case VMA_MEMORY_USAGE_UNKNOWN: + break; + case VMA_MEMORY_USAGE_GPU_ONLY: + if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + case VMA_MEMORY_USAGE_CPU_ONLY: + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + case VMA_MEMORY_USAGE_CPU_TO_GPU: + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + case VMA_MEMORY_USAGE_GPU_TO_CPU: + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + break; + case VMA_MEMORY_USAGE_CPU_COPY: + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + break; + case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED: + outRequiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; + break; + case VMA_MEMORY_USAGE_AUTO: + case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE: + case VMA_MEMORY_USAGE_AUTO_PREFER_HOST: + { + if(bufImgUsage == UINT32_MAX) + { + VMA_ASSERT(0 && "VMA_MEMORY_USAGE_AUTO* values can only be used with functions like vmaCreateBuffer, vmaCreateImage so that the details of the created resource are known."); + return false; + } + // This relies on values of VK_IMAGE_USAGE_TRANSFER* being the same VK_BUFFER_IMAGE_TRANSFER*. + const bool deviceAccess = (bufImgUsage & ~(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) != 0; + const bool hostAccessSequentialWrite = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) != 0; + const bool hostAccessRandom = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) != 0; + const bool hostAccessAllowTransferInstead = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) != 0; + const bool preferDevice = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + const bool preferHost = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST; + + // CPU random access - e.g. a buffer written to or transferred from GPU to read back on CPU. + if(hostAccessRandom) + { + if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost) + { + // Nice if it will end up in HOST_VISIBLE, but more importantly prefer DEVICE_LOCAL. + // Omitting HOST_VISIBLE here is intentional. + // In case there is DEVICE_LOCAL | HOST_VISIBLE | HOST_CACHED, it will pick that one. + // Otherwise, this will give same weight to DEVICE_LOCAL as HOST_VISIBLE | HOST_CACHED and select the former if occurs first on the list. + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + } + else + { + // Always CPU memory, cached. + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + } + } + // CPU sequential write - may be CPU or host-visible GPU memory, uncached and write-combined. + else if(hostAccessSequentialWrite) + { + // Want uncached and write-combined. + outNotPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + + if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost) + { + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } + else + { + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + // Direct GPU access, CPU sequential write (e.g. a dynamic uniform buffer updated every frame) + if(deviceAccess) + { + // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose GPU memory. + if(preferHost) + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + // GPU no direct access, CPU sequential write (e.g. an upload buffer to be transferred to the GPU) + else + { + // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose CPU memory. + if(preferDevice) + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + } + } + // No CPU access + else + { + // GPU access, no CPU access (e.g. a color attachment image) - prefer GPU memory + if(deviceAccess) + { + // ...unless there is a clear preference from the user not to do so. + if(preferHost) + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + // No direct GPU access, no CPU access, just transfers. + // It may be staging copy intended for e.g. preserving image for next frame (then better GPU memory) or + // a "swap file" copy to free some GPU memory (then better CPU memory). + // Up to the user to decide. If no preferece, assume the former and choose GPU memory. + if(preferHost) + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + } + default: + VMA_ASSERT(0); + } + + // Avoid DEVICE_COHERENT unless explicitly requested. + if(((allocCreateInfo.requiredFlags | allocCreateInfo.preferredFlags) & + (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0) + { + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Memory allocation + +static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment) +{ + void* result = VMA_NULL; + if ((pAllocationCallbacks != VMA_NULL) && + (pAllocationCallbacks->pfnAllocation != VMA_NULL)) + { + result = (*pAllocationCallbacks->pfnAllocation)( + pAllocationCallbacks->pUserData, + size, + alignment, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + } + else + { + result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment); + } + VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed."); + return result; +} + +static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr) +{ + if ((pAllocationCallbacks != VMA_NULL) && + (pAllocationCallbacks->pfnFree != VMA_NULL)) + { + (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr); + } + else + { + VMA_SYSTEM_ALIGNED_FREE(ptr); + } +} + +template +static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks) +{ + return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T)); +} + +template +static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count) +{ + return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T)); +} + +#define vma_new(allocator, type) new(VmaAllocate(allocator))(type) + +#define vma_new_array(allocator, type, count) new(VmaAllocateArray((allocator), (count)))(type) + +template +static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr) +{ + ptr->~T(); + VmaFree(pAllocationCallbacks, ptr); +} + +template +static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count) +{ + if (ptr != VMA_NULL) + { + for (size_t i = count; i--; ) + { + ptr[i].~T(); + } + VmaFree(pAllocationCallbacks, ptr); + } +} + +static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr) +{ + if (srcStr != VMA_NULL) + { + const size_t len = strlen(srcStr); + char* const result = vma_new_array(allocs, char, len + 1); + memcpy(result, srcStr, len + 1); + return result; + } + return VMA_NULL; +} + +#if VMA_STATS_STRING_ENABLED +static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen) +{ + if (srcStr != VMA_NULL) + { + char* const result = vma_new_array(allocs, char, strLen + 1); + memcpy(result, srcStr, strLen); + result[strLen] = '\0'; + return result; + } + return VMA_NULL; +} +#endif // VMA_STATS_STRING_ENABLED + +static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str) +{ + if (str != VMA_NULL) + { + const size_t len = strlen(str); + vma_delete_array(allocs, str, len + 1); + } +} + +template +size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value) +{ + const size_t indexToInsert = VmaBinaryFindFirstNotLess( + vector.data(), + vector.data() + vector.size(), + value, + CmpLess()) - vector.data(); + VmaVectorInsert(vector, indexToInsert, value); + return indexToInsert; +} + +template +bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value) +{ + CmpLess comparator; + typename VectorT::iterator it = VmaBinaryFindFirstNotLess( + vector.begin(), + vector.end(), + value, + comparator); + if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it)) + { + size_t indexToRemove = it - vector.begin(); + VmaVectorRemove(vector, indexToRemove); + return true; + } + return false; +} +#endif // _VMA_FUNCTIONS + +#ifndef _VMA_STATISTICS_FUNCTIONS + +static void VmaClearStatistics(VmaStatistics& outStats) +{ + outStats.blockCount = 0; + outStats.allocationCount = 0; + outStats.blockBytes = 0; + outStats.allocationBytes = 0; +} + +static void VmaAddStatistics(VmaStatistics& inoutStats, const VmaStatistics& src) +{ + inoutStats.blockCount += src.blockCount; + inoutStats.allocationCount += src.allocationCount; + inoutStats.blockBytes += src.blockBytes; + inoutStats.allocationBytes += src.allocationBytes; +} + +static void VmaClearDetailedStatistics(VmaDetailedStatistics& outStats) +{ + VmaClearStatistics(outStats.statistics); + outStats.unusedRangeCount = 0; + outStats.allocationSizeMin = VK_WHOLE_SIZE; + outStats.allocationSizeMax = 0; + outStats.unusedRangeSizeMin = VK_WHOLE_SIZE; + outStats.unusedRangeSizeMax = 0; +} + +static void VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics& inoutStats, VkDeviceSize size) +{ + inoutStats.statistics.allocationCount++; + inoutStats.statistics.allocationBytes += size; + inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, size); + inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, size); +} + +static void VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics& inoutStats, VkDeviceSize size) +{ + inoutStats.unusedRangeCount++; + inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, size); + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, size); +} + +static void VmaAddDetailedStatistics(VmaDetailedStatistics& inoutStats, const VmaDetailedStatistics& src) +{ + VmaAddStatistics(inoutStats.statistics, src.statistics); + inoutStats.unusedRangeCount += src.unusedRangeCount; + inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, src.allocationSizeMin); + inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, src.allocationSizeMax); + inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, src.unusedRangeSizeMin); + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, src.unusedRangeSizeMax); +} + +#endif // _VMA_STATISTICS_FUNCTIONS + +#ifndef _VMA_MUTEX_LOCK +// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope). +struct VmaMutexLock +{ + VMA_CLASS_NO_COPY(VmaMutexLock) +public: + VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->Lock(); } + } + ~VmaMutexLock() { if (m_pMutex) { m_pMutex->Unlock(); } } + +private: + VMA_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading. +struct VmaMutexLockRead +{ + VMA_CLASS_NO_COPY(VmaMutexLockRead) +public: + VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->LockRead(); } + } + ~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } } + +private: + VMA_RW_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing. +struct VmaMutexLockWrite +{ + VMA_CLASS_NO_COPY(VmaMutexLockWrite) +public: + VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) + : m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->LockWrite(); } + } + ~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } } + +private: + VMA_RW_MUTEX* m_pMutex; +}; + +#if VMA_DEBUG_GLOBAL_MUTEX + static VMA_MUTEX gDebugGlobalMutex; + #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true); +#else + #define VMA_DEBUG_GLOBAL_MUTEX_LOCK +#endif +#endif // _VMA_MUTEX_LOCK + +#ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT +// An object that increments given atomic but decrements it back in the destructor unless Commit() is called. +template +struct AtomicTransactionalIncrement +{ +public: + typedef std::atomic AtomicT; + + ~AtomicTransactionalIncrement() + { + if(m_Atomic) + --(*m_Atomic); + } + + void Commit() { m_Atomic = nullptr; } + T Increment(AtomicT* atomic) + { + m_Atomic = atomic; + return m_Atomic->fetch_add(1); + } + +private: + AtomicT* m_Atomic = nullptr; +}; +#endif // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT + +#ifndef _VMA_STL_ALLOCATOR +// STL-compatible allocator. +template +struct VmaStlAllocator +{ + const VkAllocationCallbacks* const m_pCallbacks; + typedef T value_type; + + VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) {} + template + VmaStlAllocator(const VmaStlAllocator& src) : m_pCallbacks(src.m_pCallbacks) {} + VmaStlAllocator(const VmaStlAllocator&) = default; + VmaStlAllocator& operator=(const VmaStlAllocator&) = delete; + + T* allocate(size_t n) { return VmaAllocateArray(m_pCallbacks, n); } + void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); } + + template + bool operator==(const VmaStlAllocator& rhs) const + { + return m_pCallbacks == rhs.m_pCallbacks; + } + template + bool operator!=(const VmaStlAllocator& rhs) const + { + return m_pCallbacks != rhs.m_pCallbacks; + } +}; +#endif // _VMA_STL_ALLOCATOR + +#ifndef _VMA_VECTOR +/* Class with interface compatible with subset of std::vector. +T must be POD because constructors and destructors are not called and memcpy is +used for these objects. */ +template +class VmaVector +{ +public: + typedef T value_type; + typedef T* iterator; + typedef const T* const_iterator; + + VmaVector(const AllocatorT& allocator); + VmaVector(size_t count, const AllocatorT& allocator); + // This version of the constructor is here for compatibility with pre-C++14 std::vector. + // value is unused. + VmaVector(size_t count, const T& value, const AllocatorT& allocator) : VmaVector(count, allocator) {} + VmaVector(const VmaVector& src); + VmaVector& operator=(const VmaVector& rhs); + ~VmaVector() { VmaFree(m_Allocator.m_pCallbacks, m_pArray); } + + bool empty() const { return m_Count == 0; } + size_t size() const { return m_Count; } + T* data() { return m_pArray; } + T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; } + T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; } + const T* data() const { return m_pArray; } + const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; } + const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; } + + iterator begin() { return m_pArray; } + iterator end() { return m_pArray + m_Count; } + const_iterator cbegin() const { return m_pArray; } + const_iterator cend() const { return m_pArray + m_Count; } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + + void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); } + void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); } + void push_front(const T& src) { insert(0, src); } + + void push_back(const T& src); + void reserve(size_t newCapacity, bool freeMemory = false); + void resize(size_t newCount); + void clear() { resize(0); } + void shrink_to_fit(); + void insert(size_t index, const T& src); + void remove(size_t index); + + T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; } + const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; } + +private: + AllocatorT m_Allocator; + T* m_pArray; + size_t m_Count; + size_t m_Capacity; +}; + +#ifndef _VMA_VECTOR_FUNCTIONS +template +VmaVector::VmaVector(const AllocatorT& allocator) + : m_Allocator(allocator), + m_pArray(VMA_NULL), + m_Count(0), + m_Capacity(0) {} + +template +VmaVector::VmaVector(size_t count, const AllocatorT& allocator) + : m_Allocator(allocator), + m_pArray(count ? (T*)VmaAllocateArray(allocator.m_pCallbacks, count) : VMA_NULL), + m_Count(count), + m_Capacity(count) {} + +template +VmaVector::VmaVector(const VmaVector& src) + : m_Allocator(src.m_Allocator), + m_pArray(src.m_Count ? (T*)VmaAllocateArray(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL), + m_Count(src.m_Count), + m_Capacity(src.m_Count) +{ + if (m_Count != 0) + { + memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T)); + } +} + +template +VmaVector& VmaVector::operator=(const VmaVector& rhs) +{ + if (&rhs != this) + { + resize(rhs.m_Count); + if (m_Count != 0) + { + memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T)); + } + } + return *this; +} + +template +void VmaVector::push_back(const T& src) +{ + const size_t newIndex = size(); + resize(newIndex + 1); + m_pArray[newIndex] = src; +} + +template +void VmaVector::reserve(size_t newCapacity, bool freeMemory) +{ + newCapacity = VMA_MAX(newCapacity, m_Count); + + if ((newCapacity < m_Capacity) && !freeMemory) + { + newCapacity = m_Capacity; + } + + if (newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator, newCapacity) : VMA_NULL; + if (m_Count != 0) + { + memcpy(newArray, m_pArray, m_Count * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } +} + +template +void VmaVector::resize(size_t newCount) +{ + size_t newCapacity = m_Capacity; + if (newCount > m_Capacity) + { + newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8)); + } + + if (newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL; + const size_t elementsToCopy = VMA_MIN(m_Count, newCount); + if (elementsToCopy != 0) + { + memcpy(newArray, m_pArray, elementsToCopy * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } + + m_Count = newCount; +} + +template +void VmaVector::shrink_to_fit() +{ + if (m_Capacity > m_Count) + { + T* newArray = VMA_NULL; + if (m_Count > 0) + { + newArray = VmaAllocateArray(m_Allocator.m_pCallbacks, m_Count); + memcpy(newArray, m_pArray, m_Count * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = m_Count; + m_pArray = newArray; + } +} + +template +void VmaVector::insert(size_t index, const T& src) +{ + VMA_HEAVY_ASSERT(index <= m_Count); + const size_t oldCount = size(); + resize(oldCount + 1); + if (index < oldCount) + { + memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T)); + } + m_pArray[index] = src; +} + +template +void VmaVector::remove(size_t index) +{ + VMA_HEAVY_ASSERT(index < m_Count); + const size_t oldCount = size(); + if (index < oldCount - 1) + { + memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T)); + } + resize(oldCount - 1); +} +#endif // _VMA_VECTOR_FUNCTIONS + +template +static void VmaVectorInsert(VmaVector& vec, size_t index, const T& item) +{ + vec.insert(index, item); +} + +template +static void VmaVectorRemove(VmaVector& vec, size_t index) +{ + vec.remove(index); +} +#endif // _VMA_VECTOR + +#ifndef _VMA_SMALL_VECTOR +/* +This is a vector (a variable-sized array), optimized for the case when the array is small. + +It contains some number of elements in-place, which allows it to avoid heap allocation +when the actual number of elements is below that threshold. This allows normal "small" +cases to be fast without losing generality for large inputs. +*/ +template +class VmaSmallVector +{ +public: + typedef T value_type; + typedef T* iterator; + + VmaSmallVector(const AllocatorT& allocator); + VmaSmallVector(size_t count, const AllocatorT& allocator); + template + VmaSmallVector(const VmaSmallVector&) = delete; + template + VmaSmallVector& operator=(const VmaSmallVector&) = delete; + ~VmaSmallVector() = default; + + bool empty() const { return m_Count == 0; } + size_t size() const { return m_Count; } + T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; } + T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; } + T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; } + const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; } + const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; } + const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; } + + iterator begin() { return data(); } + iterator end() { return data() + m_Count; } + + void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); } + void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); } + void push_front(const T& src) { insert(0, src); } + + void push_back(const T& src); + void resize(size_t newCount, bool freeMemory = false); + void clear(bool freeMemory = false); + void insert(size_t index, const T& src); + void remove(size_t index); + + T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; } + const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; } + +private: + size_t m_Count; + T m_StaticArray[N]; // Used when m_Size <= N + VmaVector m_DynamicArray; // Used when m_Size > N +}; + +#ifndef _VMA_SMALL_VECTOR_FUNCTIONS +template +VmaSmallVector::VmaSmallVector(const AllocatorT& allocator) + : m_Count(0), + m_DynamicArray(allocator) {} + +template +VmaSmallVector::VmaSmallVector(size_t count, const AllocatorT& allocator) + : m_Count(count), + m_DynamicArray(count > N ? count : 0, allocator) {} + +template +void VmaSmallVector::push_back(const T& src) +{ + const size_t newIndex = size(); + resize(newIndex + 1); + data()[newIndex] = src; +} + +template +void VmaSmallVector::resize(size_t newCount, bool freeMemory) +{ + if (newCount > N && m_Count > N) + { + // Any direction, staying in m_DynamicArray + m_DynamicArray.resize(newCount); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + } + else if (newCount > N && m_Count <= N) + { + // Growing, moving from m_StaticArray to m_DynamicArray + m_DynamicArray.resize(newCount); + if (m_Count > 0) + { + memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T)); + } + } + else if (newCount <= N && m_Count > N) + { + // Shrinking, moving from m_DynamicArray to m_StaticArray + if (newCount > 0) + { + memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T)); + } + m_DynamicArray.resize(0); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + } + else + { + // Any direction, staying in m_StaticArray - nothing to do here + } + m_Count = newCount; +} + +template +void VmaSmallVector::clear(bool freeMemory) +{ + m_DynamicArray.clear(); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + m_Count = 0; +} + +template +void VmaSmallVector::insert(size_t index, const T& src) +{ + VMA_HEAVY_ASSERT(index <= m_Count); + const size_t oldCount = size(); + resize(oldCount + 1); + T* const dataPtr = data(); + if (index < oldCount) + { + // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray. + memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T)); + } + dataPtr[index] = src; +} + +template +void VmaSmallVector::remove(size_t index) +{ + VMA_HEAVY_ASSERT(index < m_Count); + const size_t oldCount = size(); + if (index < oldCount - 1) + { + // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray. + T* const dataPtr = data(); + memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T)); + } + resize(oldCount - 1); +} +#endif // _VMA_SMALL_VECTOR_FUNCTIONS +#endif // _VMA_SMALL_VECTOR + +#ifndef _VMA_POOL_ALLOCATOR +/* +Allocator for objects of type T using a list of arrays (pools) to speed up +allocation. Number of elements that can be allocated is not bounded because +allocator can create multiple blocks. +*/ +template +class VmaPoolAllocator +{ + VMA_CLASS_NO_COPY(VmaPoolAllocator) +public: + VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity); + ~VmaPoolAllocator(); + template T* Alloc(Types&&... args); + void Free(T* ptr); + +private: + union Item + { + uint32_t NextFreeIndex; + alignas(T) char Value[sizeof(T)]; + }; + struct ItemBlock + { + Item* pItems; + uint32_t Capacity; + uint32_t FirstFreeIndex; + }; + + const VkAllocationCallbacks* m_pAllocationCallbacks; + const uint32_t m_FirstBlockCapacity; + VmaVector> m_ItemBlocks; + + ItemBlock& CreateNewBlock(); +}; + +#ifndef _VMA_POOL_ALLOCATOR_FUNCTIONS +template +VmaPoolAllocator::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) + : m_pAllocationCallbacks(pAllocationCallbacks), + m_FirstBlockCapacity(firstBlockCapacity), + m_ItemBlocks(VmaStlAllocator(pAllocationCallbacks)) +{ + VMA_ASSERT(m_FirstBlockCapacity > 1); +} + +template +VmaPoolAllocator::~VmaPoolAllocator() +{ + for (size_t i = m_ItemBlocks.size(); i--;) + vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity); + m_ItemBlocks.clear(); +} + +template +template T* VmaPoolAllocator::Alloc(Types&&... args) +{ + for (size_t i = m_ItemBlocks.size(); i--; ) + { + ItemBlock& block = m_ItemBlocks[i]; + // This block has some free items: Use first one. + if (block.FirstFreeIndex != UINT32_MAX) + { + Item* const pItem = &block.pItems[block.FirstFreeIndex]; + block.FirstFreeIndex = pItem->NextFreeIndex; + T* result = (T*)&pItem->Value; + new(result)T(std::forward(args)...); // Explicit constructor call. + return result; + } + } + + // No block has free item: Create new one and use it. + ItemBlock& newBlock = CreateNewBlock(); + Item* const pItem = &newBlock.pItems[0]; + newBlock.FirstFreeIndex = pItem->NextFreeIndex; + T* result = (T*)&pItem->Value; + new(result) T(std::forward(args)...); // Explicit constructor call. + return result; +} + +template +void VmaPoolAllocator::Free(T* ptr) +{ + // Search all memory blocks to find ptr. + for (size_t i = m_ItemBlocks.size(); i--; ) + { + ItemBlock& block = m_ItemBlocks[i]; + + // Casting to union. + Item* pItemPtr; + memcpy(&pItemPtr, &ptr, sizeof(pItemPtr)); + + // Check if pItemPtr is in address range of this block. + if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity)) + { + ptr->~T(); // Explicit destructor call. + const uint32_t index = static_cast(pItemPtr - block.pItems); + pItemPtr->NextFreeIndex = block.FirstFreeIndex; + block.FirstFreeIndex = index; + return; + } + } + VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool."); +} + +template +typename VmaPoolAllocator::ItemBlock& VmaPoolAllocator::CreateNewBlock() +{ + const uint32_t newBlockCapacity = m_ItemBlocks.empty() ? + m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2; + + const ItemBlock newBlock = + { + vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity), + newBlockCapacity, + 0 + }; + + m_ItemBlocks.push_back(newBlock); + + // Setup singly-linked list of all free items in this block. + for (uint32_t i = 0; i < newBlockCapacity - 1; ++i) + newBlock.pItems[i].NextFreeIndex = i + 1; + newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX; + return m_ItemBlocks.back(); +} +#endif // _VMA_POOL_ALLOCATOR_FUNCTIONS +#endif // _VMA_POOL_ALLOCATOR + +#ifndef _VMA_RAW_LIST +template +struct VmaListItem +{ + VmaListItem* pPrev; + VmaListItem* pNext; + T Value; +}; + +// Doubly linked list. +template +class VmaRawList +{ + VMA_CLASS_NO_COPY(VmaRawList) +public: + typedef VmaListItem ItemType; + + VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks); + // Intentionally not calling Clear, because that would be unnecessary + // computations to return all items to m_ItemAllocator as free. + ~VmaRawList() = default; + + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + + ItemType* Front() { return m_pFront; } + ItemType* Back() { return m_pBack; } + const ItemType* Front() const { return m_pFront; } + const ItemType* Back() const { return m_pBack; } + + ItemType* PushFront(); + ItemType* PushBack(); + ItemType* PushFront(const T& value); + ItemType* PushBack(const T& value); + void PopFront(); + void PopBack(); + + // Item can be null - it means PushBack. + ItemType* InsertBefore(ItemType* pItem); + // Item can be null - it means PushFront. + ItemType* InsertAfter(ItemType* pItem); + ItemType* InsertBefore(ItemType* pItem, const T& value); + ItemType* InsertAfter(ItemType* pItem, const T& value); + + void Clear(); + void Remove(ItemType* pItem); + +private: + const VkAllocationCallbacks* const m_pAllocationCallbacks; + VmaPoolAllocator m_ItemAllocator; + ItemType* m_pFront; + ItemType* m_pBack; + size_t m_Count; +}; + +#ifndef _VMA_RAW_LIST_FUNCTIONS +template +VmaRawList::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) + : m_pAllocationCallbacks(pAllocationCallbacks), + m_ItemAllocator(pAllocationCallbacks, 128), + m_pFront(VMA_NULL), + m_pBack(VMA_NULL), + m_Count(0) {} + +template +VmaListItem* VmaRawList::PushFront() +{ + ItemType* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pPrev = VMA_NULL; + if (IsEmpty()) + { + pNewItem->pNext = VMA_NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; + } + else + { + pNewItem->pNext = m_pFront; + m_pFront->pPrev = pNewItem; + m_pFront = pNewItem; + ++m_Count; + } + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushBack() +{ + ItemType* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pNext = VMA_NULL; + if(IsEmpty()) + { + pNewItem->pPrev = VMA_NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; + } + else + { + pNewItem->pPrev = m_pBack; + m_pBack->pNext = pNewItem; + m_pBack = pNewItem; + ++m_Count; + } + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushFront(const T& value) +{ + ItemType* const pNewItem = PushFront(); + pNewItem->Value = value; + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushBack(const T& value) +{ + ItemType* const pNewItem = PushBack(); + pNewItem->Value = value; + return pNewItem; +} + +template +void VmaRawList::PopFront() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const pFrontItem = m_pFront; + ItemType* const pNextItem = pFrontItem->pNext; + if (pNextItem != VMA_NULL) + { + pNextItem->pPrev = VMA_NULL; + } + m_pFront = pNextItem; + m_ItemAllocator.Free(pFrontItem); + --m_Count; +} + +template +void VmaRawList::PopBack() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const pBackItem = m_pBack; + ItemType* const pPrevItem = pBackItem->pPrev; + if(pPrevItem != VMA_NULL) + { + pPrevItem->pNext = VMA_NULL; + } + m_pBack = pPrevItem; + m_ItemAllocator.Free(pBackItem); + --m_Count; +} + +template +void VmaRawList::Clear() +{ + if (IsEmpty() == false) + { + ItemType* pItem = m_pBack; + while (pItem != VMA_NULL) + { + ItemType* const pPrevItem = pItem->pPrev; + m_ItemAllocator.Free(pItem); + pItem = pPrevItem; + } + m_pFront = VMA_NULL; + m_pBack = VMA_NULL; + m_Count = 0; + } +} + +template +void VmaRawList::Remove(ItemType* pItem) +{ + VMA_HEAVY_ASSERT(pItem != VMA_NULL); + VMA_HEAVY_ASSERT(m_Count > 0); + + if(pItem->pPrev != VMA_NULL) + { + pItem->pPrev->pNext = pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(m_pFront == pItem); + m_pFront = pItem->pNext; + } + + if(pItem->pNext != VMA_NULL) + { + pItem->pNext->pPrev = pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(m_pBack == pItem); + m_pBack = pItem->pPrev; + } + + m_ItemAllocator.Free(pItem); + --m_Count; +} + +template +VmaListItem* VmaRawList::InsertBefore(ItemType* pItem) +{ + if(pItem != VMA_NULL) + { + ItemType* const prevItem = pItem->pPrev; + ItemType* const newItem = m_ItemAllocator.Alloc(); + newItem->pPrev = prevItem; + newItem->pNext = pItem; + pItem->pPrev = newItem; + if(prevItem != VMA_NULL) + { + prevItem->pNext = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_pFront == pItem); + m_pFront = newItem; + } + ++m_Count; + return newItem; + } + else + return PushBack(); +} + +template +VmaListItem* VmaRawList::InsertAfter(ItemType* pItem) +{ + if(pItem != VMA_NULL) + { + ItemType* const nextItem = pItem->pNext; + ItemType* const newItem = m_ItemAllocator.Alloc(); + newItem->pNext = nextItem; + newItem->pPrev = pItem; + pItem->pNext = newItem; + if(nextItem != VMA_NULL) + { + nextItem->pPrev = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_pBack == pItem); + m_pBack = newItem; + } + ++m_Count; + return newItem; + } + else + return PushFront(); +} + +template +VmaListItem* VmaRawList::InsertBefore(ItemType* pItem, const T& value) +{ + ItemType* const newItem = InsertBefore(pItem); + newItem->Value = value; + return newItem; +} + +template +VmaListItem* VmaRawList::InsertAfter(ItemType* pItem, const T& value) +{ + ItemType* const newItem = InsertAfter(pItem); + newItem->Value = value; + return newItem; +} +#endif // _VMA_RAW_LIST_FUNCTIONS +#endif // _VMA_RAW_LIST + +#ifndef _VMA_LIST +template +class VmaList +{ + VMA_CLASS_NO_COPY(VmaList) +public: + class reverse_iterator; + class const_iterator; + class const_reverse_iterator; + + class iterator + { + friend class const_iterator; + friend class VmaList; + public: + iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + iterator operator++(int) { iterator result = *this; ++*this; return result; } + iterator operator--(int) { iterator result = *this; --*this; return result; } + + iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; } + iterator& operator--(); + + private: + VmaRawList* m_pList; + VmaListItem* m_pItem; + + iterator(VmaRawList* pList, VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + class reverse_iterator + { + friend class const_reverse_iterator; + friend class VmaList; + public: + reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + reverse_iterator operator++(int) { reverse_iterator result = *this; ++* this; return result; } + reverse_iterator operator--(int) { reverse_iterator result = *this; --* this; return result; } + + reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; } + reverse_iterator& operator--(); + + private: + VmaRawList* m_pList; + VmaListItem* m_pItem; + + reverse_iterator(VmaRawList* pList, VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + class const_iterator + { + friend class VmaList; + public: + const_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + const_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + iterator drop_const() { return { const_cast*>(m_pList), const_cast*>(m_pItem) }; } + + const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + const_iterator operator++(int) { const_iterator result = *this; ++* this; return result; } + const_iterator operator--(int) { const_iterator result = *this; --* this; return result; } + + const_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; } + const_iterator& operator--(); + + private: + const VmaRawList* m_pList; + const VmaListItem* m_pItem; + + const_iterator(const VmaRawList* pList, const VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + class const_reverse_iterator + { + friend class VmaList; + public: + const_reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + const_reverse_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + reverse_iterator drop_const() { return { const_cast*>(m_pList), const_cast*>(m_pItem) }; } + + const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + const_reverse_iterator operator++(int) { const_reverse_iterator result = *this; ++* this; return result; } + const_reverse_iterator operator--(int) { const_reverse_iterator result = *this; --* this; return result; } + + const_reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; } + const_reverse_iterator& operator--(); + + private: + const VmaRawList* m_pList; + const VmaListItem* m_pItem; + + const_reverse_iterator(const VmaRawList* pList, const VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + + VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) {} + + bool empty() const { return m_RawList.IsEmpty(); } + size_t size() const { return m_RawList.GetCount(); } + + iterator begin() { return iterator(&m_RawList, m_RawList.Front()); } + iterator end() { return iterator(&m_RawList, VMA_NULL); } + + const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); } + const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); } + + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + + reverse_iterator rbegin() { return reverse_iterator(&m_RawList, m_RawList.Back()); } + reverse_iterator rend() { return reverse_iterator(&m_RawList, VMA_NULL); } + + const_reverse_iterator crbegin() const { return const_reverse_iterator(&m_RawList, m_RawList.Back()); } + const_reverse_iterator crend() const { return const_reverse_iterator(&m_RawList, VMA_NULL); } + + const_reverse_iterator rbegin() const { return crbegin(); } + const_reverse_iterator rend() const { return crend(); } + + void push_back(const T& value) { m_RawList.PushBack(value); } + iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); } + + void clear() { m_RawList.Clear(); } + void erase(iterator it) { m_RawList.Remove(it.m_pItem); } + +private: + VmaRawList m_RawList; +}; + +#ifndef _VMA_LIST_FUNCTIONS +template +typename VmaList::iterator& VmaList::iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} + +template +typename VmaList::reverse_iterator& VmaList::reverse_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Front(); + } + return *this; +} + +template +typename VmaList::const_iterator& VmaList::const_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} + +template +typename VmaList::const_reverse_iterator& VmaList::const_reverse_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} +#endif // _VMA_LIST_FUNCTIONS +#endif // _VMA_LIST + +#ifndef _VMA_INTRUSIVE_LINKED_LIST +/* +Expected interface of ItemTypeTraits: +struct MyItemTypeTraits +{ + typedef MyItem ItemType; + static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; } + static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; } + static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; } + static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; } +}; +*/ +template +class VmaIntrusiveLinkedList +{ +public: + typedef typename ItemTypeTraits::ItemType ItemType; + static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); } + static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); } + + // Movable, not copyable. + VmaIntrusiveLinkedList() = default; + VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src); + VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList&) = delete; + VmaIntrusiveLinkedList& operator=(VmaIntrusiveLinkedList&& src); + VmaIntrusiveLinkedList& operator=(const VmaIntrusiveLinkedList&) = delete; + ~VmaIntrusiveLinkedList() { VMA_HEAVY_ASSERT(IsEmpty()); } + + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + ItemType* Front() { return m_Front; } + ItemType* Back() { return m_Back; } + const ItemType* Front() const { return m_Front; } + const ItemType* Back() const { return m_Back; } + + void PushBack(ItemType* item); + void PushFront(ItemType* item); + ItemType* PopBack(); + ItemType* PopFront(); + + // MyItem can be null - it means PushBack. + void InsertBefore(ItemType* existingItem, ItemType* newItem); + // MyItem can be null - it means PushFront. + void InsertAfter(ItemType* existingItem, ItemType* newItem); + void Remove(ItemType* item); + void RemoveAll(); + +private: + ItemType* m_Front = VMA_NULL; + ItemType* m_Back = VMA_NULL; + size_t m_Count = 0; +}; + +#ifndef _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS +template +VmaIntrusiveLinkedList::VmaIntrusiveLinkedList(VmaIntrusiveLinkedList&& src) + : m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count) +{ + src.m_Front = src.m_Back = VMA_NULL; + src.m_Count = 0; +} + +template +VmaIntrusiveLinkedList& VmaIntrusiveLinkedList::operator=(VmaIntrusiveLinkedList&& src) +{ + if (&src != this) + { + VMA_HEAVY_ASSERT(IsEmpty()); + m_Front = src.m_Front; + m_Back = src.m_Back; + m_Count = src.m_Count; + src.m_Front = src.m_Back = VMA_NULL; + src.m_Count = 0; + } + return *this; +} + +template +void VmaIntrusiveLinkedList::PushBack(ItemType* item) +{ + VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL); + if (IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessPrev(item) = m_Back; + ItemTypeTraits::AccessNext(m_Back) = item; + m_Back = item; + ++m_Count; + } +} + +template +void VmaIntrusiveLinkedList::PushFront(ItemType* item) +{ + VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL); + if (IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessNext(item) = m_Front; + ItemTypeTraits::AccessPrev(m_Front) = item; + m_Front = item; + ++m_Count; + } +} + +template +typename VmaIntrusiveLinkedList::ItemType* VmaIntrusiveLinkedList::PopBack() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const backItem = m_Back; + ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem); + if (prevItem != VMA_NULL) + { + ItemTypeTraits::AccessNext(prevItem) = VMA_NULL; + } + m_Back = prevItem; + --m_Count; + ItemTypeTraits::AccessPrev(backItem) = VMA_NULL; + ItemTypeTraits::AccessNext(backItem) = VMA_NULL; + return backItem; +} + +template +typename VmaIntrusiveLinkedList::ItemType* VmaIntrusiveLinkedList::PopFront() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const frontItem = m_Front; + ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem); + if (nextItem != VMA_NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL; + } + m_Front = nextItem; + --m_Count; + ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL; + ItemTypeTraits::AccessNext(frontItem) = VMA_NULL; + return frontItem; +} + +template +void VmaIntrusiveLinkedList::InsertBefore(ItemType* existingItem, ItemType* newItem) +{ + VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL); + if (existingItem != VMA_NULL) + { + ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem); + ItemTypeTraits::AccessPrev(newItem) = prevItem; + ItemTypeTraits::AccessNext(newItem) = existingItem; + ItemTypeTraits::AccessPrev(existingItem) = newItem; + if (prevItem != VMA_NULL) + { + ItemTypeTraits::AccessNext(prevItem) = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_Front == existingItem); + m_Front = newItem; + } + ++m_Count; + } + else + PushBack(newItem); +} + +template +void VmaIntrusiveLinkedList::InsertAfter(ItemType* existingItem, ItemType* newItem) +{ + VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL); + if (existingItem != VMA_NULL) + { + ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem); + ItemTypeTraits::AccessNext(newItem) = nextItem; + ItemTypeTraits::AccessPrev(newItem) = existingItem; + ItemTypeTraits::AccessNext(existingItem) = newItem; + if (nextItem != VMA_NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_Back == existingItem); + m_Back = newItem; + } + ++m_Count; + } + else + return PushFront(newItem); +} + +template +void VmaIntrusiveLinkedList::Remove(ItemType* item) +{ + VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0); + if (ItemTypeTraits::GetPrev(item) != VMA_NULL) + { + ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item); + } + else + { + VMA_HEAVY_ASSERT(m_Front == item); + m_Front = ItemTypeTraits::GetNext(item); + } + + if (ItemTypeTraits::GetNext(item) != VMA_NULL) + { + ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item); + } + else + { + VMA_HEAVY_ASSERT(m_Back == item); + m_Back = ItemTypeTraits::GetPrev(item); + } + ItemTypeTraits::AccessPrev(item) = VMA_NULL; + ItemTypeTraits::AccessNext(item) = VMA_NULL; + --m_Count; +} + +template +void VmaIntrusiveLinkedList::RemoveAll() +{ + if (!IsEmpty()) + { + ItemType* item = m_Back; + while (item != VMA_NULL) + { + ItemType* const prevItem = ItemTypeTraits::AccessPrev(item); + ItemTypeTraits::AccessPrev(item) = VMA_NULL; + ItemTypeTraits::AccessNext(item) = VMA_NULL; + item = prevItem; + } + m_Front = VMA_NULL; + m_Back = VMA_NULL; + m_Count = 0; + } +} +#endif // _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS +#endif // _VMA_INTRUSIVE_LINKED_LIST + +// Unused in this version. +#if 0 + +#ifndef _VMA_PAIR +template +struct VmaPair +{ + T1 first; + T2 second; + + VmaPair() : first(), second() {} + VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) {} +}; + +template +struct VmaPairFirstLess +{ + bool operator()(const VmaPair& lhs, const VmaPair& rhs) const + { + return lhs.first < rhs.first; + } + bool operator()(const VmaPair& lhs, const FirstT& rhsFirst) const + { + return lhs.first < rhsFirst; + } +}; +#endif // _VMA_PAIR + +#ifndef _VMA_MAP +/* Class compatible with subset of interface of std::unordered_map. +KeyT, ValueT must be POD because they will be stored in VmaVector. +*/ +template +class VmaMap +{ +public: + typedef VmaPair PairType; + typedef PairType* iterator; + + VmaMap(const VmaStlAllocator& allocator) : m_Vector(allocator) {} + + iterator begin() { return m_Vector.begin(); } + iterator end() { return m_Vector.end(); } + size_t size() { return m_Vector.size(); } + + void insert(const PairType& pair); + iterator find(const KeyT& key); + void erase(iterator it); + +private: + VmaVector< PairType, VmaStlAllocator> m_Vector; +}; + +#ifndef _VMA_MAP_FUNCTIONS +template +void VmaMap::insert(const PairType& pair) +{ + const size_t indexToInsert = VmaBinaryFindFirstNotLess( + m_Vector.data(), + m_Vector.data() + m_Vector.size(), + pair, + VmaPairFirstLess()) - m_Vector.data(); + VmaVectorInsert(m_Vector, indexToInsert, pair); +} + +template +VmaPair* VmaMap::find(const KeyT& key) +{ + PairType* it = VmaBinaryFindFirstNotLess( + m_Vector.data(), + m_Vector.data() + m_Vector.size(), + key, + VmaPairFirstLess()); + if ((it != m_Vector.end()) && (it->first == key)) + { + return it; + } + else + { + return m_Vector.end(); + } +} + +template +void VmaMap::erase(iterator it) +{ + VmaVectorRemove(m_Vector, it - m_Vector.begin()); +} +#endif // _VMA_MAP_FUNCTIONS +#endif // _VMA_MAP + +#endif // #if 0 + +#if !defined(_VMA_STRING_BUILDER) && VMA_STATS_STRING_ENABLED +class VmaStringBuilder +{ +public: + VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator(allocationCallbacks)) {} + ~VmaStringBuilder() = default; + + size_t GetLength() const { return m_Data.size(); } + const char* GetData() const { return m_Data.data(); } + void AddNewLine() { Add('\n'); } + void Add(char ch) { m_Data.push_back(ch); } + + void Add(const char* pStr); + void AddNumber(uint32_t num); + void AddNumber(uint64_t num); + void AddPointer(const void* ptr); + +private: + VmaVector> m_Data; +}; + +#ifndef _VMA_STRING_BUILDER_FUNCTIONS +void VmaStringBuilder::Add(const char* pStr) +{ + const size_t strLen = strlen(pStr); + if (strLen > 0) + { + const size_t oldCount = m_Data.size(); + m_Data.resize(oldCount + strLen); + memcpy(m_Data.data() + oldCount, pStr, strLen); + } +} + +void VmaStringBuilder::AddNumber(uint32_t num) +{ + char buf[11]; + buf[10] = '\0'; + char* p = &buf[10]; + do + { + *--p = '0' + (num % 10); + num /= 10; + } while (num); + Add(p); +} + +void VmaStringBuilder::AddNumber(uint64_t num) +{ + char buf[21]; + buf[20] = '\0'; + char* p = &buf[20]; + do + { + *--p = '0' + (num % 10); + num /= 10; + } while (num); + Add(p); +} + +void VmaStringBuilder::AddPointer(const void* ptr) +{ + char buf[21]; + VmaPtrToStr(buf, sizeof(buf), ptr); + Add(buf); +} +#endif //_VMA_STRING_BUILDER_FUNCTIONS +#endif // _VMA_STRING_BUILDER + +#if !defined(_VMA_JSON_WRITER) && VMA_STATS_STRING_ENABLED +/* +Allows to conveniently build a correct JSON document to be written to the +VmaStringBuilder passed to the constructor. +*/ +class VmaJsonWriter +{ + VMA_CLASS_NO_COPY(VmaJsonWriter) +public: + // sb - string builder to write the document to. Must remain alive for the whole lifetime of this object. + VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb); + ~VmaJsonWriter(); + + // Begins object by writing "{". + // Inside an object, you must call pairs of WriteString and a value, e.g.: + // j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject(); + // Will write: { "A": 1, "B": 2 } + void BeginObject(bool singleLine = false); + // Ends object by writing "}". + void EndObject(); + + // Begins array by writing "[". + // Inside an array, you can write a sequence of any values. + void BeginArray(bool singleLine = false); + // Ends array by writing "[". + void EndArray(); + + // Writes a string value inside "". + // pStr can contain any ANSI characters, including '"', new line etc. - they will be properly escaped. + void WriteString(const char* pStr); + + // Begins writing a string value. + // Call BeginString, ContinueString, ContinueString, ..., EndString instead of + // WriteString to conveniently build the string content incrementally, made of + // parts including numbers. + void BeginString(const char* pStr = VMA_NULL); + // Posts next part of an open string. + void ContinueString(const char* pStr); + // Posts next part of an open string. The number is converted to decimal characters. + void ContinueString(uint32_t n); + void ContinueString(uint64_t n); + void ContinueString_Size(size_t n); + // Posts next part of an open string. Pointer value is converted to characters + // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00 + void ContinueString_Pointer(const void* ptr); + // Ends writing a string value by writing '"'. + void EndString(const char* pStr = VMA_NULL); + + // Writes a number value. + void WriteNumber(uint32_t n); + void WriteNumber(uint64_t n); + void WriteSize(size_t n); + // Writes a boolean value - false or true. + void WriteBool(bool b); + // Writes a null value. + void WriteNull(); + +private: + enum COLLECTION_TYPE + { + COLLECTION_TYPE_OBJECT, + COLLECTION_TYPE_ARRAY, + }; + struct StackItem + { + COLLECTION_TYPE type; + uint32_t valueCount; + bool singleLineMode; + }; + + static const char* const INDENT; + + VmaStringBuilder& m_SB; + VmaVector< StackItem, VmaStlAllocator > m_Stack; + bool m_InsideString; + + // Write size_t for less than 64bits + void WriteSize(size_t n, std::integral_constant) { m_SB.AddNumber(static_cast(n)); } + // Write size_t for 64bits + void WriteSize(size_t n, std::integral_constant) { m_SB.AddNumber(static_cast(n)); } + + void BeginValue(bool isString); + void WriteIndent(bool oneLess = false); +}; +const char* const VmaJsonWriter::INDENT = " "; + +#ifndef _VMA_JSON_WRITER_FUNCTIONS +VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) + : m_SB(sb), + m_Stack(VmaStlAllocator(pAllocationCallbacks)), + m_InsideString(false) {} + +VmaJsonWriter::~VmaJsonWriter() +{ + VMA_ASSERT(!m_InsideString); + VMA_ASSERT(m_Stack.empty()); +} + +void VmaJsonWriter::BeginObject(bool singleLine) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(false); + m_SB.Add('{'); + + StackItem item; + item.type = COLLECTION_TYPE_OBJECT; + item.valueCount = 0; + item.singleLineMode = singleLine; + m_Stack.push_back(item); +} + +void VmaJsonWriter::EndObject() +{ + VMA_ASSERT(!m_InsideString); + + WriteIndent(true); + m_SB.Add('}'); + + VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT); + m_Stack.pop_back(); +} + +void VmaJsonWriter::BeginArray(bool singleLine) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(false); + m_SB.Add('['); + + StackItem item; + item.type = COLLECTION_TYPE_ARRAY; + item.valueCount = 0; + item.singleLineMode = singleLine; + m_Stack.push_back(item); +} + +void VmaJsonWriter::EndArray() +{ + VMA_ASSERT(!m_InsideString); + + WriteIndent(true); + m_SB.Add(']'); + + VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY); + m_Stack.pop_back(); +} + +void VmaJsonWriter::WriteString(const char* pStr) +{ + BeginString(pStr); + EndString(); +} + +void VmaJsonWriter::BeginString(const char* pStr) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(true); + m_SB.Add('"'); + m_InsideString = true; + if (pStr != VMA_NULL && pStr[0] != '\0') + { + ContinueString(pStr); + } +} + +void VmaJsonWriter::ContinueString(const char* pStr) +{ + VMA_ASSERT(m_InsideString); + + const size_t strLen = strlen(pStr); + for (size_t i = 0; i < strLen; ++i) + { + char ch = pStr[i]; + if (ch == '\\') + { + m_SB.Add("\\\\"); + } + else if (ch == '"') + { + m_SB.Add("\\\""); + } + else if (ch >= 32) + { + m_SB.Add(ch); + } + else switch (ch) + { + case '\b': + m_SB.Add("\\b"); + break; + case '\f': + m_SB.Add("\\f"); + break; + case '\n': + m_SB.Add("\\n"); + break; + case '\r': + m_SB.Add("\\r"); + break; + case '\t': + m_SB.Add("\\t"); + break; + default: + VMA_ASSERT(0 && "Character not currently supported."); + break; + } + } +} + +void VmaJsonWriter::ContinueString(uint32_t n) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::ContinueString(uint64_t n) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::ContinueString_Size(size_t n) +{ + VMA_ASSERT(m_InsideString); + // Fix for AppleClang incorrect type casting + // TODO: Change to if constexpr when C++17 used as minimal standard + WriteSize(n, std::is_same{}); +} + +void VmaJsonWriter::ContinueString_Pointer(const void* ptr) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddPointer(ptr); +} + +void VmaJsonWriter::EndString(const char* pStr) +{ + VMA_ASSERT(m_InsideString); + if (pStr != VMA_NULL && pStr[0] != '\0') + { + ContinueString(pStr); + } + m_SB.Add('"'); + m_InsideString = false; +} + +void VmaJsonWriter::WriteNumber(uint32_t n) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::WriteNumber(uint64_t n) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::WriteSize(size_t n) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + // Fix for AppleClang incorrect type casting + // TODO: Change to if constexpr when C++17 used as minimal standard + WriteSize(n, std::is_same{}); +} + +void VmaJsonWriter::WriteBool(bool b) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.Add(b ? "true" : "false"); +} + +void VmaJsonWriter::WriteNull() +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.Add("null"); +} + +void VmaJsonWriter::BeginValue(bool isString) +{ + if (!m_Stack.empty()) + { + StackItem& currItem = m_Stack.back(); + if (currItem.type == COLLECTION_TYPE_OBJECT && + currItem.valueCount % 2 == 0) + { + VMA_ASSERT(isString); + } + + if (currItem.type == COLLECTION_TYPE_OBJECT && + currItem.valueCount % 2 != 0) + { + m_SB.Add(": "); + } + else if (currItem.valueCount > 0) + { + m_SB.Add(", "); + WriteIndent(); + } + else + { + WriteIndent(); + } + ++currItem.valueCount; + } +} + +void VmaJsonWriter::WriteIndent(bool oneLess) +{ + if (!m_Stack.empty() && !m_Stack.back().singleLineMode) + { + m_SB.AddNewLine(); + + size_t count = m_Stack.size(); + if (count > 0 && oneLess) + { + --count; + } + for (size_t i = 0; i < count; ++i) + { + m_SB.Add(INDENT); + } + } +} +#endif // _VMA_JSON_WRITER_FUNCTIONS + +static void VmaPrintDetailedStatistics(VmaJsonWriter& json, const VmaDetailedStatistics& stat) +{ + json.BeginObject(); + + json.WriteString("BlockCount"); + json.WriteNumber(stat.statistics.blockCount); + json.WriteString("BlockBytes"); + json.WriteNumber(stat.statistics.blockBytes); + json.WriteString("AllocationCount"); + json.WriteNumber(stat.statistics.allocationCount); + json.WriteString("AllocationBytes"); + json.WriteNumber(stat.statistics.allocationBytes); + json.WriteString("UnusedRangeCount"); + json.WriteNumber(stat.unusedRangeCount); + + if (stat.statistics.allocationCount > 1) + { + json.WriteString("AllocationSizeMin"); + json.WriteNumber(stat.allocationSizeMin); + json.WriteString("AllocationSizeMax"); + json.WriteNumber(stat.allocationSizeMax); + } + if (stat.unusedRangeCount > 1) + { + json.WriteString("UnusedRangeSizeMin"); + json.WriteNumber(stat.unusedRangeSizeMin); + json.WriteString("UnusedRangeSizeMax"); + json.WriteNumber(stat.unusedRangeSizeMax); + } + json.EndObject(); +} +#endif // _VMA_JSON_WRITER + +#ifndef _VMA_MAPPING_HYSTERESIS + +class VmaMappingHysteresis +{ + VMA_CLASS_NO_COPY(VmaMappingHysteresis) +public: + VmaMappingHysteresis() = default; + + uint32_t GetExtraMapping() const { return m_ExtraMapping; } + + // Call when Map was called. + // Returns true if switched to extra +1 mapping reference count. + bool PostMap() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 0) + { + ++m_MajorCounter; + if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING) + { + m_ExtraMapping = 1; + m_MajorCounter = 0; + m_MinorCounter = 0; + return true; + } + } + else // m_ExtraMapping == 1 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + return false; + } + + // Call when Unmap was called. + void PostUnmap() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 0) + ++m_MajorCounter; + else // m_ExtraMapping == 1 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + } + + // Call when allocation was made from the memory block. + void PostAlloc() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 1) + ++m_MajorCounter; + else // m_ExtraMapping == 0 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + } + + // Call when allocation was freed from the memory block. + // Returns true if switched to extra -1 mapping reference count. + bool PostFree() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 1) + { + ++m_MajorCounter; + if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING && + m_MajorCounter > m_MinorCounter + 1) + { + m_ExtraMapping = 0; + m_MajorCounter = 0; + m_MinorCounter = 0; + return true; + } + } + else // m_ExtraMapping == 0 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + return false; + } + +private: + static const int32_t COUNTER_MIN_EXTRA_MAPPING = 7; + + uint32_t m_MinorCounter = 0; + uint32_t m_MajorCounter = 0; + uint32_t m_ExtraMapping = 0; // 0 or 1. + + void PostMinorCounter() + { + if(m_MinorCounter < m_MajorCounter) + { + ++m_MinorCounter; + } + else if(m_MajorCounter > 0) + { + --m_MajorCounter; + --m_MinorCounter; + } + } +}; + +#endif // _VMA_MAPPING_HYSTERESIS + +#ifndef _VMA_DEVICE_MEMORY_BLOCK +/* +Represents a single block of device memory (`VkDeviceMemory`) with all the +data about its regions (aka suballocations, #VmaAllocation), assigned and free. + +Thread-safety: +- Access to m_pMetadata must be externally synchronized. +- Map, Unmap, Bind* are synchronized internally. +*/ +class VmaDeviceMemoryBlock +{ + VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock) +public: + VmaBlockMetadata* m_pMetadata; + + VmaDeviceMemoryBlock(VmaAllocator hAllocator); + ~VmaDeviceMemoryBlock(); + + // Always call after construction. + void Init( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t newMemoryTypeIndex, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + uint32_t id, + uint32_t algorithm, + VkDeviceSize bufferImageGranularity); + // Always call before destruction. + void Destroy(VmaAllocator allocator); + + VmaPool GetParentPool() const { return m_hParentPool; } + VkDeviceMemory GetDeviceMemory() const { return m_hMemory; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + uint32_t GetId() const { return m_Id; } + void* GetMappedData() const { return m_pMappedData; } + uint32_t GetMapRefCount() const { return m_MapCount; } + + // Call when allocation/free was made from m_pMetadata. + // Used for m_MappingHysteresis. + void PostAlloc() { m_MappingHysteresis.PostAlloc(); } + void PostFree(VmaAllocator hAllocator); + + // Validates all data structures inside this object. If not valid, returns false. + bool Validate() const; + VkResult CheckCorruption(VmaAllocator hAllocator); + + // ppData can be null. + VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData); + void Unmap(VmaAllocator hAllocator, uint32_t count); + + VkResult WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); + VkResult ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); + + VkResult BindBufferMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext); + VkResult BindImageMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext); + +private: + VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. + uint32_t m_MemoryTypeIndex; + uint32_t m_Id; + VkDeviceMemory m_hMemory; + + /* + Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory. + Also protects m_MapCount, m_pMappedData. + Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex. + */ + VMA_MUTEX m_MapAndBindMutex; + VmaMappingHysteresis m_MappingHysteresis; + uint32_t m_MapCount; + void* m_pMappedData; +}; +#endif // _VMA_DEVICE_MEMORY_BLOCK + +#ifndef _VMA_ALLOCATION_T +struct VmaAllocation_T +{ + friend struct VmaDedicatedAllocationListItemTraits; + + enum FLAGS + { + FLAG_PERSISTENT_MAP = 0x01, + FLAG_MAPPING_ALLOWED = 0x02, + }; + +public: + enum ALLOCATION_TYPE + { + ALLOCATION_TYPE_NONE, + ALLOCATION_TYPE_BLOCK, + ALLOCATION_TYPE_DEDICATED, + }; + + // This struct is allocated using VmaPoolAllocator. + VmaAllocation_T(bool mappingAllowed); + ~VmaAllocation_T(); + + void InitBlockAllocation( + VmaDeviceMemoryBlock* block, + VmaAllocHandle allocHandle, + VkDeviceSize alignment, + VkDeviceSize size, + uint32_t memoryTypeIndex, + VmaSuballocationType suballocationType, + bool mapped); + // pMappedData not null means allocation is created with MAPPED flag. + void InitDedicatedAllocation( + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceMemory hMemory, + VmaSuballocationType suballocationType, + void* pMappedData, + VkDeviceSize size); + + ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; } + VkDeviceSize GetAlignment() const { return m_Alignment; } + VkDeviceSize GetSize() const { return m_Size; } + void* GetUserData() const { return m_pUserData; } + const char* GetName() const { return m_pName; } + VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; } + + VmaDeviceMemoryBlock* GetBlock() const { VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); return m_BlockAllocation.m_Block; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + bool IsPersistentMap() const { return (m_Flags & FLAG_PERSISTENT_MAP) != 0; } + bool IsMappingAllowed() const { return (m_Flags & FLAG_MAPPING_ALLOWED) != 0; } + + void SetUserData(VmaAllocator hAllocator, void* pUserData) { m_pUserData = pUserData; } + void SetName(VmaAllocator hAllocator, const char* pName); + void FreeName(VmaAllocator hAllocator); + uint8_t SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation); + VmaAllocHandle GetAllocHandle() const; + VkDeviceSize GetOffset() const; + VmaPool GetParentPool() const; + VkDeviceMemory GetMemory() const; + void* GetMappedData() const; + + void BlockAllocMap(); + void BlockAllocUnmap(); + VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData); + void DedicatedAllocUnmap(VmaAllocator hAllocator); + +#if VMA_STATS_STRING_ENABLED + uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; } + + void InitBufferImageUsage(uint32_t bufferImageUsage); + void PrintParameters(class VmaJsonWriter& json) const; +#endif + +private: + // Allocation out of VmaDeviceMemoryBlock. + struct BlockAllocation + { + VmaDeviceMemoryBlock* m_Block; + VmaAllocHandle m_AllocHandle; + }; + // Allocation for an object that has its own private VkDeviceMemory. + struct DedicatedAllocation + { + VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. + VkDeviceMemory m_hMemory; + void* m_pMappedData; // Not null means memory is mapped. + VmaAllocation_T* m_Prev; + VmaAllocation_T* m_Next; + }; + union + { + // Allocation out of VmaDeviceMemoryBlock. + BlockAllocation m_BlockAllocation; + // Allocation for an object that has its own private VkDeviceMemory. + DedicatedAllocation m_DedicatedAllocation; + }; + + VkDeviceSize m_Alignment; + VkDeviceSize m_Size; + void* m_pUserData; + char* m_pName; + uint32_t m_MemoryTypeIndex; + uint8_t m_Type; // ALLOCATION_TYPE + uint8_t m_SuballocationType; // VmaSuballocationType + // Reference counter for vmaMapMemory()/vmaUnmapMemory(). + uint8_t m_MapCount; + uint8_t m_Flags; // enum FLAGS +#if VMA_STATS_STRING_ENABLED + uint32_t m_BufferImageUsage; // 0 if unknown. +#endif +}; +#endif // _VMA_ALLOCATION_T + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS +struct VmaDedicatedAllocationListItemTraits +{ + typedef VmaAllocation_T ItemType; + + static ItemType* GetPrev(const ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Prev; + } + static ItemType* GetNext(const ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Next; + } + static ItemType*& AccessPrev(ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Prev; + } + static ItemType*& AccessNext(ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Next; + } +}; +#endif // _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST +/* +Stores linked list of VmaAllocation_T objects. +Thread-safe, synchronized internally. +*/ +class VmaDedicatedAllocationList +{ +public: + VmaDedicatedAllocationList() {} + ~VmaDedicatedAllocationList(); + + void Init(bool useMutex) { m_UseMutex = useMutex; } + bool Validate(); + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats); + void AddStatistics(VmaStatistics& inoutStats); +#if VMA_STATS_STRING_ENABLED + // Writes JSON array with the list of allocations. + void BuildStatsString(VmaJsonWriter& json); +#endif + + bool IsEmpty(); + void Register(VmaAllocation alloc); + void Unregister(VmaAllocation alloc); + +private: + typedef VmaIntrusiveLinkedList DedicatedAllocationLinkedList; + + bool m_UseMutex = true; + VMA_RW_MUTEX m_Mutex; + DedicatedAllocationLinkedList m_AllocationList; +}; + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS + +VmaDedicatedAllocationList::~VmaDedicatedAllocationList() +{ + VMA_HEAVY_ASSERT(Validate()); + + if (!m_AllocationList.IsEmpty()) + { + VMA_ASSERT(false && "Unfreed dedicated allocations found!"); + } +} + +bool VmaDedicatedAllocationList::Validate() +{ + const size_t declaredCount = m_AllocationList.GetCount(); + size_t actualCount = 0; + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + for (VmaAllocation alloc = m_AllocationList.Front(); + alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) + { + ++actualCount; + } + VMA_VALIDATE(actualCount == declaredCount); + + return true; +} + +void VmaDedicatedAllocationList::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) +{ + for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item)) + { + const VkDeviceSize size = item->GetSize(); + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += size; + VmaAddDetailedStatisticsAllocation(inoutStats, item->GetSize()); + } +} + +void VmaDedicatedAllocationList::AddStatistics(VmaStatistics& inoutStats) +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + + const uint32_t allocCount = (uint32_t)m_AllocationList.GetCount(); + inoutStats.blockCount += allocCount; + inoutStats.allocationCount += allocCount; + + for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item)) + { + const VkDeviceSize size = item->GetSize(); + inoutStats.blockBytes += size; + inoutStats.allocationBytes += size; + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json) +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + json.BeginArray(); + for (VmaAllocation alloc = m_AllocationList.Front(); + alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) + { + json.BeginObject(true); + alloc->PrintParameters(json); + json.EndObject(); + } + json.EndArray(); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaDedicatedAllocationList::IsEmpty() +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + return m_AllocationList.IsEmpty(); +} + +void VmaDedicatedAllocationList::Register(VmaAllocation alloc) +{ + VmaMutexLockWrite lock(m_Mutex, m_UseMutex); + m_AllocationList.PushBack(alloc); +} + +void VmaDedicatedAllocationList::Unregister(VmaAllocation alloc) +{ + VmaMutexLockWrite lock(m_Mutex, m_UseMutex); + m_AllocationList.Remove(alloc); +} +#endif // _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS +#endif // _VMA_DEDICATED_ALLOCATION_LIST + +#ifndef _VMA_SUBALLOCATION +/* +Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as +allocated memory block or free. +*/ +struct VmaSuballocation +{ + VkDeviceSize offset; + VkDeviceSize size; + void* userData; + VmaSuballocationType type; +}; + +// Comparator for offsets. +struct VmaSuballocationOffsetLess +{ + bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const + { + return lhs.offset < rhs.offset; + } +}; + +struct VmaSuballocationOffsetGreater +{ + bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const + { + return lhs.offset > rhs.offset; + } +}; + +struct VmaSuballocationItemSizeLess +{ + bool operator()(const VmaSuballocationList::iterator lhs, + const VmaSuballocationList::iterator rhs) const + { + return lhs->size < rhs->size; + } + + bool operator()(const VmaSuballocationList::iterator lhs, + VkDeviceSize rhsSize) const + { + return lhs->size < rhsSize; + } +}; +#endif // _VMA_SUBALLOCATION + +#ifndef _VMA_ALLOCATION_REQUEST +/* +Parameters of planned allocation inside a VmaDeviceMemoryBlock. +item points to a FREE suballocation. +*/ +struct VmaAllocationRequest +{ + VmaAllocHandle allocHandle; + VkDeviceSize size; + VmaSuballocationList::iterator item; + void* customData; + uint64_t algorithmData; + VmaAllocationRequestType type; +}; +#endif // _VMA_ALLOCATION_REQUEST + +#ifndef _VMA_BLOCK_METADATA +/* +Data structure used for bookkeeping of allocations and unused ranges of memory +in a single VkDeviceMemory block. +*/ +class VmaBlockMetadata +{ +public: + // pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object. + VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata() = default; + + virtual void Init(VkDeviceSize size) { m_Size = size; } + bool IsVirtual() const { return m_IsVirtual; } + VkDeviceSize GetSize() const { return m_Size; } + + // Validates all data structures inside this object. If not valid, returns false. + virtual bool Validate() const = 0; + virtual size_t GetAllocationCount() const = 0; + virtual size_t GetFreeRegionsCount() const = 0; + virtual VkDeviceSize GetSumFreeSize() const = 0; + // Returns true if this block is empty - contains only single free suballocation. + virtual bool IsEmpty() const = 0; + virtual void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) = 0; + virtual VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const = 0; + virtual void* GetAllocationUserData(VmaAllocHandle allocHandle) const = 0; + + virtual VmaAllocHandle GetAllocationListBegin() const = 0; + virtual VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const = 0; + virtual VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const = 0; + + // Shouldn't modify blockCount. + virtual void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const = 0; + virtual void AddStatistics(VmaStatistics& inoutStats) const = 0; + +#if VMA_STATS_STRING_ENABLED + virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0; +#endif + + // Tries to find a place for suballocation with given parameters inside this block. + // If succeeded, fills pAllocationRequest and returns true. + // If failed, returns false. + virtual bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags. + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) = 0; + + virtual VkResult CheckCorruption(const void* pBlockData) = 0; + + // Makes actual allocation based on request. Request must already be checked and valid. + virtual void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) = 0; + + // Frees suballocation assigned to given memory region. + virtual void Free(VmaAllocHandle allocHandle) = 0; + + // Frees all allocations. + // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations! + virtual void Clear() = 0; + + virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0; + virtual void DebugLogAllAllocations() const = 0; + +protected: + const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; } + VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } + VkDeviceSize GetDebugMargin() const { return IsVirtual() ? 0 : VMA_DEBUG_MARGIN; } + + void DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const; +#if VMA_STATS_STRING_ENABLED + // mapRefCount == UINT32_MAX means unspecified. + void PrintDetailedMap_Begin(class VmaJsonWriter& json, + VkDeviceSize unusedBytes, + size_t allocationCount, + size_t unusedRangeCount) const; + void PrintDetailedMap_Allocation(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size, void* userData) const; + void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, + VkDeviceSize offset, + VkDeviceSize size) const; + void PrintDetailedMap_End(class VmaJsonWriter& json) const; +#endif + +private: + VkDeviceSize m_Size; + const VkAllocationCallbacks* m_pAllocationCallbacks; + const VkDeviceSize m_BufferImageGranularity; + const bool m_IsVirtual; +}; + +#ifndef _VMA_BLOCK_METADATA_FUNCTIONS +VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : m_Size(0), + m_pAllocationCallbacks(pAllocationCallbacks), + m_BufferImageGranularity(bufferImageGranularity), + m_IsVirtual(isVirtual) {} + +void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const +{ + if (IsVirtual()) + { + VMA_DEBUG_LOG("UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; UserData: %p", offset, size, userData); + } + else + { + VMA_ASSERT(userData != VMA_NULL); + VmaAllocation allocation = reinterpret_cast(userData); + + userData = allocation->GetUserData(); + const char* name = allocation->GetName(); + +#if VMA_STATS_STRING_ENABLED + VMA_DEBUG_LOG("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %s; Usage: %u", + offset, size, userData, name ? name : "vma_empty", + VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()], + allocation->GetBufferImageUsage()); +#else + VMA_DEBUG_LOG("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %u", + offset, size, userData, name ? name : "vma_empty", + (uint32_t)allocation->GetSuballocationType()); +#endif // VMA_STATS_STRING_ENABLED + } + +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json, + VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const +{ + json.WriteString("TotalBytes"); + json.WriteNumber(GetSize()); + + json.WriteString("UnusedBytes"); + json.WriteSize(unusedBytes); + + json.WriteString("Allocations"); + json.WriteSize(allocationCount); + + json.WriteString("UnusedRanges"); + json.WriteSize(unusedRangeCount); + + json.WriteString("Suballocations"); + json.BeginArray(); +} + +void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size, void* userData) const +{ + json.BeginObject(true); + + json.WriteString("Offset"); + json.WriteNumber(offset); + + if (IsVirtual()) + { + json.WriteString("Size"); + json.WriteNumber(size); + if (userData) + { + json.WriteString("CustomData"); + json.BeginString(); + json.ContinueString_Pointer(userData); + json.EndString(); + } + } + else + { + ((VmaAllocation)userData)->PrintParameters(json); + } + + json.EndObject(); +} + +void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size) const +{ + json.BeginObject(true); + + json.WriteString("Offset"); + json.WriteNumber(offset); + + json.WriteString("Type"); + json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]); + + json.WriteString("Size"); + json.WriteNumber(size); + + json.EndObject(); +} + +void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const +{ + json.EndArray(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_BLOCK_METADATA_FUNCTIONS +#endif // _VMA_BLOCK_METADATA + +#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY +// Before deleting object of this class remember to call 'Destroy()' +class VmaBlockBufferImageGranularity final +{ +public: + struct ValidationContext + { + const VkAllocationCallbacks* allocCallbacks; + uint16_t* pageAllocs; + }; + + VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity); + ~VmaBlockBufferImageGranularity(); + + bool IsEnabled() const { return m_BufferImageGranularity > MAX_LOW_BUFFER_IMAGE_GRANULARITY; } + + void Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size); + // Before destroying object you must call free it's memory + void Destroy(const VkAllocationCallbacks* pAllocationCallbacks); + + void RoundupAllocRequest(VmaSuballocationType allocType, + VkDeviceSize& inOutAllocSize, + VkDeviceSize& inOutAllocAlignment) const; + + bool CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset, + VkDeviceSize allocSize, + VkDeviceSize blockOffset, + VkDeviceSize blockSize, + VmaSuballocationType allocType) const; + + void AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size); + void FreePages(VkDeviceSize offset, VkDeviceSize size); + void Clear(); + + ValidationContext StartValidation(const VkAllocationCallbacks* pAllocationCallbacks, + bool isVirutal) const; + bool Validate(ValidationContext& ctx, VkDeviceSize offset, VkDeviceSize size) const; + bool FinishValidation(ValidationContext& ctx) const; + +private: + static const uint16_t MAX_LOW_BUFFER_IMAGE_GRANULARITY = 256; + + struct RegionInfo + { + uint8_t allocType; + uint16_t allocCount; + }; + + VkDeviceSize m_BufferImageGranularity; + uint32_t m_RegionCount; + RegionInfo* m_RegionInfo; + + uint32_t GetStartPage(VkDeviceSize offset) const { return OffsetToPageIndex(offset & ~(m_BufferImageGranularity - 1)); } + uint32_t GetEndPage(VkDeviceSize offset, VkDeviceSize size) const { return OffsetToPageIndex((offset + size - 1) & ~(m_BufferImageGranularity - 1)); } + + uint32_t OffsetToPageIndex(VkDeviceSize offset) const; + void AllocPage(RegionInfo& page, uint8_t allocType); +}; + +#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS +VmaBlockBufferImageGranularity::VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity) + : m_BufferImageGranularity(bufferImageGranularity), + m_RegionCount(0), + m_RegionInfo(VMA_NULL) {} + +VmaBlockBufferImageGranularity::~VmaBlockBufferImageGranularity() +{ + VMA_ASSERT(m_RegionInfo == VMA_NULL && "Free not called before destroying object!"); +} + +void VmaBlockBufferImageGranularity::Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size) +{ + if (IsEnabled()) + { + m_RegionCount = static_cast(VmaDivideRoundingUp(size, m_BufferImageGranularity)); + m_RegionInfo = vma_new_array(pAllocationCallbacks, RegionInfo, m_RegionCount); + memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo)); + } +} + +void VmaBlockBufferImageGranularity::Destroy(const VkAllocationCallbacks* pAllocationCallbacks) +{ + if (m_RegionInfo) + { + vma_delete_array(pAllocationCallbacks, m_RegionInfo, m_RegionCount); + m_RegionInfo = VMA_NULL; + } +} + +void VmaBlockBufferImageGranularity::RoundupAllocRequest(VmaSuballocationType allocType, + VkDeviceSize& inOutAllocSize, + VkDeviceSize& inOutAllocAlignment) const +{ + if (m_BufferImageGranularity > 1 && + m_BufferImageGranularity <= MAX_LOW_BUFFER_IMAGE_GRANULARITY) + { + if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL) + { + inOutAllocAlignment = VMA_MAX(inOutAllocAlignment, m_BufferImageGranularity); + inOutAllocSize = VmaAlignUp(inOutAllocSize, m_BufferImageGranularity); + } + } +} + +bool VmaBlockBufferImageGranularity::CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset, + VkDeviceSize allocSize, + VkDeviceSize blockOffset, + VkDeviceSize blockSize, + VmaSuballocationType allocType) const +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(inOutAllocOffset); + if (m_RegionInfo[startPage].allocCount > 0 && + VmaIsBufferImageGranularityConflict(static_cast(m_RegionInfo[startPage].allocType), allocType)) + { + inOutAllocOffset = VmaAlignUp(inOutAllocOffset, m_BufferImageGranularity); + if (blockSize < allocSize + inOutAllocOffset - blockOffset) + return true; + ++startPage; + } + uint32_t endPage = GetEndPage(inOutAllocOffset, allocSize); + if (endPage != startPage && + m_RegionInfo[endPage].allocCount > 0 && + VmaIsBufferImageGranularityConflict(static_cast(m_RegionInfo[endPage].allocType), allocType)) + { + return true; + } + } + return false; +} + +void VmaBlockBufferImageGranularity::AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size) +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(offset); + AllocPage(m_RegionInfo[startPage], allocType); + + uint32_t endPage = GetEndPage(offset, size); + if (startPage != endPage) + AllocPage(m_RegionInfo[endPage], allocType); + } +} + +void VmaBlockBufferImageGranularity::FreePages(VkDeviceSize offset, VkDeviceSize size) +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(offset); + --m_RegionInfo[startPage].allocCount; + if (m_RegionInfo[startPage].allocCount == 0) + m_RegionInfo[startPage].allocType = VMA_SUBALLOCATION_TYPE_FREE; + uint32_t endPage = GetEndPage(offset, size); + if (startPage != endPage) + { + --m_RegionInfo[endPage].allocCount; + if (m_RegionInfo[endPage].allocCount == 0) + m_RegionInfo[endPage].allocType = VMA_SUBALLOCATION_TYPE_FREE; + } + } +} + +void VmaBlockBufferImageGranularity::Clear() +{ + if (m_RegionInfo) + memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo)); +} + +VmaBlockBufferImageGranularity::ValidationContext VmaBlockBufferImageGranularity::StartValidation( + const VkAllocationCallbacks* pAllocationCallbacks, bool isVirutal) const +{ + ValidationContext ctx{ pAllocationCallbacks, VMA_NULL }; + if (!isVirutal && IsEnabled()) + { + ctx.pageAllocs = vma_new_array(pAllocationCallbacks, uint16_t, m_RegionCount); + memset(ctx.pageAllocs, 0, m_RegionCount * sizeof(uint16_t)); + } + return ctx; +} + +bool VmaBlockBufferImageGranularity::Validate(ValidationContext& ctx, + VkDeviceSize offset, VkDeviceSize size) const +{ + if (IsEnabled()) + { + uint32_t start = GetStartPage(offset); + ++ctx.pageAllocs[start]; + VMA_VALIDATE(m_RegionInfo[start].allocCount > 0); + + uint32_t end = GetEndPage(offset, size); + if (start != end) + { + ++ctx.pageAllocs[end]; + VMA_VALIDATE(m_RegionInfo[end].allocCount > 0); + } + } + return true; +} + +bool VmaBlockBufferImageGranularity::FinishValidation(ValidationContext& ctx) const +{ + // Check proper page structure + if (IsEnabled()) + { + VMA_ASSERT(ctx.pageAllocs != VMA_NULL && "Validation context not initialized!"); + + for (uint32_t page = 0; page < m_RegionCount; ++page) + { + VMA_VALIDATE(ctx.pageAllocs[page] == m_RegionInfo[page].allocCount); + } + vma_delete_array(ctx.allocCallbacks, ctx.pageAllocs, m_RegionCount); + ctx.pageAllocs = VMA_NULL; + } + return true; +} + +uint32_t VmaBlockBufferImageGranularity::OffsetToPageIndex(VkDeviceSize offset) const +{ + return static_cast(offset >> VMA_BITSCAN_MSB(m_BufferImageGranularity)); +} + +void VmaBlockBufferImageGranularity::AllocPage(RegionInfo& page, uint8_t allocType) +{ + // When current alloc type is free then it can be overriden by new type + if (page.allocCount == 0 || (page.allocCount > 0 && page.allocType == VMA_SUBALLOCATION_TYPE_FREE)) + page.allocType = allocType; + + ++page.allocCount; +} +#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS +#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY + +#if 0 +#ifndef _VMA_BLOCK_METADATA_GENERIC +class VmaBlockMetadata_Generic : public VmaBlockMetadata +{ + friend class VmaDefragmentationAlgorithm_Generic; + friend class VmaDefragmentationAlgorithm_Fast; + VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic) +public: + VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_Generic() = default; + + size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; } + VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; } + bool IsEmpty() const override { return (m_Suballocations.size() == 1) && (m_FreeCount == 1); } + void Free(VmaAllocHandle allocHandle) override { FreeSuballocation(FindAtOffset((VkDeviceSize)allocHandle - 1)); } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }; + + void Init(VkDeviceSize size) override; + bool Validate() const override; + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; + void AddStatistics(VmaStatistics& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + VkResult CheckCorruption(const void* pBlockData) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void* GetAllocationUserData(VmaAllocHandle allocHandle) const override; + VmaAllocHandle GetAllocationListBegin() const override; + VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + void DebugLogAllAllocations() const override; + +private: + uint32_t m_FreeCount; + VkDeviceSize m_SumFreeSize; + VmaSuballocationList m_Suballocations; + // Suballocations that are free. Sorted by size, ascending. + VmaVector> m_FreeSuballocationsBySize; + + VkDeviceSize AlignAllocationSize(VkDeviceSize size) const { return IsVirtual() ? size : VmaAlignUp(size, (VkDeviceSize)16); } + + VmaSuballocationList::iterator FindAtOffset(VkDeviceSize offset) const; + bool ValidateFreeSuballocationList() const; + + // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem. + // If yes, fills pOffset and returns true. If no, returns false. + bool CheckAllocation( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaSuballocationList::const_iterator suballocItem, + VmaAllocHandle* pAllocHandle) const; + + // Given free suballocation, it merges it with following one, which must also be free. + void MergeFreeWithNext(VmaSuballocationList::iterator item); + // Releases given suballocation, making it free. + // Merges it with adjacent free suballocations if applicable. + // Returns iterator to new free suballocation at this place. + VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem); + // Given free suballocation, it inserts it into sorted list of + // m_FreeSuballocationsBySize if it is suitable. + void RegisterFreeSuballocation(VmaSuballocationList::iterator item); + // Given free suballocation, it removes it from sorted list of + // m_FreeSuballocationsBySize if it is suitable. + void UnregisterFreeSuballocation(VmaSuballocationList::iterator item); +}; + +#ifndef _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS +VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_FreeCount(0), + m_SumFreeSize(0), + m_Suballocations(VmaStlAllocator(pAllocationCallbacks)), + m_FreeSuballocationsBySize(VmaStlAllocator(pAllocationCallbacks)) {} + +void VmaBlockMetadata_Generic::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + m_FreeCount = 1; + m_SumFreeSize = size; + + VmaSuballocation suballoc = {}; + suballoc.offset = 0; + suballoc.size = size; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + + m_Suballocations.push_back(suballoc); + m_FreeSuballocationsBySize.push_back(m_Suballocations.begin()); +} + +bool VmaBlockMetadata_Generic::Validate() const +{ + VMA_VALIDATE(!m_Suballocations.empty()); + + // Expected offset of new suballocation as calculated from previous ones. + VkDeviceSize calculatedOffset = 0; + // Expected number of free suballocations as calculated from traversing their list. + uint32_t calculatedFreeCount = 0; + // Expected sum size of free suballocations as calculated from traversing their list. + VkDeviceSize calculatedSumFreeSize = 0; + // Expected number of free suballocations that should be registered in + // m_FreeSuballocationsBySize calculated from traversing their list. + size_t freeSuballocationsToRegister = 0; + // True if previous visited suballocation was free. + bool prevFree = false; + + const VkDeviceSize debugMargin = GetDebugMargin(); + + for (const auto& subAlloc : m_Suballocations) + { + // Actual offset of this suballocation doesn't match expected one. + VMA_VALIDATE(subAlloc.offset == calculatedOffset); + + const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE); + // Two adjacent free suballocations are invalid. They should be merged. + VMA_VALIDATE(!prevFree || !currFree); + + VmaAllocation alloc = (VmaAllocation)subAlloc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + + if (currFree) + { + calculatedSumFreeSize += subAlloc.size; + ++calculatedFreeCount; + ++freeSuballocationsToRegister; + + // Margin required between allocations - every free space must be at least that large. + VMA_VALIDATE(subAlloc.size >= debugMargin); + } + else + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == subAlloc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == subAlloc.size); + } + + // Margin required between allocations - previous allocation must be free. + VMA_VALIDATE(debugMargin == 0 || prevFree); + } + + calculatedOffset += subAlloc.size; + prevFree = currFree; + } + + // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't + // match expected one. + VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister); + + VkDeviceSize lastSize = 0; + for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i) + { + VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i]; + + // Only free suballocations can be registered in m_FreeSuballocationsBySize. + VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE); + // They must be sorted by size ascending. + VMA_VALIDATE(suballocItem->size >= lastSize); + + lastSize = suballocItem->size; + } + + // Check if totals match calculated values. + VMA_VALIDATE(ValidateFreeSuballocationList()); + VMA_VALIDATE(calculatedOffset == GetSize()); + VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize); + VMA_VALIDATE(calculatedFreeCount == m_FreeCount); + + return true; +} + +void VmaBlockMetadata_Generic::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const +{ + const uint32_t rangeCount = (uint32_t)m_Suballocations.size(); + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += GetSize(); + + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + else + VmaAddDetailedStatisticsUnusedRange(inoutStats, suballoc.size); + } +} + +void VmaBlockMetadata_Generic::AddStatistics(VmaStatistics& inoutStats) const +{ + inoutStats.blockCount++; + inoutStats.allocationCount += (uint32_t)m_Suballocations.size() - m_FreeCount; + inoutStats.blockBytes += GetSize(); + inoutStats.allocationBytes += GetSize() - m_SumFreeSize; +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const +{ + PrintDetailedMap_Begin(json, + m_SumFreeSize, // unusedBytes + m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount + m_FreeCount, // unusedRangeCount + mapRefCount); + + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE) + { + PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size); + } + else + { + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + } + } + + PrintDetailedMap_End(json); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Generic::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(!upperAddress); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(pAllocationRequest != VMA_NULL); + VMA_HEAVY_ASSERT(Validate()); + + allocSize = AlignAllocationSize(allocSize); + + pAllocationRequest->type = VmaAllocationRequestType::Normal; + pAllocationRequest->size = allocSize; + + const VkDeviceSize debugMargin = GetDebugMargin(); + + // There is not enough total free space in this block to fulfill the request: Early return. + if (m_SumFreeSize < allocSize + debugMargin) + { + return false; + } + + // New algorithm, efficiently searching freeSuballocationsBySize. + const size_t freeSuballocCount = m_FreeSuballocationsBySize.size(); + if (freeSuballocCount > 0) + { + if (strategy == 0 || + strategy == VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT) + { + // Find first free suballocation with size not less than allocSize + debugMargin. + VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( + m_FreeSuballocationsBySize.data(), + m_FreeSuballocationsBySize.data() + freeSuballocCount, + allocSize + debugMargin, + VmaSuballocationItemSizeLess()); + size_t index = it - m_FreeSuballocationsBySize.data(); + for (; index < freeSuballocCount; ++index) + { + if (CheckAllocation( + allocSize, + allocAlignment, + allocType, + m_FreeSuballocationsBySize[index], + &pAllocationRequest->allocHandle)) + { + pAllocationRequest->item = m_FreeSuballocationsBySize[index]; + return true; + } + } + } + else if (strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET) + { + for (VmaSuballocationList::iterator it = m_Suballocations.begin(); + it != m_Suballocations.end(); + ++it) + { + if (it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation( + allocSize, + allocAlignment, + allocType, + it, + &pAllocationRequest->allocHandle)) + { + pAllocationRequest->item = it; + return true; + } + } + } + else + { + VMA_ASSERT(strategy & (VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT | VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT )); + // Search staring from biggest suballocations. + for (size_t index = freeSuballocCount; index--; ) + { + if (CheckAllocation( + allocSize, + allocAlignment, + allocType, + m_FreeSuballocationsBySize[index], + &pAllocationRequest->allocHandle)) + { + pAllocationRequest->item = m_FreeSuballocationsBySize[index]; + return true; + } + } + } + } + + return false; +} + +VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData) +{ + for (auto& suballoc : m_Suballocations) + { + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_Generic::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + VMA_ASSERT(request.type == VmaAllocationRequestType::Normal); + VMA_ASSERT(request.item != m_Suballocations.end()); + VmaSuballocation& suballoc = *request.item; + // Given suballocation is a free block. + VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + // Given offset is inside this suballocation. + VMA_ASSERT((VkDeviceSize)request.allocHandle - 1 >= suballoc.offset); + const VkDeviceSize paddingBegin = (VkDeviceSize)request.allocHandle - suballoc.offset - 1; + VMA_ASSERT(suballoc.size >= paddingBegin + request.size); + const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - request.size; + + // Unregister this free suballocation from m_FreeSuballocationsBySize and update + // it to become used. + UnregisterFreeSuballocation(request.item); + + suballoc.offset = (VkDeviceSize)request.allocHandle - 1; + suballoc.size = request.size; + suballoc.type = type; + suballoc.userData = userData; + + // If there are any free bytes remaining at the end, insert new free suballocation after current one. + if (paddingEnd) + { + VmaSuballocation paddingSuballoc = {}; + paddingSuballoc.offset = suballoc.offset + suballoc.size; + paddingSuballoc.size = paddingEnd; + paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + VmaSuballocationList::iterator next = request.item; + ++next; + const VmaSuballocationList::iterator paddingEndItem = + m_Suballocations.insert(next, paddingSuballoc); + RegisterFreeSuballocation(paddingEndItem); + } + + // If there are any free bytes remaining at the beginning, insert new free suballocation before current one. + if (paddingBegin) + { + VmaSuballocation paddingSuballoc = {}; + paddingSuballoc.offset = suballoc.offset - paddingBegin; + paddingSuballoc.size = paddingBegin; + paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + const VmaSuballocationList::iterator paddingBeginItem = + m_Suballocations.insert(request.item, paddingSuballoc); + RegisterFreeSuballocation(paddingBeginItem); + } + + // Update totals. + m_FreeCount = m_FreeCount - 1; + if (paddingBegin > 0) + { + ++m_FreeCount; + } + if (paddingEnd > 0) + { + ++m_FreeCount; + } + m_SumFreeSize -= request.size; +} + +void VmaBlockMetadata_Generic::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + outInfo.offset = (VkDeviceSize)allocHandle - 1; + const VmaSuballocation& suballoc = *FindAtOffset(outInfo.offset); + outInfo.size = suballoc.size; + outInfo.pUserData = suballoc.userData; +} + +void* VmaBlockMetadata_Generic::GetAllocationUserData(VmaAllocHandle allocHandle) const +{ + return FindAtOffset((VkDeviceSize)allocHandle - 1)->userData; +} + +VmaAllocHandle VmaBlockMetadata_Generic::GetAllocationListBegin() const +{ + if (IsEmpty()) + return VK_NULL_HANDLE; + + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + return (VmaAllocHandle)(suballoc.offset + 1); + } + VMA_ASSERT(false && "Should contain at least 1 allocation!"); + return VK_NULL_HANDLE; +} + +VmaAllocHandle VmaBlockMetadata_Generic::GetNextAllocation(VmaAllocHandle prevAlloc) const +{ + VmaSuballocationList::const_iterator prev = FindAtOffset((VkDeviceSize)prevAlloc - 1); + + for (VmaSuballocationList::const_iterator it = ++prev; it != m_Suballocations.end(); ++it) + { + if (it->type != VMA_SUBALLOCATION_TYPE_FREE) + return (VmaAllocHandle)(it->offset + 1); + } + return VK_NULL_HANDLE; +} + +void VmaBlockMetadata_Generic::Clear() +{ + const VkDeviceSize size = GetSize(); + + VMA_ASSERT(IsVirtual()); + m_FreeCount = 1; + m_SumFreeSize = size; + m_Suballocations.clear(); + m_FreeSuballocationsBySize.clear(); + + VmaSuballocation suballoc = {}; + suballoc.offset = 0; + suballoc.size = size; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + m_Suballocations.push_back(suballoc); + + m_FreeSuballocationsBySize.push_back(m_Suballocations.begin()); +} + +void VmaBlockMetadata_Generic::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + VmaSuballocation& suballoc = *FindAtOffset((VkDeviceSize)allocHandle - 1); + suballoc.userData = userData; +} + +void VmaBlockMetadata_Generic::DebugLogAllAllocations() const +{ + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + DebugLogAllocation(suballoc.offset, suballoc.size, suballoc.userData); + } +} + +VmaSuballocationList::iterator VmaBlockMetadata_Generic::FindAtOffset(VkDeviceSize offset) const +{ + VMA_HEAVY_ASSERT(!m_Suballocations.empty()); + const VkDeviceSize last = m_Suballocations.rbegin()->offset; + if (last == offset) + return m_Suballocations.rbegin().drop_const(); + const VkDeviceSize first = m_Suballocations.begin()->offset; + if (first == offset) + return m_Suballocations.begin().drop_const(); + + const size_t suballocCount = m_Suballocations.size(); + const VkDeviceSize step = (last - first + m_Suballocations.begin()->size) / suballocCount; + auto findSuballocation = [&](auto begin, auto end) -> VmaSuballocationList::iterator + { + for (auto suballocItem = begin; + suballocItem != end; + ++suballocItem) + { + if (suballocItem->offset == offset) + return suballocItem.drop_const(); + } + VMA_ASSERT(false && "Not found!"); + return m_Suballocations.end().drop_const(); + }; + // If requested offset is closer to the end of range, search from the end + if (offset - first > suballocCount * step / 2) + { + return findSuballocation(m_Suballocations.rbegin(), m_Suballocations.rend()); + } + return findSuballocation(m_Suballocations.begin(), m_Suballocations.end()); +} + +bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const +{ + VkDeviceSize lastSize = 0; + for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i) + { + const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i]; + + VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_VALIDATE(it->size >= lastSize); + lastSize = it->size; + } + return true; +} + +bool VmaBlockMetadata_Generic::CheckAllocation( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaSuballocationList::const_iterator suballocItem, + VmaAllocHandle* pAllocHandle) const +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(suballocItem != m_Suballocations.cend()); + VMA_ASSERT(pAllocHandle != VMA_NULL); + + const VkDeviceSize debugMargin = GetDebugMargin(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + + const VmaSuballocation& suballoc = *suballocItem; + VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + // Size of this suballocation is too small for this request: Early return. + if (suballoc.size < allocSize) + { + return false; + } + + // Start from offset equal to beginning of this suballocation. + VkDeviceSize offset = suballoc.offset + (suballocItem == m_Suballocations.cbegin() ? 0 : GetDebugMargin()); + + // Apply debugMargin from the end of previous alloc. + if (debugMargin > 0) + { + offset += debugMargin; + } + + // Apply alignment. + offset = VmaAlignUp(offset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment) + { + bool bufferImageGranularityConflict = false; + VmaSuballocationList::const_iterator prevSuballocItem = suballocItem; + while (prevSuballocItem != m_Suballocations.cbegin()) + { + --prevSuballocItem; + const VmaSuballocation& prevSuballoc = *prevSuballocItem; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + offset = VmaAlignUp(offset, bufferImageGranularity); + } + } + + // Calculate padding at the beginning based on current offset. + const VkDeviceSize paddingBegin = offset - suballoc.offset; + + // Fail if requested size plus margin after is bigger than size of this suballocation. + if (paddingBegin + allocSize + debugMargin > suballoc.size) + { + return false; + } + + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (allocSize % bufferImageGranularity || offset % bufferImageGranularity) + { + VmaSuballocationList::const_iterator nextSuballocItem = suballocItem; + ++nextSuballocItem; + while (nextSuballocItem != m_Suballocations.cend()) + { + const VmaSuballocation& nextSuballoc = *nextSuballocItem; + if (VmaBlocksOnSamePage(offset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + ++nextSuballocItem; + } + } + + *pAllocHandle = (VmaAllocHandle)(offset + 1); + // All tests passed: Success. pAllocHandle is already filled. + return true; +} + +void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item != m_Suballocations.end()); + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaSuballocationList::iterator nextItem = item; + ++nextItem; + VMA_ASSERT(nextItem != m_Suballocations.end()); + VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE); + + item->size += nextItem->size; + --m_FreeCount; + m_Suballocations.erase(nextItem); +} + +VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem) +{ + // Change this suballocation to be marked as free. + VmaSuballocation& suballoc = *suballocItem; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + suballoc.userData = VMA_NULL; + + // Update totals. + ++m_FreeCount; + m_SumFreeSize += suballoc.size; + + // Merge with previous and/or next suballocation if it's also free. + bool mergeWithNext = false; + bool mergeWithPrev = false; + + VmaSuballocationList::iterator nextItem = suballocItem; + ++nextItem; + if ((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)) + { + mergeWithNext = true; + } + + VmaSuballocationList::iterator prevItem = suballocItem; + if (suballocItem != m_Suballocations.begin()) + { + --prevItem; + if (prevItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + mergeWithPrev = true; + } + } + + if (mergeWithNext) + { + UnregisterFreeSuballocation(nextItem); + MergeFreeWithNext(suballocItem); + } + + if (mergeWithPrev) + { + UnregisterFreeSuballocation(prevItem); + MergeFreeWithNext(prevItem); + RegisterFreeSuballocation(prevItem); + return prevItem; + } + else + { + RegisterFreeSuballocation(suballocItem); + return suballocItem; + } +} + +void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(item->size > 0); + + // You may want to enable this validation at the beginning or at the end of + // this function, depending on what do you want to check. + VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + + if (m_FreeSuballocationsBySize.empty()) + { + m_FreeSuballocationsBySize.push_back(item); + } + else + { + VmaVectorInsertSorted(m_FreeSuballocationsBySize, item); + } + + //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); +} + +void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(item->size > 0); + + // You may want to enable this validation at the beginning or at the end of + // this function, depending on what do you want to check. + VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + + VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( + m_FreeSuballocationsBySize.data(), + m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(), + item, + VmaSuballocationItemSizeLess()); + for (size_t index = it - m_FreeSuballocationsBySize.data(); + index < m_FreeSuballocationsBySize.size(); + ++index) + { + if (m_FreeSuballocationsBySize[index] == item) + { + VmaVectorRemove(m_FreeSuballocationsBySize, index); + return; + } + VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found."); + } + VMA_ASSERT(0 && "Not found."); + + //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); +} +#endif // _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_GENERIC +#endif // #if 0 + +#ifndef _VMA_BLOCK_METADATA_LINEAR +/* +Allocations and their references in internal data structure look like this: + +if(m_2ndVectorMode == SECOND_VECTOR_EMPTY): + + 0 +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | + | | + | | +GetSize() +-------+ + +if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER): + + 0 +-------+ + | Alloc | 2nd[0] + +-------+ + | Alloc | 2nd[1] + +-------+ + | ... | + +-------+ + | Alloc | 2nd[2nd.size() - 1] + +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | +GetSize() +-------+ + +if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK): + + 0 +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | + | | + | | + +-------+ + | Alloc | 2nd[2nd.size() - 1] + +-------+ + | ... | + +-------+ + | Alloc | 2nd[1] + +-------+ + | Alloc | 2nd[0] +GetSize() +-------+ + +*/ +class VmaBlockMetadata_Linear : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear) +public: + VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_Linear() = default; + + VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; } + bool IsEmpty() const override { return GetAllocationCount() == 0; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }; + + void Init(VkDeviceSize size) override; + bool Validate() const override; + size_t GetAllocationCount() const override; + size_t GetFreeRegionsCount() const override; + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; + void AddStatistics(VmaStatistics& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + VkResult CheckCorruption(const void* pBlockData) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void* GetAllocationUserData(VmaAllocHandle allocHandle) const override; + VmaAllocHandle GetAllocationListBegin() const override; + VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override; + VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + void DebugLogAllAllocations() const override; + +private: + /* + There are two suballocation vectors, used in ping-pong way. + The one with index m_1stVectorIndex is called 1st. + The one with index (m_1stVectorIndex ^ 1) is called 2nd. + 2nd can be non-empty only when 1st is not empty. + When 2nd is not empty, m_2ndVectorMode indicates its mode of operation. + */ + typedef VmaVector> SuballocationVectorType; + + enum SECOND_VECTOR_MODE + { + SECOND_VECTOR_EMPTY, + /* + Suballocations in 2nd vector are created later than the ones in 1st, but they + all have smaller offset. + */ + SECOND_VECTOR_RING_BUFFER, + /* + Suballocations in 2nd vector are upper side of double stack. + They all have offsets higher than those in 1st vector. + Top of this stack means smaller offsets, but higher indices in this vector. + */ + SECOND_VECTOR_DOUBLE_STACK, + }; + + VkDeviceSize m_SumFreeSize; + SuballocationVectorType m_Suballocations0, m_Suballocations1; + uint32_t m_1stVectorIndex; + SECOND_VECTOR_MODE m_2ndVectorMode; + // Number of items in 1st vector with hAllocation = null at the beginning. + size_t m_1stNullItemsBeginCount; + // Number of other items in 1st vector with hAllocation = null somewhere in the middle. + size_t m_1stNullItemsMiddleCount; + // Number of items in 2nd vector with hAllocation = null. + size_t m_2ndNullItemsCount; + + SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + + VmaSuballocation& FindSuballocation(VkDeviceSize offset) const; + bool ShouldCompact1st() const; + void CleanupAfterFree(); + + bool CreateAllocationRequest_LowerAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); + bool CreateAllocationRequest_UpperAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); +}; + +#ifndef _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS +VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_SumFreeSize(0), + m_Suballocations0(VmaStlAllocator(pAllocationCallbacks)), + m_Suballocations1(VmaStlAllocator(pAllocationCallbacks)), + m_1stVectorIndex(0), + m_2ndVectorMode(SECOND_VECTOR_EMPTY), + m_1stNullItemsBeginCount(0), + m_1stNullItemsMiddleCount(0), + m_2ndNullItemsCount(0) {} + +void VmaBlockMetadata_Linear::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + m_SumFreeSize = size; +} + +bool VmaBlockMetadata_Linear::Validate() const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY)); + VMA_VALIDATE(!suballocations1st.empty() || + suballocations2nd.empty() || + m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER); + + if (!suballocations1st.empty()) + { + // Null item at the beginning should be accounted into m_1stNullItemsBeginCount. + VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE); + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE); + } + if (!suballocations2nd.empty()) + { + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE); + } + + VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size()); + VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size()); + + VkDeviceSize sumUsedSize = 0; + const size_t suballoc1stCount = suballocations1st.size(); + const VkDeviceSize debugMargin = GetDebugMargin(); + VkDeviceSize offset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for (size_t i = 0; i < suballoc2ndCount; ++i) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + + VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE && + suballoc.userData == VMA_NULL); + } + + size_t nullItem1stCount = m_1stNullItemsBeginCount; + + for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem1stCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount); + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for (size_t i = suballoc2ndCount; i--; ) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + + VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + VMA_VALIDATE(offset <= GetSize()); + VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize); + + return true; +} + +size_t VmaBlockMetadata_Linear::GetAllocationCount() const +{ + return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount + + AccessSuballocations2nd().size() - m_2ndNullItemsCount; +} + +size_t VmaBlockMetadata_Linear::GetFreeRegionsCount() const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return SIZE_MAX; +} + +void VmaBlockMetadata_Linear::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const +{ + const VkDeviceSize size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += size; + + VkDeviceSize lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + if (lastOffset < freeSpace2ndTo1stEnd) + { + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + if (lastOffset < freeSpace1stTo2ndEnd) + { + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to size. + if (lastOffset < size) + { + const VkDeviceSize unusedRangeSize = size - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } +} + +void VmaBlockMetadata_Linear::AddStatistics(VmaStatistics& inoutStats) const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const VkDeviceSize size = GetSize(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + inoutStats.blockCount++; + inoutStats.blockBytes += size; + inoutStats.allocationBytes += size - m_SumFreeSize; + + VkDeviceSize lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace1stTo2ndEnd) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + const VkDeviceSize unusedRangeSize = size - lastOffset; + } + + // End of loop. + lastOffset = size; + } + } + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const +{ + const VkDeviceSize size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + // FIRST PASS + + size_t unusedRangeCount = 0; + VkDeviceSize usedBytes = 0; + + VkDeviceSize lastOffset = 0; + + size_t alloc2ndCount = 0; + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + size_t alloc1stCount = 0; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc1stCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = size; + } + } + } + + const VkDeviceSize unusedBytes = size - usedBytes; + PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount); + + // SECOND PASS + lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + nextAlloc1stIndex = m_1stNullItemsBeginCount; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace1stTo2ndEnd) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + const VkDeviceSize unusedRangeSize = size - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } + + PrintDetailedMap_End(json); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Linear::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(pAllocationRequest != VMA_NULL); + VMA_HEAVY_ASSERT(Validate()); + pAllocationRequest->size = allocSize; + return upperAddress ? + CreateAllocationRequest_UpperAddress( + allocSize, allocAlignment, allocType, strategy, pAllocationRequest) : + CreateAllocationRequest_LowerAddress( + allocSize, allocAlignment, allocType, strategy, pAllocationRequest); +} + +VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData) +{ + VMA_ASSERT(!IsVirtual()); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_Linear::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1; + const VmaSuballocation newSuballoc = { offset, request.size, userData, type }; + + switch (request.type) + { + case VmaAllocationRequestType::UpperAddress: + { + VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER && + "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer."); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + suballocations2nd.push_back(newSuballoc); + m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK; + } + break; + case VmaAllocationRequestType::EndOf1st: + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + + VMA_ASSERT(suballocations1st.empty() || + offset >= suballocations1st.back().offset + suballocations1st.back().size); + // Check if it fits before the end of the block. + VMA_ASSERT(offset + request.size <= GetSize()); + + suballocations1st.push_back(newSuballoc); + } + break; + case VmaAllocationRequestType::EndOf2nd: + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector. + VMA_ASSERT(!suballocations1st.empty() && + offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + switch (m_2ndVectorMode) + { + case SECOND_VECTOR_EMPTY: + // First allocation from second part ring buffer. + VMA_ASSERT(suballocations2nd.empty()); + m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER; + break; + case SECOND_VECTOR_RING_BUFFER: + // 2-part ring buffer is already started. + VMA_ASSERT(!suballocations2nd.empty()); + break; + case SECOND_VECTOR_DOUBLE_STACK: + VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack."); + break; + default: + VMA_ASSERT(0); + } + + suballocations2nd.push_back(newSuballoc); + } + break; + default: + VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR."); + } + + m_SumFreeSize -= newSuballoc.size; +} + +void VmaBlockMetadata_Linear::Free(VmaAllocHandle allocHandle) +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + VkDeviceSize offset = (VkDeviceSize)allocHandle - 1; + + if (!suballocations1st.empty()) + { + // First allocation: Mark it as next empty at the beginning. + VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount]; + if (firstSuballoc.offset == offset) + { + firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + firstSuballoc.userData = VMA_NULL; + m_SumFreeSize += firstSuballoc.size; + ++m_1stNullItemsBeginCount; + CleanupAfterFree(); + return; + } + } + + // Last allocation in 2-part ring buffer or top of upper stack (same logic). + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER || + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + VmaSuballocation& lastSuballoc = suballocations2nd.back(); + if (lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations2nd.pop_back(); + CleanupAfterFree(); + return; + } + } + // Last allocation in 1st vector. + else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY) + { + VmaSuballocation& lastSuballoc = suballocations1st.back(); + if (lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations1st.pop_back(); + CleanupAfterFree(); + return; + } + } + + VmaSuballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + + // Item from the middle of 1st vector. + { + const SuballocationVectorType::iterator it = VmaBinaryFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), + refSuballoc, + VmaSuballocationOffsetLess()); + if (it != suballocations1st.end()) + { + it->type = VMA_SUBALLOCATION_TYPE_FREE; + it->userData = VMA_NULL; + ++m_1stNullItemsMiddleCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Item from the middle of 2nd vector. + const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) : + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater()); + if (it != suballocations2nd.end()) + { + it->type = VMA_SUBALLOCATION_TYPE_FREE; + it->userData = VMA_NULL; + ++m_2ndNullItemsCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + VMA_ASSERT(0 && "Allocation to free not found in linear allocator!"); +} + +void VmaBlockMetadata_Linear::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + outInfo.offset = (VkDeviceSize)allocHandle - 1; + VmaSuballocation& suballoc = FindSuballocation(outInfo.offset); + outInfo.size = suballoc.size; + outInfo.pUserData = suballoc.userData; +} + +void* VmaBlockMetadata_Linear::GetAllocationUserData(VmaAllocHandle allocHandle) const +{ + return FindSuballocation((VkDeviceSize)allocHandle - 1).userData; +} + +VmaAllocHandle VmaBlockMetadata_Linear::GetAllocationListBegin() const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return VK_NULL_HANDLE; +} + +VmaAllocHandle VmaBlockMetadata_Linear::GetNextAllocation(VmaAllocHandle prevAlloc) const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return VK_NULL_HANDLE; +} + +VkDeviceSize VmaBlockMetadata_Linear::GetNextFreeRegionSize(VmaAllocHandle alloc) const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return 0; +} + +void VmaBlockMetadata_Linear::Clear() +{ + m_SumFreeSize = GetSize(); + m_Suballocations0.clear(); + m_Suballocations1.clear(); + // Leaving m_1stVectorIndex unchanged - it doesn't matter. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; +} + +void VmaBlockMetadata_Linear::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle - 1); + suballoc.userData = userData; +} + +void VmaBlockMetadata_Linear::DebugLogAllAllocations() const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it) + if (it->type != VMA_SUBALLOCATION_TYPE_FREE) + DebugLogAllocation(it->offset, it->size, it->userData); + + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it) + if (it->type != VMA_SUBALLOCATION_TYPE_FREE) + DebugLogAllocation(it->offset, it->size, it->userData); +} + +VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset) const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + VmaSuballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + + // Item from the 1st vector. + { + SuballocationVectorType::const_iterator it = VmaBinaryFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), + refSuballoc, + VmaSuballocationOffsetLess()); + if (it != suballocations1st.end()) + { + return const_cast(*it); + } + } + + if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Rest of members stays uninitialized intentionally for better performance. + SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) : + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater()); + if (it != suballocations2nd.end()) + { + return const_cast(*it); + } + } + + VMA_ASSERT(0 && "Allocation not found in linear allocator!"); + return const_cast(suballocations1st.back()); // Should never occur. +} + +bool VmaBlockMetadata_Linear::ShouldCompact1st() const +{ + const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + const size_t suballocCount = AccessSuballocations1st().size(); + return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3; +} + +void VmaBlockMetadata_Linear::CleanupAfterFree() +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (IsEmpty()) + { + suballocations1st.clear(); + suballocations2nd.clear(); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + else + { + const size_t suballoc1stCount = suballocations1st.size(); + const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + VMA_ASSERT(nullItem1stCount <= suballoc1stCount); + + // Find more null items at the beginning of 1st vector. + while (m_1stNullItemsBeginCount < suballoc1stCount && + suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + + // Find more null items at the end of 1st vector. + while (m_1stNullItemsMiddleCount > 0 && + suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_1stNullItemsMiddleCount; + suballocations1st.pop_back(); + } + + // Find more null items at the end of 2nd vector. + while (m_2ndNullItemsCount > 0 && + suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_2ndNullItemsCount; + suballocations2nd.pop_back(); + } + + // Find more null items at the beginning of 2nd vector. + while (m_2ndNullItemsCount > 0 && + suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_2ndNullItemsCount; + VmaVectorRemove(suballocations2nd, 0); + } + + if (ShouldCompact1st()) + { + const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount; + size_t srcIndex = m_1stNullItemsBeginCount; + for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex) + { + while (suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++srcIndex; + } + if (dstIndex != srcIndex) + { + suballocations1st[dstIndex] = suballocations1st[srcIndex]; + } + ++srcIndex; + } + suballocations1st.resize(nonNullItemCount); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + } + + // 2nd vector became empty. + if (suballocations2nd.empty()) + { + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + + // 1st vector became empty. + if (suballocations1st.size() - m_1stNullItemsBeginCount == 0) + { + suballocations1st.clear(); + m_1stNullItemsBeginCount = 0; + + if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + // Swap 1st with 2nd. Now 2nd is empty. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsMiddleCount = m_2ndNullItemsCount; + while (m_1stNullItemsBeginCount < suballocations2nd.size() && + suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + m_2ndNullItemsCount = 0; + m_1stVectorIndex ^= 1; + } + } + } + + VMA_HEAVY_ASSERT(Validate()); +} + +bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + const VkDeviceSize blockSize = GetSize(); + const VkDeviceSize debugMargin = GetDebugMargin(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + // Try to allocate at the end of 1st vector. + + VkDeviceSize resultBaseOffset = 0; + if (!suballocations1st.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations1st.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin; + } + + // Start from offset equal to beginning of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply alignment. + resultOffset = VmaAlignUp(resultOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity); + } + } + + const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? + suballocations2nd.back().offset : blockSize; + + // There is enough free space at the end after alignment. + if (resultOffset + allocSize + debugMargin <= freeSpaceEnd) + { + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if ((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) + { + const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on previous page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1); + // pAllocationRequest->item, customData unused. + pAllocationRequest->type = VmaAllocationRequestType::EndOf1st; + return true; + } + } + + // Wrap-around to end of 2nd vector. Try to allocate there, watching for the + // beginning of 1st vector as the end of free space. + if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + VMA_ASSERT(!suballocations1st.empty()); + + VkDeviceSize resultBaseOffset = 0; + if (!suballocations2nd.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin; + } + + // Start from offset equal to beginning of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply alignment. + resultOffset = VmaAlignUp(resultOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity); + } + } + + size_t index1st = m_1stNullItemsBeginCount; + + // There is enough free space at the end after alignment. + if ((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) || + (index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset)) + { + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) + { + for (size_t nextSuballocIndex = index1st; + nextSuballocIndex < suballocations1st.size(); + nextSuballocIndex++) + { + const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1); + pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd; + // pAllocationRequest->item, customData unused. + return true; + } + } + + return false; +} + +bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + const VkDeviceSize blockSize = GetSize(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer."); + return false; + } + + // Try to allocate before 2nd.back(), or end of block if 2nd.empty(). + if (allocSize > blockSize) + { + return false; + } + VkDeviceSize resultBaseOffset = blockSize - allocSize; + if (!suballocations2nd.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset - allocSize; + if (allocSize > lastSuballoc.offset) + { + return false; + } + } + + // Start from offset equal to end of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + const VkDeviceSize debugMargin = GetDebugMargin(); + + // Apply debugMargin at the end. + if (debugMargin > 0) + { + if (resultOffset < debugMargin) + { + return false; + } + resultOffset -= debugMargin; + } + + // Apply alignment. + resultOffset = VmaAlignDown(resultOffset, allocAlignment); + + // Check next suballocations from 2nd for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) + { + const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity); + } + } + + // There is enough free space. + const VkDeviceSize endOf1st = !suballocations1st.empty() ? + suballocations1st.back().offset + suballocations1st.back().size : + 0; + if (endOf1st + debugMargin <= resultOffset) + { + // Check previous suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (bufferImageGranularity > 1) + { + for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1); + // pAllocationRequest->item unused. + pAllocationRequest->type = VmaAllocationRequestType::UpperAddress; + return true; + } + + return false; +} +#endif // _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_LINEAR + +#if 0 +#ifndef _VMA_BLOCK_METADATA_BUDDY +/* +- GetSize() is the original size of allocated memory block. +- m_UsableSize is this size aligned down to a power of two. + All allocations and calculations happen relative to m_UsableSize. +- GetUnusableSize() is the difference between them. + It is reported as separate, unused range, not available for allocations. + +Node at level 0 has size = m_UsableSize. +Each next level contains nodes with size 2 times smaller than current level. +m_LevelCount is the maximum number of levels to use in the current object. +*/ +class VmaBlockMetadata_Buddy : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy) +public: + VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_Buddy(); + + size_t GetAllocationCount() const override { return m_AllocationCount; } + VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize + GetUnusableSize(); } + bool IsEmpty() const override { return m_Root->type == Node::TYPE_FREE; } + VkResult CheckCorruption(const void* pBlockData) override { return VK_ERROR_FEATURE_NOT_PRESENT; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }; + void DebugLogAllAllocations() const override { DebugLogAllAllocationNode(m_Root, 0); } + + void Init(VkDeviceSize size) override; + bool Validate() const override; + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; + void AddStatistics(VmaStatistics& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void* GetAllocationUserData(VmaAllocHandle allocHandle) const override; + VmaAllocHandle GetAllocationListBegin() const override; + VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + +private: + static const size_t MAX_LEVELS = 48; + + struct ValidationContext + { + size_t calculatedAllocationCount = 0; + size_t calculatedFreeCount = 0; + VkDeviceSize calculatedSumFreeSize = 0; + }; + struct Node + { + VkDeviceSize offset; + enum TYPE + { + TYPE_FREE, + TYPE_ALLOCATION, + TYPE_SPLIT, + TYPE_COUNT + } type; + Node* parent; + Node* buddy; + + union + { + struct + { + Node* prev; + Node* next; + } free; + struct + { + void* userData; + } allocation; + struct + { + Node* leftChild; + } split; + }; + }; + + // Size of the memory block aligned down to a power of two. + VkDeviceSize m_UsableSize; + uint32_t m_LevelCount; + VmaPoolAllocator m_NodeAllocator; + Node* m_Root; + struct + { + Node* front; + Node* back; + } m_FreeList[MAX_LEVELS]; + + // Number of nodes in the tree with type == TYPE_ALLOCATION. + size_t m_AllocationCount; + // Number of nodes in the tree with type == TYPE_FREE. + size_t m_FreeCount; + // Doesn't include space wasted due to internal fragmentation - allocation sizes are just aligned up to node sizes. + // Doesn't include unusable size. + VkDeviceSize m_SumFreeSize; + + VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; } + VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; } + + VkDeviceSize AlignAllocationSize(VkDeviceSize size) const + { + if (!IsVirtual()) + { + size = VmaAlignUp(size, (VkDeviceSize)16); + } + return VmaNextPow2(size); + } + Node* FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const; + void DeleteNodeChildren(Node* node); + bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const; + uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const; + void AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const; + // Adds node to the front of FreeList at given level. + // node->type must be FREE. + // node->free.prev, next can be undefined. + void AddToFreeListFront(uint32_t level, Node* node); + // Removes node from FreeList at given level. + // node->type must be FREE. + // node->free.prev, next stay untouched. + void RemoveFromFreeList(uint32_t level, Node* node); + void DebugLogAllAllocationNode(Node* node, uint32_t level) const; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const; +#endif +}; + +#ifndef _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS +VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_NodeAllocator(pAllocationCallbacks, 32), // firstBlockCapacity + m_Root(VMA_NULL), + m_AllocationCount(0), + m_FreeCount(1), + m_SumFreeSize(0) +{ + memset(m_FreeList, 0, sizeof(m_FreeList)); +} + +VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy() +{ + DeleteNodeChildren(m_Root); + m_NodeAllocator.Free(m_Root); +} + +void VmaBlockMetadata_Buddy::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + m_UsableSize = VmaPrevPow2(size); + m_SumFreeSize = m_UsableSize; + + // Calculate m_LevelCount. + const VkDeviceSize minNodeSize = IsVirtual() ? 1 : 16; + m_LevelCount = 1; + while (m_LevelCount < MAX_LEVELS && + LevelToNodeSize(m_LevelCount) >= minNodeSize) + { + ++m_LevelCount; + } + + Node* rootNode = m_NodeAllocator.Alloc(); + rootNode->offset = 0; + rootNode->type = Node::TYPE_FREE; + rootNode->parent = VMA_NULL; + rootNode->buddy = VMA_NULL; + + m_Root = rootNode; + AddToFreeListFront(0, rootNode); +} + +bool VmaBlockMetadata_Buddy::Validate() const +{ + // Validate tree. + ValidationContext ctx; + if (!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0))) + { + VMA_VALIDATE(false && "ValidateNode failed."); + } + VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount); + VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize); + + // Validate free node lists. + for (uint32_t level = 0; level < m_LevelCount; ++level) + { + VMA_VALIDATE(m_FreeList[level].front == VMA_NULL || + m_FreeList[level].front->free.prev == VMA_NULL); + + for (Node* node = m_FreeList[level].front; + node != VMA_NULL; + node = node->free.next) + { + VMA_VALIDATE(node->type == Node::TYPE_FREE); + + if (node->free.next == VMA_NULL) + { + VMA_VALIDATE(m_FreeList[level].back == node); + } + else + { + VMA_VALIDATE(node->free.next->free.prev == node); + } + } + } + + // Validate that free lists ar higher levels are empty. + for (uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level) + { + VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL); + } + + return true; +} + +void VmaBlockMetadata_Buddy::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const +{ + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += GetSize(); + + AddNodeToDetailedStatistics(inoutStats, m_Root, LevelToNodeSize(0)); + + const VkDeviceSize unusableSize = GetUnusableSize(); + if (unusableSize > 0) + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusableSize); +} + +void VmaBlockMetadata_Buddy::AddStatistics(VmaStatistics& inoutStats) const +{ + inoutStats.blockCount++; + inoutStats.allocationCount += (uint32_t)m_AllocationCount; + inoutStats.blockBytes += GetSize(); + inoutStats.allocationBytes += GetSize() - m_SumFreeSize; +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const +{ + VmaDetailedStatistics stats; + VmaClearDetailedStatistics(stats); + AddDetailedStatistics(stats); + + PrintDetailedMap_Begin( + json, + stats.statistics.blockBytes - stats.statistics.allocationBytes, + stats.statistics.allocationCount, + stats.unusedRangeCount, + mapRefCount); + + PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0)); + + const VkDeviceSize unusableSize = GetUnusableSize(); + if (unusableSize > 0) + { + PrintDetailedMap_UnusedRange(json, + m_UsableSize, // offset + unusableSize); // size + } + + PrintDetailedMap_End(json); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Buddy::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm."); + + allocSize = AlignAllocationSize(allocSize); + + // Simple way to respect bufferImageGranularity. May be optimized some day. + // Whenever it might be an OPTIMAL image... + if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL) + { + allocAlignment = VMA_MAX(allocAlignment, GetBufferImageGranularity()); + allocSize = VmaAlignUp(allocSize, GetBufferImageGranularity()); + } + + if (allocSize > m_UsableSize) + { + return false; + } + + const uint32_t targetLevel = AllocSizeToLevel(allocSize); + for (uint32_t level = targetLevel; level--; ) + { + for (Node* freeNode = m_FreeList[level].front; + freeNode != VMA_NULL; + freeNode = freeNode->free.next) + { + if (freeNode->offset % allocAlignment == 0) + { + pAllocationRequest->type = VmaAllocationRequestType::Normal; + pAllocationRequest->allocHandle = (VmaAllocHandle)(freeNode->offset + 1); + pAllocationRequest->size = allocSize; + pAllocationRequest->customData = (void*)(uintptr_t)level; + return true; + } + } + } + + return false; +} + +void VmaBlockMetadata_Buddy::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + VMA_ASSERT(request.type == VmaAllocationRequestType::Normal); + + const uint32_t targetLevel = AllocSizeToLevel(request.size); + uint32_t currLevel = (uint32_t)(uintptr_t)request.customData; + + Node* currNode = m_FreeList[currLevel].front; + VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE); + const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1; + while (currNode->offset != offset) + { + currNode = currNode->free.next; + VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE); + } + + // Go down, splitting free nodes. + while (currLevel < targetLevel) + { + // currNode is already first free node at currLevel. + // Remove it from list of free nodes at this currLevel. + RemoveFromFreeList(currLevel, currNode); + + const uint32_t childrenLevel = currLevel + 1; + + // Create two free sub-nodes. + Node* leftChild = m_NodeAllocator.Alloc(); + Node* rightChild = m_NodeAllocator.Alloc(); + + leftChild->offset = currNode->offset; + leftChild->type = Node::TYPE_FREE; + leftChild->parent = currNode; + leftChild->buddy = rightChild; + + rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel); + rightChild->type = Node::TYPE_FREE; + rightChild->parent = currNode; + rightChild->buddy = leftChild; + + // Convert current currNode to split type. + currNode->type = Node::TYPE_SPLIT; + currNode->split.leftChild = leftChild; + + // Add child nodes to free list. Order is important! + AddToFreeListFront(childrenLevel, rightChild); + AddToFreeListFront(childrenLevel, leftChild); + + ++m_FreeCount; + ++currLevel; + currNode = m_FreeList[currLevel].front; + + /* + We can be sure that currNode, as left child of node previously split, + also fulfills the alignment requirement. + */ + } + + // Remove from free list. + VMA_ASSERT(currLevel == targetLevel && + currNode != VMA_NULL && + currNode->type == Node::TYPE_FREE); + RemoveFromFreeList(currLevel, currNode); + + // Convert to allocation node. + currNode->type = Node::TYPE_ALLOCATION; + currNode->allocation.userData = userData; + + ++m_AllocationCount; + --m_FreeCount; + m_SumFreeSize -= request.size; +} + +void VmaBlockMetadata_Buddy::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + uint32_t level = 0; + outInfo.offset = (VkDeviceSize)allocHandle - 1; + const Node* const node = FindAllocationNode(outInfo.offset, level); + outInfo.size = LevelToNodeSize(level); + outInfo.pUserData = node->allocation.userData; +} + +void* VmaBlockMetadata_Buddy::GetAllocationUserData(VmaAllocHandle allocHandle) const +{ + uint32_t level = 0; + const Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level); + return node->allocation.userData; +} + +VmaAllocHandle VmaBlockMetadata_Buddy::GetAllocationListBegin() const +{ + // Function only used for defragmentation, which is disabled for this algorithm + return VK_NULL_HANDLE; +} + +VmaAllocHandle VmaBlockMetadata_Buddy::GetNextAllocation(VmaAllocHandle prevAlloc) const +{ + // Function only used for defragmentation, which is disabled for this algorithm + return VK_NULL_HANDLE; +} + +void VmaBlockMetadata_Buddy::DeleteNodeChildren(Node* node) +{ + if (node->type == Node::TYPE_SPLIT) + { + DeleteNodeChildren(node->split.leftChild->buddy); + DeleteNodeChildren(node->split.leftChild); + const VkAllocationCallbacks* allocationCallbacks = GetAllocationCallbacks(); + m_NodeAllocator.Free(node->split.leftChild->buddy); + m_NodeAllocator.Free(node->split.leftChild); + } +} + +void VmaBlockMetadata_Buddy::Clear() +{ + DeleteNodeChildren(m_Root); + m_Root->type = Node::TYPE_FREE; + m_AllocationCount = 0; + m_FreeCount = 1; + m_SumFreeSize = m_UsableSize; +} + +void VmaBlockMetadata_Buddy::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + uint32_t level = 0; + Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level); + node->allocation.userData = userData; +} + +VmaBlockMetadata_Buddy::Node* VmaBlockMetadata_Buddy::FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const +{ + Node* node = m_Root; + VkDeviceSize nodeOffset = 0; + outLevel = 0; + VkDeviceSize levelNodeSize = LevelToNodeSize(0); + while (node->type == Node::TYPE_SPLIT) + { + const VkDeviceSize nextLevelNodeSize = levelNodeSize >> 1; + if (offset < nodeOffset + nextLevelNodeSize) + { + node = node->split.leftChild; + } + else + { + node = node->split.leftChild->buddy; + nodeOffset += nextLevelNodeSize; + } + ++outLevel; + levelNodeSize = nextLevelNodeSize; + } + + VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION); + return node; +} + +bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const +{ + VMA_VALIDATE(level < m_LevelCount); + VMA_VALIDATE(curr->parent == parent); + VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL)); + VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr); + switch (curr->type) + { + case Node::TYPE_FREE: + // curr->free.prev, next are validated separately. + ctx.calculatedSumFreeSize += levelNodeSize; + ++ctx.calculatedFreeCount; + break; + case Node::TYPE_ALLOCATION: + ++ctx.calculatedAllocationCount; + if (!IsVirtual()) + { + VMA_VALIDATE(curr->allocation.userData != VMA_NULL); + } + break; + case Node::TYPE_SPLIT: + { + const uint32_t childrenLevel = level + 1; + const VkDeviceSize childrenLevelNodeSize = levelNodeSize >> 1; + const Node* const leftChild = curr->split.leftChild; + VMA_VALIDATE(leftChild != VMA_NULL); + VMA_VALIDATE(leftChild->offset == curr->offset); + if (!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize)) + { + VMA_VALIDATE(false && "ValidateNode for left child failed."); + } + const Node* const rightChild = leftChild->buddy; + VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize); + if (!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize)) + { + VMA_VALIDATE(false && "ValidateNode for right child failed."); + } + } + break; + default: + return false; + } + + return true; +} + +uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const +{ + // I know this could be optimized somehow e.g. by using std::log2p1 from C++20. + uint32_t level = 0; + VkDeviceSize currLevelNodeSize = m_UsableSize; + VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1; + while (allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount) + { + ++level; + currLevelNodeSize >>= 1; + nextLevelNodeSize >>= 1; + } + return level; +} + +void VmaBlockMetadata_Buddy::Free(VmaAllocHandle allocHandle) +{ + uint32_t level = 0; + Node* node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level); + + ++m_FreeCount; + --m_AllocationCount; + m_SumFreeSize += LevelToNodeSize(level); + + node->type = Node::TYPE_FREE; + + // Join free nodes if possible. + while (level > 0 && node->buddy->type == Node::TYPE_FREE) + { + RemoveFromFreeList(level, node->buddy); + Node* const parent = node->parent; + + m_NodeAllocator.Free(node->buddy); + m_NodeAllocator.Free(node); + parent->type = Node::TYPE_FREE; + + node = parent; + --level; + --m_FreeCount; + } + + AddToFreeListFront(level, node); +} + +void VmaBlockMetadata_Buddy::AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const +{ + switch (node->type) + { + case Node::TYPE_FREE: + VmaAddDetailedStatisticsUnusedRange(inoutStats, levelNodeSize); + break; + case Node::TYPE_ALLOCATION: + VmaAddDetailedStatisticsAllocation(inoutStats, levelNodeSize); + break; + case Node::TYPE_SPLIT: + { + const VkDeviceSize childrenNodeSize = levelNodeSize / 2; + const Node* const leftChild = node->split.leftChild; + AddNodeToDetailedStatistics(inoutStats, leftChild, childrenNodeSize); + const Node* const rightChild = leftChild->buddy; + AddNodeToDetailedStatistics(inoutStats, rightChild, childrenNodeSize); + } + break; + default: + VMA_ASSERT(0); + } +} + +void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node) +{ + VMA_ASSERT(node->type == Node::TYPE_FREE); + + // List is empty. + Node* const frontNode = m_FreeList[level].front; + if (frontNode == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].back == VMA_NULL); + node->free.prev = node->free.next = VMA_NULL; + m_FreeList[level].front = m_FreeList[level].back = node; + } + else + { + VMA_ASSERT(frontNode->free.prev == VMA_NULL); + node->free.prev = VMA_NULL; + node->free.next = frontNode; + frontNode->free.prev = node; + m_FreeList[level].front = node; + } +} + +void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node) +{ + VMA_ASSERT(m_FreeList[level].front != VMA_NULL); + + // It is at the front. + if (node->free.prev == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].front == node); + m_FreeList[level].front = node->free.next; + } + else + { + Node* const prevFreeNode = node->free.prev; + VMA_ASSERT(prevFreeNode->free.next == node); + prevFreeNode->free.next = node->free.next; + } + + // It is at the back. + if (node->free.next == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].back == node); + m_FreeList[level].back = node->free.prev; + } + else + { + Node* const nextFreeNode = node->free.next; + VMA_ASSERT(nextFreeNode->free.prev == node); + nextFreeNode->free.prev = node->free.prev; + } +} + +void VmaBlockMetadata_Buddy::DebugLogAllAllocationNode(Node* node, uint32_t level) const +{ + switch (node->type) + { + case Node::TYPE_FREE: + break; + case Node::TYPE_ALLOCATION: + DebugLogAllocation(node->offset, LevelToNodeSize(level), node->allocation.userData); + break; + case Node::TYPE_SPLIT: + { + ++level; + DebugLogAllAllocationNode(node->split.leftChild, level); + DebugLogAllAllocationNode(node->split.leftChild->buddy, level); + } + break; + default: + VMA_ASSERT(0); + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const +{ + switch (node->type) + { + case Node::TYPE_FREE: + PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize); + break; + case Node::TYPE_ALLOCATION: + PrintDetailedMap_Allocation(json, node->offset, levelNodeSize, node->allocation.userData); + break; + case Node::TYPE_SPLIT: + { + const VkDeviceSize childrenNodeSize = levelNodeSize / 2; + const Node* const leftChild = node->split.leftChild; + PrintDetailedMapNode(json, leftChild, childrenNodeSize); + const Node* const rightChild = leftChild->buddy; + PrintDetailedMapNode(json, rightChild, childrenNodeSize); + } + break; + default: + VMA_ASSERT(0); + } +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_BUDDY +#endif // #if 0 + +#ifndef _VMA_BLOCK_METADATA_TLSF +// To not search current larger region if first allocation won't succeed and skip to smaller range +// use with VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as strategy in CreateAllocationRequest(). +// When fragmentation and reusal of previous blocks doesn't matter then use with +// VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT for fastest alloc time possible. +class VmaBlockMetadata_TLSF : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_TLSF) +public: + VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_TLSF(); + + size_t GetAllocationCount() const override { return m_AllocCount; } + size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; } + VkDeviceSize GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; } + bool IsEmpty() const override { return m_NullBlock->offset == 0; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; }; + + void Init(VkDeviceSize size) override; + bool Validate() const override; + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; + void AddStatistics(VmaStatistics& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + VkResult CheckCorruption(const void* pBlockData) override; + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void* GetAllocationUserData(VmaAllocHandle allocHandle) const override; + VmaAllocHandle GetAllocationListBegin() const override; + VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override; + VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + void DebugLogAllAllocations() const override; + +private: + // According to original paper it should be preferable 4 or 5: + // M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems" + // http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf + static const uint8_t SECOND_LEVEL_INDEX = 5; + static const uint16_t SMALL_BUFFER_SIZE = 256; + static const uint32_t INITIAL_BLOCK_ALLOC_COUNT = 16; + static const uint8_t MEMORY_CLASS_SHIFT = 7; + static const uint8_t MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT; + + class Block + { + public: + VkDeviceSize offset; + VkDeviceSize size; + Block* prevPhysical; + Block* nextPhysical; + + void MarkFree() { prevFree = VMA_NULL; } + void MarkTaken() { prevFree = this; } + bool IsFree() const { return prevFree != this; } + void*& UserData() { VMA_HEAVY_ASSERT(!IsFree()); return userData; } + Block*& PrevFree() { return prevFree; } + Block*& NextFree() { VMA_HEAVY_ASSERT(IsFree()); return nextFree; } + + private: + Block* prevFree; // Address of the same block here indicates that block is taken + union + { + Block* nextFree; + void* userData; + }; + }; + + size_t m_AllocCount; + // Total number of free blocks besides null block + size_t m_BlocksFreeCount; + // Total size of free blocks excluding null block + VkDeviceSize m_BlocksFreeSize; + uint32_t m_IsFreeBitmap; + uint8_t m_MemoryClasses; + uint32_t m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES]; + uint32_t m_ListsCount; + /* + * 0: 0-3 lists for small buffers + * 1+: 0-(2^SLI-1) lists for normal buffers + */ + Block** m_FreeList; + VmaPoolAllocator m_BlockAllocator; + Block* m_NullBlock; + VmaBlockBufferImageGranularity m_GranularityHandler; + + uint8_t SizeToMemoryClass(VkDeviceSize size) const; + uint16_t SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const; + uint32_t GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const; + uint32_t GetListIndex(VkDeviceSize size) const; + + void RemoveFreeBlock(Block* block); + void InsertFreeBlock(Block* block); + void MergeBlock(Block* block, Block* prev); + + Block* FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const; + bool CheckBlock( + Block& block, + uint32_t listIndex, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaAllocationRequest* pAllocationRequest); +}; + +#ifndef _VMA_BLOCK_METADATA_TLSF_FUNCTIONS +VmaBlockMetadata_TLSF::VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_AllocCount(0), + m_BlocksFreeCount(0), + m_BlocksFreeSize(0), + m_IsFreeBitmap(0), + m_MemoryClasses(0), + m_ListsCount(0), + m_FreeList(VMA_NULL), + m_BlockAllocator(pAllocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT), + m_NullBlock(VMA_NULL), + m_GranularityHandler(bufferImageGranularity) {} + +VmaBlockMetadata_TLSF::~VmaBlockMetadata_TLSF() +{ + if (m_FreeList) + vma_delete_array(GetAllocationCallbacks(), m_FreeList, m_ListsCount); + m_GranularityHandler.Destroy(GetAllocationCallbacks()); +} + +void VmaBlockMetadata_TLSF::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + if (!IsVirtual()) + m_GranularityHandler.Init(GetAllocationCallbacks(), size); + + m_NullBlock = m_BlockAllocator.Alloc(); + m_NullBlock->size = size; + m_NullBlock->offset = 0; + m_NullBlock->prevPhysical = VMA_NULL; + m_NullBlock->nextPhysical = VMA_NULL; + m_NullBlock->MarkFree(); + m_NullBlock->NextFree() = VMA_NULL; + m_NullBlock->PrevFree() = VMA_NULL; + uint8_t memoryClass = SizeToMemoryClass(size); + uint16_t sli = SizeToSecondIndex(size, memoryClass); + m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1; + if (IsVirtual()) + m_ListsCount += 1UL << SECOND_LEVEL_INDEX; + else + m_ListsCount += 4; + + m_MemoryClasses = memoryClass + 2; + memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(uint32_t)); + + m_FreeList = vma_new_array(GetAllocationCallbacks(), Block*, m_ListsCount); + memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); +} + +bool VmaBlockMetadata_TLSF::Validate() const +{ + VMA_VALIDATE(GetSumFreeSize() <= GetSize()); + + VkDeviceSize calculatedSize = m_NullBlock->size; + VkDeviceSize calculatedFreeSize = m_NullBlock->size; + size_t allocCount = 0; + size_t freeCount = 0; + + // Check integrity of free lists + for (uint32_t list = 0; list < m_ListsCount; ++list) + { + Block* block = m_FreeList[list]; + if (block != VMA_NULL) + { + VMA_VALIDATE(block->IsFree()); + VMA_VALIDATE(block->PrevFree() == VMA_NULL); + while (block->NextFree()) + { + VMA_VALIDATE(block->NextFree()->IsFree()); + VMA_VALIDATE(block->NextFree()->PrevFree() == block); + block = block->NextFree(); + } + } + } + + VkDeviceSize nextOffset = m_NullBlock->offset; + auto validateCtx = m_GranularityHandler.StartValidation(GetAllocationCallbacks(), IsVirtual()); + + VMA_VALIDATE(m_NullBlock->nextPhysical == VMA_NULL); + if (m_NullBlock->prevPhysical) + { + VMA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock); + } + // Check all blocks + for (Block* prev = m_NullBlock->prevPhysical; prev != VMA_NULL; prev = prev->prevPhysical) + { + VMA_VALIDATE(prev->offset + prev->size == nextOffset); + nextOffset = prev->offset; + calculatedSize += prev->size; + + uint32_t listIndex = GetListIndex(prev->size); + if (prev->IsFree()) + { + ++freeCount; + // Check if free block belongs to free list + Block* freeBlock = m_FreeList[listIndex]; + VMA_VALIDATE(freeBlock != VMA_NULL); + + bool found = false; + do + { + if (freeBlock == prev) + found = true; + + freeBlock = freeBlock->NextFree(); + } while (!found && freeBlock != VMA_NULL); + + VMA_VALIDATE(found); + calculatedFreeSize += prev->size; + } + else + { + ++allocCount; + // Check if taken block is not on a free list + Block* freeBlock = m_FreeList[listIndex]; + while (freeBlock) + { + VMA_VALIDATE(freeBlock != prev); + freeBlock = freeBlock->NextFree(); + } + + if (!IsVirtual()) + { + VMA_VALIDATE(m_GranularityHandler.Validate(validateCtx, prev->offset, prev->size)); + } + } + + if (prev->prevPhysical) + { + VMA_VALIDATE(prev->prevPhysical->nextPhysical == prev); + } + } + + if (!IsVirtual()) + { + VMA_VALIDATE(m_GranularityHandler.FinishValidation(validateCtx)); + } + + VMA_VALIDATE(nextOffset == 0); + VMA_VALIDATE(calculatedSize == GetSize()); + VMA_VALIDATE(calculatedFreeSize == GetSumFreeSize()); + VMA_VALIDATE(allocCount == m_AllocCount); + VMA_VALIDATE(freeCount == m_BlocksFreeCount); + + return true; +} + +void VmaBlockMetadata_TLSF::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const +{ + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += GetSize(); + if (m_NullBlock->size > 0) + VmaAddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size); + + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + if (block->IsFree()) + VmaAddDetailedStatisticsUnusedRange(inoutStats, block->size); + else + VmaAddDetailedStatisticsAllocation(inoutStats, block->size); + } +} + +void VmaBlockMetadata_TLSF::AddStatistics(VmaStatistics& inoutStats) const +{ + inoutStats.blockCount++; + inoutStats.allocationCount += (uint32_t)m_AllocCount; + inoutStats.blockBytes += GetSize(); + inoutStats.allocationBytes += GetSize() - GetSumFreeSize(); +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const +{ + size_t blockCount = m_AllocCount + m_BlocksFreeCount; + VmaStlAllocator allocator(GetAllocationCallbacks()); + VmaVector> blockList(blockCount, allocator); + + size_t i = blockCount; + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + blockList[--i] = block; + } + VMA_ASSERT(i == 0); + + VmaDetailedStatistics stats; + VmaClearDetailedStatistics(stats); + AddDetailedStatistics(stats); + + PrintDetailedMap_Begin(json, + stats.statistics.blockBytes - stats.statistics.allocationBytes, + stats.statistics.allocationCount, + stats.unusedRangeCount); + + for (; i < blockCount; ++i) + { + Block* block = blockList[i]; + if (block->IsFree()) + PrintDetailedMap_UnusedRange(json, block->offset, block->size); + else + PrintDetailedMap_Allocation(json, block->offset, block->size, block->UserData()); + } + if (m_NullBlock->size > 0) + PrintDetailedMap_UnusedRange(json, m_NullBlock->offset, m_NullBlock->size); + + PrintDetailedMap_End(json); +} +#endif + +bool VmaBlockMetadata_TLSF::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0 && "Cannot allocate empty block!"); + VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm."); + + // For small granularity round up + if (!IsVirtual()) + m_GranularityHandler.RoundupAllocRequest(allocType, allocSize, allocAlignment); + + allocSize += GetDebugMargin(); + // Quick check for too small pool + if (allocSize > GetSumFreeSize()) + return false; + + // If no free blocks in pool then check only null block + if (m_BlocksFreeCount == 0) + return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest); + + // Round up to the next block + VkDeviceSize sizeForNextList = allocSize; + VkDeviceSize smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4); + if (allocSize > SMALL_BUFFER_SIZE) + { + sizeForNextList += (1ULL << (VMA_BITSCAN_MSB(allocSize) - SECOND_LEVEL_INDEX)); + } + else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep) + sizeForNextList = SMALL_BUFFER_SIZE + 1; + else + sizeForNextList += smallSizeStep; + + uint32_t nextListIndex = 0; + uint32_t prevListIndex = 0; + Block* nextListBlock = VMA_NULL; + Block* prevListBlock = VMA_NULL; + + // Check blocks according to strategies + if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) + { + // Quick check for larger block first + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + if (nextListBlock != VMA_NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // If not fitted then null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Null block failed, search larger bucket + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + + // Failed again, check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + } + else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT) + { + // Check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Check larger bucket + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + } + else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT ) + { + // Perform search from the start + VmaStlAllocator allocator(GetAllocationCallbacks()); + VmaVector> blockList(m_BlocksFreeCount, allocator); + + size_t i = m_BlocksFreeCount; + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + if (block->IsFree() && block->size >= allocSize) + blockList[--i] = block; + } + + for (; i < m_BlocksFreeCount; ++i) + { + Block& block = *blockList[i]; + if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Whole range searched, no more memory + return false; + } + else + { + // Check larger bucket + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + } + + // Worst case, full search has to be done + while (++nextListIndex < m_ListsCount) + { + nextListBlock = m_FreeList[nextListIndex]; + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + } + + // No more memory sadly + return false; +} + +VkResult VmaBlockMetadata_TLSF::CheckCorruption(const void* pBlockData) +{ + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + if (!block->IsFree()) + { + if (!VmaValidateMagicValue(pBlockData, block->offset + block->size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_TLSF::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + VMA_ASSERT(request.type == VmaAllocationRequestType::TLSF); + + // Get block and pop it from the free list + Block* currentBlock = (Block*)request.allocHandle; + VkDeviceSize offset = request.algorithmData; + VMA_ASSERT(currentBlock != VMA_NULL); + VMA_ASSERT(currentBlock->offset <= offset); + + if (currentBlock != m_NullBlock) + RemoveFreeBlock(currentBlock); + + VkDeviceSize debugMargin = GetDebugMargin(); + VkDeviceSize misssingAlignment = offset - currentBlock->offset; + + // Append missing alignment to prev block or create new one + if (misssingAlignment) + { + Block* prevBlock = currentBlock->prevPhysical; + VMA_ASSERT(prevBlock != VMA_NULL && "There should be no missing alignment at offset 0!"); + + if (prevBlock->IsFree() && prevBlock->size != debugMargin) + { + uint32_t oldList = GetListIndex(prevBlock->size); + prevBlock->size += misssingAlignment; + // Check if new size crosses list bucket + if (oldList != GetListIndex(prevBlock->size)) + { + prevBlock->size -= misssingAlignment; + RemoveFreeBlock(prevBlock); + prevBlock->size += misssingAlignment; + InsertFreeBlock(prevBlock); + } + else + m_BlocksFreeSize += misssingAlignment; + } + else + { + Block* newBlock = m_BlockAllocator.Alloc(); + currentBlock->prevPhysical = newBlock; + prevBlock->nextPhysical = newBlock; + newBlock->prevPhysical = prevBlock; + newBlock->nextPhysical = currentBlock; + newBlock->size = misssingAlignment; + newBlock->offset = currentBlock->offset; + newBlock->MarkTaken(); + + InsertFreeBlock(newBlock); + } + + currentBlock->size -= misssingAlignment; + currentBlock->offset += misssingAlignment; + } + + VkDeviceSize size = request.size + debugMargin; + if (currentBlock->size == size) + { + if (currentBlock == m_NullBlock) + { + // Setup new null block + m_NullBlock = m_BlockAllocator.Alloc(); + m_NullBlock->size = 0; + m_NullBlock->offset = currentBlock->offset + size; + m_NullBlock->prevPhysical = currentBlock; + m_NullBlock->nextPhysical = VMA_NULL; + m_NullBlock->MarkFree(); + m_NullBlock->PrevFree() = VMA_NULL; + m_NullBlock->NextFree() = VMA_NULL; + currentBlock->nextPhysical = m_NullBlock; + currentBlock->MarkTaken(); + } + } + else + { + VMA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!"); + + // Create new free block + Block* newBlock = m_BlockAllocator.Alloc(); + newBlock->size = currentBlock->size - size; + newBlock->offset = currentBlock->offset + size; + newBlock->prevPhysical = currentBlock; + newBlock->nextPhysical = currentBlock->nextPhysical; + currentBlock->nextPhysical = newBlock; + currentBlock->size = size; + + if (currentBlock == m_NullBlock) + { + m_NullBlock = newBlock; + m_NullBlock->MarkFree(); + m_NullBlock->NextFree() = VMA_NULL; + m_NullBlock->PrevFree() = VMA_NULL; + currentBlock->MarkTaken(); + } + else + { + newBlock->nextPhysical->prevPhysical = newBlock; + newBlock->MarkTaken(); + InsertFreeBlock(newBlock); + } + } + currentBlock->UserData() = userData; + + if (debugMargin > 0) + { + currentBlock->size -= debugMargin; + Block* newBlock = m_BlockAllocator.Alloc(); + newBlock->size = debugMargin; + newBlock->offset = currentBlock->offset + currentBlock->size; + newBlock->prevPhysical = currentBlock; + newBlock->nextPhysical = currentBlock->nextPhysical; + newBlock->MarkTaken(); + currentBlock->nextPhysical->prevPhysical = newBlock; + currentBlock->nextPhysical = newBlock; + InsertFreeBlock(newBlock); + } + + if (!IsVirtual()) + m_GranularityHandler.AllocPages((uint8_t)(uintptr_t)request.customData, + currentBlock->offset, currentBlock->size); + ++m_AllocCount; +} + +void VmaBlockMetadata_TLSF::Free(VmaAllocHandle allocHandle) +{ + Block* block = (Block*)allocHandle; + Block* next = block->nextPhysical; + VMA_ASSERT(!block->IsFree() && "Block is already free!"); + + if (!IsVirtual()) + m_GranularityHandler.FreePages(block->offset, block->size); + --m_AllocCount; + + VkDeviceSize debugMargin = GetDebugMargin(); + if (debugMargin > 0) + { + RemoveFreeBlock(next); + MergeBlock(next, block); + block = next; + next = next->nextPhysical; + } + + // Try merging + Block* prev = block->prevPhysical; + if (prev != VMA_NULL && prev->IsFree() && prev->size != debugMargin) + { + RemoveFreeBlock(prev); + MergeBlock(block, prev); + } + + if (!next->IsFree()) + InsertFreeBlock(block); + else if (next == m_NullBlock) + MergeBlock(m_NullBlock, block); + else + { + RemoveFreeBlock(next); + MergeBlock(next, block); + InsertFreeBlock(next); + } +} + +void VmaBlockMetadata_TLSF::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!"); + outInfo.offset = block->offset; + outInfo.size = block->size; + outInfo.pUserData = block->UserData(); +} + +void* VmaBlockMetadata_TLSF::GetAllocationUserData(VmaAllocHandle allocHandle) const +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Cannot get user data for free block!"); + return block->UserData(); +} + +VmaAllocHandle VmaBlockMetadata_TLSF::GetAllocationListBegin() const +{ + if (m_AllocCount == 0) + return VK_NULL_HANDLE; + + for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical) + { + if (!block->IsFree()) + return (VmaAllocHandle)block; + } + VMA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!"); + return VK_NULL_HANDLE; +} + +VmaAllocHandle VmaBlockMetadata_TLSF::GetNextAllocation(VmaAllocHandle prevAlloc) const +{ + Block* startBlock = (Block*)prevAlloc; + VMA_ASSERT(!startBlock->IsFree() && "Incorrect block!"); + + for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical) + { + if (!block->IsFree()) + return (VmaAllocHandle)block; + } + return VK_NULL_HANDLE; +} + +VkDeviceSize VmaBlockMetadata_TLSF::GetNextFreeRegionSize(VmaAllocHandle alloc) const +{ + Block* block = (Block*)alloc; + VMA_ASSERT(!block->IsFree() && "Incorrect block!"); + + if (block->prevPhysical) + return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0; + return 0; +} + +void VmaBlockMetadata_TLSF::Clear() +{ + m_AllocCount = 0; + m_BlocksFreeCount = 0; + m_BlocksFreeSize = 0; + m_IsFreeBitmap = 0; + m_NullBlock->offset = 0; + m_NullBlock->size = GetSize(); + Block* block = m_NullBlock->prevPhysical; + m_NullBlock->prevPhysical = VMA_NULL; + while (block) + { + Block* prev = block->prevPhysical; + m_BlockAllocator.Free(block); + block = prev; + } + memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); + memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(uint32_t)); + m_GranularityHandler.Clear(); +} + +void VmaBlockMetadata_TLSF::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!"); + block->UserData() = userData; +} + +void VmaBlockMetadata_TLSF::DebugLogAllAllocations() const +{ + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + if (!block->IsFree()) + DebugLogAllocation(block->offset, block->size, block->UserData()); +} + +uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size) const +{ + if (size > SMALL_BUFFER_SIZE) + return VMA_BITSCAN_MSB(size) - MEMORY_CLASS_SHIFT; + return 0; +} + +uint16_t VmaBlockMetadata_TLSF::SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const +{ + if (memoryClass == 0) + { + if (IsVirtual()) + return static_cast((size - 1) / 8); + else + return static_cast((size - 1) / 64); + } + return static_cast((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX)); +} + +uint32_t VmaBlockMetadata_TLSF::GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const +{ + if (memoryClass == 0) + return secondIndex; + + const uint32_t index = static_cast(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex; + if (IsVirtual()) + return index + (1 << SECOND_LEVEL_INDEX); + else + return index + 4; +} + +uint32_t VmaBlockMetadata_TLSF::GetListIndex(VkDeviceSize size) const +{ + uint8_t memoryClass = SizeToMemoryClass(size); + return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass)); +} + +void VmaBlockMetadata_TLSF::RemoveFreeBlock(Block* block) +{ + VMA_ASSERT(block != m_NullBlock); + VMA_ASSERT(block->IsFree()); + + if (block->NextFree() != VMA_NULL) + block->NextFree()->PrevFree() = block->PrevFree(); + if (block->PrevFree() != VMA_NULL) + block->PrevFree()->NextFree() = block->NextFree(); + else + { + uint8_t memClass = SizeToMemoryClass(block->size); + uint16_t secondIndex = SizeToSecondIndex(block->size, memClass); + uint32_t index = GetListIndex(memClass, secondIndex); + VMA_ASSERT(m_FreeList[index] == block); + m_FreeList[index] = block->NextFree(); + if (block->NextFree() == VMA_NULL) + { + m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex); + if (m_InnerIsFreeBitmap[memClass] == 0) + m_IsFreeBitmap &= ~(1UL << memClass); + } + } + block->MarkTaken(); + block->UserData() = VMA_NULL; + --m_BlocksFreeCount; + m_BlocksFreeSize -= block->size; +} + +void VmaBlockMetadata_TLSF::InsertFreeBlock(Block* block) +{ + VMA_ASSERT(block != m_NullBlock); + VMA_ASSERT(!block->IsFree() && "Cannot insert block twice!"); + + uint8_t memClass = SizeToMemoryClass(block->size); + uint16_t secondIndex = SizeToSecondIndex(block->size, memClass); + uint32_t index = GetListIndex(memClass, secondIndex); + VMA_ASSERT(index < m_ListsCount); + block->PrevFree() = VMA_NULL; + block->NextFree() = m_FreeList[index]; + m_FreeList[index] = block; + if (block->NextFree() != VMA_NULL) + block->NextFree()->PrevFree() = block; + else + { + m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex; + m_IsFreeBitmap |= 1UL << memClass; + } + ++m_BlocksFreeCount; + m_BlocksFreeSize += block->size; +} + +void VmaBlockMetadata_TLSF::MergeBlock(Block* block, Block* prev) +{ + VMA_ASSERT(block->prevPhysical == prev && "Cannot merge seperate physical regions!"); + VMA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!"); + + block->offset = prev->offset; + block->size += prev->size; + block->prevPhysical = prev->prevPhysical; + if (block->prevPhysical) + block->prevPhysical->nextPhysical = block; + m_BlockAllocator.Free(prev); +} + +VmaBlockMetadata_TLSF::Block* VmaBlockMetadata_TLSF::FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const +{ + uint8_t memoryClass = SizeToMemoryClass(size); + uint32_t innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass)); + if (!innerFreeMap) + { + // Check higher levels for avaiable blocks + uint32_t freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1)); + if (!freeMap) + return VMA_NULL; // No more memory avaible + + // Find lowest free region + memoryClass = VMA_BITSCAN_LSB(freeMap); + innerFreeMap = m_InnerIsFreeBitmap[memoryClass]; + VMA_ASSERT(innerFreeMap != 0); + } + // Find lowest free subregion + listIndex = GetListIndex(memoryClass, VMA_BITSCAN_LSB(innerFreeMap)); + VMA_ASSERT(m_FreeList[listIndex]); + return m_FreeList[listIndex]; +} + +bool VmaBlockMetadata_TLSF::CheckBlock( + Block& block, + uint32_t listIndex, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(block.IsFree() && "Block is already taken!"); + + VkDeviceSize alignedOffset = VmaAlignUp(block.offset, allocAlignment); + if (block.size < allocSize + alignedOffset - block.offset) + return false; + + // Check for granularity conflicts + if (!IsVirtual() && + m_GranularityHandler.CheckConflictAndAlignUp(alignedOffset, allocSize, block.offset, block.size, allocType)) + return false; + + // Alloc successful + pAllocationRequest->type = VmaAllocationRequestType::TLSF; + pAllocationRequest->allocHandle = (VmaAllocHandle)█ + pAllocationRequest->size = allocSize - GetDebugMargin(); + pAllocationRequest->customData = (void*)allocType; + pAllocationRequest->algorithmData = alignedOffset; + + // Place block at the start of list if it's normal block + if (listIndex != m_ListsCount && block.PrevFree()) + { + block.PrevFree()->NextFree() = block.NextFree(); + if (block.NextFree()) + block.NextFree()->PrevFree() = block.PrevFree(); + block.PrevFree() = VMA_NULL; + block.NextFree() = m_FreeList[listIndex]; + m_FreeList[listIndex] = █ + if (block.NextFree()) + block.NextFree()->PrevFree() = █ + } + + return true; +} +#endif // _VMA_BLOCK_METADATA_TLSF_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_TLSF + +#ifndef _VMA_BLOCK_VECTOR +/* +Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific +Vulkan memory type. + +Synchronized internally with a mutex. +*/ +class VmaBlockVector +{ + friend struct VmaDefragmentationContext_T; + VMA_CLASS_NO_COPY(VmaBlockVector) +public: + VmaBlockVector( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceSize preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + VkDeviceSize bufferImageGranularity, + bool explicitBlockSize, + uint32_t algorithm, + float priority, + VkDeviceSize minAllocationAlignment, + void* pMemoryAllocateNext); + ~VmaBlockVector(); + + VmaAllocator GetAllocator() const { return m_hAllocator; } + VmaPool GetParentPool() const { return m_hParentPool; } + bool IsCustomPool() const { return m_hParentPool != VMA_NULL; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; } + VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } + uint32_t GetAlgorithm() const { return m_Algorithm; } + bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; } + float GetPriority() const { return m_Priority; } + const void* GetAllocationNextPtr() const { return m_pMemoryAllocateNext; } + // To be used only while the m_Mutex is locked. Used during defragmentation. + size_t GetBlockCount() const { return m_Blocks.size(); } + // To be used only while the m_Mutex is locked. Used during defragmentation. + VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; } + VMA_RW_MUTEX &GetMutex() { return m_Mutex; } + + VkResult CreateMinBlocks(); + void AddStatistics(VmaStatistics& inoutStats); + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats); + bool IsEmpty(); + bool IsCorruptionDetectionEnabled() const; + + VkResult Allocate( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + void Free(const VmaAllocation hAllocation); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json); +#endif + + VkResult CheckCorruption(); + +private: + const VmaAllocator m_hAllocator; + const VmaPool m_hParentPool; + const uint32_t m_MemoryTypeIndex; + const VkDeviceSize m_PreferredBlockSize; + const size_t m_MinBlockCount; + const size_t m_MaxBlockCount; + const VkDeviceSize m_BufferImageGranularity; + const bool m_ExplicitBlockSize; + const uint32_t m_Algorithm; + const float m_Priority; + const VkDeviceSize m_MinAllocationAlignment; + + void* const m_pMemoryAllocateNext; + VMA_RW_MUTEX m_Mutex; + // Incrementally sorted by sumFreeSize, ascending. + VmaVector> m_Blocks; + uint32_t m_NextBlockId; + bool m_IncrementalSort = true; + + void SetIncrementalSort(bool val) { m_IncrementalSort = val; } + + VkDeviceSize CalcMaxBlockSize() const; + // Finds and removes given block from vector. + void Remove(VmaDeviceMemoryBlock* pBlock); + // Performs single step in sorting m_Blocks. They may not be fully sorted + // after this call. + void IncrementallySortBlocks(); + void SortByFreeSize(); + + VkResult AllocatePage( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation); + + VkResult AllocateFromBlock( + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize size, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + uint32_t strategy, + VmaAllocation* pAllocation); + + VkResult CommitAllocationRequest( + VmaAllocationRequest& allocRequest, + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation); + + VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex); + bool HasEmptyBlock(); +}; +#endif // _VMA_BLOCK_VECTOR + +#ifndef _VMA_DEFRAGMENTATION_CONTEXT +struct VmaDefragmentationContext_T +{ + VMA_CLASS_NO_COPY(VmaDefragmentationContext_T) +public: + VmaDefragmentationContext_T( + VmaAllocator hAllocator, + const VmaDefragmentationInfo& info); + ~VmaDefragmentationContext_T(); + + void GetStats(VmaDefragmentationStats& outStats) { outStats = m_GlobalStats; } + + VkResult DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo); + VkResult DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo); + +private: + // Max number of allocations to ignore due to size constraints before ending single pass + static const uint8_t MAX_ALLOCS_TO_IGNORE = 16; + enum class CounterStatus { Pass, Ignore, End }; + + struct FragmentedBlock + { + uint32_t data; + VmaDeviceMemoryBlock* block; + }; + struct StateBalanced + { + VkDeviceSize avgFreeSize = 0; + VkDeviceSize avgAllocSize = UINT64_MAX; + }; + struct StateExtensive + { + enum class Operation : uint8_t + { + FindFreeBlockBuffer, FindFreeBlockTexture, FindFreeBlockAll, + MoveBuffers, MoveTextures, MoveAll, + Cleanup, Done + }; + + Operation operation = Operation::FindFreeBlockTexture; + size_t firstFreeBlock = SIZE_MAX; + }; + struct MoveAllocationData + { + VkDeviceSize size; + VkDeviceSize alignment; + VmaSuballocationType type; + VmaAllocationCreateFlags flags; + VmaDefragmentationMove move = {}; + }; + + const VkDeviceSize m_MaxPassBytes; + const uint32_t m_MaxPassAllocations; + + VmaStlAllocator m_MoveAllocator; + VmaVector> m_Moves; + + uint8_t m_IgnoredAllocs = 0; + uint32_t m_Algorithm; + uint32_t m_BlockVectorCount; + VmaBlockVector* m_PoolBlockVector; + VmaBlockVector** m_pBlockVectors; + size_t m_ImmovableBlockCount = 0; + VmaDefragmentationStats m_GlobalStats = { 0 }; + VmaDefragmentationStats m_PassStats = { 0 }; + void* m_AlgorithmState = VMA_NULL; + + static MoveAllocationData GetMoveData(VmaAllocHandle handle, VmaBlockMetadata* metadata); + CounterStatus CheckCounters(VkDeviceSize bytes); + bool IncrementCounters(VkDeviceSize bytes); + bool ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block); + bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector); + + bool ComputeDefragmentation(VmaBlockVector& vector, size_t index); + bool ComputeDefragmentation_Fast(VmaBlockVector& vector); + bool ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update); + bool ComputeDefragmentation_Full(VmaBlockVector& vector); + bool ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index); + + void UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state); + bool MoveDataToFreeBlocks(VmaSuballocationType currentType, + VmaBlockVector& vector, size_t firstFreeBlock, + bool& texturePresent, bool& bufferPresent, bool& otherPresent); +}; +#endif // _VMA_DEFRAGMENTATION_CONTEXT + +#ifndef _VMA_POOL_T +struct VmaPool_T +{ + friend struct VmaPoolListItemTraits; + VMA_CLASS_NO_COPY(VmaPool_T) +public: + VmaBlockVector m_BlockVector; + VmaDedicatedAllocationList m_DedicatedAllocations; + + VmaPool_T( + VmaAllocator hAllocator, + const VmaPoolCreateInfo& createInfo, + VkDeviceSize preferredBlockSize); + ~VmaPool_T(); + + uint32_t GetId() const { return m_Id; } + void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; } + + const char* GetName() const { return m_Name; } + void SetName(const char* pName); + +#if VMA_STATS_STRING_ENABLED + //void PrintDetailedMap(class VmaStringBuilder& sb); +#endif + +private: + uint32_t m_Id; + char* m_Name; + VmaPool_T* m_PrevPool = VMA_NULL; + VmaPool_T* m_NextPool = VMA_NULL; +}; + +struct VmaPoolListItemTraits +{ + typedef VmaPool_T ItemType; + + static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; } + static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; } + static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; } + static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; } +}; +#endif // _VMA_POOL_T + +#ifndef _VMA_CURRENT_BUDGET_DATA +struct VmaCurrentBudgetData +{ + VMA_ATOMIC_UINT32 m_BlockCount[VK_MAX_MEMORY_HEAPS]; + VMA_ATOMIC_UINT32 m_AllocationCount[VK_MAX_MEMORY_HEAPS]; + VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS]; + VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS]; + +#if VMA_MEMORY_BUDGET + VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch; + VMA_RW_MUTEX m_BudgetMutex; + uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS]; + uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS]; + uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS]; +#endif // VMA_MEMORY_BUDGET + + VmaCurrentBudgetData(); + + void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize); + void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize); +}; + +#ifndef _VMA_CURRENT_BUDGET_DATA_FUNCTIONS +VmaCurrentBudgetData::VmaCurrentBudgetData() +{ + for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex) + { + m_BlockCount[heapIndex] = 0; + m_AllocationCount[heapIndex] = 0; + m_BlockBytes[heapIndex] = 0; + m_AllocationBytes[heapIndex] = 0; +#if VMA_MEMORY_BUDGET + m_VulkanUsage[heapIndex] = 0; + m_VulkanBudget[heapIndex] = 0; + m_BlockBytesAtBudgetFetch[heapIndex] = 0; +#endif + } + +#if VMA_MEMORY_BUDGET + m_OperationsSinceBudgetFetch = 0; +#endif +} + +void VmaCurrentBudgetData::AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize) +{ + m_AllocationBytes[heapIndex] += allocationSize; + ++m_AllocationCount[heapIndex]; +#if VMA_MEMORY_BUDGET + ++m_OperationsSinceBudgetFetch; +#endif +} + +void VmaCurrentBudgetData::RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize) +{ + VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); + m_AllocationBytes[heapIndex] -= allocationSize; + VMA_ASSERT(m_AllocationCount[heapIndex] > 0); + --m_AllocationCount[heapIndex]; +#if VMA_MEMORY_BUDGET + ++m_OperationsSinceBudgetFetch; +#endif +} +#endif // _VMA_CURRENT_BUDGET_DATA_FUNCTIONS +#endif // _VMA_CURRENT_BUDGET_DATA + +#ifndef _VMA_ALLOCATION_OBJECT_ALLOCATOR +/* +Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects. +*/ +class VmaAllocationObjectAllocator +{ + VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator) +public: + VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) + : m_Allocator(pAllocationCallbacks, 1024) {} + + template VmaAllocation Allocate(Types&&... args); + void Free(VmaAllocation hAlloc); + +private: + VMA_MUTEX m_Mutex; + VmaPoolAllocator m_Allocator; +}; + +template +VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args) +{ + VmaMutexLock mutexLock(m_Mutex); + return m_Allocator.Alloc(std::forward(args)...); +} + +void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc) +{ + VmaMutexLock mutexLock(m_Mutex); + m_Allocator.Free(hAlloc); +} +#endif // _VMA_ALLOCATION_OBJECT_ALLOCATOR + +#ifndef _VMA_VIRTUAL_BLOCK_T +struct VmaVirtualBlock_T +{ + VMA_CLASS_NO_COPY(VmaVirtualBlock_T) +public: + const bool m_AllocationCallbacksSpecified; + const VkAllocationCallbacks m_AllocationCallbacks; + + VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo); + ~VmaVirtualBlock_T(); + + VkResult Init() { return VK_SUCCESS; } + bool IsEmpty() const { return m_Metadata->IsEmpty(); } + void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); } + void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); } + void Clear() { m_Metadata->Clear(); } + + const VkAllocationCallbacks* GetAllocationCallbacks() const; + void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo); + VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation, + VkDeviceSize* outOffset); + void GetStatistics(VmaStatistics& outStats) const; + void CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const; +#if VMA_STATS_STRING_ENABLED + void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const; +#endif + +private: + VmaBlockMetadata* m_Metadata; +}; + +#ifndef _VMA_VIRTUAL_BLOCK_T_FUNCTIONS +VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo) + : m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL), + m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks) +{ + const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK; + switch (algorithm) + { + default: + VMA_ASSERT(0); + case 0: + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true); + break; + case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT: + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, 1, true); + break; + } + + m_Metadata->Init(createInfo.size); +} + +VmaVirtualBlock_T::~VmaVirtualBlock_T() +{ + // Define macro VMA_DEBUG_LOG to receive the list of the unfreed allocations + if (!m_Metadata->IsEmpty()) + m_Metadata->DebugLogAllAllocations(); + // This is the most important assert in the entire library. + // Hitting it means you have some memory leak - unreleased virtual allocations. + VMA_ASSERT(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!"); + + vma_delete(GetAllocationCallbacks(), m_Metadata); +} + +const VkAllocationCallbacks* VmaVirtualBlock_T::GetAllocationCallbacks() const +{ + return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL; +} + +void VmaVirtualBlock_T::GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo) +{ + m_Metadata->GetAllocationInfo((VmaAllocHandle)allocation, outInfo); +} + +VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation, + VkDeviceSize* outOffset) +{ + VmaAllocationRequest request = {}; + if (m_Metadata->CreateAllocationRequest( + createInfo.size, // allocSize + VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment + (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress + VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant + createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy + &request)) + { + m_Metadata->Alloc(request, + VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant + createInfo.pUserData); + outAllocation = (VmaVirtualAllocation)request.allocHandle; + if(outOffset) + *outOffset = m_Metadata->GetAllocationOffset(request.allocHandle); + return VK_SUCCESS; + } + outAllocation = (VmaVirtualAllocation)VK_NULL_HANDLE; + if (outOffset) + *outOffset = UINT64_MAX; + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +void VmaVirtualBlock_T::GetStatistics(VmaStatistics& outStats) const +{ + VmaClearStatistics(outStats); + m_Metadata->AddStatistics(outStats); +} + +void VmaVirtualBlock_T::CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const +{ + VmaClearDetailedStatistics(outStats); + m_Metadata->AddDetailedStatistics(outStats); +} + +#if VMA_STATS_STRING_ENABLED +void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const +{ + VmaJsonWriter json(GetAllocationCallbacks(), sb); + json.BeginObject(); + + VmaDetailedStatistics stats; + CalculateDetailedStatistics(stats); + + json.WriteString("Stats"); + VmaPrintDetailedStatistics(json, stats); + + if (detailedMap) + { + json.WriteString("Details"); + json.BeginObject(); + m_Metadata->PrintDetailedMap(json); + json.EndObject(); + } + + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_VIRTUAL_BLOCK_T_FUNCTIONS +#endif // _VMA_VIRTUAL_BLOCK_T + + +// Main allocator object. +struct VmaAllocator_T +{ + VMA_CLASS_NO_COPY(VmaAllocator_T) +public: + bool m_UseMutex; + uint32_t m_VulkanApiVersion; + bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0). + bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0). + bool m_UseExtMemoryBudget; + bool m_UseAmdDeviceCoherentMemory; + bool m_UseKhrBufferDeviceAddress; + bool m_UseExtMemoryPriority; + VkDevice m_hDevice; + VkInstance m_hInstance; + bool m_AllocationCallbacksSpecified; + VkAllocationCallbacks m_AllocationCallbacks; + VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks; + VmaAllocationObjectAllocator m_AllocationObjectAllocator; + + // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size. + uint32_t m_HeapSizeLimitMask; + + VkPhysicalDeviceProperties m_PhysicalDeviceProperties; + VkPhysicalDeviceMemoryProperties m_MemProps; + + // Default pools. + VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES]; + VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES]; + + VmaCurrentBudgetData m_Budget; + VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects. + + VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); + VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo); + ~VmaAllocator_T(); + + const VkAllocationCallbacks* GetAllocationCallbacks() const + { + return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL; + } + const VmaVulkanFunctions& GetVulkanFunctions() const + { + return m_VulkanFunctions; + } + + VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; } + + VkDeviceSize GetBufferImageGranularity() const + { + return VMA_MAX( + static_cast(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY), + m_PhysicalDeviceProperties.limits.bufferImageGranularity); + } + + uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; } + uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; } + + uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const + { + VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount); + return m_MemProps.memoryTypes[memTypeIndex].heapIndex; + } + // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT. + bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const + { + return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) == + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } + // Minimum alignment for all allocations in specific memory type. + VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const + { + return IsMemoryTypeNonCoherent(memTypeIndex) ? + VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) : + (VkDeviceSize)VMA_MIN_ALIGNMENT; + } + + bool IsIntegratedGpu() const + { + return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + } + + uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; } + + void GetBufferMemoryRequirements( + VkBuffer hBuffer, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const; + void GetImageMemoryRequirements( + VkImage hImage, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const; + VkResult FindMemoryTypeIndex( + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkFlags bufImgUsage, // VkBufferCreateInfo::usage or VkImageCreateInfo::usage. UINT32_MAX if unknown. + uint32_t* pMemoryTypeIndex) const; + + // Main allocation function. + VkResult AllocateMemory( + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, // UINT32_MAX if unknown. + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Main deallocation function. + void FreeMemory( + size_t allocationCount, + const VmaAllocation* pAllocations); + + void CalculateStatistics(VmaTotalStatistics* pStats); + + void GetHeapBudgets( + VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json); +#endif + + void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo); + + VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool); + void DestroyPool(VmaPool pool); + void GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats); + void CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats); + + void SetCurrentFrameIndex(uint32_t frameIndex); + uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); } + + VkResult CheckPoolCorruption(VmaPool hPool); + VkResult CheckCorruption(uint32_t memoryTypeBits); + + // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping. + VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory); + // Call to Vulkan function vkFreeMemory with accompanying bookkeeping. + void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory); + // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR. + VkResult BindVulkanBuffer( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkBuffer buffer, + const void* pNext); + // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR. + VkResult BindVulkanImage( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkImage image, + const void* pNext); + + VkResult Map(VmaAllocation hAllocation, void** ppData); + void Unmap(VmaAllocation hAllocation); + + VkResult BindBufferMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext); + VkResult BindImageMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext); + + VkResult FlushOrInvalidateAllocation( + VmaAllocation hAllocation, + VkDeviceSize offset, VkDeviceSize size, + VMA_CACHE_OPERATION op); + VkResult FlushOrInvalidateAllocations( + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, const VkDeviceSize* sizes, + VMA_CACHE_OPERATION op); + + void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern); + + /* + Returns bit mask of memory types that can support defragmentation on GPU as + they support creation of required buffer for copy operations. + */ + uint32_t GetGpuDefragmentationMemoryTypeBits(); + +#if VMA_EXTERNAL_MEMORY + VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const + { + return m_TypeExternalMemoryHandleTypes[memTypeIndex]; + } +#endif // #if VMA_EXTERNAL_MEMORY + +private: + VkDeviceSize m_PreferredLargeHeapBlockSize; + + VkPhysicalDevice m_PhysicalDevice; + VMA_ATOMIC_UINT32 m_CurrentFrameIndex; + VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized. +#if VMA_EXTERNAL_MEMORY + VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES]; +#endif // #if VMA_EXTERNAL_MEMORY + + VMA_RW_MUTEX m_PoolsMutex; + typedef VmaIntrusiveLinkedList PoolList; + // Protected by m_PoolsMutex. + PoolList m_Pools; + uint32_t m_NextPoolId; + + VmaVulkanFunctions m_VulkanFunctions; + + // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types. + uint32_t m_GlobalMemoryTypeBits; + + void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions); + +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + void ImportVulkanFunctions_Static(); +#endif + + void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions); + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + void ImportVulkanFunctions_Dynamic(); +#endif + + void ValidateVulkanFunctions(); + + VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex); + + VkResult AllocateMemoryOfType( + VmaPool pool, + VkDeviceSize size, + VkDeviceSize alignment, + bool dedicatedPreferred, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, + const VmaAllocationCreateInfo& createInfo, + uint32_t memTypeIndex, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + VmaBlockVector& blockVector, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Helper function only to be used inside AllocateDedicatedMemory. + VkResult AllocateDedicatedMemoryPage( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + const VkMemoryAllocateInfo& allocInfo, + bool map, + bool isUserDataString, + bool isMappingAllowed, + void* pUserData, + VmaAllocation* pAllocation); + + // Allocates and registers new VkDeviceMemory specifically for dedicated allocations. + VkResult AllocateDedicatedMemory( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + uint32_t memTypeIndex, + bool map, + bool isUserDataString, + bool isMappingAllowed, + bool canAliasMemory, + void* pUserData, + float priority, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, + size_t allocationCount, + VmaAllocation* pAllocations, + const void* pNextChain = nullptr); + + void FreeDedicatedMemory(const VmaAllocation allocation); + + VkResult CalcMemTypeParams( + VmaAllocationCreateInfo& outCreateInfo, + uint32_t memTypeIndex, + VkDeviceSize size, + size_t allocationCount); + VkResult CalcAllocationParams( + VmaAllocationCreateInfo& outCreateInfo, + bool dedicatedRequired, + bool dedicatedPreferred); + + /* + Calculates and returns bit mask of memory types that can support defragmentation + on GPU as they support creation of required buffer for copy operations. + */ + uint32_t CalculateGpuDefragmentationMemoryTypeBits() const; + uint32_t CalculateGlobalMemoryTypeBits() const; + + bool GetFlushOrInvalidateRange( + VmaAllocation allocation, + VkDeviceSize offset, VkDeviceSize size, + VkMappedMemoryRange& outRange) const; + +#if VMA_MEMORY_BUDGET + void UpdateVulkanBudget(); +#endif // #if VMA_MEMORY_BUDGET +}; + + +#ifndef _VMA_MEMORY_FUNCTIONS +static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment) +{ + return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment); +} + +static void VmaFree(VmaAllocator hAllocator, void* ptr) +{ + VmaFree(&hAllocator->m_AllocationCallbacks, ptr); +} + +template +static T* VmaAllocate(VmaAllocator hAllocator) +{ + return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T)); +} + +template +static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count) +{ + return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T)); +} + +template +static void vma_delete(VmaAllocator hAllocator, T* ptr) +{ + if(ptr != VMA_NULL) + { + ptr->~T(); + VmaFree(hAllocator, ptr); + } +} + +template +static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count) +{ + if(ptr != VMA_NULL) + { + for(size_t i = count; i--; ) + ptr[i].~T(); + VmaFree(hAllocator, ptr); + } +} +#endif // _VMA_MEMORY_FUNCTIONS + +#ifndef _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS +VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) + : m_pMetadata(VMA_NULL), + m_MemoryTypeIndex(UINT32_MAX), + m_Id(0), + m_hMemory(VK_NULL_HANDLE), + m_MapCount(0), + m_pMappedData(VMA_NULL) {} + +VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock() +{ + VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped."); + VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); +} + +void VmaDeviceMemoryBlock::Init( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t newMemoryTypeIndex, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + uint32_t id, + uint32_t algorithm, + VkDeviceSize bufferImageGranularity) +{ + VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); + + m_hParentPool = hParentPool; + m_MemoryTypeIndex = newMemoryTypeIndex; + m_Id = id; + m_hMemory = newMemory; + + switch (algorithm) + { + case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + break; + default: + VMA_ASSERT(0); + // Fall-through. + case 0: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + } + m_pMetadata->Init(newSize); +} + +void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator) +{ + // Define macro VMA_DEBUG_LOG to receive the list of the unfreed allocations + if (!m_pMetadata->IsEmpty()) + m_pMetadata->DebugLogAllAllocations(); + // This is the most important assert in the entire library. + // Hitting it means you have some memory leak - unreleased VmaAllocation objects. + VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!"); + + VMA_ASSERT(m_hMemory != VK_NULL_HANDLE); + allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory); + m_hMemory = VK_NULL_HANDLE; + + vma_delete(allocator, m_pMetadata); + m_pMetadata = VMA_NULL; +} + +void VmaDeviceMemoryBlock::PostFree(VmaAllocator hAllocator) +{ + if(m_MappingHysteresis.PostFree()) + { + VMA_ASSERT(m_MappingHysteresis.GetExtraMapping() == 0); + if (m_MapCount == 0) + { + m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory); + } + } +} + +bool VmaDeviceMemoryBlock::Validate() const +{ + VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) && + (m_pMetadata->GetSize() != 0)); + + return m_pMetadata->Validate(); +} + +VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator) +{ + void* pData = nullptr; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + res = m_pMetadata->CheckCorruption(pData); + + Unmap(hAllocator, 1); + + return res; +} + +VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData) +{ + if (count == 0) + { + return VK_SUCCESS; + } + + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + const uint32_t oldTotalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping(); + m_MappingHysteresis.PostMap(); + if (oldTotalMapCount != 0) + { + m_MapCount += count; + VMA_ASSERT(m_pMappedData != VMA_NULL); + if (ppData != VMA_NULL) + { + *ppData = m_pMappedData; + } + return VK_SUCCESS; + } + else + { + VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( + hAllocator->m_hDevice, + m_hMemory, + 0, // offset + VK_WHOLE_SIZE, + 0, // flags + &m_pMappedData); + if (result == VK_SUCCESS) + { + if (ppData != VMA_NULL) + { + *ppData = m_pMappedData; + } + m_MapCount = count; + } + return result; + } +} + +void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count) +{ + if (count == 0) + { + return; + } + + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + if (m_MapCount >= count) + { + m_MapCount -= count; + const uint32_t totalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping(); + if (totalMapCount == 0) + { + m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory); + } + m_MappingHysteresis.PostUnmap(); + } + else + { + VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped."); + } +} + +VkResult VmaDeviceMemoryBlock::WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); + + void* pData; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + VmaWriteMagicValue(pData, allocOffset + allocSize); + + Unmap(hAllocator, 1); + return VK_SUCCESS; +} + +VkResult VmaDeviceMemoryBlock::ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); + + void* pData; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + if (!VmaValidateMagicValue(pData, allocOffset + allocSize)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!"); + } + + Unmap(hAllocator, 1); + return VK_SUCCESS; +} + +VkResult VmaDeviceMemoryBlock::BindBufferMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext) +{ + VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && + hAllocation->GetBlock() == this); + VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() && + "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?"); + const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset; + // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext); +} + +VkResult VmaDeviceMemoryBlock::BindImageMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext) +{ + VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && + hAllocation->GetBlock() == this); + VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() && + "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?"); + const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset; + // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext); +} +#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS + +#ifndef _VMA_ALLOCATION_T_FUNCTIONS +VmaAllocation_T::VmaAllocation_T(bool mappingAllowed) + : m_Alignment{ 1 }, + m_Size{ 0 }, + m_pUserData{ VMA_NULL }, + m_pName{ VMA_NULL }, + m_MemoryTypeIndex{ 0 }, + m_Type{ (uint8_t)ALLOCATION_TYPE_NONE }, + m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN }, + m_MapCount{ 0 }, + m_Flags{ 0 } +{ + if(mappingAllowed) + m_Flags |= (uint8_t)FLAG_MAPPING_ALLOWED; + +#if VMA_STATS_STRING_ENABLED + m_BufferImageUsage = 0; +#endif +} + +VmaAllocation_T::~VmaAllocation_T() +{ + VMA_ASSERT(m_MapCount == 0 && "Allocation was not unmapped before destruction."); + + // Check if owned string was freed. + VMA_ASSERT(m_pName == VMA_NULL); +} + +void VmaAllocation_T::InitBlockAllocation( + VmaDeviceMemoryBlock* block, + VmaAllocHandle allocHandle, + VkDeviceSize alignment, + VkDeviceSize size, + uint32_t memoryTypeIndex, + VmaSuballocationType suballocationType, + bool mapped) +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(block != VMA_NULL); + m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK; + m_Alignment = alignment; + m_Size = size; + m_MemoryTypeIndex = memoryTypeIndex; + if(mapped) + { + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP; + } + m_SuballocationType = (uint8_t)suballocationType; + m_BlockAllocation.m_Block = block; + m_BlockAllocation.m_AllocHandle = allocHandle; +} + +void VmaAllocation_T::InitDedicatedAllocation( + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceMemory hMemory, + VmaSuballocationType suballocationType, + void* pMappedData, + VkDeviceSize size) +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(hMemory != VK_NULL_HANDLE); + m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED; + m_Alignment = 0; + m_Size = size; + m_MemoryTypeIndex = memoryTypeIndex; + m_SuballocationType = (uint8_t)suballocationType; + if(pMappedData != VMA_NULL) + { + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP; + } + m_DedicatedAllocation.m_hParentPool = hParentPool; + m_DedicatedAllocation.m_hMemory = hMemory; + m_DedicatedAllocation.m_pMappedData = pMappedData; + m_DedicatedAllocation.m_Prev = VMA_NULL; + m_DedicatedAllocation.m_Next = VMA_NULL; +} + +void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName) +{ + VMA_ASSERT(pName == VMA_NULL || pName != m_pName); + + FreeName(hAllocator); + + if (pName != VMA_NULL) + m_pName = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), pName); +} + +uint8_t VmaAllocation_T::SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation) +{ + VMA_ASSERT(allocation != VMA_NULL); + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + VMA_ASSERT(allocation->m_Type == ALLOCATION_TYPE_BLOCK); + + if (m_MapCount != 0) + m_BlockAllocation.m_Block->Unmap(hAllocator, m_MapCount); + + m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, allocation); + VMA_SWAP(m_BlockAllocation, allocation->m_BlockAllocation); + m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, this); + +#if VMA_STATS_STRING_ENABLED + VMA_SWAP(m_BufferImageUsage, allocation->m_BufferImageUsage); +#endif + return m_MapCount; +} + +VmaAllocHandle VmaAllocation_T::GetAllocHandle() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_AllocHandle; + case ALLOCATION_TYPE_DEDICATED: + return VK_NULL_HANDLE; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +VkDeviceSize VmaAllocation_T::GetOffset() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->m_pMetadata->GetAllocationOffset(m_BlockAllocation.m_AllocHandle); + case ALLOCATION_TYPE_DEDICATED: + return 0; + default: + VMA_ASSERT(0); + return 0; + } +} + +VmaPool VmaAllocation_T::GetParentPool() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->GetParentPool(); + case ALLOCATION_TYPE_DEDICATED: + return m_DedicatedAllocation.m_hParentPool; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +VkDeviceMemory VmaAllocation_T::GetMemory() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->GetDeviceMemory(); + case ALLOCATION_TYPE_DEDICATED: + return m_DedicatedAllocation.m_hMemory; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +void* VmaAllocation_T::GetMappedData() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + if (m_MapCount != 0 || IsPersistentMap()) + { + void* pBlockData = m_BlockAllocation.m_Block->GetMappedData(); + VMA_ASSERT(pBlockData != VMA_NULL); + return (char*)pBlockData + GetOffset(); + } + else + { + return VMA_NULL; + } + break; + case ALLOCATION_TYPE_DEDICATED: + VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap())); + return m_DedicatedAllocation.m_pMappedData; + default: + VMA_ASSERT(0); + return VMA_NULL; + } +} + +void VmaAllocation_T::BlockAllocMap() +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + + if (m_MapCount < 0xFF) + { + ++m_MapCount; + } + else + { + VMA_ASSERT(0 && "Allocation mapped too many times simultaneously."); + } +} + +void VmaAllocation_T::BlockAllocUnmap() +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); + + if (m_MapCount > 0) + { + --m_MapCount; + } + else + { + VMA_ASSERT(0 && "Unmapping allocation not previously mapped."); + } +} + +VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData) +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + + if (m_MapCount != 0 || IsPersistentMap()) + { + if (m_MapCount < 0xFF) + { + VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL); + *ppData = m_DedicatedAllocation.m_pMappedData; + ++m_MapCount; + return VK_SUCCESS; + } + else + { + VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously."); + return VK_ERROR_MEMORY_MAP_FAILED; + } + } + else + { + VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( + hAllocator->m_hDevice, + m_DedicatedAllocation.m_hMemory, + 0, // offset + VK_WHOLE_SIZE, + 0, // flags + ppData); + if (result == VK_SUCCESS) + { + m_DedicatedAllocation.m_pMappedData = *ppData; + m_MapCount = 1; + } + return result; + } +} + +void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator) +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); + + if (m_MapCount > 0) + { + --m_MapCount; + if (m_MapCount == 0 && !IsPersistentMap()) + { + m_DedicatedAllocation.m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)( + hAllocator->m_hDevice, + m_DedicatedAllocation.m_hMemory); + } + } + else + { + VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped."); + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaAllocation_T::InitBufferImageUsage(uint32_t bufferImageUsage) +{ + VMA_ASSERT(m_BufferImageUsage == 0); + m_BufferImageUsage = bufferImageUsage; +} + +void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const +{ + json.WriteString("Type"); + json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]); + + json.WriteString("Size"); + json.WriteNumber(m_Size); + json.WriteString("Usage"); + json.WriteNumber(m_BufferImageUsage); + + if (m_pUserData != VMA_NULL) + { + json.WriteString("CustomData"); + json.BeginString(); + json.ContinueString_Pointer(m_pUserData); + json.EndString(); + } + if (m_pName != VMA_NULL) + { + json.WriteString("Name"); + json.WriteString(m_pName); + } +} +#endif // VMA_STATS_STRING_ENABLED + +void VmaAllocation_T::FreeName(VmaAllocator hAllocator) +{ + if(m_pName) + { + VmaFreeString(hAllocator->GetAllocationCallbacks(), m_pName); + m_pName = VMA_NULL; + } +} +#endif // _VMA_ALLOCATION_T_FUNCTIONS + +#ifndef _VMA_BLOCK_VECTOR_FUNCTIONS +VmaBlockVector::VmaBlockVector( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceSize preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + VkDeviceSize bufferImageGranularity, + bool explicitBlockSize, + uint32_t algorithm, + float priority, + VkDeviceSize minAllocationAlignment, + void* pMemoryAllocateNext) + : m_hAllocator(hAllocator), + m_hParentPool(hParentPool), + m_MemoryTypeIndex(memoryTypeIndex), + m_PreferredBlockSize(preferredBlockSize), + m_MinBlockCount(minBlockCount), + m_MaxBlockCount(maxBlockCount), + m_BufferImageGranularity(bufferImageGranularity), + m_ExplicitBlockSize(explicitBlockSize), + m_Algorithm(algorithm), + m_Priority(priority), + m_MinAllocationAlignment(minAllocationAlignment), + m_pMemoryAllocateNext(pMemoryAllocateNext), + m_Blocks(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_NextBlockId(0) {} + +VmaBlockVector::~VmaBlockVector() +{ + for (size_t i = m_Blocks.size(); i--; ) + { + m_Blocks[i]->Destroy(m_hAllocator); + vma_delete(m_hAllocator, m_Blocks[i]); + } +} + +VkResult VmaBlockVector::CreateMinBlocks() +{ + for (size_t i = 0; i < m_MinBlockCount; ++i) + { + VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL); + if (res != VK_SUCCESS) + { + return res; + } + } + return VK_SUCCESS; +} + +void VmaBlockVector::AddStatistics(VmaStatistics& inoutStats) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + const size_t blockCount = m_Blocks.size(); + for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VMA_HEAVY_ASSERT(pBlock->Validate()); + pBlock->m_pMetadata->AddStatistics(inoutStats); + } +} + +void VmaBlockVector::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + const size_t blockCount = m_Blocks.size(); + for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VMA_HEAVY_ASSERT(pBlock->Validate()); + pBlock->m_pMetadata->AddDetailedStatistics(inoutStats); + } +} + +bool VmaBlockVector::IsEmpty() +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + return m_Blocks.empty(); +} + +bool VmaBlockVector::IsCorruptionDetectionEnabled() const +{ + const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + return (VMA_DEBUG_DETECT_CORRUPTION != 0) && + (VMA_DEBUG_MARGIN > 0) && + (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) && + (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags; +} + +VkResult VmaBlockVector::Allocate( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + size_t allocIndex; + VkResult res = VK_SUCCESS; + + alignment = VMA_MAX(alignment, m_MinAllocationAlignment); + + if (IsCorruptionDetectionEnabled()) + { + size = VmaAlignUp(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE)); + alignment = VmaAlignUp(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE)); + } + + { + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocatePage( + size, + alignment, + createInfo, + suballocType, + pAllocations + allocIndex); + if (res != VK_SUCCESS) + { + break; + } + } + } + + if (res != VK_SUCCESS) + { + // Free all already created allocations. + while (allocIndex--) + Free(pAllocations[allocIndex]); + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + } + + return res; +} + +VkResult VmaBlockVector::AllocatePage( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation) +{ + const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; + + VkDeviceSize freeMemory; + { + const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex); + VmaBudget heapBudget = {}; + m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1); + freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0; + } + + const bool canFallbackToDedicated = !HasExplicitBlockSize() && + (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0; + const bool canCreateNewBlock = + ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) && + (m_Blocks.size() < m_MaxBlockCount) && + (freeMemory >= size || !canFallbackToDedicated); + uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK; + + // Upper address can only be used with linear allocator and within single memory block. + if (isUpperAddress && + (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1)) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + // Early reject: requested allocation size is larger that maximum block size for this block vector. + if (size + VMA_DEBUG_MARGIN > m_PreferredBlockSize) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + + // 1. Search existing allocations. Try to allocate. + if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + { + // Use only last block. + if (!m_Blocks.empty()) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back(); + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + else + { + if (strategy != VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) // MIN_MEMORY or default + { + const bool isHostVisible = + (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; + if(isHostVisible) + { + const bool isMappingAllowed = (createInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0; + /* + For non-mappable allocations, check blocks that are not mapped first. + For mappable allocations, check blocks that are already mapped first. + This way, having many blocks, we will separate mappable and non-mappable allocations, + hopefully limiting the number of blocks that are mapped, which will help tools like RenderDoc. + */ + for(size_t mappingI = 0; mappingI < 2; ++mappingI) + { + // Forward order in m_Blocks - prefer blocks with smallest amount of free space. + for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + const bool isBlockMapped = pCurrBlock->GetMappedData() != VMA_NULL; + if((mappingI == 0) == (isMappingAllowed == isBlockMapped)) + { + VkResult res = AllocateFromBlock( + pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + } + } + else + { + // Forward order in m_Blocks - prefer blocks with smallest amount of free space. + for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + } + else // VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT + { + // Backward order in m_Blocks - prefer blocks with largest amount of free space. + for (size_t blockIndex = m_Blocks.size(); blockIndex--; ) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock(pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + } + + // 2. Try to create new block. + if (canCreateNewBlock) + { + // Calculate optimal size for new block. + VkDeviceSize newBlockSize = m_PreferredBlockSize; + uint32_t newBlockSizeShift = 0; + const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3; + + if (!m_ExplicitBlockSize) + { + // Allocate 1/8, 1/4, 1/2 as first blocks. + const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize(); + for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i) + { + const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; + if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + } + else + { + break; + } + } + } + + size_t newBlockIndex = 0; + VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ? + CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY; + // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize. + if (!m_ExplicitBlockSize) + { + while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX) + { + const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; + if (smallerNewBlockSize >= size) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ? + CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + else + { + break; + } + } + } + + if (res == VK_SUCCESS) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex]; + VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size); + + res = AllocateFromBlock( + pBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + else + { + // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment. + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + } + + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +void VmaBlockVector::Free(const VmaAllocation hAllocation) +{ + VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL; + + bool budgetExceeded = false; + { + const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex); + VmaBudget heapBudget = {}; + m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1); + budgetExceeded = heapBudget.usage >= heapBudget.budget; + } + + // Scope for lock. + { + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + + VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); + + if (IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->ValidateMagicValueAfterAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize()); + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value."); + } + + if (hAllocation->IsPersistentMap()) + { + pBlock->Unmap(m_hAllocator, 1); + } + + const bool hadEmptyBlockBeforeFree = HasEmptyBlock(); + pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle()); + pBlock->PostFree(m_hAllocator); + VMA_HEAVY_ASSERT(pBlock->Validate()); + + VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex); + + const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount; + // pBlock became empty after this deallocation. + if (pBlock->m_pMetadata->IsEmpty()) + { + // Already had empty block. We don't want to have two, so delete this one. + if ((hadEmptyBlockBeforeFree || budgetExceeded) && canDeleteBlock) + { + pBlockToDelete = pBlock; + Remove(pBlock); + } + // else: We now have one empty block - leave it. A hysteresis to avoid allocating whole block back and forth. + } + // pBlock didn't become empty, but we have another empty block - find and free that one. + // (This is optional, heuristics.) + else if (hadEmptyBlockBeforeFree && canDeleteBlock) + { + VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back(); + if (pLastBlock->m_pMetadata->IsEmpty()) + { + pBlockToDelete = pLastBlock; + m_Blocks.pop_back(); + } + } + + IncrementallySortBlocks(); + } + + // Destruction of a free block. Deferred until this point, outside of mutex + // lock, for performance reason. + if (pBlockToDelete != VMA_NULL) + { + VMA_DEBUG_LOG(" Deleted empty block #%u", pBlockToDelete->GetId()); + pBlockToDelete->Destroy(m_hAllocator); + vma_delete(m_hAllocator, pBlockToDelete); + } + + m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize()); + m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation); +} + +VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const +{ + VkDeviceSize result = 0; + for (size_t i = m_Blocks.size(); i--; ) + { + result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize()); + if (result >= m_PreferredBlockSize) + { + break; + } + } + return result; +} + +void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock) +{ + for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + if (m_Blocks[blockIndex] == pBlock) + { + VmaVectorRemove(m_Blocks, blockIndex); + return; + } + } + VMA_ASSERT(0); +} + +void VmaBlockVector::IncrementallySortBlocks() +{ + if (!m_IncrementalSort) + return; + if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + { + // Bubble sort only until first swap. + for (size_t i = 1; i < m_Blocks.size(); ++i) + { + if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize()) + { + VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]); + return; + } + } + } +} + +void VmaBlockVector::SortByFreeSize() +{ + VMA_SORT(m_Blocks.begin(), m_Blocks.end(), + [](VmaDeviceMemoryBlock* b1, VmaDeviceMemoryBlock* b2) -> bool + { + return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize(); + }); +} + +VkResult VmaBlockVector::AllocateFromBlock( + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize size, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + uint32_t strategy, + VmaAllocation* pAllocation) +{ + const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; + + VmaAllocationRequest currRequest = {}; + if (pBlock->m_pMetadata->CreateAllocationRequest( + size, + alignment, + isUpperAddress, + suballocType, + strategy, + &currRequest)) + { + return CommitAllocationRequest(currRequest, pBlock, alignment, allocFlags, pUserData, suballocType, pAllocation); + } + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +VkResult VmaBlockVector::CommitAllocationRequest( + VmaAllocationRequest& allocRequest, + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation) +{ + const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; + const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0; + const bool isMappingAllowed = (allocFlags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0; + + pBlock->PostAlloc(); + // Allocate from pCurrBlock. + if (mapped) + { + VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL); + if (res != VK_SUCCESS) + { + return res; + } + } + + *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(isMappingAllowed); + pBlock->m_pMetadata->Alloc(allocRequest, suballocType, *pAllocation); + (*pAllocation)->InitBlockAllocation( + pBlock, + allocRequest.allocHandle, + alignment, + allocRequest.size, // Not size, as actual allocation size may be larger than requested! + m_MemoryTypeIndex, + suballocType, + mapped); + VMA_HEAVY_ASSERT(pBlock->Validate()); + if (isUserDataString) + (*pAllocation)->SetName(m_hAllocator, (const char*)pUserData); + else + (*pAllocation)->SetUserData(m_hAllocator, pUserData); + m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), allocRequest.size); + if (VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); + } + if (IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->WriteMagicValueAfterAllocation(m_hAllocator, (*pAllocation)->GetOffset(), allocRequest.size); + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value."); + } + return VK_SUCCESS; +} + +VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex) +{ + VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocInfo.pNext = m_pMemoryAllocateNext; + allocInfo.memoryTypeIndex = m_MemoryTypeIndex; + allocInfo.allocationSize = blockSize; + +#if VMA_BUFFER_DEVICE_ADDRESS + // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature. + VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR }; + if (m_hAllocator->m_UseKhrBufferDeviceAddress) + { + allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo); + } +#endif // VMA_BUFFER_DEVICE_ADDRESS + +#if VMA_MEMORY_PRIORITY + VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT }; + if (m_hAllocator->m_UseExtMemoryPriority) + { + VMA_ASSERT(m_Priority >= 0.f && m_Priority <= 1.f); + priorityInfo.priority = m_Priority; + VmaPnextChainPushFront(&allocInfo, &priorityInfo); + } +#endif // VMA_MEMORY_PRIORITY + +#if VMA_EXTERNAL_MEMORY + // Attach VkExportMemoryAllocateInfoKHR if necessary. + VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR }; + exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex); + if (exportMemoryAllocInfo.handleTypes != 0) + { + VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo); + } +#endif // VMA_EXTERNAL_MEMORY + + VkDeviceMemory mem = VK_NULL_HANDLE; + VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem); + if (res < 0) + { + return res; + } + + // New VkDeviceMemory successfully created. + + // Create new Allocation for it. + VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator); + pBlock->Init( + m_hAllocator, + m_hParentPool, + m_MemoryTypeIndex, + mem, + allocInfo.allocationSize, + m_NextBlockId++, + m_Algorithm, + m_BufferImageGranularity); + + m_Blocks.push_back(pBlock); + if (pNewBlockIndex != VMA_NULL) + { + *pNewBlockIndex = m_Blocks.size() - 1; + } + + return VK_SUCCESS; +} + +bool VmaBlockVector::HasEmptyBlock() +{ + for (size_t index = 0, count = m_Blocks.size(); index < count; ++index) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[index]; + if (pBlock->m_pMetadata->IsEmpty()) + { + return true; + } + } + return false; +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + + json.BeginObject(); + for (size_t i = 0; i < m_Blocks.size(); ++i) + { + json.BeginString(); + json.ContinueString(m_Blocks[i]->GetId()); + json.EndString(); + + json.BeginObject(); + json.WriteString("MapRefCount"); + json.WriteNumber(m_Blocks[i]->GetMapRefCount()); + + m_Blocks[i]->m_pMetadata->PrintDetailedMap(json); + json.EndObject(); + } + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED + +VkResult VmaBlockVector::CheckCorruption() +{ + if (!IsCorruptionDetectionEnabled()) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VkResult res = pBlock->CheckCorruption(m_hAllocator); + if (res != VK_SUCCESS) + { + return res; + } + } + return VK_SUCCESS; +} + +#endif // _VMA_BLOCK_VECTOR_FUNCTIONS + +#ifndef _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS +VmaDefragmentationContext_T::VmaDefragmentationContext_T( + VmaAllocator hAllocator, + const VmaDefragmentationInfo& info) + : m_MaxPassBytes(info.maxBytesPerPass == 0 ? VK_WHOLE_SIZE : info.maxBytesPerPass), + m_MaxPassAllocations(info.maxAllocationsPerPass == 0 ? UINT32_MAX : info.maxAllocationsPerPass), + m_MoveAllocator(hAllocator->GetAllocationCallbacks()), + m_Moves(m_MoveAllocator) +{ + m_Algorithm = info.flags & VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK; + + if (info.pool != VMA_NULL) + { + m_BlockVectorCount = 1; + m_PoolBlockVector = &info.pool->m_BlockVector; + m_pBlockVectors = &m_PoolBlockVector; + m_PoolBlockVector->SetIncrementalSort(false); + m_PoolBlockVector->SortByFreeSize(); + } + else + { + m_BlockVectorCount = hAllocator->GetMemoryTypeCount(); + m_PoolBlockVector = VMA_NULL; + m_pBlockVectors = hAllocator->m_pBlockVectors; + for (uint32_t i = 0; i < m_BlockVectorCount; ++i) + { + VmaBlockVector* vector = m_pBlockVectors[i]; + if (vector != VMA_NULL) + { + vector->SetIncrementalSort(false); + vector->SortByFreeSize(); + } + } + } + + switch (m_Algorithm) + { + case 0: // Default algorithm + m_Algorithm = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT; + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT: + { + m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount); + break; + } + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + { + if (hAllocator->GetBufferImageGranularity() > 1) + { + m_AlgorithmState = vma_new_array(hAllocator, StateExtensive, m_BlockVectorCount); + } + break; + } + } +} + +VmaDefragmentationContext_T::~VmaDefragmentationContext_T() +{ + if (m_PoolBlockVector != VMA_NULL) + { + m_PoolBlockVector->SetIncrementalSort(true); + } + else + { + for (uint32_t i = 0; i < m_BlockVectorCount; ++i) + { + VmaBlockVector* vector = m_pBlockVectors[i]; + if (vector != VMA_NULL) + vector->SetIncrementalSort(true); + } + } + + if (m_AlgorithmState) + { + switch (m_Algorithm) + { + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT: + vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast(m_AlgorithmState), m_BlockVectorCount); + break; + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast(m_AlgorithmState), m_BlockVectorCount); + break; + default: + VMA_ASSERT(0); + } + } +} + +VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo) +{ + if (m_PoolBlockVector != VMA_NULL) + { + VmaMutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->GetAllocator()->m_UseMutex); + + if (m_PoolBlockVector->GetBlockCount() > 1) + ComputeDefragmentation(*m_PoolBlockVector, 0); + else if (m_PoolBlockVector->GetBlockCount() == 1) + ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0)); + } + else + { + for (uint32_t i = 0; i < m_BlockVectorCount; ++i) + { + if (m_pBlockVectors[i] != VMA_NULL) + { + VmaMutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->GetAllocator()->m_UseMutex); + + if (m_pBlockVectors[i]->GetBlockCount() > 1) + { + if (ComputeDefragmentation(*m_pBlockVectors[i], i)) + break; + } + else if (m_pBlockVectors[i]->GetBlockCount() == 1) + { + if (ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0))) + break; + } + } + } + } + + moveInfo.moveCount = static_cast(m_Moves.size()); + if (moveInfo.moveCount > 0) + { + moveInfo.pMoves = m_Moves.data(); + return VK_INCOMPLETE; + } + + moveInfo.pMoves = VMA_NULL; + return VK_SUCCESS; +} + +VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo) +{ + VMA_ASSERT(moveInfo.moveCount > 0 ? moveInfo.pMoves != VMA_NULL : true); + + VkResult result = VK_SUCCESS; + VmaStlAllocator blockAllocator(m_MoveAllocator.m_pCallbacks); + VmaVector> immovableBlocks(blockAllocator); + VmaVector> mappedBlocks(blockAllocator); + + VmaAllocator allocator = VMA_NULL; + for (uint32_t i = 0; i < moveInfo.moveCount; ++i) + { + VmaDefragmentationMove& move = moveInfo.pMoves[i]; + size_t prevCount = 0, currentCount = 0; + VkDeviceSize freedBlockSize = 0; + + uint32_t vectorIndex; + VmaBlockVector* vector; + if (m_PoolBlockVector != VMA_NULL) + { + vectorIndex = 0; + vector = m_PoolBlockVector; + } + else + { + vectorIndex = move.srcAllocation->GetMemoryTypeIndex(); + vector = m_pBlockVectors[vectorIndex]; + VMA_ASSERT(vector != VMA_NULL); + } + + switch (move.operation) + { + case VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY: + { + uint8_t mapCount = move.srcAllocation->SwapBlockAllocation(vector->m_hAllocator, move.dstTmpAllocation); + if (mapCount > 0) + { + allocator = vector->m_hAllocator; + VmaDeviceMemoryBlock* newMapBlock = move.srcAllocation->GetBlock(); + bool notPresent = true; + for (FragmentedBlock& block : mappedBlocks) + { + if (block.block == newMapBlock) + { + notPresent = false; + block.data += mapCount; + break; + } + } + if (notPresent) + mappedBlocks.push_back({ mapCount, newMapBlock }); + } + + // Scope for locks, Free have it's own lock + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + prevCount = vector->GetBlockCount(); + freedBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize(); + } + vector->Free(move.dstTmpAllocation); + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + currentCount = vector->GetBlockCount(); + } + + result = VK_INCOMPLETE; + break; + } + case VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE: + { + m_PassStats.bytesMoved -= move.srcAllocation->GetSize(); + --m_PassStats.allocationsMoved; + vector->Free(move.dstTmpAllocation); + + VmaDeviceMemoryBlock* newBlock = move.srcAllocation->GetBlock(); + bool notPresent = true; + for (const FragmentedBlock& block : immovableBlocks) + { + if (block.block == newBlock) + { + notPresent = false; + break; + } + } + if (notPresent) + immovableBlocks.push_back({ vectorIndex, newBlock }); + break; + } + case VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY: + { + m_PassStats.bytesMoved -= move.srcAllocation->GetSize(); + --m_PassStats.allocationsMoved; + // Scope for locks, Free have it's own lock + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + prevCount = vector->GetBlockCount(); + freedBlockSize = move.srcAllocation->GetBlock()->m_pMetadata->GetSize(); + } + vector->Free(move.srcAllocation); + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + currentCount = vector->GetBlockCount(); + } + freedBlockSize *= prevCount - currentCount; + + VkDeviceSize dstBlockSize; + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + dstBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize(); + } + vector->Free(move.dstTmpAllocation); + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount()); + currentCount = vector->GetBlockCount(); + } + + result = VK_INCOMPLETE; + break; + } + default: + VMA_ASSERT(0); + } + + if (prevCount > currentCount) + { + size_t freedBlocks = prevCount - currentCount; + m_PassStats.deviceMemoryBlocksFreed += static_cast(freedBlocks); + m_PassStats.bytesFreed += freedBlockSize; + } + + switch (m_Algorithm) + { + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + { + if (m_AlgorithmState != VMA_NULL) + { + // Avoid unnecessary tries to allocate when new free block is avaiable + StateExtensive& state = reinterpret_cast(m_AlgorithmState)[vectorIndex]; + if (state.firstFreeBlock != SIZE_MAX) + { + const size_t diff = prevCount - currentCount; + if (state.firstFreeBlock >= diff) + { + state.firstFreeBlock -= diff; + if (state.firstFreeBlock != 0) + state.firstFreeBlock -= vector->GetBlock(state.firstFreeBlock - 1)->m_pMetadata->IsEmpty(); + } + else + state.firstFreeBlock = 0; + } + } + } + } + } + moveInfo.moveCount = 0; + moveInfo.pMoves = VMA_NULL; + m_Moves.clear(); + + // Update stats + m_GlobalStats.allocationsMoved += m_PassStats.allocationsMoved; + m_GlobalStats.bytesFreed += m_PassStats.bytesFreed; + m_GlobalStats.bytesMoved += m_PassStats.bytesMoved; + m_GlobalStats.deviceMemoryBlocksFreed += m_PassStats.deviceMemoryBlocksFreed; + m_PassStats = { 0 }; + + // Move blocks with immovable allocations according to algorithm + if (immovableBlocks.size() > 0) + { + switch (m_Algorithm) + { + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + { + if (m_AlgorithmState != VMA_NULL) + { + bool swapped = false; + // Move to the start of free blocks range + for (const FragmentedBlock& block : immovableBlocks) + { + StateExtensive& state = reinterpret_cast(m_AlgorithmState)[block.data]; + if (state.operation != StateExtensive::Operation::Cleanup) + { + VmaBlockVector* vector = m_pBlockVectors[block.data]; + VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + + for (size_t i = 0, count = vector->GetBlockCount() - m_ImmovableBlockCount; i < count; ++i) + { + if (vector->GetBlock(i) == block.block) + { + VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[vector->GetBlockCount() - ++m_ImmovableBlockCount]); + if (state.firstFreeBlock != SIZE_MAX) + { + if (i + 1 < state.firstFreeBlock) + { + if (state.firstFreeBlock > 1) + VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]); + else + --state.firstFreeBlock; + } + } + swapped = true; + break; + } + } + } + } + if (swapped) + result = VK_INCOMPLETE; + break; + } + } + default: + { + // Move to the begining + for (const FragmentedBlock& block : immovableBlocks) + { + VmaBlockVector* vector = m_pBlockVectors[block.data]; + VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + + for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i) + { + if (vector->GetBlock(i) == block.block) + { + VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]); + break; + } + } + } + break; + } + } + } + + // Bulk-map destination blocks + for (const FragmentedBlock& block : mappedBlocks) + { + VkResult res = block.block->Map(allocator, block.data, VMA_NULL); + VMA_ASSERT(res == VK_SUCCESS); + } + return result; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation(VmaBlockVector& vector, size_t index) +{ + switch (m_Algorithm) + { + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT: + return ComputeDefragmentation_Fast(vector); + default: + VMA_ASSERT(0); + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT: + return ComputeDefragmentation_Balanced(vector, index, true); + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT: + return ComputeDefragmentation_Full(vector); + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + return ComputeDefragmentation_Extensive(vector, index); + } +} + +VmaDefragmentationContext_T::MoveAllocationData VmaDefragmentationContext_T::GetMoveData( + VmaAllocHandle handle, VmaBlockMetadata* metadata) +{ + MoveAllocationData moveData; + moveData.move.srcAllocation = (VmaAllocation)metadata->GetAllocationUserData(handle); + moveData.size = moveData.move.srcAllocation->GetSize(); + moveData.alignment = moveData.move.srcAllocation->GetAlignment(); + moveData.type = moveData.move.srcAllocation->GetSuballocationType(); + moveData.flags = 0; + + if (moveData.move.srcAllocation->IsPersistentMap()) + moveData.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; + if (moveData.move.srcAllocation->IsMappingAllowed()) + moveData.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; + + return moveData; +} + +VmaDefragmentationContext_T::CounterStatus VmaDefragmentationContext_T::CheckCounters(VkDeviceSize bytes) +{ + // Ignore allocation if will exceed max size for copy + if (m_PassStats.bytesMoved + bytes > m_MaxPassBytes) + { + if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE) + return CounterStatus::Ignore; + else + return CounterStatus::End; + } + return CounterStatus::Pass; +} + +bool VmaDefragmentationContext_T::IncrementCounters(VkDeviceSize bytes) +{ + m_PassStats.bytesMoved += bytes; + // Early return when max found + if (++m_PassStats.allocationsMoved >= m_MaxPassAllocations || m_PassStats.bytesMoved >= m_MaxPassBytes) + { + VMA_ASSERT(m_PassStats.allocationsMoved == m_MaxPassAllocations || + m_PassStats.bytesMoved == m_MaxPassBytes && "Exceeded maximal pass threshold!"); + return true; + } + return false; +} + +bool VmaDefragmentationContext_T::ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block) +{ + VmaBlockMetadata* metadata = block->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + default: + VMA_ASSERT(0); + case CounterStatus::Pass: + break; + } + + VkDeviceSize offset = moveData.move.srcAllocation->GetOffset(); + if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + VmaAllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + moveData.type, + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (vector.CommitAllocationRequest( + request, + block, + moveData.alignment, + moveData.flags, + this, + moveData.type, + &moveData.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + return false; +} + +bool VmaDefragmentationContext_T::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector) +{ + for (; start < end; ++start) + { + VmaDeviceMemoryBlock* dstBlock = vector.GetBlock(start); + if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size) + { + if (vector.AllocateFromBlock(dstBlock, + data.size, + data.alignment, + data.flags, + this, + data.type, + 0, + &data.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(data.move); + if (IncrementCounters(data.size)) + return true; + break; + } + } + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Fast(VmaBlockVector& vector) +{ + // Move only between blocks + + // Go through allocations in last blocks and try to fit them inside first ones + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + default: + VMA_ASSERT(0); + case CounterStatus::Pass: + break; + } + + // Check all previous blocks for free space + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + } + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update) +{ + // Go over every allocation and try to fit it in previous blocks at lowest offsets, + // if not possible: realloc within single block to minimize offset (exclude offset == 0), + // but only if there are noticable gaps between them (some heuristic, ex. average size of allocation in block) + VMA_ASSERT(m_AlgorithmState != VMA_NULL); + + StateBalanced& vectorState = reinterpret_cast(m_AlgorithmState)[index]; + if (update && vectorState.avgAllocSize == UINT64_MAX) + UpdateVectorStatistics(vector, vectorState); + + const size_t startMoveCount = m_Moves.size(); + VkDeviceSize minimalFreeRegion = vectorState.avgFreeSize / 2; + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + VmaDeviceMemoryBlock* block = vector.GetBlock(i); + VmaBlockMetadata* metadata = block->m_pMetadata; + VkDeviceSize prevFreeRegionSize = 0; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + default: + VMA_ASSERT(0); + case CounterStatus::Pass: + break; + } + + // Check all previous blocks for free space + const size_t prevMoveCount = m_Moves.size(); + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + + VkDeviceSize nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle); + // If no room found then realloc within block for lower offset + VkDeviceSize offset = moveData.move.srcAllocation->GetOffset(); + if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + // Check if realloc will make sense + if (prevFreeRegionSize >= minimalFreeRegion || + nextFreeRegionSize >= minimalFreeRegion || + moveData.size <= vectorState.avgFreeSize || + moveData.size <= vectorState.avgAllocSize) + { + VmaAllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + moveData.type, + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (vector.CommitAllocationRequest( + request, + block, + moveData.alignment, + moveData.flags, + this, + moveData.type, + &moveData.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + prevFreeRegionSize = nextFreeRegionSize; + } + } + + // No moves perfomed, update statistics to current vector state + if (startMoveCount == m_Moves.size() && !update) + { + vectorState.avgAllocSize = UINT64_MAX; + return ComputeDefragmentation_Balanced(vector, index, false); + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Full(VmaBlockVector& vector) +{ + // Go over every allocation and try to fit it in previous blocks at lowest offsets, + // if not possible: realloc within single block to minimize offset (exclude offset == 0) + + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + VmaDeviceMemoryBlock* block = vector.GetBlock(i); + VmaBlockMetadata* metadata = block->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + default: + VMA_ASSERT(0); + case CounterStatus::Pass: + break; + } + + // Check all previous blocks for free space + const size_t prevMoveCount = m_Moves.size(); + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + + // If no room found then realloc within block for lower offset + VkDeviceSize offset = moveData.move.srcAllocation->GetOffset(); + if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + VmaAllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + moveData.type, + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (vector.CommitAllocationRequest( + request, + block, + moveData.alignment, + moveData.flags, + this, + moveData.type, + &moveData.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index) +{ + // First free single block, then populate it to the brim, then free another block, and so on + + // Fallback to previous algorithm since without granularity conflicts it can achieve max packing + if (vector.m_BufferImageGranularity == 1) + return ComputeDefragmentation_Full(vector); + + VMA_ASSERT(m_AlgorithmState != VMA_NULL); + + StateExtensive& vectorState = reinterpret_cast(m_AlgorithmState)[index]; + + bool texturePresent = false, bufferPresent = false, otherPresent = false; + switch (vectorState.operation) + { + case StateExtensive::Operation::Done: // Vector defragmented + return false; + case StateExtensive::Operation::FindFreeBlockBuffer: + case StateExtensive::Operation::FindFreeBlockTexture: + case StateExtensive::Operation::FindFreeBlockAll: + { + // No more blocks to free, just perform fast realloc and move to cleanup + if (vectorState.firstFreeBlock == 0) + { + vectorState.operation = StateExtensive::Operation::Cleanup; + return ComputeDefragmentation_Fast(vector); + } + + // No free blocks, have to clear last one + size_t last = (vectorState.firstFreeBlock == SIZE_MAX ? vector.GetBlockCount() : vectorState.firstFreeBlock) - 1; + VmaBlockMetadata* freeMetadata = vector.GetBlock(last)->m_pMetadata; + + const size_t prevMoveCount = m_Moves.size(); + for (VmaAllocHandle handle = freeMetadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = freeMetadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, freeMetadata); + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + default: + VMA_ASSERT(0); + case CounterStatus::Pass: + break; + } + + // Check all previous blocks for free space + if (AllocInOtherBlock(0, last, moveData, vector)) + { + // Full clear performed already + if (prevMoveCount != m_Moves.size() && freeMetadata->GetNextAllocation(handle) == VK_NULL_HANDLE) + reinterpret_cast(m_AlgorithmState)[index] = last; + return true; + } + } + + if (prevMoveCount == m_Moves.size()) + { + // Cannot perform full clear, have to move data in other blocks around + if (last != 0) + { + for (size_t i = last - 1; i; --i) + { + if (ReallocWithinBlock(vector, vector.GetBlock(i))) + return true; + } + } + + if (prevMoveCount == m_Moves.size()) + { + // No possible reallocs within blocks, try to move them around fast + return ComputeDefragmentation_Fast(vector); + } + } + else + { + switch (vectorState.operation) + { + case StateExtensive::Operation::FindFreeBlockBuffer: + vectorState.operation = StateExtensive::Operation::MoveBuffers; + break; + default: + VMA_ASSERT(0); + case StateExtensive::Operation::FindFreeBlockTexture: + vectorState.operation = StateExtensive::Operation::MoveTextures; + break; + case StateExtensive::Operation::FindFreeBlockAll: + vectorState.operation = StateExtensive::Operation::MoveAll; + break; + } + vectorState.firstFreeBlock = last; + // Nothing done, block found without reallocations, can perform another reallocs in same pass + return ComputeDefragmentation_Extensive(vector, index); + } + break; + } + case StateExtensive::Operation::MoveTextures: + { + if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL, vector, + vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent)) + { + if (texturePresent) + { + vectorState.operation = StateExtensive::Operation::FindFreeBlockTexture; + return ComputeDefragmentation_Extensive(vector, index); + } + + if (!bufferPresent && !otherPresent) + { + vectorState.operation = StateExtensive::Operation::Cleanup; + break; + } + + // No more textures to move, check buffers + vectorState.operation = StateExtensive::Operation::MoveBuffers; + bufferPresent = false; + otherPresent = false; + } + else + break; + } + case StateExtensive::Operation::MoveBuffers: + { + if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_BUFFER, vector, + vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent)) + { + if (bufferPresent) + { + vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer; + return ComputeDefragmentation_Extensive(vector, index); + } + + if (!otherPresent) + { + vectorState.operation = StateExtensive::Operation::Cleanup; + break; + } + + // No more buffers to move, check all others + vectorState.operation = StateExtensive::Operation::MoveAll; + otherPresent = false; + } + else + break; + } + case StateExtensive::Operation::MoveAll: + { + if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_FREE, vector, + vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent)) + { + if (otherPresent) + { + vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer; + return ComputeDefragmentation_Extensive(vector, index); + } + // Everything moved + vectorState.operation = StateExtensive::Operation::Cleanup; + } + break; + } + case StateExtensive::Operation::Cleanup: + // Cleanup is handled below so that other operations may reuse the cleanup code. This case is here to prevent the unhandled enum value warning (C4062). + break; + } + + if (vectorState.operation == StateExtensive::Operation::Cleanup) + { + // All other work done, pack data in blocks even tighter if possible + const size_t prevMoveCount = m_Moves.size(); + for (size_t i = 0; i < vector.GetBlockCount(); ++i) + { + if (ReallocWithinBlock(vector, vector.GetBlock(i))) + return true; + } + + if (prevMoveCount == m_Moves.size()) + vectorState.operation = StateExtensive::Operation::Done; + } + return false; +} + +void VmaDefragmentationContext_T::UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state) +{ + size_t allocCount = 0; + size_t freeCount = 0; + state.avgFreeSize = 0; + state.avgAllocSize = 0; + + for (size_t i = 0; i < vector.GetBlockCount(); ++i) + { + VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata; + + allocCount += metadata->GetAllocationCount(); + freeCount += metadata->GetFreeRegionsCount(); + state.avgFreeSize += metadata->GetSumFreeSize(); + state.avgAllocSize += metadata->GetSize(); + } + + state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount; + state.avgFreeSize /= freeCount; +} + +bool VmaDefragmentationContext_T::MoveDataToFreeBlocks(VmaSuballocationType currentType, + VmaBlockVector& vector, size_t firstFreeBlock, + bool& texturePresent, bool& bufferPresent, bool& otherPresent) +{ + const size_t prevMoveCount = m_Moves.size(); + for (size_t i = firstFreeBlock ; i;) + { + VmaDeviceMemoryBlock* block = vector.GetBlock(--i); + VmaBlockMetadata* metadata = block->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + default: + VMA_ASSERT(0); + case CounterStatus::Pass: + break; + } + + // Move only single type of resources at once + if (!VmaIsBufferImageGranularityConflict(moveData.type, currentType)) + { + // Try to fit allocation into free blocks + if (AllocInOtherBlock(firstFreeBlock, vector.GetBlockCount(), moveData, vector)) + return false; + } + + if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)) + texturePresent = true; + else if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_BUFFER)) + bufferPresent = true; + else + otherPresent = true; + } + } + return prevMoveCount == m_Moves.size(); +} +#endif // _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS + +#ifndef _VMA_POOL_T_FUNCTIONS +VmaPool_T::VmaPool_T( + VmaAllocator hAllocator, + const VmaPoolCreateInfo& createInfo, + VkDeviceSize preferredBlockSize) + : m_BlockVector( + hAllocator, + this, // hParentPool + createInfo.memoryTypeIndex, + createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize, + createInfo.minBlockCount, + createInfo.maxBlockCount, + (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(), + createInfo.blockSize != 0, // explicitBlockSize + createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm + createInfo.priority, + VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment), + createInfo.pMemoryAllocateNext), + m_Id(0), + m_Name(VMA_NULL) {} + +VmaPool_T::~VmaPool_T() +{ + VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL); +} + +void VmaPool_T::SetName(const char* pName) +{ + const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks(); + VmaFreeString(allocs, m_Name); + + if (pName != VMA_NULL) + { + m_Name = VmaCreateStringCopy(allocs, pName); + } + else + { + m_Name = VMA_NULL; + } +} +#endif // _VMA_POOL_T_FUNCTIONS + +#ifndef _VMA_ALLOCATOR_T_FUNCTIONS +VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : + m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0), + m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0), + m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0), + m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0), + m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0), + m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0), + m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0), + m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0), + m_hDevice(pCreateInfo->device), + m_hInstance(pCreateInfo->instance), + m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL), + m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ? + *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks), + m_AllocationObjectAllocator(&m_AllocationCallbacks), + m_HeapSizeLimitMask(0), + m_DeviceMemoryCount(0), + m_PreferredLargeHeapBlockSize(0), + m_PhysicalDevice(pCreateInfo->physicalDevice), + m_GpuDefragmentationMemoryTypeBits(UINT32_MAX), + m_NextPoolId(0), + m_GlobalMemoryTypeBits(UINT32_MAX) +{ + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + m_UseKhrDedicatedAllocation = false; + m_UseKhrBindMemory2 = false; + } + + if(VMA_DEBUG_DETECT_CORRUPTION) + { + // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it. + VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0); + } + + VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance); + + if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0)) + { +#if !(VMA_DEDICATED_ALLOCATION) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros."); + } +#endif +#if !(VMA_BIND_MEMORY2) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros."); + } +#endif + } +#if !(VMA_MEMORY_BUDGET) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros."); + } +#endif +#if !(VMA_BUFFER_DEVICE_ADDRESS) + if(m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); + } +#endif +#if VMA_VULKAN_VERSION < 1002000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0)) + { + VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros."); + } +#endif +#if VMA_VULKAN_VERSION < 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros."); + } +#endif +#if !(VMA_MEMORY_PRIORITY) + if(m_UseExtMemoryPriority) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); + } +#endif + + memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks)); + memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties)); + memset(&m_MemProps, 0, sizeof(m_MemProps)); + + memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors)); + memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions)); + +#if VMA_EXTERNAL_MEMORY + memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes)); +#endif // #if VMA_EXTERNAL_MEMORY + + if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL) + { + m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData; + m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate; + m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree; + } + + ImportVulkanFunctions(pCreateInfo->pVulkanFunctions); + + (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties); + (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps); + + VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT)); + VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY)); + VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity)); + VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize)); + + m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ? + pCreateInfo->preferredLargeHeapBlockSize : static_cast(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE); + + m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits(); + +#if VMA_EXTERNAL_MEMORY + if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL) + { + memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes, + sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount()); + } +#endif // #if VMA_EXTERNAL_MEMORY + + if(pCreateInfo->pHeapSizeLimit != VMA_NULL) + { + for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex) + { + const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex]; + if(limit != VK_WHOLE_SIZE) + { + m_HeapSizeLimitMask |= 1u << heapIndex; + if(limit < m_MemProps.memoryHeaps[heapIndex].size) + { + m_MemProps.memoryHeaps[heapIndex].size = limit; + } + } + } + } + + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + // Create only supported types + if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0) + { + const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex); + m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)( + this, + VK_NULL_HANDLE, // hParentPool + memTypeIndex, + preferredBlockSize, + 0, + SIZE_MAX, + GetBufferImageGranularity(), + false, // explicitBlockSize + 0, // algorithm + 0.5f, // priority (0.5 is the default per Vulkan spec) + GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment + VMA_NULL); // // pMemoryAllocateNext + // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here, + // becase minBlockCount is 0. + } + } +} + +VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo) +{ + VkResult res = VK_SUCCESS; + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + UpdateVulkanBudget(); + } +#endif // #if VMA_MEMORY_BUDGET + + return res; +} + +VmaAllocator_T::~VmaAllocator_T() +{ + VMA_ASSERT(m_Pools.IsEmpty()); + + for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; ) + { + vma_delete(this, m_pBlockVectors[memTypeIndex]); + } +} + +void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions) +{ +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + ImportVulkanFunctions_Static(); +#endif + + if(pVulkanFunctions != VMA_NULL) + { + ImportVulkanFunctions_Custom(pVulkanFunctions); + } + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + ImportVulkanFunctions_Dynamic(); +#endif + + ValidateVulkanFunctions(); +} + +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Static() +{ + // Vulkan 1.0 + m_VulkanFunctions.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr; + m_VulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetDeviceProcAddr; + m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties; + m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties; + m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory; + m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory; + m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory; + m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory; + m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges; + m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges; + m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory; + m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory; + m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements; + m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements; + m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer; + m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer; + m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage; + m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage; + m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer; + + // Vulkan 1.1 +#if VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2; + m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2; + m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2; + m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2; + m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2; + } +#endif + +#if VMA_VULKAN_VERSION >= 1003000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0)) + { + m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)vkGetDeviceBufferMemoryRequirements; + m_VulkanFunctions.vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)vkGetDeviceImageMemoryRequirements; + } +#endif +} + +#endif // VMA_STATIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions) +{ + VMA_ASSERT(pVulkanFunctions != VMA_NULL); + +#define VMA_COPY_IF_NOT_NULL(funcName) \ + if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName; + + VMA_COPY_IF_NOT_NULL(vkGetInstanceProcAddr); + VMA_COPY_IF_NOT_NULL(vkGetDeviceProcAddr); + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties); + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties); + VMA_COPY_IF_NOT_NULL(vkAllocateMemory); + VMA_COPY_IF_NOT_NULL(vkFreeMemory); + VMA_COPY_IF_NOT_NULL(vkMapMemory); + VMA_COPY_IF_NOT_NULL(vkUnmapMemory); + VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges); + VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges); + VMA_COPY_IF_NOT_NULL(vkBindBufferMemory); + VMA_COPY_IF_NOT_NULL(vkBindImageMemory); + VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkCreateBuffer); + VMA_COPY_IF_NOT_NULL(vkDestroyBuffer); + VMA_COPY_IF_NOT_NULL(vkCreateImage); + VMA_COPY_IF_NOT_NULL(vkDestroyImage); + VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer); + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR); + VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR); +#endif + +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR); + VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR); +#endif + +#if VMA_MEMORY_BUDGET + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR); +#endif + +#if VMA_VULKAN_VERSION >= 1003000 + VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements); +#endif + +#undef VMA_COPY_IF_NOT_NULL +} + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Dynamic() +{ + VMA_ASSERT(m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr && + "To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass " + "VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. " + "Other members can be null."); + +#define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \ + if(m_VulkanFunctions.memberName == VMA_NULL) \ + m_VulkanFunctions.memberName = \ + (functionPointerType)m_VulkanFunctions.vkGetInstanceProcAddr(m_hInstance, functionNameString); +#define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \ + if(m_VulkanFunctions.memberName == VMA_NULL) \ + m_VulkanFunctions.memberName = \ + (functionPointerType)m_VulkanFunctions.vkGetDeviceProcAddr(m_hDevice, functionNameString); + + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties"); + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties"); + VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory"); + VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory"); + VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory"); + VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory"); + VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges"); + VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges"); + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory"); + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer"); + VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer"); + VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage"); + VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage"); + VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer"); + +#if VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2"); + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2"); + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2"); + } +#endif + +#if VMA_DEDICATED_ALLOCATION + if(m_UseKhrDedicatedAllocation) + { + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR"); + } +#endif + +#if VMA_BIND_MEMORY2 + if(m_UseKhrBindMemory2) + { + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR"); + } +#endif // #if VMA_BIND_MEMORY2 + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR"); + } +#endif // #if VMA_MEMORY_BUDGET + +#if VMA_VULKAN_VERSION >= 1003000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0)) + { + VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirements, "vkGetDeviceBufferMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirements, "vkGetDeviceImageMemoryRequirements"); + } +#endif + +#undef VMA_FETCH_DEVICE_FUNC +#undef VMA_FETCH_INSTANCE_FUNC +} + +#endif // VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ValidateVulkanFunctions() +{ + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL); + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation) + { + VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL); + } +#endif + +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2) + { + VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL); + } +#endif + +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL); + } +#endif + +#if VMA_VULKAN_VERSION >= 1003000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0)) + { + VMA_ASSERT(m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetDeviceImageMemoryRequirements != VMA_NULL); + } +#endif +} + +VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex) +{ + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); + const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size; + const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE; + return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32); +} + +VkResult VmaAllocator_T::AllocateMemoryOfType( + VmaPool pool, + VkDeviceSize size, + VkDeviceSize alignment, + bool dedicatedPreferred, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, + const VmaAllocationCreateInfo& createInfo, + uint32_t memTypeIndex, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + VmaBlockVector& blockVector, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + VMA_ASSERT(pAllocations != VMA_NULL); + VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size); + + VmaAllocationCreateInfo finalCreateInfo = createInfo; + VkResult res = CalcMemTypeParams( + finalCreateInfo, + memTypeIndex, + size, + allocationCount); + if(res != VK_SUCCESS) + return res; + + if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) + { + return AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); + } + else + { + const bool canAllocateDedicated = + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 && + (pool == VK_NULL_HANDLE || !blockVector.HasExplicitBlockSize()); + + if(canAllocateDedicated) + { + // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size. + if(size > blockVector.GetPreferredBlockSize() / 2) + { + dedicatedPreferred = true; + } + // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget, + // which can quickly deplete maxMemoryAllocationCount: Don't prefer dedicated allocations when above + // 3/4 of the maximum allocation count. + if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4) + { + dedicatedPreferred = false; + } + + if(dedicatedPreferred) + { + res = AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); + if(res == VK_SUCCESS) + { + // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here. + VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); + return VK_SUCCESS; + } + } + } + + res = blockVector.Allocate( + size, + alignment, + finalCreateInfo, + suballocType, + allocationCount, + pAllocations); + if(res == VK_SUCCESS) + return VK_SUCCESS; + + // Try dedicated memory. + if(canAllocateDedicated && !dedicatedPreferred) + { + res = AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); + if(res == VK_SUCCESS) + { + // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here. + VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); + return VK_SUCCESS; + } + } + // Everything failed: Return error code. + VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); + return res; + } +} + +VkResult VmaAllocator_T::AllocateDedicatedMemory( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + uint32_t memTypeIndex, + bool map, + bool isUserDataString, + bool isMappingAllowed, + bool canAliasMemory, + void* pUserData, + float priority, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, + size_t allocationCount, + VmaAllocation* pAllocations, + const void* pNextChain) +{ + VMA_ASSERT(allocationCount > 0 && pAllocations); + + VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocInfo.memoryTypeIndex = memTypeIndex; + allocInfo.allocationSize = size; + allocInfo.pNext = pNextChain; + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR }; + if(!canAliasMemory) + { + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + if(dedicatedBuffer != VK_NULL_HANDLE) + { + VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE); + dedicatedAllocInfo.buffer = dedicatedBuffer; + VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo); + } + else if(dedicatedImage != VK_NULL_HANDLE) + { + dedicatedAllocInfo.image = dedicatedImage; + VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo); + } + } + } +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + +#if VMA_BUFFER_DEVICE_ADDRESS + VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR }; + if(m_UseKhrBufferDeviceAddress) + { + bool canContainBufferWithDeviceAddress = true; + if(dedicatedBuffer != VK_NULL_HANDLE) + { + canContainBufferWithDeviceAddress = dedicatedBufferImageUsage == UINT32_MAX || // Usage flags unknown + (dedicatedBufferImageUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0; + } + else if(dedicatedImage != VK_NULL_HANDLE) + { + canContainBufferWithDeviceAddress = false; + } + if(canContainBufferWithDeviceAddress) + { + allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo); + } + } +#endif // #if VMA_BUFFER_DEVICE_ADDRESS + +#if VMA_MEMORY_PRIORITY + VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT }; + if(m_UseExtMemoryPriority) + { + VMA_ASSERT(priority >= 0.f && priority <= 1.f); + priorityInfo.priority = priority; + VmaPnextChainPushFront(&allocInfo, &priorityInfo); + } +#endif // #if VMA_MEMORY_PRIORITY + +#if VMA_EXTERNAL_MEMORY + // Attach VkExportMemoryAllocateInfoKHR if necessary. + VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR }; + exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex); + if(exportMemoryAllocInfo.handleTypes != 0) + { + VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo); + } +#endif // #if VMA_EXTERNAL_MEMORY + + size_t allocIndex; + VkResult res = VK_SUCCESS; + for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocateDedicatedMemoryPage( + pool, + size, + suballocType, + memTypeIndex, + allocInfo, + map, + isUserDataString, + isMappingAllowed, + pUserData, + pAllocations + allocIndex); + if(res != VK_SUCCESS) + { + break; + } + } + + if(res == VK_SUCCESS) + { + for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + dedicatedAllocations.Register(pAllocations[allocIndex]); + } + VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex); + } + else + { + // Free all already created allocations. + while(allocIndex--) + { + VmaAllocation currAlloc = pAllocations[allocIndex]; + VkDeviceMemory hMemory = currAlloc->GetMemory(); + + /* + There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory + before vkFreeMemory. + + if(currAlloc->GetMappedData() != VMA_NULL) + { + (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); + } + */ + + FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory); + m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize()); + m_AllocationObjectAllocator.Free(currAlloc); + } + + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + } + + return res; +} + +VkResult VmaAllocator_T::AllocateDedicatedMemoryPage( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + const VkMemoryAllocateInfo& allocInfo, + bool map, + bool isUserDataString, + bool isMappingAllowed, + void* pUserData, + VmaAllocation* pAllocation) +{ + VkDeviceMemory hMemory = VK_NULL_HANDLE; + VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory); + if(res < 0) + { + VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); + return res; + } + + void* pMappedData = VMA_NULL; + if(map) + { + res = (*m_VulkanFunctions.vkMapMemory)( + m_hDevice, + hMemory, + 0, + VK_WHOLE_SIZE, + 0, + &pMappedData); + if(res < 0) + { + VMA_DEBUG_LOG(" vkMapMemory FAILED"); + FreeVulkanMemory(memTypeIndex, size, hMemory); + return res; + } + } + + *pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed); + (*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size); + if (isUserDataString) + (*pAllocation)->SetName(this, (const char*)pUserData); + else + (*pAllocation)->SetUserData(this, pUserData); + m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size); + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); + } + + return VK_SUCCESS; +} + +void VmaAllocator_T::GetBufferMemoryRequirements( + VkBuffer hBuffer, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const +{ +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR }; + memReqInfo.buffer = hBuffer; + + VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; + + VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR }; + VmaPnextChainPushFront(&memReq2, &memDedicatedReq); + + (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); + + memReq = memReq2.memoryRequirements; + requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE); + prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); + } + else +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + { + (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq); + requiresDedicatedAllocation = false; + prefersDedicatedAllocation = false; + } +} + +void VmaAllocator_T::GetImageMemoryRequirements( + VkImage hImage, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const +{ +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR }; + memReqInfo.image = hImage; + + VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; + + VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR }; + VmaPnextChainPushFront(&memReq2, &memDedicatedReq); + + (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); + + memReq = memReq2.memoryRequirements; + requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE); + prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); + } + else +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + { + (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq); + requiresDedicatedAllocation = false; + prefersDedicatedAllocation = false; + } +} + +VkResult VmaAllocator_T::FindMemoryTypeIndex( + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkFlags bufImgUsage, + uint32_t* pMemoryTypeIndex) const +{ + memoryTypeBits &= GetGlobalMemoryTypeBits(); + + if(pAllocationCreateInfo->memoryTypeBits != 0) + { + memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits; + } + + VkMemoryPropertyFlags requiredFlags = 0, preferredFlags = 0, notPreferredFlags = 0; + if(!FindMemoryPreferences( + IsIntegratedGpu(), + *pAllocationCreateInfo, + bufImgUsage, + requiredFlags, preferredFlags, notPreferredFlags)) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + *pMemoryTypeIndex = UINT32_MAX; + uint32_t minCost = UINT32_MAX; + for(uint32_t memTypeIndex = 0, memTypeBit = 1; + memTypeIndex < GetMemoryTypeCount(); + ++memTypeIndex, memTypeBit <<= 1) + { + // This memory type is acceptable according to memoryTypeBits bitmask. + if((memTypeBit & memoryTypeBits) != 0) + { + const VkMemoryPropertyFlags currFlags = + m_MemProps.memoryTypes[memTypeIndex].propertyFlags; + // This memory type contains requiredFlags. + if((requiredFlags & ~currFlags) == 0) + { + // Calculate cost as number of bits from preferredFlags not present in this memory type. + uint32_t currCost = VMA_COUNT_BITS_SET(preferredFlags & ~currFlags) + + VMA_COUNT_BITS_SET(currFlags & notPreferredFlags); + // Remember memory type with lowest cost. + if(currCost < minCost) + { + *pMemoryTypeIndex = memTypeIndex; + if(currCost == 0) + { + return VK_SUCCESS; + } + minCost = currCost; + } + } + } + } + return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT; +} + +VkResult VmaAllocator_T::CalcMemTypeParams( + VmaAllocationCreateInfo& inoutCreateInfo, + uint32_t memTypeIndex, + VkDeviceSize size, + size_t allocationCount) +{ + // If memory type is not HOST_VISIBLE, disable MAPPED. + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && + (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + inoutCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0) + { + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); + VmaBudget heapBudget = {}; + GetHeapBudgets(&heapBudget, heapIndex, 1); + if(heapBudget.usage + size * allocationCount > heapBudget.budget) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + return VK_SUCCESS; +} + +VkResult VmaAllocator_T::CalcAllocationParams( + VmaAllocationCreateInfo& inoutCreateInfo, + bool dedicatedRequired, + bool dedicatedPreferred) +{ + VMA_ASSERT((inoutCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) && + "Specifying both flags VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT and VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT is incorrect."); + VMA_ASSERT((((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) == 0 || + (inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0)) && + "Specifying VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT requires also VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT."); + if(inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST) + { + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0) + { + VMA_ASSERT((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0 && + "When using VMA_ALLOCATION_CREATE_MAPPED_BIT and usage = VMA_MEMORY_USAGE_AUTO*, you must also specify VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT."); + } + } + + // If memory is lazily allocated, it should be always dedicated. + if(dedicatedRequired || + inoutCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + + if(inoutCreateInfo.pool != VK_NULL_HANDLE) + { + if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations."); + return VK_ERROR_FEATURE_NOT_PRESENT; + } + inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority(); + } + + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense."); + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + if(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + + // Non-auto USAGE values imply HOST_ACCESS flags. + // And so does VMA_MEMORY_USAGE_UNKNOWN because it is used with custom pools. + // Which specific flag is used doesn't matter. They change things only when used with VMA_MEMORY_USAGE_AUTO*. + // Otherwise they just protect from assert on mapping. + if(inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO && + inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE && + inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_HOST) + { + if((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) == 0) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; + } + } + + return VK_SUCCESS; +} + +VkResult VmaAllocator_T::AllocateMemory( + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + + VMA_ASSERT(VmaIsPow2(vkMemReq.alignment)); + + if(vkMemReq.size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VmaAllocationCreateInfo createInfoFinal = createInfo; + VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation, prefersDedicatedAllocation); + if(res != VK_SUCCESS) + return res; + + if(createInfoFinal.pool != VK_NULL_HANDLE) + { + VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector; + return AllocateMemoryOfType( + createInfoFinal.pool, + vkMemReq.size, + vkMemReq.alignment, + prefersDedicatedAllocation, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + createInfoFinal, + blockVector.GetMemoryTypeIndex(), + suballocType, + createInfoFinal.pool->m_DedicatedAllocations, + blockVector, + allocationCount, + pAllocations); + } + else + { + // Bit mask of memory Vulkan types acceptable for this allocation. + uint32_t memoryTypeBits = vkMemReq.memoryTypeBits; + uint32_t memTypeIndex = UINT32_MAX; + res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex); + // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT. + if(res != VK_SUCCESS) + return res; + do + { + VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(blockVector && "Trying to use unsupported memory type!"); + res = AllocateMemoryOfType( + VK_NULL_HANDLE, + vkMemReq.size, + vkMemReq.alignment, + requiresDedicatedAllocation || prefersDedicatedAllocation, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + createInfoFinal, + memTypeIndex, + suballocType, + m_DedicatedAllocations[memTypeIndex], + *blockVector, + allocationCount, + pAllocations); + // Allocation succeeded + if(res == VK_SUCCESS) + return VK_SUCCESS; + + // Remove old memTypeIndex from list of possibilities. + memoryTypeBits &= ~(1u << memTypeIndex); + // Find alternative memTypeIndex. + res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex); + } while(res == VK_SUCCESS); + + // No other matching memory type index could be found. + // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once. + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } +} + +void VmaAllocator_T::FreeMemory( + size_t allocationCount, + const VmaAllocation* pAllocations) +{ + VMA_ASSERT(pAllocations); + + for(size_t allocIndex = allocationCount; allocIndex--; ) + { + VmaAllocation allocation = pAllocations[allocIndex]; + + if(allocation != VK_NULL_HANDLE) + { + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED); + } + + allocation->FreeName(this); + + switch(allocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaBlockVector* pBlockVector = VMA_NULL; + VmaPool hPool = allocation->GetParentPool(); + if(hPool != VK_NULL_HANDLE) + { + pBlockVector = &hPool->m_BlockVector; + } + else + { + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + pBlockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!"); + } + pBlockVector->Free(allocation); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + FreeDedicatedMemory(allocation); + break; + default: + VMA_ASSERT(0); + } + } + } +} + +void VmaAllocator_T::CalculateStatistics(VmaTotalStatistics* pStats) +{ + // Initialize. + VmaClearDetailedStatistics(pStats->total); + for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) + VmaClearDetailedStatistics(pStats->memoryType[i]); + for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i) + VmaClearDetailedStatistics(pStats->memoryHeap[i]); + + // Process default pools. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; + if (pBlockVector != VMA_NULL) + pBlockVector->AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + } + + // Process custom pools. + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) + { + VmaBlockVector& blockVector = pool->m_BlockVector; + const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex(); + blockVector.AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + pool->m_DedicatedAllocations.AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + } + } + + // Process dedicated allocations. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + m_DedicatedAllocations[memTypeIndex].AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + } + + // Sum from memory types to memory heaps. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + const uint32_t memHeapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex; + VmaAddDetailedStatistics(pStats->memoryHeap[memHeapIndex], pStats->memoryType[memTypeIndex]); + } + + // Sum from memory heaps to total. + for(uint32_t memHeapIndex = 0; memHeapIndex < GetMemoryHeapCount(); ++memHeapIndex) + VmaAddDetailedStatistics(pStats->total, pStats->memoryHeap[memHeapIndex]); + + VMA_ASSERT(pStats->total.statistics.allocationCount == 0 || + pStats->total.allocationSizeMax >= pStats->total.allocationSizeMin); + VMA_ASSERT(pStats->total.unusedRangeCount == 0 || + pStats->total.unusedRangeSizeMax >= pStats->total.unusedRangeSizeMin); +} + +void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount) +{ +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + if(m_Budget.m_OperationsSinceBudgetFetch < 30) + { + VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex); + for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets) + { + const uint32_t heapIndex = firstHeap + i; + + outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex]; + outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex]; + outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex]; + outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex]; + + if(m_Budget.m_VulkanUsage[heapIndex] + outBudgets->statistics.blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]) + { + outBudgets->usage = m_Budget.m_VulkanUsage[heapIndex] + + outBudgets->statistics.blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]; + } + else + { + outBudgets->usage = 0; + } + + // Have to take MIN with heap size because explicit HeapSizeLimit is included in it. + outBudgets->budget = VMA_MIN( + m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size); + } + } + else + { + UpdateVulkanBudget(); // Outside of mutex lock + GetHeapBudgets(outBudgets, firstHeap, heapCount); // Recursion + } + } + else +#endif + { + for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets) + { + const uint32_t heapIndex = firstHeap + i; + + outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex]; + outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex]; + outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex]; + outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex]; + + outBudgets->usage = outBudgets->statistics.blockBytes; + outBudgets->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics. + } + } +} + +void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo) +{ + pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex(); + pAllocationInfo->deviceMemory = hAllocation->GetMemory(); + pAllocationInfo->offset = hAllocation->GetOffset(); + pAllocationInfo->size = hAllocation->GetSize(); + pAllocationInfo->pMappedData = hAllocation->GetMappedData(); + pAllocationInfo->pUserData = hAllocation->GetUserData(); + pAllocationInfo->pName = hAllocation->GetName(); +} + +VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool) +{ + VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags); + + VmaPoolCreateInfo newCreateInfo = *pCreateInfo; + + // Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash. + if(pCreateInfo->pMemoryAllocateNext) + { + VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0); + } + + if(newCreateInfo.maxBlockCount == 0) + { + newCreateInfo.maxBlockCount = SIZE_MAX; + } + if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + // Memory type index out of range or forbidden. + if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() || + ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + if(newCreateInfo.minAllocationAlignment > 0) + { + VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment)); + } + + const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex); + + *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize); + + VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks(); + if(res != VK_SUCCESS) + { + vma_delete(this, *pPool); + *pPool = VMA_NULL; + return res; + } + + // Add to m_Pools. + { + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + (*pPool)->SetId(m_NextPoolId++); + m_Pools.PushBack(*pPool); + } + + return VK_SUCCESS; +} + +void VmaAllocator_T::DestroyPool(VmaPool pool) +{ + // Remove from m_Pools. + { + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + m_Pools.Remove(pool); + } + + vma_delete(this, pool); +} + +void VmaAllocator_T::GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats) +{ + VmaClearStatistics(*pPoolStats); + pool->m_BlockVector.AddStatistics(*pPoolStats); + pool->m_DedicatedAllocations.AddStatistics(*pPoolStats); +} + +void VmaAllocator_T::CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats) +{ + VmaClearDetailedStatistics(*pPoolStats); + pool->m_BlockVector.AddDetailedStatistics(*pPoolStats); + pool->m_DedicatedAllocations.AddDetailedStatistics(*pPoolStats); +} + +void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex) +{ + m_CurrentFrameIndex.store(frameIndex); + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + UpdateVulkanBudget(); + } +#endif // #if VMA_MEMORY_BUDGET +} + +VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool) +{ + return hPool->m_BlockVector.CheckCorruption(); +} + +VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits) +{ + VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT; + + // Process default pools. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; + if(pBlockVector != VMA_NULL) + { + VkResult localRes = pBlockVector->CheckCorruption(); + switch(localRes) + { + case VK_ERROR_FEATURE_NOT_PRESENT: + break; + case VK_SUCCESS: + finalRes = VK_SUCCESS; + break; + default: + return localRes; + } + } + } + + // Process custom pools. + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) + { + if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0) + { + VkResult localRes = pool->m_BlockVector.CheckCorruption(); + switch(localRes) + { + case VK_ERROR_FEATURE_NOT_PRESENT: + break; + case VK_SUCCESS: + finalRes = VK_SUCCESS; + break; + default: + return localRes; + } + } + } + } + + return finalRes; +} + +VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory) +{ + AtomicTransactionalIncrement deviceMemoryCountIncrement; + const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount); +#if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT + if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount) + { + return VK_ERROR_TOO_MANY_OBJECTS; + } +#endif + + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex); + + // HeapSizeLimit is in effect for this heap. + if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0) + { + const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size; + VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex]; + for(;;) + { + const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize; + if(blockBytesAfterAllocation > heapSize) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation)) + { + break; + } + } + } + else + { + m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize; + } + ++m_Budget.m_BlockCount[heapIndex]; + + // VULKAN CALL vkAllocateMemory. + VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory); + + if(res == VK_SUCCESS) + { +#if VMA_MEMORY_BUDGET + ++m_Budget.m_OperationsSinceBudgetFetch; +#endif + + // Informative callback. + if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData); + } + + deviceMemoryCountIncrement.Commit(); + } + else + { + --m_Budget.m_BlockCount[heapIndex]; + m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize; + } + + return res; +} + +void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory) +{ + // Informative callback. + if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData); + } + + // VULKAN CALL vkFreeMemory. + (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks()); + + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType); + --m_Budget.m_BlockCount[heapIndex]; + m_Budget.m_BlockBytes[heapIndex] -= size; + + --m_DeviceMemoryCount; +} + +VkResult VmaAllocator_T::BindVulkanBuffer( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkBuffer buffer, + const void* pNext) +{ + if(pNext != VMA_NULL) + { +#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) && + m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL) + { + VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR }; + bindBufferMemoryInfo.pNext = pNext; + bindBufferMemoryInfo.buffer = buffer; + bindBufferMemoryInfo.memory = memory; + bindBufferMemoryInfo.memoryOffset = memoryOffset; + return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo); + } + else +#endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + } + else + { + return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset); + } +} + +VkResult VmaAllocator_T::BindVulkanImage( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkImage image, + const void* pNext) +{ + if(pNext != VMA_NULL) + { +#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) && + m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL) + { + VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR }; + bindBufferMemoryInfo.pNext = pNext; + bindBufferMemoryInfo.image = image; + bindBufferMemoryInfo.memory = memory; + bindBufferMemoryInfo.memoryOffset = memoryOffset; + return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo); + } + else +#endif // #if VMA_BIND_MEMORY2 + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + } + else + { + return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset); + } +} + +VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData) +{ + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + char *pBytes = VMA_NULL; + VkResult res = pBlock->Map(this, 1, (void**)&pBytes); + if(res == VK_SUCCESS) + { + *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset(); + hAllocation->BlockAllocMap(); + } + return res; + } + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + return hAllocation->DedicatedAllocMap(this, ppData); + default: + VMA_ASSERT(0); + return VK_ERROR_MEMORY_MAP_FAILED; + } +} + +void VmaAllocator_T::Unmap(VmaAllocation hAllocation) +{ + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + hAllocation->BlockAllocUnmap(); + pBlock->Unmap(this, 1); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + hAllocation->DedicatedAllocUnmap(this); + break; + default: + VMA_ASSERT(0); + } +} + +VkResult VmaAllocator_T::BindBufferMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext) +{ + VkResult res = VK_SUCCESS; + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext); + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block."); + res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext); + break; + } + default: + VMA_ASSERT(0); + } + return res; +} + +VkResult VmaAllocator_T::BindImageMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext) +{ + VkResult res = VK_SUCCESS; + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext); + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); + VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block."); + res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext); + break; + } + default: + VMA_ASSERT(0); + } + return res; +} + +VkResult VmaAllocator_T::FlushOrInvalidateAllocation( + VmaAllocation hAllocation, + VkDeviceSize offset, VkDeviceSize size, + VMA_CACHE_OPERATION op) +{ + VkResult res = VK_SUCCESS; + + VkMappedMemoryRange memRange = {}; + if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange)) + { + switch(op) + { + case VMA_CACHE_FLUSH: + res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange); + break; + case VMA_CACHE_INVALIDATE: + res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange); + break; + default: + VMA_ASSERT(0); + } + } + // else: Just ignore this call. + return res; +} + +VkResult VmaAllocator_T::FlushOrInvalidateAllocations( + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, const VkDeviceSize* sizes, + VMA_CACHE_OPERATION op) +{ + typedef VmaStlAllocator RangeAllocator; + typedef VmaSmallVector RangeVector; + RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks())); + + for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + const VmaAllocation alloc = allocations[allocIndex]; + const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0; + const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE; + VkMappedMemoryRange newRange; + if(GetFlushOrInvalidateRange(alloc, offset, size, newRange)) + { + ranges.push_back(newRange); + } + } + + VkResult res = VK_SUCCESS; + if(!ranges.empty()) + { + switch(op) + { + case VMA_CACHE_FLUSH: + res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data()); + break; + case VMA_CACHE_INVALIDATE: + res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data()); + break; + default: + VMA_ASSERT(0); + } + } + // else: Just ignore this call. + return res; +} + +void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation) +{ + VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + VmaPool parentPool = allocation->GetParentPool(); + if(parentPool == VK_NULL_HANDLE) + { + // Default pool + m_DedicatedAllocations[memTypeIndex].Unregister(allocation); + } + else + { + // Custom pool + parentPool->m_DedicatedAllocations.Unregister(allocation); + } + + VkDeviceMemory hMemory = allocation->GetMemory(); + + /* + There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory + before vkFreeMemory. + + if(allocation->GetMappedData() != VMA_NULL) + { + (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); + } + */ + + FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory); + + m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize()); + m_AllocationObjectAllocator.Free(allocation); + + VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex); +} + +uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const +{ + VkBufferCreateInfo dummyBufCreateInfo; + VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo); + + uint32_t memoryTypeBits = 0; + + // Create buffer. + VkBuffer buf = VK_NULL_HANDLE; + VkResult res = (*GetVulkanFunctions().vkCreateBuffer)( + m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf); + if(res == VK_SUCCESS) + { + // Query for supported memory types. + VkMemoryRequirements memReq; + (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq); + memoryTypeBits = memReq.memoryTypeBits; + + // Destroy buffer. + (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks()); + } + + return memoryTypeBits; +} + +uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const +{ + // Make sure memory information is already fetched. + VMA_ASSERT(GetMemoryTypeCount() > 0); + + uint32_t memoryTypeBits = UINT32_MAX; + + if(!m_UseAmdDeviceCoherentMemory) + { + // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0) + { + memoryTypeBits &= ~(1u << memTypeIndex); + } + } + } + + return memoryTypeBits; +} + +bool VmaAllocator_T::GetFlushOrInvalidateRange( + VmaAllocation allocation, + VkDeviceSize offset, VkDeviceSize size, + VkMappedMemoryRange& outRange) const +{ + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex)) + { + const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; + const VkDeviceSize allocationSize = allocation->GetSize(); + VMA_ASSERT(offset <= allocationSize); + + outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + outRange.pNext = VMA_NULL; + outRange.memory = allocation->GetMemory(); + + switch(allocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize); + if(size == VK_WHOLE_SIZE) + { + outRange.size = allocationSize - outRange.offset; + } + else + { + VMA_ASSERT(offset + size <= allocationSize); + outRange.size = VMA_MIN( + VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize), + allocationSize - outRange.offset); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + // 1. Still within this allocation. + outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize); + if(size == VK_WHOLE_SIZE) + { + size = allocationSize - offset; + } + else + { + VMA_ASSERT(offset + size <= allocationSize); + } + outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize); + + // 2. Adjust to whole block. + const VkDeviceSize allocationOffset = allocation->GetOffset(); + VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0); + const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize(); + outRange.offset += allocationOffset; + outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset); + + break; + } + default: + VMA_ASSERT(0); + } + return true; + } + return false; +} + +#if VMA_MEMORY_BUDGET +void VmaAllocator_T::UpdateVulkanBudget() +{ + VMA_ASSERT(m_UseExtMemoryBudget); + + VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR }; + + VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT }; + VmaPnextChainPushFront(&memProps, &budgetProps); + + GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps); + + { + VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex); + + for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex) + { + m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex]; + m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex]; + m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load(); + + // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size. + if(m_Budget.m_VulkanBudget[heapIndex] == 0) + { + m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics. + } + else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size) + { + m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size; + } + if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0) + { + m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]; + } + } + m_Budget.m_OperationsSinceBudgetFetch = 0; + } +} +#endif // VMA_MEMORY_BUDGET + +void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern) +{ + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS && + hAllocation->IsMappingAllowed() && + (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + { + void* pData = VMA_NULL; + VkResult res = Map(hAllocation, &pData); + if(res == VK_SUCCESS) + { + memset(pData, (int)pattern, (size_t)hAllocation->GetSize()); + FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH); + Unmap(hAllocation); + } + else + { + VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation."); + } + } +} + +uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits() +{ + uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load(); + if(memoryTypeBits == UINT32_MAX) + { + memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits(); + m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits); + } + return memoryTypeBits; +} + +#if VMA_STATS_STRING_ENABLED +void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json) +{ + json.WriteString("DefaultPools"); + json.BeginObject(); + { + for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex]; + VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex]; + if (pBlockVector != VMA_NULL) + { + json.BeginString("Type "); + json.ContinueString(memTypeIndex); + json.EndString(); + json.BeginObject(); + { + json.WriteString("PreferredBlockSize"); + json.WriteNumber(pBlockVector->GetPreferredBlockSize()); + + json.WriteString("Blocks"); + pBlockVector->PrintDetailedMap(json); + + json.WriteString("DedicatedAllocations"); + dedicatedAllocList.BuildStatsString(json); + } + json.EndObject(); + } + } + } + json.EndObject(); + + json.WriteString("CustomPools"); + json.BeginObject(); + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + if (!m_Pools.IsEmpty()) + { + for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + bool displayType = true; + size_t index = 0; + for (VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) + { + VmaBlockVector& blockVector = pool->m_BlockVector; + if (blockVector.GetMemoryTypeIndex() == memTypeIndex) + { + if (displayType) + { + json.BeginString("Type "); + json.ContinueString(memTypeIndex); + json.EndString(); + json.BeginArray(); + displayType = false; + } + + json.BeginObject(); + { + json.WriteString("Name"); + json.BeginString(); + json.ContinueString_Size(index++); + if (pool->GetName()) + { + json.ContinueString(" - "); + json.ContinueString(pool->GetName()); + } + json.EndString(); + + json.WriteString("PreferredBlockSize"); + json.WriteNumber(blockVector.GetPreferredBlockSize()); + + json.WriteString("Blocks"); + blockVector.PrintDetailedMap(json); + + json.WriteString("DedicatedAllocations"); + pool->m_DedicatedAllocations.BuildStatsString(json); + } + json.EndObject(); + } + } + + if (!displayType) + json.EndArray(); + } + } + } + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_ALLOCATOR_T_FUNCTIONS + + +#ifndef _VMA_PUBLIC_INTERFACE +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator( + const VmaAllocatorCreateInfo* pCreateInfo, + VmaAllocator* pAllocator) +{ + VMA_ASSERT(pCreateInfo && pAllocator); + VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 || + (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 3)); + VMA_DEBUG_LOG("vmaCreateAllocator"); + *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo); + VkResult result = (*pAllocator)->Init(pCreateInfo); + if(result < 0) + { + vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator); + *pAllocator = VK_NULL_HANDLE; + } + return result; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator( + VmaAllocator allocator) +{ + if(allocator != VK_NULL_HANDLE) + { + VMA_DEBUG_LOG("vmaDestroyAllocator"); + VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying. + vma_delete(&allocationCallbacks, allocator); + } +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo) +{ + VMA_ASSERT(allocator && pAllocatorInfo); + pAllocatorInfo->instance = allocator->m_hInstance; + pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice(); + pAllocatorInfo->device = allocator->m_hDevice; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties( + VmaAllocator allocator, + const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties) +{ + VMA_ASSERT(allocator && ppPhysicalDeviceProperties); + *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties( + VmaAllocator allocator, + const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties) +{ + VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties); + *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties( + VmaAllocator allocator, + uint32_t memoryTypeIndex, + VkMemoryPropertyFlags* pFlags) +{ + VMA_ASSERT(allocator && pFlags); + VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount()); + *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex( + VmaAllocator allocator, + uint32_t frameIndex) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->SetCurrentFrameIndex(frameIndex); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics( + VmaAllocator allocator, + VmaTotalStatistics* pStats) +{ + VMA_ASSERT(allocator && pStats); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + allocator->CalculateStatistics(pStats); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets( + VmaAllocator allocator, + VmaBudget* pBudgets) +{ + VMA_ASSERT(allocator && pBudgets); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + allocator->GetHeapBudgets(pBudgets, 0, allocator->GetMemoryHeapCount()); +} + +#if VMA_STATS_STRING_ENABLED + +VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString( + VmaAllocator allocator, + char** ppStatsString, + VkBool32 detailedMap) +{ + VMA_ASSERT(allocator && ppStatsString); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VmaStringBuilder sb(allocator->GetAllocationCallbacks()); + { + VmaBudget budgets[VK_MAX_MEMORY_HEAPS]; + allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount()); + + VmaTotalStatistics stats; + allocator->CalculateStatistics(&stats); + + VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb); + json.BeginObject(); + { + json.WriteString("General"); + json.BeginObject(); + { + const VkPhysicalDeviceProperties& deviceProperties = allocator->m_PhysicalDeviceProperties; + const VkPhysicalDeviceMemoryProperties& memoryProperties = allocator->m_MemProps; + + json.WriteString("API"); + json.WriteString("Vulkan"); + + json.WriteString("apiVersion"); + json.BeginString(); + json.ContinueString(VK_API_VERSION_MAJOR(deviceProperties.apiVersion)); + json.ContinueString("."); + json.ContinueString(VK_API_VERSION_MINOR(deviceProperties.apiVersion)); + json.ContinueString("."); + json.ContinueString(VK_API_VERSION_PATCH(deviceProperties.apiVersion)); + json.EndString(); + + json.WriteString("GPU"); + json.WriteString(deviceProperties.deviceName); + json.WriteString("deviceType"); + json.WriteNumber(static_cast(deviceProperties.deviceType)); + + json.WriteString("maxMemoryAllocationCount"); + json.WriteNumber(deviceProperties.limits.maxMemoryAllocationCount); + json.WriteString("bufferImageGranularity"); + json.WriteNumber(deviceProperties.limits.bufferImageGranularity); + json.WriteString("nonCoherentAtomSize"); + json.WriteNumber(deviceProperties.limits.nonCoherentAtomSize); + + json.WriteString("memoryHeapCount"); + json.WriteNumber(memoryProperties.memoryHeapCount); + json.WriteString("memoryTypeCount"); + json.WriteNumber(memoryProperties.memoryTypeCount); + } + json.EndObject(); + } + { + json.WriteString("Total"); + VmaPrintDetailedStatistics(json, stats.total); + } + { + json.WriteString("MemoryInfo"); + json.BeginObject(); + { + for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex) + { + json.BeginString("Heap "); + json.ContinueString(heapIndex); + json.EndString(); + json.BeginObject(); + { + const VkMemoryHeap& heapInfo = allocator->m_MemProps.memoryHeaps[heapIndex]; + json.WriteString("Flags"); + json.BeginArray(true); + { + if (heapInfo.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) + json.WriteString("DEVICE_LOCAL"); + #if VMA_VULKAN_VERSION >= 1001000 + if (heapInfo.flags & VK_MEMORY_HEAP_MULTI_INSTANCE_BIT) + json.WriteString("MULTI_INSTANCE"); + #endif + + VkMemoryHeapFlags flags = heapInfo.flags & + ~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT + #if VMA_VULKAN_VERSION >= 1001000 + | VK_MEMORY_HEAP_MULTI_INSTANCE_BIT + #endif + ); + if (flags != 0) + json.WriteNumber(flags); + } + json.EndArray(); + + json.WriteString("Size"); + json.WriteNumber(heapInfo.size); + + json.WriteString("Budget"); + json.BeginObject(); + { + json.WriteString("BudgetBytes"); + json.WriteNumber(budgets[heapIndex].budget); + json.WriteString("UsageBytes"); + json.WriteNumber(budgets[heapIndex].usage); + } + json.EndObject(); + + json.WriteString("Stats"); + VmaPrintDetailedStatistics(json, stats.memoryHeap[heapIndex]); + + json.WriteString("MemoryPools"); + json.BeginObject(); + { + for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex) + { + if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex) + { + json.BeginString("Type "); + json.ContinueString(typeIndex); + json.EndString(); + json.BeginObject(); + { + json.WriteString("Flags"); + json.BeginArray(true); + { + VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags; + if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + json.WriteString("DEVICE_LOCAL"); + if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) + json.WriteString("HOST_VISIBLE"); + if (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) + json.WriteString("HOST_COHERENT"); + if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) + json.WriteString("HOST_CACHED"); + if (flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) + json.WriteString("LAZILY_ALLOCATED"); + #if VMA_VULKAN_VERSION >= 1001000 + if (flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) + json.WriteString("PROTECTED"); + #endif + #if VK_AMD_device_coherent_memory + if (flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) + json.WriteString("DEVICE_COHERENT_AMD"); + if (flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) + json.WriteString("DEVICE_UNCACHED_AMD"); + #endif + + flags &= ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + #if VMA_VULKAN_VERSION >= 1001000 + | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT + #endif + #if VK_AMD_device_coherent_memory + | VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY + | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY + #endif + | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT + | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + | VK_MEMORY_PROPERTY_HOST_CACHED_BIT); + if (flags != 0) + json.WriteNumber(flags); + } + json.EndArray(); + + json.WriteString("Stats"); + VmaPrintDetailedStatistics(json, stats.memoryType[typeIndex]); + } + json.EndObject(); + } + } + + } + json.EndObject(); + } + json.EndObject(); + } + } + json.EndObject(); + } + + if (detailedMap == VK_TRUE) + allocator->PrintDetailedMap(json); + + json.EndObject(); + } + + *ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength()); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString( + VmaAllocator allocator, + char* pStatsString) +{ + if(pStatsString != VMA_NULL) + { + VMA_ASSERT(allocator); + VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString); + } +} + +#endif // VMA_STATS_STRING_ENABLED + +/* +This function is not protected by any mutex because it just reads immutable data. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex( + VmaAllocator allocator, + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + return allocator->FindMemoryTypeIndex(memoryTypeBits, pAllocationCreateInfo, UINT32_MAX, pMemoryTypeIndex); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pBufferCreateInfo != VMA_NULL); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + const VkDevice hDev = allocator->m_hDevice; + const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions(); + VkResult res; + +#if VMA_VULKAN_VERSION >= 1003000 + if(funcs->vkGetDeviceBufferMemoryRequirements) + { + // Can query straight from VkBufferCreateInfo :) + VkDeviceBufferMemoryRequirements devBufMemReq = {VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS}; + devBufMemReq.pCreateInfo = pBufferCreateInfo; + + VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2}; + (*funcs->vkGetDeviceBufferMemoryRequirements)(hDev, &devBufMemReq, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex); + } + else +#endif // #if VMA_VULKAN_VERSION >= 1003000 + { + // Must create a dummy buffer to query :( + VkBuffer hBuffer = VK_NULL_HANDLE; + res = funcs->vkCreateBuffer( + hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer); + if(res == VK_SUCCESS) + { + VkMemoryRequirements memReq = {}; + funcs->vkGetBufferMemoryRequirements(hDev, hBuffer, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex); + + funcs->vkDestroyBuffer( + hDev, hBuffer, allocator->GetAllocationCallbacks()); + } + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pImageCreateInfo != VMA_NULL); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + const VkDevice hDev = allocator->m_hDevice; + const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions(); + VkResult res; + +#if VMA_VULKAN_VERSION >= 1003000 + if(funcs->vkGetDeviceImageMemoryRequirements) + { + // Can query straight from VkImageCreateInfo :) + VkDeviceImageMemoryRequirements devImgMemReq = {VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS}; + devImgMemReq.pCreateInfo = pImageCreateInfo; + VMA_ASSERT(pImageCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY && (pImageCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT_COPY) == 0 && + "Cannot use this VkImageCreateInfo with vmaFindMemoryTypeIndexForImageInfo as I don't know what to pass as VkDeviceImageMemoryRequirements::planeAspect."); + + VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2}; + (*funcs->vkGetDeviceImageMemoryRequirements)(hDev, &devImgMemReq, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex); + } + else +#endif // #if VMA_VULKAN_VERSION >= 1003000 + { + // Must create a dummy image to query :( + VkImage hImage = VK_NULL_HANDLE; + res = funcs->vkCreateImage( + hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage); + if(res == VK_SUCCESS) + { + VkMemoryRequirements memReq = {}; + funcs->vkGetImageMemoryRequirements(hDev, hImage, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex); + + funcs->vkDestroyImage( + hDev, hImage, allocator->GetAllocationCallbacks()); + } + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool( + VmaAllocator allocator, + const VmaPoolCreateInfo* pCreateInfo, + VmaPool* pPool) +{ + VMA_ASSERT(allocator && pCreateInfo && pPool); + + VMA_DEBUG_LOG("vmaCreatePool"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->CreatePool(pCreateInfo, pPool); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool( + VmaAllocator allocator, + VmaPool pool) +{ + VMA_ASSERT(allocator); + + if(pool == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyPool"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->DestroyPool(pool); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics( + VmaAllocator allocator, + VmaPool pool, + VmaStatistics* pPoolStats) +{ + VMA_ASSERT(allocator && pool && pPoolStats); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->GetPoolStatistics(pool, pPoolStats); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics( + VmaAllocator allocator, + VmaPool pool, + VmaDetailedStatistics* pPoolStats) +{ + VMA_ASSERT(allocator && pool && pPoolStats); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->CalculatePoolStatistics(pool, pPoolStats); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool) +{ + VMA_ASSERT(allocator && pool); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VMA_DEBUG_LOG("vmaCheckPoolCorruption"); + + return allocator->CheckPoolCorruption(pool); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName( + VmaAllocator allocator, + VmaPool pool, + const char** ppName) +{ + VMA_ASSERT(allocator && pool && ppName); + + VMA_DEBUG_LOG("vmaGetPoolName"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *ppName = pool->GetName(); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName( + VmaAllocator allocator, + VmaPool pool, + const char* pName) +{ + VMA_ASSERT(allocator && pool); + + VMA_DEBUG_LOG("vmaSetPoolName"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + pool->SetName(pName); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult result = allocator->AllocateMemory( + *pVkMemoryRequirements, + false, // requiresDedicatedAllocation + false, // prefersDedicatedAllocation + VK_NULL_HANDLE, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + UINT32_MAX, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_UNKNOWN, + 1, // allocationCount + pAllocation); + + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + size_t allocationCount, + VmaAllocation* pAllocations, + VmaAllocationInfo* pAllocationInfo) +{ + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations); + + VMA_DEBUG_LOG("vmaAllocateMemoryPages"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult result = allocator->AllocateMemory( + *pVkMemoryRequirements, + false, // requiresDedicatedAllocation + false, // prefersDedicatedAllocation + VK_NULL_HANDLE, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + UINT32_MAX, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_UNKNOWN, + allocationCount, + pAllocations); + + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) + { + for(size_t i = 0; i < allocationCount; ++i) + { + allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i); + } + } + + return result; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer( + VmaAllocator allocator, + VkBuffer buffer, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(buffer, vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation); + + VkResult result = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + buffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + UINT32_MAX, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + + if(pAllocationInfo && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage( + VmaAllocator allocator, + VkImage image, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemoryForImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(image, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + VkResult result = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + image, // dedicatedImage + UINT32_MAX, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN, + 1, // allocationCount + pAllocation); + + if(pAllocationInfo && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory( + VmaAllocator allocator, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator); + + if(allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaFreeMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->FreeMemory( + 1, // allocationCount + &allocation); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages( + VmaAllocator allocator, + size_t allocationCount, + const VmaAllocation* pAllocations) +{ + if(allocationCount == 0) + { + return; + } + + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaFreeMemoryPages"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->FreeMemory(allocationCount, pAllocations); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo( + VmaAllocator allocator, + VmaAllocation allocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && allocation && pAllocationInfo); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->GetAllocationInfo(allocation, pAllocationInfo); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData( + VmaAllocator allocator, + VmaAllocation allocation, + void* pUserData) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocation->SetUserData(allocator, pUserData); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const char* VMA_NULLABLE pName) +{ + allocation->SetName(allocator, pName); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags) +{ + VMA_ASSERT(allocator && allocation && pFlags); + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + *pFlags = allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory( + VmaAllocator allocator, + VmaAllocation allocation, + void** ppData) +{ + VMA_ASSERT(allocator && allocation && ppData); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->Map(allocation, ppData); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory( + VmaAllocator allocator, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->Unmap(allocation); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize offset, + VkDeviceSize size) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaFlushAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize offset, + VkDeviceSize size) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaInvalidateAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations( + VmaAllocator allocator, + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, + const VkDeviceSize* sizes) +{ + VMA_ASSERT(allocator); + + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocations); + + VMA_DEBUG_LOG("vmaFlushAllocations"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations( + VmaAllocator allocator, + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, + const VkDeviceSize* sizes) +{ + VMA_ASSERT(allocator); + + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocations); + + VMA_DEBUG_LOG("vmaInvalidateAllocations"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption( + VmaAllocator allocator, + uint32_t memoryTypeBits) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaCheckCorruption"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->CheckCorruption(memoryTypeBits); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation( + VmaAllocator allocator, + const VmaDefragmentationInfo* pInfo, + VmaDefragmentationContext* pContext) +{ + VMA_ASSERT(allocator && pInfo && pContext); + + VMA_DEBUG_LOG("vmaBeginDefragmentation"); + + if (pInfo->pool != VMA_NULL) + { + // Check if run on supported algorithms + if (pInfo->pool->m_BlockVector.GetAlgorithm() & VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pContext = vma_new(allocator, VmaDefragmentationContext_T)(allocator, *pInfo); + return VK_SUCCESS; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation( + VmaAllocator allocator, + VmaDefragmentationContext context, + VmaDefragmentationStats* pStats) +{ + VMA_ASSERT(allocator && context); + + VMA_DEBUG_LOG("vmaEndDefragmentation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if (pStats) + context->GetStats(*pStats); + vma_delete(allocator, context); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo) +{ + VMA_ASSERT(context && pPassInfo); + + VMA_DEBUG_LOG("vmaBeginDefragmentationPass"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return context->DefragmentPassBegin(*pPassInfo); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo) +{ + VMA_ASSERT(context && pPassInfo); + + VMA_DEBUG_LOG("vmaEndDefragmentationPass"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return context->DefragmentPassEnd(*pPassInfo); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkBuffer buffer) +{ + VMA_ASSERT(allocator && allocation && buffer); + + VMA_DEBUG_LOG("vmaBindBufferMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize allocationLocalOffset, + VkBuffer buffer, + const void* pNext) +{ + VMA_ASSERT(allocator && allocation && buffer); + + VMA_DEBUG_LOG("vmaBindBufferMemory2"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkImage image) +{ + VMA_ASSERT(allocator && allocation && image); + + VMA_DEBUG_LOG("vmaBindImageMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindImageMemory(allocation, 0, image, VMA_NULL); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize allocationLocalOffset, + VkImage image, + const void* pNext) +{ + VMA_ASSERT(allocator && allocation && image); + + VMA_DEBUG_LOG("vmaBindImageMemory2"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkBuffer* pBuffer, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation); + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG("vmaCreateBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pBuffer = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if(res >= 0) + { + // 2. vkGetBufferMemoryRequirements. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + // 3. Allocate memory using allocator. + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pBuffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + pBufferCreateInfo->usage, // dedicatedBufferImageUsage + *pAllocationCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + + if(res >= 0) + { + // 3. Bind buffer with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkDeviceSize minAlignment, + VkBuffer* pBuffer, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation); + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG("vmaCreateBufferWithAlignment"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pBuffer = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if(res >= 0) + { + // 2. vkGetBufferMemoryRequirements. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + // 2a. Include minAlignment + vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment); + + // 3. Allocate memory using allocator. + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pBuffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + pBufferCreateInfo->usage, // dedicatedBufferImageUsage + *pAllocationCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + + if(res >= 0) + { + // 3. Bind buffer with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pBuffer && allocation); + + VMA_DEBUG_LOG("vmaCreateAliasingBuffer"); + + *pBuffer = VK_NULL_HANDLE; + + if (pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if ((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if (res >= 0) + { + // 2. Bind buffer with memory. + res = allocator->BindBufferMemory(allocation, 0, *pBuffer, VMA_NULL); + if (res >= 0) + { + return VK_SUCCESS; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer( + VmaAllocator allocator, + VkBuffer buffer, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator); + + if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(buffer != VK_NULL_HANDLE) + { + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks()); + } + + if(allocation != VK_NULL_HANDLE) + { + allocator->FreeMemory( + 1, // allocationCount + &allocation); + } +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkImage* pImage, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation); + + if(pImageCreateInfo->extent.width == 0 || + pImageCreateInfo->extent.height == 0 || + pImageCreateInfo->extent.depth == 0 || + pImageCreateInfo->mipLevels == 0 || + pImageCreateInfo->arrayLayers == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG("vmaCreateImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pImage = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkImage. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)( + allocator->m_hDevice, + pImageCreateInfo, + allocator->GetAllocationCallbacks(), + pImage); + if(res >= 0) + { + VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ? + VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL : + VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR; + + // 2. Allocate memory using allocator. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(*pImage, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + *pImage, // dedicatedImage + pImageCreateInfo->usage, // dedicatedBufferImageUsage + *pAllocationCreateInfo, + suballocType, + 1, // allocationCount + pAllocation); + + if(res >= 0) + { + // 3. Bind image with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + *pImage = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + *pImage = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage) +{ + VMA_ASSERT(allocator && pImageCreateInfo && pImage && allocation); + + *pImage = VK_NULL_HANDLE; + + VMA_DEBUG_LOG("vmaCreateImage"); + + if (pImageCreateInfo->extent.width == 0 || + pImageCreateInfo->extent.height == 0 || + pImageCreateInfo->extent.depth == 0 || + pImageCreateInfo->mipLevels == 0 || + pImageCreateInfo->arrayLayers == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + // 1. Create VkImage. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)( + allocator->m_hDevice, + pImageCreateInfo, + allocator->GetAllocationCallbacks(), + pImage); + if (res >= 0) + { + // 2. Bind image with memory. + res = allocator->BindImageMemory(allocation, 0, *pImage, VMA_NULL); + if (res >= 0) + { + return VK_SUCCESS; + } + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NULLABLE_NON_DISPATCHABLE image, + VmaAllocation VMA_NULLABLE allocation) +{ + VMA_ASSERT(allocator); + + if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(image != VK_NULL_HANDLE) + { + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks()); + } + if(allocation != VK_NULL_HANDLE) + { + allocator->FreeMemory( + 1, // allocationCount + &allocation); + } +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock( + const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock) +{ + VMA_ASSERT(pCreateInfo && pVirtualBlock); + VMA_ASSERT(pCreateInfo->size > 0); + VMA_DEBUG_LOG("vmaCreateVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + *pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo); + VkResult res = (*pVirtualBlock)->Init(); + if(res < 0) + { + vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock); + *pVirtualBlock = VK_NULL_HANDLE; + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock) +{ + if(virtualBlock != VK_NULL_HANDLE) + { + VMA_DEBUG_LOG("vmaDestroyVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying. + vma_delete(&allocationCallbacks, virtualBlock); + } +} + +VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL); + VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->GetAllocationInfo(allocation, *pVirtualAllocInfo); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation, + VkDeviceSize* VMA_NULLABLE pOffset) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pAllocation != VMA_NULL); + VMA_DEBUG_LOG("vmaVirtualAllocate"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation) +{ + if(allocation != VK_NULL_HANDLE) + { + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaVirtualFree"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->Free(allocation); + } +} + +VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaClearVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->Clear(); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, void* VMA_NULLABLE pUserData) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->SetAllocationUserData(allocation, pUserData); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaStatistics* VMA_NOT_NULL pStats) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL); + VMA_DEBUG_LOG("vmaGetVirtualBlockStatistics"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->GetStatistics(*pStats); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaDetailedStatistics* VMA_NOT_NULL pStats) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL); + VMA_DEBUG_LOG("vmaCalculateVirtualBlockStatistics"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->CalculateDetailedStatistics(*pStats); +} + +#if VMA_STATS_STRING_ENABLED + +VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks(); + VmaStringBuilder sb(allocationCallbacks); + virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb); + *ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength()); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE pStatsString) +{ + if(pStatsString != VMA_NULL) + { + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString); + } +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_PUBLIC_INTERFACE +#endif // VMA_IMPLEMENTATION + +/** +\page quick_start Quick start + +\section quick_start_project_setup Project setup + +Vulkan Memory Allocator comes in form of a "stb-style" single header file. +You don't need to build it as a separate library project. +You can add this file directly to your project and submit it to code repository next to your other source files. + +"Single header" doesn't mean that everything is contained in C/C++ declarations, +like it tends to be in case of inline functions or C++ templates. +It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro. +If you don't do it properly, you will get linker errors. + +To do it properly: + +-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library. + This includes declarations of all members of the library. +-# In exactly one CPP file define following macro before this include. + It enables also internal definitions. + +\code +#define VMA_IMPLEMENTATION +#include "vk_mem_alloc.h" +\endcode + +It may be a good idea to create dedicated CPP file just for this purpose. + +This library includes header ``, which in turn +includes `` on Windows. If you need some specific macros defined +before including these headers (like `WIN32_LEAN_AND_MEAN` or +`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define +them before every `#include` of this library. + +This library is written in C++, but has C-compatible interface. +Thus you can include and use vk_mem_alloc.h in C or C++ code, but full +implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C. +Some features of C++14 used. STL containers, RTTI, or C++ exceptions are not used. + + +\section quick_start_initialization Initialization + +At program startup: + +-# Initialize Vulkan to have `VkPhysicalDevice`, `VkDevice` and `VkInstance` object. +-# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by + calling vmaCreateAllocator(). + +Only members `physicalDevice`, `device`, `instance` are required. +However, you should inform the library which Vulkan version do you use by setting +VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable +by setting VmaAllocatorCreateInfo::flags (like #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT for VK_KHR_buffer_device_address). +Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions. + +You may need to configure importing Vulkan functions. There are 3 ways to do this: + +-# **If you link with Vulkan static library** (e.g. "vulkan-1.lib" on Windows): + - You don't need to do anything. + - VMA will use these, as macro `VMA_STATIC_VULKAN_FUNCTIONS` is defined to 1 by default. +-# **If you want VMA to fetch pointers to Vulkan functions dynamically** using `vkGetInstanceProcAddr`, + `vkGetDeviceProcAddr` (this is the option presented in the example below): + - Define `VMA_STATIC_VULKAN_FUNCTIONS` to 0, `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 1. + - Provide pointers to these two functions via VmaVulkanFunctions::vkGetInstanceProcAddr, + VmaVulkanFunctions::vkGetDeviceProcAddr. + - The library will fetch pointers to all other functions it needs internally. +-# **If you fetch pointers to all Vulkan functions in a custom way**, e.g. using some loader like + [Volk](https://github.com/zeux/volk): + - Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0. + - Pass these pointers via structure #VmaVulkanFunctions. + +\code +VmaVulkanFunctions vulkanFunctions = {}; +vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; +vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; + +VmaAllocatorCreateInfo allocatorCreateInfo = {}; +allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2; +allocatorCreateInfo.physicalDevice = physicalDevice; +allocatorCreateInfo.device = device; +allocatorCreateInfo.instance = instance; +allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; + +VmaAllocator allocator; +vmaCreateAllocator(&allocatorCreateInfo, &allocator); +\endcode + + +\section quick_start_resource_allocation Resource allocation + +When you want to create a buffer or image: + +-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure. +-# Fill VmaAllocationCreateInfo structure. +-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory + already allocated and bound to it, plus #VmaAllocation objects that represents its underlying memory. + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufferInfo.size = 65536; +bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +Don't forget to destroy your objects when no longer needed: + +\code +vmaDestroyBuffer(allocator, buffer, allocation); +vmaDestroyAllocator(allocator); +\endcode + + +\page choosing_memory_type Choosing memory type + +Physical devices in Vulkan support various combinations of memory heaps and +types. Help with choosing correct and optimal memory type for your specific +resource is one of the key features of this library. You can use it by filling +appropriate members of VmaAllocationCreateInfo structure, as described below. +You can also combine multiple methods. + +-# If you just want to find memory type index that meets your requirements, you + can use function: vmaFindMemoryTypeIndexForBufferInfo(), + vmaFindMemoryTypeIndexForImageInfo(), vmaFindMemoryTypeIndex(). +-# If you want to allocate a region of device memory without association with any + specific image or buffer, you can use function vmaAllocateMemory(). Usage of + this function is not recommended and usually not needed. + vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once, + which may be useful for sparse binding. +-# If you already have a buffer or an image created, you want to allocate memory + for it and then you will bind it yourself, you can use function + vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(). + For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory() + or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2(). +-# **This is the easiest and recommended way to use this library:** + If you want to create a buffer or an image, allocate memory for it and bind + them together, all in one call, you can use function vmaCreateBuffer(), + vmaCreateImage(). + +When using 3. or 4., the library internally queries Vulkan for memory types +supported for that buffer or image (function `vkGetBufferMemoryRequirements()`) +and uses only one of these types. + +If no memory type can be found that meets all the requirements, these functions +return `VK_ERROR_FEATURE_NOT_PRESENT`. + +You can leave VmaAllocationCreateInfo structure completely filled with zeros. +It means no requirements are specified for memory type. +It is valid, although not very useful. + +\section choosing_memory_type_usage Usage + +The easiest way to specify memory requirements is to fill member +VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage. +It defines high level, common usage types. +Since version 3 of the library, it is recommended to use #VMA_MEMORY_USAGE_AUTO to let it select best memory type for your resource automatically. + +For example, if you want to create a uniform buffer that will be filled using +transfer only once or infrequently and then used for rendering every frame as a uniform buffer, you can +do it using following code. The buffer will most likely end up in a memory type with +`VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT` to be fast to access by the GPU device. + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufferInfo.size = 65536; +bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +If you have a preference for putting the resource in GPU (device) memory or CPU (host) memory +on systems with discrete graphics card that have the memories separate, you can use +#VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST. + +When using `VMA_MEMORY_USAGE_AUTO*` while you want to map the allocated memory, +you also need to specify one of the host access flags: +#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +This will help the library decide about preferred memory type to ensure it has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` +so you can map it. + +For example, a staging buffer that will be filled via mapped pointer and then +used as a source of transfer to the buffer decribed previously can be created like this. +It will likely and up in a memory type that is `HOST_VISIBLE` and `HOST_COHERENT` +but not `HOST_CACHED` (meaning uncached, write-combined) and not `DEVICE_LOCAL` (meaning system RAM). + +\code +VkBufferCreateInfo stagingBufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +stagingBufferInfo.size = 65536; +stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo stagingAllocInfo = {}; +stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO; +stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + +VkBuffer stagingBuffer; +VmaAllocation stagingAllocation; +vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr); +\endcode + +For more examples of creating different kinds of resources, see chapter \ref usage_patterns. + +Usage values `VMA_MEMORY_USAGE_AUTO*` are legal to use only when the library knows +about the resource being created by having `VkBufferCreateInfo` / `VkImageCreateInfo` passed, +so they work with functions like: vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo() etc. +If you allocate raw memory using function vmaAllocateMemory(), you have to use other means of selecting +memory type, as decribed below. + +\note +Old usage values (`VMA_MEMORY_USAGE_GPU_ONLY`, `VMA_MEMORY_USAGE_CPU_ONLY`, +`VMA_MEMORY_USAGE_CPU_TO_GPU`, `VMA_MEMORY_USAGE_GPU_TO_CPU`, `VMA_MEMORY_USAGE_CPU_COPY`) +are still available and work same way as in previous versions of the library +for backward compatibility, but they are not recommended. + +\section choosing_memory_type_required_preferred_flags Required and preferred flags + +You can specify more detailed requirements by filling members +VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags +with a combination of bits from enum `VkMemoryPropertyFlags`. For example, +if you want to create a buffer that will be persistently mapped on host (so it +must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`, +use following code: + +\code +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; +allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +A memory type is chosen that has all the required flags and as many preferred +flags set as possible. + +Value passed in VmaAllocationCreateInfo::usage is internally converted to a set of required and preferred flags, +plus some extra "magic" (heuristics). + +\section choosing_memory_type_explicit_memory_types Explicit memory types + +If you inspected memory types available on the physical device and you have +a preference for memory types that you want to use, you can fill member +VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set +means that a memory type with that index is allowed to be used for the +allocation. Special value 0, just like `UINT32_MAX`, means there are no +restrictions to memory type index. + +Please note that this member is NOT just a memory type index. +Still you can use it to choose just one, specific memory type. +For example, if you already determined that your buffer should be created in +memory type 2, use following code: + +\code +uint32_t memoryTypeIndex = 2; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.memoryTypeBits = 1u << memoryTypeIndex; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + + +\section choosing_memory_type_custom_memory_pools Custom memory pools + +If you allocate from custom memory pool, all the ways of specifying memory +requirements described above are not applicable and the aforementioned members +of VmaAllocationCreateInfo structure are ignored. Memory type is selected +explicitly when creating the pool and then used to make all the allocations from +that pool. For further details, see \ref custom_memory_pools. + +\section choosing_memory_type_dedicated_allocations Dedicated allocations + +Memory for allocations is reserved out of larger block of `VkDeviceMemory` +allocated from Vulkan internally. That is the main feature of this whole library. +You can still request a separate memory block to be created for an allocation, +just like you would do in a trivial solution without using any allocator. +In that case, a buffer or image is always bound to that memory at offset 0. +This is called a "dedicated allocation". +You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +The library can also internally decide to use dedicated allocation in some cases, e.g.: + +- When the size of the allocation is large. +- When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled + and it reports that dedicated allocation is required or recommended for the resource. +- When allocation of next big memory block fails due to not enough device memory, + but allocation with the exact requested size succeeds. + + +\page memory_mapping Memory mapping + +To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`, +to be able to read from it or write to it in CPU code. +Mapping is possible only of memory allocated from a memory type that has +`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag. +Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose. +You can use them directly with memory allocated by this library, +but it is not recommended because of following issue: +Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed. +This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan. +Because of this, Vulkan Memory Allocator provides following facilities: + +\note If you want to be able to map an allocation, you need to specify one of the flags +#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT +in VmaAllocationCreateInfo::flags. These flags are required for an allocation to be mappable +when using #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` enum values. +For other usage values they are ignored and every such allocation made in `HOST_VISIBLE` memory type is mappable, +but they can still be used for consistency. + +\section memory_mapping_mapping_functions Mapping functions + +The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory(). +They are safer and more convenient to use than standard Vulkan functions. +You can map an allocation multiple times simultaneously - mapping is reference-counted internally. +You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block. +The way it is implemented is that the library always maps entire memory block, not just region of the allocation. +For further details, see description of vmaMapMemory() function. +Example: + +\code +// Having these objects initialized: +struct ConstantBuffer +{ + ... +}; +ConstantBuffer constantBufferData = ... + +VmaAllocator allocator = ... +VkBuffer constantBuffer = ... +VmaAllocation constantBufferAllocation = ... + +// You can map and fill your buffer using following code: + +void* mappedData; +vmaMapMemory(allocator, constantBufferAllocation, &mappedData); +memcpy(mappedData, &constantBufferData, sizeof(constantBufferData)); +vmaUnmapMemory(allocator, constantBufferAllocation); +\endcode + +When mapping, you may see a warning from Vulkan validation layer similar to this one: + +Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used. + +It happens because the library maps entire `VkDeviceMemory` block, where different +types of images and buffers may end up together, especially on GPUs with unified memory like Intel. +You can safely ignore it if you are sure you access only memory of the intended +object that you wanted to map. + + +\section memory_mapping_persistently_mapped_memory Persistently mapped memory + +Kepping your memory persistently mapped is generally OK in Vulkan. +You don't need to unmap it before using its data on the GPU. +The library provides a special feature designed for that: +Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in +VmaAllocationCreateInfo::flags stay mapped all the time, +so you can just access CPU pointer to it any time +without a need to call any "map" or "unmap" function. +Example: + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = sizeof(ConstantBuffer); +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +// Buffer is already mapped. You can access its memory. +memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData)); +\endcode + +\note #VMA_ALLOCATION_CREATE_MAPPED_BIT by itself doesn't guarantee that the allocation will end up +in a mappable memory type. +For this, you need to also specify #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or +#VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +#VMA_ALLOCATION_CREATE_MAPPED_BIT only guarantees that if the memory is `HOST_VISIBLE`, the allocation will be mapped on creation. +For an example of how to make use of this fact, see section \ref usage_patterns_advanced_data_uploading. + +\section memory_mapping_cache_control Cache flush and invalidate + +Memory in Vulkan doesn't need to be unmapped before using it on GPU, +but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set, +you need to manually **invalidate** cache before reading of mapped pointer +and **flush** cache after writing to mapped pointer. +Map/unmap operations don't do that automatically. +Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`, +`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient +functions that refer to given allocation object: vmaFlushAllocation(), +vmaInvalidateAllocation(), +or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations(). + +Regions of memory specified for flush/invalidate must be aligned to +`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library. +In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations +within blocks are aligned to this value, so their offsets are always multiply of +`nonCoherentAtomSize` and two different allocations never share same "line" of this size. + +Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA) +currently provide `HOST_COHERENT` flag on all memory types that are +`HOST_VISIBLE`, so on PC you may not need to bother. + + +\page staying_within_budget Staying within budget + +When developing a graphics-intensive game or program, it is important to avoid allocating +more GPU memory than it is physically available. When the memory is over-committed, +various bad things can happen, depending on the specific GPU, graphics driver, and +operating system: + +- It may just work without any problems. +- The application may slow down because some memory blocks are moved to system RAM + and the GPU has to access them through PCI Express bus. +- A new allocation may take very long time to complete, even few seconds, and possibly + freeze entire system. +- The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +- It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST` + returned somewhere later. + +\section staying_within_budget_querying_for_budget Querying for budget + +To query for current memory usage and available budget, use function vmaGetHeapBudgets(). +Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap. + +Please note that this function returns different information and works faster than +vmaCalculateStatistics(). vmaGetHeapBudgets() can be called every frame or even before every +allocation, while vmaCalculateStatistics() is intended to be used rarely, +only to obtain statistical information, e.g. for debugging purposes. + +It is recommended to use VK_EXT_memory_budget device extension to obtain information +about the budget from Vulkan device. VMA is able to use this extension automatically. +When not enabled, the allocator behaves same way, but then it estimates current usage +and available budget based on its internal information and Vulkan memory heap sizes, +which may be less precise. In order to use this extension: + +1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2 + required by it are available and enable them. Please note that the first is a device + extension and the second is instance extension! +2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object. +3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from + Vulkan inside of it to avoid overhead of querying it with every allocation. + +\section staying_within_budget_controlling_memory_usage Controlling memory usage + +There are many ways in which you can try to stay within the budget. + +First, when making new allocation requires allocating a new memory block, the library +tries not to exceed the budget automatically. If a block with default recommended size +(e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even +dedicated memory for just this resource. + +If the size of the requested resource plus current memory usage is more than the +budget, by default the library still tries to create it, leaving it to the Vulkan +implementation whether the allocation succeeds or fails. You can change this behavior +by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is +not made if it would exceed the budget or if the budget is already exceeded. +VMA then tries to make the allocation from the next eligible Vulkan memory type. +The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag +when creating resources that are not essential for the application (e.g. the texture +of a specific object) and not to pass it when creating critically important resources +(e.g. render targets). + +On AMD graphics cards there is a custom vendor extension available: VK_AMD_memory_overallocation_behavior +that allows to control the behavior of the Vulkan implementation in out-of-memory cases - +whether it should fail with an error code or still allow the allocation. +Usage of this extension involves only passing extra structure on Vulkan device creation, +so it is out of scope of this library. + +Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure +a new allocation is created only when it fits inside one of the existing memory blocks. +If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +This also ensures that the function call is very fast because it never goes to Vulkan +to obtain a new block. + +\note Creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount +set to more than 0 will currently try to allocate memory blocks without checking whether they +fit within budget. + + +\page resource_aliasing Resource aliasing (overlap) + +New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory +management, give an opportunity to alias (overlap) multiple resources in the +same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL). +It can be useful to save video memory, but it must be used with caution. + +For example, if you know the flow of your whole render frame in advance, you +are going to use some intermediate textures or buffers only during a small range of render passes, +and you know these ranges don't overlap in time, you can bind these resources to +the same place in memory, even if they have completely different parameters (width, height, format etc.). + +![Resource aliasing (overlap)](../gfx/Aliasing.png) + +Such scenario is possible using VMA, but you need to create your images manually. +Then you need to calculate parameters of an allocation to be made using formula: + +- allocation size = max(size of each image) +- allocation alignment = max(alignment of each image) +- allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image) + +Following example shows two different images bound to the same place in memory, +allocated to fit largest of them. + +\code +// A 512x512 texture to be sampled. +VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +img1CreateInfo.imageType = VK_IMAGE_TYPE_2D; +img1CreateInfo.extent.width = 512; +img1CreateInfo.extent.height = 512; +img1CreateInfo.extent.depth = 1; +img1CreateInfo.mipLevels = 10; +img1CreateInfo.arrayLayers = 1; +img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; +img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; +img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +// A full screen texture to be used as color attachment. +VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +img2CreateInfo.imageType = VK_IMAGE_TYPE_2D; +img2CreateInfo.extent.width = 1920; +img2CreateInfo.extent.height = 1080; +img2CreateInfo.extent.depth = 1; +img2CreateInfo.mipLevels = 1; +img2CreateInfo.arrayLayers = 1; +img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; +img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +VkImage img1; +res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1); +VkImage img2; +res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2); + +VkMemoryRequirements img1MemReq; +vkGetImageMemoryRequirements(device, img1, &img1MemReq); +VkMemoryRequirements img2MemReq; +vkGetImageMemoryRequirements(device, img2, &img2MemReq); + +VkMemoryRequirements finalMemReq = {}; +finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size); +finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment); +finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits; +// Validate if(finalMemReq.memoryTypeBits != 0) + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + +VmaAllocation alloc; +res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr); + +res = vmaBindImageMemory(allocator, alloc, img1); +res = vmaBindImageMemory(allocator, alloc, img2); + +// You can use img1, img2 here, but not at the same time! + +vmaFreeMemory(allocator, alloc); +vkDestroyImage(allocator, img2, nullptr); +vkDestroyImage(allocator, img1, nullptr); +\endcode + +Remember that using resources that alias in memory requires proper synchronization. +You need to issue a memory barrier to make sure commands that use `img1` and `img2` +don't overlap on GPU timeline. +You also need to treat a resource after aliasing as uninitialized - containing garbage data. +For example, if you use `img1` and then want to use `img2`, you need to issue +an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`. + +Additional considerations: + +- Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases. +See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag. +- You can create more complex layout where different images and buffers are bound +at different offsets inside one large allocation. For example, one can imagine +a big texture used in some render passes, aliasing with a set of many small buffers +used between in some further passes. To bind a resource at non-zero offset in an allocation, +use vmaBindBufferMemory2() / vmaBindImageMemory2(). +- Before allocating memory for the resources you want to alias, check `memoryTypeBits` +returned in memory requirements of each resource to make sure the bits overlap. +Some GPUs may expose multiple memory types suitable e.g. only for buffers or +images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your +resources may be disjoint. Aliasing them is not possible in that case. + + +\page custom_memory_pools Custom memory pools + +A memory pool contains a number of `VkDeviceMemory` blocks. +The library automatically creates and manages default pool for each memory type available on the device. +Default memory pool automatically grows in size. +Size of allocated blocks is also variable and managed automatically. + +You can create custom pool and allocate memory out of it. +It can be useful if you want to: + +- Keep certain kind of allocations separate from others. +- Enforce particular, fixed size of Vulkan memory blocks. +- Limit maximum amount of Vulkan memory allocated for that pool. +- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool. +- Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in + #VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain. +- Perform defragmentation on a specific subset of your allocations. + +To use custom memory pools: + +-# Fill VmaPoolCreateInfo structure. +-# Call vmaCreatePool() to obtain #VmaPool handle. +-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle. + You don't need to specify any other parameters of this structure, like `usage`. + +Example: + +\code +// Find memoryTypeIndex for the pool. +VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +sampleBufCreateInfo.size = 0x10000; // Doesn't matter. +sampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo sampleAllocCreateInfo = {}; +sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + +uint32_t memTypeIndex; +VkResult res = vmaFindMemoryTypeIndexForBufferInfo(allocator, + &sampleBufCreateInfo, &sampleAllocCreateInfo, &memTypeIndex); +// Check res... + +// Create a pool that can have at most 2 blocks, 128 MiB each. +VmaPoolCreateInfo poolCreateInfo = {}; +poolCreateInfo.memoryTypeIndex = memTypeIndex; +poolCreateInfo.blockSize = 128ull * 1024 * 1024; +poolCreateInfo.maxBlockCount = 2; + +VmaPool pool; +res = vmaCreatePool(allocator, &poolCreateInfo, &pool); +// Check res... + +// Allocate a buffer out of it. +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 1024; +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.pool = pool; + +VkBuffer buf; +VmaAllocation alloc; +res = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr); +// Check res... +\endcode + +You have to free all allocations made from this pool before destroying it. + +\code +vmaDestroyBuffer(allocator, buf, alloc); +vmaDestroyPool(allocator, pool); +\endcode + +New versions of this library support creating dedicated allocations in custom pools. +It is supported only when VmaPoolCreateInfo::blockSize = 0. +To use this feature, set VmaAllocationCreateInfo::pool to the pointer to your custom pool and +VmaAllocationCreateInfo::flags to #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + +\note Excessive use of custom pools is a common mistake when using this library. +Custom pools may be useful for special purposes - when you want to +keep certain type of resources separate e.g. to reserve minimum amount of memory +for them or limit maximum amount of memory they can occupy. For most +resources this is not needed and so it is not recommended to create #VmaPool +objects and allocations out of them. Allocating from the default pool is sufficient. + + +\section custom_memory_pools_MemTypeIndex Choosing memory type index + +When creating a pool, you must explicitly specify memory type index. +To find the one suitable for your buffers or images, you can use helper functions +vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo(). +You need to provide structures with example parameters of buffers or images +that you are going to create in that pool. + +\code +VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +exampleBufCreateInfo.size = 1024; // Doesn't matter +exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + +uint32_t memTypeIndex; +vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex); + +VmaPoolCreateInfo poolCreateInfo = {}; +poolCreateInfo.memoryTypeIndex = memTypeIndex; +// ... +\endcode + +When creating buffers/images allocated in that pool, provide following parameters: + +- `VkBufferCreateInfo`: Prefer to pass same parameters as above. + Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior. + Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers + or the other way around. +- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member. + Other members are ignored anyway. + +\section linear_algorithm Linear allocation algorithm + +Each Vulkan memory block managed by this library has accompanying metadata that +keeps track of used and unused regions. By default, the metadata structure and +algorithm tries to find best place for new allocations among free regions to +optimize memory usage. This way you can allocate and free objects in any order. + +![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png) + +Sometimes there is a need to use simpler, linear allocation algorithm. You can +create custom pool that uses such algorithm by adding flag +#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating +#VmaPool object. Then an alternative metadata management is used. It always +creates new allocations after last one and doesn't reuse free regions after +allocations freed in the middle. It results in better allocation performance and +less memory consumed by metadata. + +![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png) + +With this one flag, you can create a custom pool that can be used in many ways: +free-at-once, stack, double stack, and ring buffer. See below for details. +You don't need to specify explicitly which of these options you are going to use - it is detected automatically. + +\subsection linear_algorithm_free_at_once Free-at-once + +In a pool that uses linear algorithm, you still need to free all the allocations +individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free +them in any order. New allocations are always made after last one - free space +in the middle is not reused. However, when you release all the allocation and +the pool becomes empty, allocation starts from the beginning again. This way you +can use linear algorithm to speed up creation of allocations that you are going +to release all at once. + +![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png) + +This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount +value that allows multiple memory blocks. + +\subsection linear_algorithm_stack Stack + +When you free an allocation that was created last, its space can be reused. +Thanks to this, if you always release allocations in the order opposite to their +creation (LIFO - Last In First Out), you can achieve behavior of a stack. + +![Stack](../gfx/Linear_allocator_4_stack.png) + +This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount +value that allows multiple memory blocks. + +\subsection linear_algorithm_double_stack Double stack + +The space reserved by a custom pool with linear algorithm may be used by two +stacks: + +- First, default one, growing up from offset 0. +- Second, "upper" one, growing down from the end towards lower offsets. + +To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT +to VmaAllocationCreateInfo::flags. + +![Double stack](../gfx/Linear_allocator_7_double_stack.png) + +Double stack is available only in pools with one memory block - +VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. + +When the two stacks' ends meet so there is not enough space between them for a +new allocation, such allocation fails with usual +`VK_ERROR_OUT_OF_DEVICE_MEMORY` error. + +\subsection linear_algorithm_ring_buffer Ring buffer + +When you free some allocations from the beginning and there is not enough free space +for a new one at the end of a pool, allocator's "cursor" wraps around to the +beginning and starts allocation there. Thanks to this, if you always release +allocations in the same order as you created them (FIFO - First In First Out), +you can achieve behavior of a ring buffer / queue. + +![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png) + +Ring buffer is available only in pools with one memory block - +VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. + +\note \ref defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT. + + +\page defragmentation Defragmentation + +Interleaved allocations and deallocations of many objects of varying size can +cause fragmentation over time, which can lead to a situation where the library is unable +to find a continuous range of free memory for a new allocation despite there is +enough free space, just scattered across many small free ranges between existing +allocations. + +To mitigate this problem, you can use defragmentation feature. +It doesn't happen automatically though and needs your cooperation, +because VMA is a low level library that only allocates memory. +It cannot recreate buffers and images in a new place as it doesn't remember the contents of `VkBufferCreateInfo` / `VkImageCreateInfo` structures. +It cannot copy their contents as it doesn't record any commands to a command buffer. + +Example: + +\code +VmaDefragmentationInfo defragInfo = {}; +defragInfo.pool = myPool; +defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT; + +VmaDefragmentationContext defragCtx; +VkResult res = vmaBeginDefragmentation(allocator, &defragInfo, &defragCtx); +// Check res... + +for(;;) +{ + VmaDefragmentationPassMoveInfo pass; + res = vmaBeginDefragmentationPass(allocator, defragCtx, &pass); + if(res == VK_SUCCESS) + break; + else if(res != VK_INCOMPLETE) + // Handle error... + + for(uint32_t i = 0; i < pass.moveCount; ++i) + { + // Inspect pass.pMoves[i].srcAllocation, identify what buffer/image it represents. + VmaAllocationInfo allocInfo; + vmaGetAllocationInfo(allocator, pMoves[i].srcAllocation, &allocInfo); + MyEngineResourceData* resData = (MyEngineResourceData*)allocInfo.pUserData; + + // Recreate and bind this buffer/image at: pass.pMoves[i].dstMemory, pass.pMoves[i].dstOffset. + VkImageCreateInfo imgCreateInfo = ... + VkImage newImg; + res = vkCreateImage(device, &imgCreateInfo, nullptr, &newImg); + // Check res... + res = vmaBindImageMemory(allocator, pMoves[i].dstTmpAllocation, newImg); + // Check res... + + // Issue a vkCmdCopyBuffer/vkCmdCopyImage to copy its content to the new place. + vkCmdCopyImage(cmdBuf, resData->img, ..., newImg, ...); + } + + // Make sure the copy commands finished executing. + vkWaitForFences(...); + + // Destroy old buffers/images bound with pass.pMoves[i].srcAllocation. + for(uint32_t i = 0; i < pass.moveCount; ++i) + { + // ... + vkDestroyImage(device, resData->img, nullptr); + } + + // Update appropriate descriptors to point to the new places... + + res = vmaEndDefragmentationPass(allocator, defragCtx, &pass); + if(res == VK_SUCCESS) + break; + else if(res != VK_INCOMPLETE) + // Handle error... +} + +vmaEndDefragmentation(allocator, defragCtx, nullptr); +\endcode + +Although functions like vmaCreateBuffer(), vmaCreateImage(), vmaDestroyBuffer(), vmaDestroyImage() +create/destroy an allocation and a buffer/image at once, these are just a shortcut for +creating the resource, allocating memory, and binding them together. +Defragmentation works on memory allocations only. You must handle the rest manually. +Defragmentation is an iterative process that should repreat "passes" as long as related functions +return `VK_INCOMPLETE` not `VK_SUCCESS`. +In each pass: + +1. vmaBeginDefragmentationPass() function call: + - Calculates and returns the list of allocations to be moved in this pass. + Note this can be a time-consuming process. + - Reserves destination memory for them by creating temporary destination allocations + that you can query for their `VkDeviceMemory` + offset using vmaGetAllocationInfo(). +2. Inside the pass, **you should**: + - Inspect the returned list of allocations to be moved. + - Create new buffers/images and bind them at the returned destination temporary allocations. + - Copy data from source to destination resources if necessary. + - Destroy the source buffers/images, but NOT their allocations. +3. vmaEndDefragmentationPass() function call: + - Frees the source memory reserved for the allocations that are moved. + - Modifies source #VmaAllocation objects that are moved to point to the destination reserved memory. + - Frees `VkDeviceMemory` blocks that became empty. + +Unlike in previous iterations of the defragmentation API, there is no list of "movable" allocations passed as a parameter. +Defragmentation algorithm tries to move all suitable allocations. +You can, however, refuse to move some of them inside a defragmentation pass, by setting +`pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. +This is not recommended and may result in suboptimal packing of the allocations after defragmentation. +If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool. + +Inside a pass, for each allocation that should be moved: + +- You should copy its data from the source to the destination place by calling e.g. `vkCmdCopyBuffer()`, `vkCmdCopyImage()`. + - You need to make sure these commands finished executing before destroying the source buffers/images and before calling vmaEndDefragmentationPass(). +- If a resource doesn't contain any meaningful data, e.g. it is a transient color attachment image to be cleared, + filled, and used temporarily in each rendering frame, you can just recreate this image + without copying its data. +- If the resource is in `HOST_VISIBLE` and `HOST_CACHED` memory, you can copy its data on the CPU + using `memcpy()`. +- If you cannot move the allocation, you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. + This will cancel the move. + - vmaEndDefragmentationPass() will then free the destination memory + not the source memory of the allocation, leaving it unchanged. +- If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time), + you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY. + - vmaEndDefragmentationPass() will then free both source and destination memory, and will destroy the source #VmaAllocation object. + +You can defragment a specific custom pool by setting VmaDefragmentationInfo::pool +(like in the example above) or all the default pools by setting this member to null. + +Defragmentation is always performed in each pool separately. +Allocations are never moved between different Vulkan memory types. +The size of the destination memory reserved for a moved allocation is the same as the original one. +Alignment of an allocation as it was determined using `vkGetBufferMemoryRequirements()` etc. is also respected after defragmentation. +Buffers/images should be recreated with the same `VkBufferCreateInfo` / `VkImageCreateInfo` parameters as the original ones. + +You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved +in each pass, e.g. to call it in sync with render frames and not to experience too big hitches. +See members: VmaDefragmentationInfo::maxBytesPerPass, VmaDefragmentationInfo::maxAllocationsPerPass. + +It is also safe to perform the defragmentation asynchronously to render frames and other Vulkan and VMA +usage, possibly from multiple threads, with the exception that allocations +returned in VmaDefragmentationPassMoveInfo::pMoves shouldn't be destroyed until the defragmentation pass is ended. + +Mapping is preserved on allocations that are moved during defragmentation. +Whether through #VMA_ALLOCATION_CREATE_MAPPED_BIT or vmaMapMemory(), the allocations +are mapped at their new place. Of course, pointer to the mapped data changes, so it needs to be queried +using VmaAllocationInfo::pMappedData. + +\note Defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT. + + +\page statistics Statistics + +This library contains several functions that return information about its internal state, +especially the amount of memory allocated from Vulkan. + +\section statistics_numeric_statistics Numeric statistics + +If you need to obtain basic statistics about memory usage per heap, together with current budget, +you can call function vmaGetHeapBudgets() and inspect structure #VmaBudget. +This is useful to keep track of memory usage and stay withing budget +(see also \ref staying_within_budget). +Example: + +\code +uint32_t heapIndex = ... + +VmaBudget budgets[VK_MAX_MEMORY_HEAPS]; +vmaGetHeapBudgets(allocator, budgets); + +printf("My heap currently has %u allocations taking %llu B,\n", + budgets[heapIndex].statistics.allocationCount, + budgets[heapIndex].statistics.allocationBytes); +printf("allocated out of %u Vulkan device memory blocks taking %llu B,\n", + budgets[heapIndex].statistics.blockCount, + budgets[heapIndex].statistics.blockBytes); +printf("Vulkan reports total usage %llu B with budget %llu B.\n", + budgets[heapIndex].usage, + budgets[heapIndex].budget); +\endcode + +You can query for more detailed statistics per memory heap, type, and totals, +including minimum and maximum allocation size and unused range size, +by calling function vmaCalculateStatistics() and inspecting structure #VmaTotalStatistics. +This function is slower though, as it has to traverse all the internal data structures, +so it should be used only for debugging purposes. + +You can query for statistics of a custom pool using function vmaGetPoolStatistics() +or vmaCalculatePoolStatistics(). + +You can query for information about a specific allocation using function vmaGetAllocationInfo(). +It fill structure #VmaAllocationInfo. + +\section statistics_json_dump JSON dump + +You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString(). +The result is guaranteed to be correct JSON. +It uses ANSI encoding. +Any strings provided by user (see [Allocation names](@ref allocation_names)) +are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding, +this JSON string can be treated as using this encoding. +It must be freed using function vmaFreeStatsString(). + +The format of this JSON string is not part of official documentation of the library, +but it will not change in backward-incompatible way without increasing library major version number +and appropriate mention in changelog. + +The JSON string contains all the data that can be obtained using vmaCalculateStatistics(). +It can also contain detailed map of allocated memory blocks and their regions - +free and occupied by allocations. +This allows e.g. to visualize the memory or assess fragmentation. + + +\page allocation_annotation Allocation names and user data + +\section allocation_user_data Allocation user data + +You can annotate allocations with your own information, e.g. for debugging purposes. +To do that, fill VmaAllocationCreateInfo::pUserData field when creating +an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer, +some handle, index, key, ordinal number or any other value that would associate +the allocation with your custom metadata. +It it useful to identify appropriate data structures in your engine given #VmaAllocation, +e.g. when doing \ref defragmentation. + +\code +VkBufferCreateInfo bufCreateInfo = ... + +MyBufferMetadata* pMetadata = CreateBufferMetadata(); + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.pUserData = pMetadata; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buffer, &allocation, nullptr); +\endcode + +The pointer may be later retrieved as VmaAllocationInfo::pUserData: + +\code +VmaAllocationInfo allocInfo; +vmaGetAllocationInfo(allocator, allocation, &allocInfo); +MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData; +\endcode + +It can also be changed using function vmaSetAllocationUserData(). + +Values of (non-zero) allocations' `pUserData` are printed in JSON report created by +vmaBuildStatsString() in hexadecimal form. + +\section allocation_names Allocation names + +An allocation can also carry a null-terminated string, giving a name to the allocation. +To set it, call vmaSetAllocationName(). +The library creates internal copy of the string, so the pointer you pass doesn't need +to be valid for whole lifetime of the allocation. You can free it after the call. + +\code +std::string imageName = "Texture: "; +imageName += fileName; +vmaSetAllocationName(allocator, allocation, imageName.c_str()); +\endcode + +The string can be later retrieved by inspecting VmaAllocationInfo::pName. +It is also printed in JSON report created by vmaBuildStatsString(). + +\note Setting string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it. +You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library. + + +\page virtual_allocator Virtual allocator + +As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator". +It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block". +You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan. +A common use case is sub-allocation of pieces of one large GPU buffer. + +\section virtual_allocator_creating_virtual_block Creating virtual block + +To use this functionality, there is no main "allocator" object. +You don't need to have #VmaAllocator object created. +All you need to do is to create a separate #VmaVirtualBlock object for each block of memory you want to be managed by the allocator: + +-# Fill in #VmaVirtualBlockCreateInfo structure. +-# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object. + +Example: + +\code +VmaVirtualBlockCreateInfo blockCreateInfo = {}; +blockCreateInfo.size = 1048576; // 1 MB + +VmaVirtualBlock block; +VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block); +\endcode + +\section virtual_allocator_making_virtual_allocations Making virtual allocations + +#VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions +using the same code as the main Vulkan memory allocator. +Similarly to #VmaAllocation for standard GPU allocations, there is #VmaVirtualAllocation type +that represents an opaque handle to an allocation withing the virtual block. + +In order to make such allocation: + +-# Fill in #VmaVirtualAllocationCreateInfo structure. +-# Call vmaVirtualAllocate(). Get new #VmaVirtualAllocation object that represents the allocation. + You can also receive `VkDeviceSize offset` that was assigned to the allocation. + +Example: + +\code +VmaVirtualAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.size = 4096; // 4 KB + +VmaVirtualAllocation alloc; +VkDeviceSize offset; +res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, &offset); +if(res == VK_SUCCESS) +{ + // Use the 4 KB of your memory starting at offset. +} +else +{ + // Allocation failed - no space for it could be found. Handle this error! +} +\endcode + +\section virtual_allocator_deallocation Deallocation + +When no longer needed, an allocation can be freed by calling vmaVirtualFree(). +You can only pass to this function an allocation that was previously returned by vmaVirtualAllocate() +called for the same #VmaVirtualBlock. + +When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock(). +All allocations must be freed before the block is destroyed, which is checked internally by an assert. +However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once - +a feature not available in normal Vulkan memory allocator. Example: + +\code +vmaVirtualFree(block, alloc); +vmaDestroyVirtualBlock(block); +\endcode + +\section virtual_allocator_allocation_parameters Allocation parameters + +You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData(). +Its default value is null. +It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some +larger data structure containing more information. Example: + +\code +struct CustomAllocData +{ + std::string m_AllocName; +}; +CustomAllocData* allocData = new CustomAllocData(); +allocData->m_AllocName = "My allocation 1"; +vmaSetVirtualAllocationUserData(block, alloc, allocData); +\endcode + +The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function +vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo. +If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation! +Example: + +\code +VmaVirtualAllocationInfo allocInfo; +vmaGetVirtualAllocationInfo(block, alloc, &allocInfo); +delete (CustomAllocData*)allocInfo.pUserData; + +vmaVirtualFree(block, alloc); +\endcode + +\section virtual_allocator_alignment_and_units Alignment and units + +It feels natural to express sizes and offsets in bytes. +If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member +VmaVirtualAllocationCreateInfo::alignment to request it. Example: + +\code +VmaVirtualAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.size = 4096; // 4 KB +allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B + +VmaVirtualAllocation alloc; +res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, nullptr); +\endcode + +Alignments of different allocations made from one block may vary. +However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`, +you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes. +It might be more convenient, but you need to make sure to use this new unit consistently in all the places: + +- VmaVirtualBlockCreateInfo::size +- VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment +- Using offset returned by vmaVirtualAllocate() or in VmaVirtualAllocationInfo::offset + +\section virtual_allocator_statistics Statistics + +You can obtain statistics of a virtual block using vmaGetVirtualBlockStatistics() +(to get brief statistics that are fast to calculate) +or vmaCalculateVirtualBlockStatistics() (to get more detailed statistics, slower to calculate). +The functions fill structures #VmaStatistics, #VmaDetailedStatistics respectively - same as used by the normal Vulkan memory allocator. +Example: + +\code +VmaStatistics stats; +vmaGetVirtualBlockStatistics(block, &stats); +printf("My virtual block has %llu bytes used by %u virtual allocations\n", + stats.allocationBytes, stats.allocationCount); +\endcode + +You can also request a full list of allocations and free regions as a string in JSON format by calling +vmaBuildVirtualBlockStatsString(). +Returned string must be later freed using vmaFreeVirtualBlockStatsString(). +The format of this string differs from the one returned by the main Vulkan allocator, but it is similar. + +\section virtual_allocator_additional_considerations Additional considerations + +The "virtual allocator" functionality is implemented on a level of individual memory blocks. +Keeping track of a whole collection of blocks, allocating new ones when out of free space, +deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user. + +Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory. +See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT). +You can find their description in chapter \ref custom_memory_pools. +Allocation strategies are also supported. +See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT). + +Following features are supported only by the allocator of the real GPU memory and not by virtual allocations: +buffer-image granularity, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`. + + +\page debugging_memory_usage Debugging incorrect memory usage + +If you suspect a bug with memory usage, like usage of uninitialized memory or +memory being overwritten out of bounds of an allocation, +you can use debug features of this library to verify this. + +\section debugging_memory_usage_initialization Memory initialization + +If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used, +you can enable automatic memory initialization to verify this. +To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1. + +\code +#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 +#include "vk_mem_alloc.h" +\endcode + +It makes memory of new allocations initialized to bit pattern `0xDCDCDCDC`. +Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`. +Memory is automatically mapped and unmapped if necessary. + +If you find these values while debugging your program, good chances are that you incorrectly +read Vulkan memory that is allocated but not initialized, or already freed, respectively. + +Memory initialization works only with memory types that are `HOST_VISIBLE` and with allocations that can be mapped. +It works also with dedicated allocations. + +\section debugging_memory_usage_margins Margins + +By default, allocations are laid out in memory blocks next to each other if possible +(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`). + +![Allocations without margin](../gfx/Margins_1.png) + +Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified +number of bytes as a margin after every allocation. + +\code +#define VMA_DEBUG_MARGIN 16 +#include "vk_mem_alloc.h" +\endcode + +![Allocations with margin](../gfx/Margins_2.png) + +If your bug goes away after enabling margins, it means it may be caused by memory +being overwritten outside of allocation boundaries. It is not 100% certain though. +Change in application behavior may also be caused by different order and distribution +of allocations across memory blocks after margins are applied. + +Margins work with all types of memory. + +Margin is applied only to allocations made out of memory blocks and not to dedicated +allocations, which have their own memory block of specific size. +It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag +or those automatically decided to put into dedicated allocations, e.g. due to its +large size or recommended by VK_KHR_dedicated_allocation extension. + +Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space. + +Note that enabling margins increases memory usage and fragmentation. + +Margins do not apply to \ref virtual_allocator. + +\section debugging_memory_usage_corruption_detection Corruption detection + +You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation +of contents of the margins. + +\code +#define VMA_DEBUG_MARGIN 16 +#define VMA_DEBUG_DETECT_CORRUPTION 1 +#include "vk_mem_alloc.h" +\endcode + +When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN` +(it must be multiply of 4) after every allocation is filled with a magic number. +This idea is also know as "canary". +Memory is automatically mapped and unmapped if necessary. + +This number is validated automatically when the allocation is destroyed. +If it is not equal to the expected value, `VMA_ASSERT()` is executed. +It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation, +which indicates a serious bug. + +You can also explicitly request checking margins of all allocations in all memory blocks +that belong to specified memory types by using function vmaCheckCorruption(), +or in memory blocks that belong to specified custom pool, by using function +vmaCheckPoolCorruption(). + +Margin validation (corruption detection) works only for memory types that are +`HOST_VISIBLE` and `HOST_COHERENT`. + + +\page opengl_interop OpenGL Interop + +VMA provides some features that help with interoperability with OpenGL. + +\section opengl_interop_exporting_memory Exporting memory + +If you want to attach `VkExportMemoryAllocateInfoKHR` structure to `pNext` chain of memory allocations made by the library: + +It is recommended to create \ref custom_memory_pools for such allocations. +Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext +while creating the custom pool. +Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool, +not only while creating it, as no copy of the structure is made, +but its original pointer is used for each allocation instead. + +If you want to export all memory allocated by the library from certain memory types, +also dedicated allocations or other allocations made from default pools, +an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes. +It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library +through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type. +Please note that new versions of the library also support dedicated allocations created in custom pools. + +You should not mix these two methods in a way that allows to apply both to the same memory type. +Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`. + + +\section opengl_interop_custom_alignment Custom alignment + +Buffers or images exported to a different API like OpenGL may require a different alignment, +higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`. +To impose such alignment: + +It is recommended to create \ref custom_memory_pools for such allocations. +Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation +to be made out of this pool. +The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image +from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically. + +If you want to create a buffer with a specific minimum alignment out of default pools, +use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`. + +Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated +allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block. +Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation. + + +\page usage_patterns Recommended usage patterns + +Vulkan gives great flexibility in memory allocation. +This chapter shows the most common patterns. + +See also slides from talk: +[Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New) + + +\section usage_patterns_gpu_only GPU-only resource + +When: +Any resources that you frequently write and read on GPU, +e.g. images used as color attachments (aka "render targets"), depth-stencil attachments, +images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)"). + +What to do: +Let the library select the optimal memory type, which will likely have `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + +\code +VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; +imgCreateInfo.extent.width = 3840; +imgCreateInfo.extent.height = 2160; +imgCreateInfo.extent.depth = 1; +imgCreateInfo.mipLevels = 1; +imgCreateInfo.arrayLayers = 1; +imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; +imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; +allocCreateInfo.priority = 1.0f; + +VkImage img; +VmaAllocation alloc; +vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr); +\endcode + +Also consider: +Consider creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, +especially if they are large or if you plan to destroy and recreate them with different sizes +e.g. when display resolution changes. +Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later. +When VK_EXT_memory_priority extension is enabled, it is also worth setting high priority to such allocation +to decrease chances to be evicted to system memory by the operating system. + +\section usage_patterns_staging_copy_upload Staging copy for upload + +When: +A "staging" buffer than you want to map and fill from CPU code, then use as a source od transfer +to some GPU resource. + +What to do: +Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT. +Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 65536; +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +... + +memcpy(allocInfo.pMappedData, myData, myDataSize); +\endcode + +Also consider: +You can map the allocation using vmaMapMemory() or you can create it as persistenly mapped +using #VMA_ALLOCATION_CREATE_MAPPED_BIT, as in the example above. + + +\section usage_patterns_readback Readback + +When: +Buffers for data written by or transferred from the GPU that you want to read back on the CPU, +e.g. results of some computations. + +What to do: +Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` +and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 65536; +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +... + +const float* downloadedData = (const float*)allocInfo.pMappedData; +\endcode + + +\section usage_patterns_advanced_data_uploading Advanced data uploading + +For resources that you frequently write on CPU via mapped pointer and +freqnently read on GPU e.g. as a uniform buffer (also called "dynamic"), multiple options are possible: + +-# Easiest solution is to have one copy of the resource in `HOST_VISIBLE` memory, + even if it means system RAM (not `DEVICE_LOCAL`) on systems with a discrete graphics card, + and make the device reach out to that resource directly. + - Reads performed by the device will then go through PCI Express bus. + The performace of this access may be limited, but it may be fine depending on the size + of this resource (whether it is small enough to quickly end up in GPU cache) and the sparsity + of access. +-# On systems with unified memory (e.g. AMD APU or Intel integrated graphics, mobile chips), + a memory type may be available that is both `HOST_VISIBLE` (available for mapping) and `DEVICE_LOCAL` + (fast to access from the GPU). Then, it is likely the best choice for such type of resource. +-# Systems with a discrete graphics card and separate video memory may or may not expose + a memory type that is both `HOST_VISIBLE` and `DEVICE_LOCAL`, also known as Base Address Register (BAR). + If they do, it represents a piece of VRAM (or entire VRAM, if ReBAR is enabled in the motherboard BIOS) + that is available to CPU for mapping. + - Writes performed by the host to that memory go through PCI Express bus. + The performance of these writes may be limited, but it may be fine, especially on PCIe 4.0, + as long as rules of using uncached and write-combined memory are followed - only sequential writes and no reads. +-# Finally, you may need or prefer to create a separate copy of the resource in `DEVICE_LOCAL` memory, + a separate "staging" copy in `HOST_VISIBLE` memory and perform an explicit transfer command between them. + +Thankfully, VMA offers an aid to create and use such resources in the the way optimal +for the current Vulkan device. To help the library make the best choice, +use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT together with +#VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT. +It will then prefer a memory type that is both `DEVICE_LOCAL` and `HOST_VISIBLE` (integrated memory or BAR), +but if no such memory type is available or allocation from it fails +(PC graphics cards have only 256 MB of BAR by default, unless ReBAR is supported and enabled in BIOS), +it will fall back to `DEVICE_LOCAL` memory for fast GPU access. +It is then up to you to detect that the allocation ended up in a memory type that is not `HOST_VISIBLE`, +so you need to create another "staging" allocation and perform explicit transfers. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 65536; +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +VkMemoryPropertyFlags memPropFlags; +vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags); + +if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) +{ + // Allocation ended up in a mappable memory and is already mapped - write to it directly. + + // [Executed in runtime]: + memcpy(allocInfo.pMappedData, myData, myDataSize); +} +else +{ + // Allocation ended up in a non-mappable memory - need to transfer. + VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + stagingBufCreateInfo.size = 65536; + stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + VmaAllocationCreateInfo stagingAllocCreateInfo = {}; + stagingAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + stagingAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + + VkBuffer stagingBuf; + VmaAllocation stagingAlloc; + VmaAllocationInfo stagingAllocInfo; + vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo, + &stagingBuf, &stagingAlloc, stagingAllocInfo); + + // [Executed in runtime]: + memcpy(stagingAllocInfo.pMappedData, myData, myDataSize); + //vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT + VkBufferCopy bufCopy = { + 0, // srcOffset + 0, // dstOffset, + myDataSize); // size + vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy); +} +\endcode + +\section usage_patterns_other_use_cases Other use cases + +Here are some other, less obvious use cases and their recommended settings: + +- An image that is used only as transfer source and destination, but it should stay on the device, + as it is used to temporarily store a copy of some texture, e.g. from the current to the next frame, + for temporal antialiasing or other temporal effects. + - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT` + - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO +- An image that is used only as transfer source and destination, but it should be placed + in the system RAM despite it doesn't need to be mapped, because it serves as a "swap" copy to evict + least recently used textures from VRAM. + - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT` + - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_HOST, + as VMA needs a hint here to differentiate from the previous case. +- A buffer that you want to map and write from the CPU, directly read from the GPU + (e.g. as a uniform or vertex buffer), but you have a clear preference to place it in device or + host memory due to its large size. + - Use `VkBufferCreateInfo::usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT` + - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST + - Use VmaAllocationCreateInfo::flags = #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT + + +\page configuration Configuration + +Please check "CONFIGURATION SECTION" in the code to find macros that you can define +before each include of this file or change directly in this file to provide +your own implementation of basic facilities like assert, `min()` and `max()` functions, +mutex, atomic etc. +The library uses its own implementation of containers by default, but you can switch to using +STL containers instead. + +For example, define `VMA_ASSERT(expr)` before including the library to provide +custom implementation of the assertion, compatible with your project. +By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration +and empty otherwise. + +\section config_Vulkan_functions Pointers to Vulkan functions + +There are multiple ways to import pointers to Vulkan functions in the library. +In the simplest case you don't need to do anything. +If the compilation or linking of your program or the initialization of the #VmaAllocator +doesn't work for you, you can try to reconfigure it. + +First, the allocator tries to fetch pointers to Vulkan functions linked statically, +like this: + +\code +m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory; +\endcode + +If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`. + +Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions. +You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or +by using a helper library like [volk](https://github.com/zeux/volk). + +Third, VMA tries to fetch remaining pointers that are still null by calling +`vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own. +You need to only fill in VmaVulkanFunctions::vkGetInstanceProcAddr and VmaVulkanFunctions::vkGetDeviceProcAddr. +Other pointers will be fetched automatically. +If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`. + +Finally, all the function pointers required by the library (considering selected +Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null. + + +\section custom_memory_allocator Custom host memory allocator + +If you use custom allocator for CPU memory rather than default operator `new` +and `delete` from C++, you can make this library using your allocator as well +by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These +functions will be passed to Vulkan, as well as used by the library itself to +make any CPU-side allocations. + +\section allocation_callbacks Device memory allocation callbacks + +The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally. +You can setup callbacks to be informed about these calls, e.g. for the purpose +of gathering some statistics. To do it, fill optional member +VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. + +\section heap_memory_limit Device heap memory limit + +When device memory of certain heap runs out of free space, new allocations may +fail (returning error code) or they may succeed, silently pushing some existing_ +memory blocks from GPU VRAM to system RAM (which degrades performance). This +behavior is implementation-dependent - it depends on GPU vendor and graphics +driver. + +On AMD cards it can be controlled while creating Vulkan device object by using +VK_AMD_memory_overallocation_behavior extension, if available. + +Alternatively, if you want to test how your program behaves with limited amount of Vulkan device +memory available without switching your graphics card to one that really has +smaller VRAM, you can use a feature of this library intended for this purpose. +To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit. + + + +\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation + +VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve +performance on some GPUs. It augments Vulkan API with possibility to query +driver whether it prefers particular buffer or image to have its own, dedicated +allocation (separate `VkDeviceMemory` block) for better efficiency - to be able +to do some internal optimizations. The extension is supported by this library. +It will be used automatically when enabled. + +It has been promoted to core Vulkan 1.1, so if you use eligible Vulkan version +and inform VMA about it by setting VmaAllocatorCreateInfo::vulkanApiVersion, +you are all set. + +Otherwise, if you want to use it as an extension: + +1 . When creating Vulkan device, check if following 2 device extensions are +supported (call `vkEnumerateDeviceExtensionProperties()`). +If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`). + +- VK_KHR_get_memory_requirements2 +- VK_KHR_dedicated_allocation + +If you enabled these extensions: + +2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating +your #VmaAllocator to inform the library that you enabled required extensions +and you want the library to use them. + +\code +allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; + +vmaCreateAllocator(&allocatorInfo, &allocator); +\endcode + +That is all. The extension will be automatically used whenever you create a +buffer using vmaCreateBuffer() or image using vmaCreateImage(). + +When using the extension together with Vulkan Validation Layer, you will receive +warnings like this: + +_vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer._ + +It is OK, you should just ignore it. It happens because you use function +`vkGetBufferMemoryRequirements2KHR()` instead of standard +`vkGetBufferMemoryRequirements()`, while the validation layer seems to be +unaware of it. + +To learn more about this extension, see: + +- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation) +- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5) + + + +\page vk_ext_memory_priority VK_EXT_memory_priority + +VK_EXT_memory_priority is a device extension that allows to pass additional "priority" +value to Vulkan memory allocations that the implementation may use prefer certain +buffers and images that are critical for performance to stay in device-local memory +in cases when the memory is over-subscribed, while some others may be moved to the system memory. + +VMA offers convenient usage of this extension. +If you enable it, you can pass "priority" parameter when creating allocations or custom pools +and the library automatically passes the value to Vulkan using this extension. + +If you want to use this extension in connection with VMA, follow these steps: + +\section vk_ext_memory_priority_initialization Initialization + +1) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_EXT_memory_priority". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority` is true. + +3) While creating device with `vkCreateDevice`, enable this extension - add "VK_EXT_memory_priority" +to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to +`VkPhysicalDeviceFeatures2::pNext` chain and set its member `memoryPriority` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT +to VmaAllocatorCreateInfo::flags. + +\section vk_ext_memory_priority_usage Usage + +When using this extension, you should initialize following member: + +- VmaAllocationCreateInfo::priority when creating a dedicated allocation with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +- VmaPoolCreateInfo::priority when creating a custom pool. + +It should be a floating-point value between `0.0f` and `1.0f`, where recommended default is `0.5f`. +Memory allocated with higher value can be treated by the Vulkan implementation as higher priority +and so it can have lower chances of being pushed out to system memory, experiencing degraded performance. + +It might be a good idea to create performance-critical resources like color-attachment or depth-stencil images +as dedicated and set high priority to them. For example: + +\code +VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; +imgCreateInfo.extent.width = 3840; +imgCreateInfo.extent.height = 2160; +imgCreateInfo.extent.depth = 1; +imgCreateInfo.mipLevels = 1; +imgCreateInfo.arrayLayers = 1; +imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; +imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; +allocCreateInfo.priority = 1.0f; + +VkImage img; +VmaAllocation alloc; +vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr); +\endcode + +`priority` member is ignored in the following situations: + +- Allocations created in custom pools: They inherit the priority, along with all other allocation parameters + from the parametrs passed in #VmaPoolCreateInfo when the pool was created. +- Allocations created in default pools: They inherit the priority from the parameters + VMA used when creating default pools, which means `priority == 0.5f`. + + +\page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory + +VK_AMD_device_coherent_memory is a device extension that enables access to +additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and +`VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for +allocation of buffers intended for writing "breadcrumb markers" in between passes +or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases. + +When the extension is available but has not been enabled, Vulkan physical device +still exposes those memory types, but their usage is forbidden. VMA automatically +takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt +to allocate memory of such type is made. + +If you want to use this extension in connection with VMA, follow these steps: + +\section vk_amd_device_coherent_memory_initialization Initialization + +1) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true. + +3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory" +to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to +`VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT +to VmaAllocatorCreateInfo::flags. + +\section vk_amd_device_coherent_memory_usage Usage + +After following steps described above, you can create VMA allocations and custom pools +out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible +devices. There are multiple ways to do it, for example: + +- You can request or prefer to allocate out of such memory types by adding + `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags + or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with + other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage. +- If you manually found memory type index to use for this purpose, force allocation + from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`. + +\section vk_amd_device_coherent_memory_more_information More information + +To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_AMD_device_coherent_memory.html) + +Example use of this extension can be found in the code of the sample and test suite +accompanying this library. + + +\page enabling_buffer_device_address Enabling buffer device address + +Device extension VK_KHR_buffer_device_address +allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code. +It has been promoted to core Vulkan 1.2. + +If you want to use this feature in connection with VMA, follow these steps: + +\section enabling_buffer_device_address_initialization Initialization + +1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains +"VK_KHR_buffer_device_address". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress` is true. + +3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add +"VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to +`VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT +to VmaAllocatorCreateInfo::flags. + +\section enabling_buffer_device_address_usage Usage + +After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA. +The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to +allocated memory blocks wherever it might be needed. + +Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`. +The second part of this functionality related to "capture and replay" is not supported, +as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage. + +\section enabling_buffer_device_address_more_information More information + +To learn more about this extension, see [VK_KHR_buffer_device_address in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap46.html#VK_KHR_buffer_device_address) + +Example use of this extension can be found in the code of the sample and test suite +accompanying this library. + +\page general_considerations General considerations + +\section general_considerations_thread_safety Thread safety + +- The library has no global state, so separate #VmaAllocator objects can be used + independently. + There should be no need to create multiple such objects though - one per `VkDevice` is enough. +- By default, all calls to functions that take #VmaAllocator as first parameter + are safe to call from multiple threads simultaneously because they are + synchronized internally when needed. + This includes allocation and deallocation from default memory pool, as well as custom #VmaPool. +- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT + flag, calls to functions that take such #VmaAllocator object must be + synchronized externally. +- Access to a #VmaAllocation object must be externally synchronized. For example, + you must not call vmaGetAllocationInfo() and vmaMapMemory() from different + threads at the same time if you pass the same #VmaAllocation object to these + functions. +- #VmaVirtualBlock is not safe to be used from multiple threads simultaneously. + +\section general_considerations_versioning_and_compatibility Versioning and compatibility + +The library uses [**Semantic Versioning**](https://semver.org/), +which means version numbers follow convention: Major.Minor.Patch (e.g. 2.3.0), where: + +- Incremented Patch version means a release is backward- and forward-compatible, + introducing only some internal improvements, bug fixes, optimizations etc. + or changes that are out of scope of the official API described in this documentation. +- Incremented Minor version means a release is backward-compatible, + so existing code that uses the library should continue to work, while some new + symbols could have been added: new structures, functions, new values in existing + enums and bit flags, new structure members, but not new function parameters. +- Incrementing Major version means a release could break some backward compatibility. + +All changes between official releases are documented in file "CHANGELOG.md". + +\warning Backward compatiblity is considered on the level of C++ source code, not binary linkage. +Adding new members to existing structures is treated as backward compatible if initializing +the new members to binary zero results in the old behavior. +You should always fully initialize all library structures to zeros and not rely on their +exact binary size. + +\section general_considerations_validation_layer_warnings Validation layer warnings + +When using this library, you can meet following types of warnings issued by +Vulkan validation layer. They don't necessarily indicate a bug, so you may need +to just ignore them. + +- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.* + - It happens when VK_KHR_dedicated_allocation extension is enabled. + `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it. +- *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.* + - It happens when you map a buffer or image, because the library maps entire + `VkDeviceMemory` block, where different types of images and buffers may end + up together, especially on GPUs with unified memory like Intel. +- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.* + - It may happen when you use [defragmentation](@ref defragmentation). + +\section general_considerations_allocation_algorithm Allocation algorithm + +The library uses following algorithm for allocation, in order: + +-# Try to find free range of memory in existing blocks. +-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size. +-# If failed, try to create such block with size / 2, size / 4, size / 8. +-# If failed, try to allocate separate `VkDeviceMemory` for this allocation, + just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +-# If failed, choose other memory type that meets the requirements specified in + VmaAllocationCreateInfo and go to point 1. +-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + +\section general_considerations_features_not_supported Features not supported + +Features deliberately excluded from the scope of this library: + +-# **Data transfer.** Uploading (streaming) and downloading data of buffers and images + between CPU and GPU memory and related synchronization is responsibility of the user. + Defining some "texture" object that would automatically stream its data from a + staging copy in CPU memory to GPU memory would rather be a feature of another, + higher-level library implemented on top of VMA. + VMA doesn't record any commands to a `VkCommandBuffer`. It just allocates memory. +-# **Recreation of buffers and images.** Although the library has functions for + buffer and image creation: vmaCreateBuffer(), vmaCreateImage(), you need to + recreate these objects yourself after defragmentation. That is because the big + structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in + #VmaAllocation object. +-# **Handling CPU memory allocation failures.** When dynamically creating small C++ + objects in CPU memory (not Vulkan memory), allocation failures are not checked + and handled gracefully, because that would complicate code significantly and + is usually not needed in desktop PC applications anyway. + Success of an allocation is just checked with an assert. +-# **Code free of any compiler warnings.** Maintaining the library to compile and + work correctly on so many different platforms is hard enough. Being free of + any warnings, on any version of any compiler, is simply not feasible. + There are many preprocessor macros that make some variables unused, function parameters unreferenced, + or conditional expressions constant in some configurations. + The code of this library should not be bigger or more complicated just to silence these warnings. + It is recommended to disable such warnings instead. +-# This is a C++ library with C interface. **Bindings or ports to any other programming languages** are welcome as external projects but + are not going to be included into this repository. +*/ diff --git a/vendor/rmlui_backend/RmlUi_Vulkan/vulkan.h b/vendor/rmlui_backend/RmlUi_Vulkan/vulkan.h new file mode 100644 index 0000000..6771ceb --- /dev/null +++ b/vendor/rmlui_backend/RmlUi_Vulkan/vulkan.h @@ -0,0 +1,4486 @@ +/** + * Loader generated by glad 2.0.0-beta on Tue Jun 28 22:05:49 2022 + * + * Generator: C/C++ + * Specification: vk + * Extensions: 11 + * + * APIs: + * - vulkan=1.0 + * + * Options: + * - ALIAS = False + * - DEBUG = False + * - HEADER_ONLY = True + * - LOADER = True + * - MX = False + * - MX_GLOBAL = False + * - ON_DEMAND = False + * + * Commandline: + * --api='vulkan=1.0' --extensions='VK_EXT_debug_report,VK_EXT_debug_utils,VK_EXT_scalar_block_layout,VK_EXT_validation_features,VK_KHR_get_physical_device_properties2,VK_KHR_portability_enumeration,VK_KHR_shader_subgroup_extended_types,VK_KHR_surface,VK_KHR_swapchain,VK_KHR_win32_surface,VK_KHR_xcb_surface' c --header-only --loader + * + * Online: + * http://glad.sh/#api=vulkan%3D1.0&extensions=VK_EXT_debug_report%2CVK_EXT_debug_utils%2CVK_EXT_scalar_block_layout%2CVK_EXT_validation_features%2CVK_KHR_get_physical_device_properties2%2CVK_KHR_portability_enumeration%2CVK_KHR_shader_subgroup_extended_types%2CVK_KHR_surface%2CVK_KHR_swapchain%2CVK_KHR_win32_surface%2CVK_KHR_xcb_surface&generator=c&options=HEADER_ONLY%2CLOADER + * + */ + +#ifndef GLAD_VULKAN_H_ +#define GLAD_VULKAN_H_ + +#ifdef VULKAN_H_ + #error header already included (API: vulkan), remove previous include! +#endif +#define VULKAN_H_ 1 + +#ifdef VULKAN_CORE_H_ + #error header already included (API: vulkan), remove previous include! +#endif +#define VULKAN_CORE_H_ 1 + + +#define GLAD_VULKAN +#define GLAD_OPTION_VULKAN_HEADER_ONLY +#define GLAD_OPTION_VULKAN_LOADER + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef GLAD_PLATFORM_H_ +#define GLAD_PLATFORM_H_ + +#ifndef GLAD_PLATFORM_WIN32 + #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__) + #define GLAD_PLATFORM_WIN32 1 + #else + #define GLAD_PLATFORM_WIN32 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_APPLE + #ifdef __APPLE__ + #define GLAD_PLATFORM_APPLE 1 + #else + #define GLAD_PLATFORM_APPLE 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_EMSCRIPTEN + #ifdef __EMSCRIPTEN__ + #define GLAD_PLATFORM_EMSCRIPTEN 1 + #else + #define GLAD_PLATFORM_EMSCRIPTEN 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_UWP + #if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY) + #ifdef __has_include + #if __has_include() + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #endif + + #ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY + #include + #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + #define GLAD_PLATFORM_UWP 1 + #endif + #endif + + #ifndef GLAD_PLATFORM_UWP + #define GLAD_PLATFORM_UWP 0 + #endif +#endif + +#ifdef __GNUC__ + #define GLAD_GNUC_EXTENSION __extension__ +#else + #define GLAD_GNUC_EXTENSION +#endif + +#ifndef GLAD_API_CALL + #if defined(GLAD_API_CALL_EXPORT) + #if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) + #if defined(GLAD_API_CALL_EXPORT_BUILD) + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllexport)) extern + #else + #define GLAD_API_CALL __declspec(dllexport) extern + #endif + #else + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllimport)) extern + #else + #define GLAD_API_CALL __declspec(dllimport) extern + #endif + #endif + #elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD) + #define GLAD_API_CALL __attribute__ ((visibility ("default"))) extern + #else + #define GLAD_API_CALL extern + #endif + #else + #define GLAD_API_CALL extern + #endif +#endif + +#ifdef APIENTRY + #define GLAD_API_PTR APIENTRY +#elif GLAD_PLATFORM_WIN32 + #define GLAD_API_PTR __stdcall +#else + #define GLAD_API_PTR +#endif + +#ifndef GLAPI +#define GLAPI GLAD_API_CALL +#endif + +#ifndef GLAPIENTRY +#define GLAPIENTRY GLAD_API_PTR +#endif + +#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) +#define GLAD_VERSION_MAJOR(version) (version / 10000) +#define GLAD_VERSION_MINOR(version) (version % 10000) + +#define GLAD_GENERATOR_VERSION "2.0.0-beta" + +typedef void (*GLADapiproc)(void); + +typedef GLADapiproc (*GLADloadfunc)(const char *name); +typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); + +typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); +typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); + +#endif /* GLAD_PLATFORM_H_ */ + +#define VK_ATTACHMENT_UNUSED (~0U) +#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" +#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 10 +#define VK_EXT_DEBUG_UTILS_EXTENSION_NAME "VK_EXT_debug_utils" +#define VK_EXT_DEBUG_UTILS_SPEC_VERSION 2 +#define VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME "VK_EXT_scalar_block_layout" +#define VK_EXT_SCALAR_BLOCK_LAYOUT_SPEC_VERSION 1 +#define VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME "VK_EXT_validation_features" +#define VK_EXT_VALIDATION_FEATURES_SPEC_VERSION 5 +#define VK_FALSE 0 +#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2" +#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 2 +#define VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME "VK_KHR_portability_enumeration" +#define VK_KHR_PORTABILITY_ENUMERATION_SPEC_VERSION 1 +#define VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_EXTENSION_NAME "VK_KHR_shader_subgroup_extended_types" +#define VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_SPEC_VERSION 1 +#define VK_KHR_SURFACE_EXTENSION_NAME "VK_KHR_surface" +#define VK_KHR_SURFACE_SPEC_VERSION 25 +#define VK_KHR_SWAPCHAIN_EXTENSION_NAME "VK_KHR_swapchain" +#define VK_KHR_SWAPCHAIN_SPEC_VERSION 70 +#if defined(VK_USE_PLATFORM_WIN32_KHR) +#define VK_KHR_WIN32_SURFACE_EXTENSION_NAME "VK_KHR_win32_surface" + +#endif +#if defined(VK_USE_PLATFORM_WIN32_KHR) +#define VK_KHR_WIN32_SURFACE_SPEC_VERSION 6 + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +#define VK_KHR_XCB_SURFACE_EXTENSION_NAME "VK_KHR_xcb_surface" + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +#define VK_KHR_XCB_SURFACE_SPEC_VERSION 6 + +#endif +#define VK_LOD_CLAMP_NONE 1000.0F +#define VK_MAX_DESCRIPTION_SIZE 256 +#define VK_MAX_DEVICE_GROUP_SIZE 32 +#define VK_MAX_EXTENSION_NAME_SIZE 256 +#define VK_MAX_MEMORY_HEAPS 16 +#define VK_MAX_MEMORY_TYPES 32 +#define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256 +#define VK_QUEUE_FAMILY_IGNORED (~0U) +#define VK_REMAINING_ARRAY_LAYERS (~0U) +#define VK_REMAINING_MIP_LEVELS (~0U) +#define VK_SUBPASS_EXTERNAL (~0U) +#define VK_TRUE 1 +#define VK_UUID_SIZE 16 +#define VK_WHOLE_SIZE (~0ULL) + + +/* */ +/* File: vk_platform.h */ +/* */ +/* +** Copyright 2014-2022 The Khronos Group Inc. +** +** SPDX-License-Identifier: Apache-2.0 +*/ + + +#ifndef VK_PLATFORM_H_ +#define VK_PLATFORM_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* +*************************************************************************************************** +* Platform-specific directives and type declarations +*************************************************************************************************** +*/ + +/* Platform-specific calling convention macros. + * + * Platforms should define these so that Vulkan clients call Vulkan commands + * with the same calling conventions that the Vulkan implementation expects. + * + * VKAPI_ATTR - Placed before the return type in function declarations. + * Useful for C++11 and GCC/Clang-style function attribute syntax. + * VKAPI_CALL - Placed after the return type in function declarations. + * Useful for MSVC-style calling convention syntax. + * VKAPI_PTR - Placed between the '(' and '*' in function pointer types. + * + * Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void); + * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void); + */ +#if defined(_WIN32) + /* On Windows, Vulkan commands use the stdcall convention */ + #define VKAPI_ATTR + #define VKAPI_CALL __stdcall + #define VKAPI_PTR VKAPI_CALL +#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 + #error "Vulkan is not supported for the 'armeabi' NDK ABI" +#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) + /* On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" */ + /* calling convention, i.e. float parameters are passed in registers. This */ + /* is true even if the rest of the application passes floats on the stack, */ + /* as it does by default when compiling for the armeabi-v7a NDK ABI. */ + #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp"))) + #define VKAPI_CALL + #define VKAPI_PTR VKAPI_ATTR +#else + /* On other platforms, use the default calling convention */ + #define VKAPI_ATTR + #define VKAPI_CALL + #define VKAPI_PTR +#endif + +#if !defined(VK_NO_STDDEF_H) + #include +#endif /* !defined(VK_NO_STDDEF_H) */ + +#if !defined(VK_NO_STDINT_H) + #if defined(_MSC_VER) && (_MSC_VER < 1600) + typedef signed __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef signed __int16 int16_t; + typedef unsigned __int16 uint16_t; + typedef signed __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; + #else + #include + #endif +#endif /* !defined(VK_NO_STDINT_H) */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif +#if defined(VK_USE_PLATFORM_WIN32_KHR) +#include +#endif + +#if defined(VK_USE_PLATFORM_XCB_KHR) +#include +#endif + +#if defined(VK_USE_PLATFORM_WIN32_KHR) + +#endif + +#if defined(VK_USE_PLATFORM_WIN32_KHR) + +#endif + +#if defined(VK_USE_PLATFORM_XCB_KHR) + +#endif + +#if defined(VK_USE_PLATFORM_XCB_KHR) + +#endif + +#if defined(VK_USE_PLATFORM_XCB_KHR) + +#endif + +/* DEPRECATED: This define is deprecated. VK_MAKE_API_VERSION should be used instead. */ +#define VK_MAKE_VERSION(major, minor, patch) \ + ((((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch))) +/* DEPRECATED: This define is deprecated. VK_API_VERSION_MAJOR should be used instead. */ +#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) +/* DEPRECATED: This define is deprecated. VK_API_VERSION_MINOR should be used instead. */ +#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU) +/* DEPRECATED: This define is deprecated. VK_API_VERSION_PATCH should be used instead. */ +#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU) +#define VK_MAKE_API_VERSION(variant, major, minor, patch) \ + ((((uint32_t)(variant)) << 29) | (((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch))) +#define VK_API_VERSION_VARIANT(version) ((uint32_t)(version) >> 29) +#define VK_API_VERSION_MAJOR(version) (((uint32_t)(version) >> 22) & 0x7FU) +#define VK_API_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU) +#define VK_API_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU) +/* DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead. */ +/*#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Patch version should always be set to 0 */ +/* Vulkan 1.0 version number */ +#define VK_API_VERSION_1_0 VK_MAKE_API_VERSION(0, 1, 0, 0)/* Patch version should always be set to 0 */ +/* Version of this file */ +#define VK_HEADER_VERSION 218 +/* Complete version of this file */ +#define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 3, VK_HEADER_VERSION) +#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; +#ifndef VK_USE_64_BIT_PTR_DEFINES + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VK_USE_64_BIT_PTR_DEFINES 1 + #else + #define VK_USE_64_BIT_PTR_DEFINES 0 + #endif +#endif +#ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE + #if (VK_USE_64_BIT_PTR_DEFINES==1) + #if (defined(__cplusplus) && (__cplusplus >= 201103L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L)) + #define VK_NULL_HANDLE nullptr + #else + #define VK_NULL_HANDLE ((void*)0) + #endif + #else + #define VK_NULL_HANDLE 0ULL + #endif +#endif +#ifndef VK_NULL_HANDLE + #define VK_NULL_HANDLE 0 +#endif +#ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE + #if (VK_USE_64_BIT_PTR_DEFINES==1) + #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; + #else + #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; + #endif +#endif + + + + + + + + +VK_DEFINE_HANDLE(VkInstance) +VK_DEFINE_HANDLE(VkPhysicalDevice) +VK_DEFINE_HANDLE(VkDevice) +VK_DEFINE_HANDLE(VkQueue) +VK_DEFINE_HANDLE(VkCommandBuffer) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeviceMemory) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCommandPool) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBuffer) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferView) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImageView) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkShaderModule) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipeline) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineLayout) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSampler) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSet) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSetLayout) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorPool) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFence) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSemaphore) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkEvent) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkQueryPool) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFramebuffer) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkRenderPass) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineCache) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugUtilsMessengerEXT) +typedef enum VkAttachmentLoadOp { + VK_ATTACHMENT_LOAD_OP_LOAD = 0, + VK_ATTACHMENT_LOAD_OP_CLEAR = 1, + VK_ATTACHMENT_LOAD_OP_DONT_CARE = 2, + VK_ATTACHMENT_LOAD_OP_MAX_ENUM = 0x7FFFFFFF +} VkAttachmentLoadOp; +typedef enum VkAttachmentStoreOp { + VK_ATTACHMENT_STORE_OP_STORE = 0, + VK_ATTACHMENT_STORE_OP_DONT_CARE = 1, + VK_ATTACHMENT_STORE_OP_MAX_ENUM = 0x7FFFFFFF +} VkAttachmentStoreOp; +typedef enum VkBlendFactor { + VK_BLEND_FACTOR_ZERO = 0, + VK_BLEND_FACTOR_ONE = 1, + VK_BLEND_FACTOR_SRC_COLOR = 2, + VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR = 3, + VK_BLEND_FACTOR_DST_COLOR = 4, + VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR = 5, + VK_BLEND_FACTOR_SRC_ALPHA = 6, + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA = 7, + VK_BLEND_FACTOR_DST_ALPHA = 8, + VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA = 9, + VK_BLEND_FACTOR_CONSTANT_COLOR = 10, + VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR = 11, + VK_BLEND_FACTOR_CONSTANT_ALPHA = 12, + VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA = 13, + VK_BLEND_FACTOR_SRC_ALPHA_SATURATE = 14, + VK_BLEND_FACTOR_SRC1_COLOR = 15, + VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR = 16, + VK_BLEND_FACTOR_SRC1_ALPHA = 17, + VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA = 18, + VK_BLEND_FACTOR_MAX_ENUM = 0x7FFFFFFF +} VkBlendFactor; +typedef enum VkBlendOp { + VK_BLEND_OP_ADD = 0, + VK_BLEND_OP_SUBTRACT = 1, + VK_BLEND_OP_REVERSE_SUBTRACT = 2, + VK_BLEND_OP_MIN = 3, + VK_BLEND_OP_MAX = 4, + VK_BLEND_OP_MAX_ENUM = 0x7FFFFFFF +} VkBlendOp; +typedef enum VkBorderColor { + VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK = 0, + VK_BORDER_COLOR_INT_TRANSPARENT_BLACK = 1, + VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK = 2, + VK_BORDER_COLOR_INT_OPAQUE_BLACK = 3, + VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE = 4, + VK_BORDER_COLOR_INT_OPAQUE_WHITE = 5, + VK_BORDER_COLOR_MAX_ENUM = 0x7FFFFFFF +} VkBorderColor; +typedef enum VkPipelineCacheHeaderVersion { + VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1, + VK_PIPELINE_CACHE_HEADER_VERSION_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCacheHeaderVersion; +typedef enum VkInstanceCreateFlagBits { + VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR = 1, + VK_INSTANCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkInstanceCreateFlagBits; +typedef enum VkBufferCreateFlagBits { + VK_BUFFER_CREATE_SPARSE_BINDING_BIT = 1, + VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 2, + VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 4, + VK_BUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkBufferCreateFlagBits; +typedef enum VkBufferUsageFlagBits { + VK_BUFFER_USAGE_TRANSFER_SRC_BIT = 1, + VK_BUFFER_USAGE_TRANSFER_DST_BIT = 2, + VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT = 4, + VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT = 8, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT = 16, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT = 32, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 64, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 128, + VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 256, + VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkBufferUsageFlagBits; +typedef enum VkColorComponentFlagBits { + VK_COLOR_COMPONENT_R_BIT = 1, + VK_COLOR_COMPONENT_G_BIT = 2, + VK_COLOR_COMPONENT_B_BIT = 4, + VK_COLOR_COMPONENT_A_BIT = 8, + VK_COLOR_COMPONENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkColorComponentFlagBits; +typedef enum VkComponentSwizzle { + VK_COMPONENT_SWIZZLE_IDENTITY = 0, + VK_COMPONENT_SWIZZLE_ZERO = 1, + VK_COMPONENT_SWIZZLE_ONE = 2, + VK_COMPONENT_SWIZZLE_R = 3, + VK_COMPONENT_SWIZZLE_G = 4, + VK_COMPONENT_SWIZZLE_B = 5, + VK_COMPONENT_SWIZZLE_A = 6, + VK_COMPONENT_SWIZZLE_MAX_ENUM = 0x7FFFFFFF +} VkComponentSwizzle; +typedef enum VkCommandPoolCreateFlagBits { + VK_COMMAND_POOL_CREATE_TRANSIENT_BIT = 1, + VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT = 2, + VK_COMMAND_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandPoolCreateFlagBits; +typedef enum VkCommandPoolResetFlagBits { + VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT = 1, + VK_COMMAND_POOL_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandPoolResetFlagBits; +typedef enum VkCommandBufferResetFlagBits { + VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT = 1, + VK_COMMAND_BUFFER_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandBufferResetFlagBits; +typedef enum VkCommandBufferLevel { + VK_COMMAND_BUFFER_LEVEL_PRIMARY = 0, + VK_COMMAND_BUFFER_LEVEL_SECONDARY = 1, + VK_COMMAND_BUFFER_LEVEL_MAX_ENUM = 0x7FFFFFFF +} VkCommandBufferLevel; +typedef enum VkCommandBufferUsageFlagBits { + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT = 1, + VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT = 2, + VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT = 4, + VK_COMMAND_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandBufferUsageFlagBits; +typedef enum VkCompareOp { + VK_COMPARE_OP_NEVER = 0, + VK_COMPARE_OP_LESS = 1, + VK_COMPARE_OP_EQUAL = 2, + VK_COMPARE_OP_LESS_OR_EQUAL = 3, + VK_COMPARE_OP_GREATER = 4, + VK_COMPARE_OP_NOT_EQUAL = 5, + VK_COMPARE_OP_GREATER_OR_EQUAL = 6, + VK_COMPARE_OP_ALWAYS = 7, + VK_COMPARE_OP_MAX_ENUM = 0x7FFFFFFF +} VkCompareOp; +typedef enum VkCullModeFlagBits { + VK_CULL_MODE_NONE = 0, + VK_CULL_MODE_FRONT_BIT = 1, + VK_CULL_MODE_BACK_BIT = 2, + VK_CULL_MODE_FRONT_AND_BACK = 0x00000003, + VK_CULL_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCullModeFlagBits; +typedef enum VkDescriptorType { + VK_DESCRIPTOR_TYPE_SAMPLER = 0, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER = 1, + VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE = 2, + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE = 3, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER = 4, + VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER = 5, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER = 6, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER = 7, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 8, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 9, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10, + VK_DESCRIPTOR_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorType; +typedef enum VkDynamicState { + VK_DYNAMIC_STATE_VIEWPORT = 0, + VK_DYNAMIC_STATE_SCISSOR = 1, + VK_DYNAMIC_STATE_LINE_WIDTH = 2, + VK_DYNAMIC_STATE_DEPTH_BIAS = 3, + VK_DYNAMIC_STATE_BLEND_CONSTANTS = 4, + VK_DYNAMIC_STATE_DEPTH_BOUNDS = 5, + VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK = 6, + VK_DYNAMIC_STATE_STENCIL_WRITE_MASK = 7, + VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8, + VK_DYNAMIC_STATE_MAX_ENUM = 0x7FFFFFFF +} VkDynamicState; +typedef enum VkFenceCreateFlagBits { + VK_FENCE_CREATE_SIGNALED_BIT = 1, + VK_FENCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkFenceCreateFlagBits; +typedef enum VkPolygonMode { + VK_POLYGON_MODE_FILL = 0, + VK_POLYGON_MODE_LINE = 1, + VK_POLYGON_MODE_POINT = 2, + VK_POLYGON_MODE_MAX_ENUM = 0x7FFFFFFF +} VkPolygonMode; +typedef enum VkFormat { + VK_FORMAT_UNDEFINED = 0, + VK_FORMAT_R4G4_UNORM_PACK8 = 1, + VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, + VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, + VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, + VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, + VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, + VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, + VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, + VK_FORMAT_R8_UNORM = 9, + VK_FORMAT_R8_SNORM = 10, + VK_FORMAT_R8_USCALED = 11, + VK_FORMAT_R8_SSCALED = 12, + VK_FORMAT_R8_UINT = 13, + VK_FORMAT_R8_SINT = 14, + VK_FORMAT_R8_SRGB = 15, + VK_FORMAT_R8G8_UNORM = 16, + VK_FORMAT_R8G8_SNORM = 17, + VK_FORMAT_R8G8_USCALED = 18, + VK_FORMAT_R8G8_SSCALED = 19, + VK_FORMAT_R8G8_UINT = 20, + VK_FORMAT_R8G8_SINT = 21, + VK_FORMAT_R8G8_SRGB = 22, + VK_FORMAT_R8G8B8_UNORM = 23, + VK_FORMAT_R8G8B8_SNORM = 24, + VK_FORMAT_R8G8B8_USCALED = 25, + VK_FORMAT_R8G8B8_SSCALED = 26, + VK_FORMAT_R8G8B8_UINT = 27, + VK_FORMAT_R8G8B8_SINT = 28, + VK_FORMAT_R8G8B8_SRGB = 29, + VK_FORMAT_B8G8R8_UNORM = 30, + VK_FORMAT_B8G8R8_SNORM = 31, + VK_FORMAT_B8G8R8_USCALED = 32, + VK_FORMAT_B8G8R8_SSCALED = 33, + VK_FORMAT_B8G8R8_UINT = 34, + VK_FORMAT_B8G8R8_SINT = 35, + VK_FORMAT_B8G8R8_SRGB = 36, + VK_FORMAT_R8G8B8A8_UNORM = 37, + VK_FORMAT_R8G8B8A8_SNORM = 38, + VK_FORMAT_R8G8B8A8_USCALED = 39, + VK_FORMAT_R8G8B8A8_SSCALED = 40, + VK_FORMAT_R8G8B8A8_UINT = 41, + VK_FORMAT_R8G8B8A8_SINT = 42, + VK_FORMAT_R8G8B8A8_SRGB = 43, + VK_FORMAT_B8G8R8A8_UNORM = 44, + VK_FORMAT_B8G8R8A8_SNORM = 45, + VK_FORMAT_B8G8R8A8_USCALED = 46, + VK_FORMAT_B8G8R8A8_SSCALED = 47, + VK_FORMAT_B8G8R8A8_UINT = 48, + VK_FORMAT_B8G8R8A8_SINT = 49, + VK_FORMAT_B8G8R8A8_SRGB = 50, + VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, + VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, + VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, + VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, + VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, + VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, + VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, + VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, + VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, + VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, + VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, + VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, + VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, + VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, + VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, + VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, + VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, + VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, + VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, + VK_FORMAT_R16_UNORM = 70, + VK_FORMAT_R16_SNORM = 71, + VK_FORMAT_R16_USCALED = 72, + VK_FORMAT_R16_SSCALED = 73, + VK_FORMAT_R16_UINT = 74, + VK_FORMAT_R16_SINT = 75, + VK_FORMAT_R16_SFLOAT = 76, + VK_FORMAT_R16G16_UNORM = 77, + VK_FORMAT_R16G16_SNORM = 78, + VK_FORMAT_R16G16_USCALED = 79, + VK_FORMAT_R16G16_SSCALED = 80, + VK_FORMAT_R16G16_UINT = 81, + VK_FORMAT_R16G16_SINT = 82, + VK_FORMAT_R16G16_SFLOAT = 83, + VK_FORMAT_R16G16B16_UNORM = 84, + VK_FORMAT_R16G16B16_SNORM = 85, + VK_FORMAT_R16G16B16_USCALED = 86, + VK_FORMAT_R16G16B16_SSCALED = 87, + VK_FORMAT_R16G16B16_UINT = 88, + VK_FORMAT_R16G16B16_SINT = 89, + VK_FORMAT_R16G16B16_SFLOAT = 90, + VK_FORMAT_R16G16B16A16_UNORM = 91, + VK_FORMAT_R16G16B16A16_SNORM = 92, + VK_FORMAT_R16G16B16A16_USCALED = 93, + VK_FORMAT_R16G16B16A16_SSCALED = 94, + VK_FORMAT_R16G16B16A16_UINT = 95, + VK_FORMAT_R16G16B16A16_SINT = 96, + VK_FORMAT_R16G16B16A16_SFLOAT = 97, + VK_FORMAT_R32_UINT = 98, + VK_FORMAT_R32_SINT = 99, + VK_FORMAT_R32_SFLOAT = 100, + VK_FORMAT_R32G32_UINT = 101, + VK_FORMAT_R32G32_SINT = 102, + VK_FORMAT_R32G32_SFLOAT = 103, + VK_FORMAT_R32G32B32_UINT = 104, + VK_FORMAT_R32G32B32_SINT = 105, + VK_FORMAT_R32G32B32_SFLOAT = 106, + VK_FORMAT_R32G32B32A32_UINT = 107, + VK_FORMAT_R32G32B32A32_SINT = 108, + VK_FORMAT_R32G32B32A32_SFLOAT = 109, + VK_FORMAT_R64_UINT = 110, + VK_FORMAT_R64_SINT = 111, + VK_FORMAT_R64_SFLOAT = 112, + VK_FORMAT_R64G64_UINT = 113, + VK_FORMAT_R64G64_SINT = 114, + VK_FORMAT_R64G64_SFLOAT = 115, + VK_FORMAT_R64G64B64_UINT = 116, + VK_FORMAT_R64G64B64_SINT = 117, + VK_FORMAT_R64G64B64_SFLOAT = 118, + VK_FORMAT_R64G64B64A64_UINT = 119, + VK_FORMAT_R64G64B64A64_SINT = 120, + VK_FORMAT_R64G64B64A64_SFLOAT = 121, + VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, + VK_FORMAT_D16_UNORM = 124, + VK_FORMAT_X8_D24_UNORM_PACK32 = 125, + VK_FORMAT_D32_SFLOAT = 126, + VK_FORMAT_S8_UINT = 127, + VK_FORMAT_D16_UNORM_S8_UINT = 128, + VK_FORMAT_D24_UNORM_S8_UINT = 129, + VK_FORMAT_D32_SFLOAT_S8_UINT = 130, + VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, + VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, + VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, + VK_FORMAT_BC2_UNORM_BLOCK = 135, + VK_FORMAT_BC2_SRGB_BLOCK = 136, + VK_FORMAT_BC3_UNORM_BLOCK = 137, + VK_FORMAT_BC3_SRGB_BLOCK = 138, + VK_FORMAT_BC4_UNORM_BLOCK = 139, + VK_FORMAT_BC4_SNORM_BLOCK = 140, + VK_FORMAT_BC5_UNORM_BLOCK = 141, + VK_FORMAT_BC5_SNORM_BLOCK = 142, + VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, + VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, + VK_FORMAT_BC7_UNORM_BLOCK = 145, + VK_FORMAT_BC7_SRGB_BLOCK = 146, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, + VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, + VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, + VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, + VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, + VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, + VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, + VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, + VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, + VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, + VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, + VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, + VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, + VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, + VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, + VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, + VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, + VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, + VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, + VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, + VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, + VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, + VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, + VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, + VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, + VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, + VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, + VK_FORMAT_MAX_ENUM = 0x7FFFFFFF +} VkFormat; +typedef enum VkFormatFeatureFlagBits { + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT = 1, + VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT = 2, + VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT = 4, + VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT = 8, + VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT = 16, + VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT = 32, + VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT = 64, + VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT = 128, + VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT = 256, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT = 512, + VK_FORMAT_FEATURE_BLIT_SRC_BIT = 1024, + VK_FORMAT_FEATURE_BLIT_DST_BIT = 2048, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 4096, + VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkFormatFeatureFlagBits; +typedef enum VkFrontFace { + VK_FRONT_FACE_COUNTER_CLOCKWISE = 0, + VK_FRONT_FACE_CLOCKWISE = 1, + VK_FRONT_FACE_MAX_ENUM = 0x7FFFFFFF +} VkFrontFace; +typedef enum VkImageAspectFlagBits { + VK_IMAGE_ASPECT_COLOR_BIT = 1, + VK_IMAGE_ASPECT_DEPTH_BIT = 2, + VK_IMAGE_ASPECT_STENCIL_BIT = 4, + VK_IMAGE_ASPECT_METADATA_BIT = 8, + VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageAspectFlagBits; +typedef enum VkImageCreateFlagBits { + VK_IMAGE_CREATE_SPARSE_BINDING_BIT = 1, + VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT = 2, + VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 4, + VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 8, + VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 16, + VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageCreateFlagBits; +typedef enum VkImageLayout { + VK_IMAGE_LAYOUT_UNDEFINED = 0, + VK_IMAGE_LAYOUT_GENERAL = 1, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL = 2, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL = 3, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL = 4, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL = 5, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 6, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7, + VK_IMAGE_LAYOUT_PREINITIALIZED = 8, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, + VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF +} VkImageLayout; +typedef enum VkImageTiling { + VK_IMAGE_TILING_OPTIMAL = 0, + VK_IMAGE_TILING_LINEAR = 1, + VK_IMAGE_TILING_MAX_ENUM = 0x7FFFFFFF +} VkImageTiling; +typedef enum VkImageType { + VK_IMAGE_TYPE_1D = 0, + VK_IMAGE_TYPE_2D = 1, + VK_IMAGE_TYPE_3D = 2, + VK_IMAGE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkImageType; +typedef enum VkImageUsageFlagBits { + VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 1, + VK_IMAGE_USAGE_TRANSFER_DST_BIT = 2, + VK_IMAGE_USAGE_SAMPLED_BIT = 4, + VK_IMAGE_USAGE_STORAGE_BIT = 8, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 16, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 32, + VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 64, + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 128, + VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageUsageFlagBits; +typedef enum VkImageViewType { + VK_IMAGE_VIEW_TYPE_1D = 0, + VK_IMAGE_VIEW_TYPE_2D = 1, + VK_IMAGE_VIEW_TYPE_3D = 2, + VK_IMAGE_VIEW_TYPE_CUBE = 3, + VK_IMAGE_VIEW_TYPE_1D_ARRAY = 4, + VK_IMAGE_VIEW_TYPE_2D_ARRAY = 5, + VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6, + VK_IMAGE_VIEW_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkImageViewType; +typedef enum VkSharingMode { + VK_SHARING_MODE_EXCLUSIVE = 0, + VK_SHARING_MODE_CONCURRENT = 1, + VK_SHARING_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSharingMode; +typedef enum VkIndexType { + VK_INDEX_TYPE_UINT16 = 0, + VK_INDEX_TYPE_UINT32 = 1, + VK_INDEX_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkIndexType; +typedef enum VkLogicOp { + VK_LOGIC_OP_CLEAR = 0, + VK_LOGIC_OP_AND = 1, + VK_LOGIC_OP_AND_REVERSE = 2, + VK_LOGIC_OP_COPY = 3, + VK_LOGIC_OP_AND_INVERTED = 4, + VK_LOGIC_OP_NO_OP = 5, + VK_LOGIC_OP_XOR = 6, + VK_LOGIC_OP_OR = 7, + VK_LOGIC_OP_NOR = 8, + VK_LOGIC_OP_EQUIVALENT = 9, + VK_LOGIC_OP_INVERT = 10, + VK_LOGIC_OP_OR_REVERSE = 11, + VK_LOGIC_OP_COPY_INVERTED = 12, + VK_LOGIC_OP_OR_INVERTED = 13, + VK_LOGIC_OP_NAND = 14, + VK_LOGIC_OP_SET = 15, + VK_LOGIC_OP_MAX_ENUM = 0x7FFFFFFF +} VkLogicOp; +typedef enum VkMemoryHeapFlagBits { + VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 1, + VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkMemoryHeapFlagBits; +typedef enum VkAccessFlagBits { + VK_ACCESS_INDIRECT_COMMAND_READ_BIT = 1, + VK_ACCESS_INDEX_READ_BIT = 2, + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT = 4, + VK_ACCESS_UNIFORM_READ_BIT = 8, + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT = 16, + VK_ACCESS_SHADER_READ_BIT = 32, + VK_ACCESS_SHADER_WRITE_BIT = 64, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT = 128, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT = 256, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 512, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 1024, + VK_ACCESS_TRANSFER_READ_BIT = 2048, + VK_ACCESS_TRANSFER_WRITE_BIT = 4096, + VK_ACCESS_HOST_READ_BIT = 8192, + VK_ACCESS_HOST_WRITE_BIT = 16384, + VK_ACCESS_MEMORY_READ_BIT = 32768, + VK_ACCESS_MEMORY_WRITE_BIT = 65536, + VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkAccessFlagBits; +typedef enum VkMemoryPropertyFlagBits { + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 1, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 2, + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 4, + VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 8, + VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 16, + VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkMemoryPropertyFlagBits; +typedef enum VkPhysicalDeviceType { + VK_PHYSICAL_DEVICE_TYPE_OTHER = 0, + VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1, + VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2, + VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3, + VK_PHYSICAL_DEVICE_TYPE_CPU = 4, + VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkPhysicalDeviceType; +typedef enum VkPipelineBindPoint { + VK_PIPELINE_BIND_POINT_GRAPHICS = 0, + VK_PIPELINE_BIND_POINT_COMPUTE = 1, + VK_PIPELINE_BIND_POINT_MAX_ENUM = 0x7FFFFFFF +} VkPipelineBindPoint; +typedef enum VkPipelineCreateFlagBits { + VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 1, + VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 2, + VK_PIPELINE_CREATE_DERIVATIVE_BIT = 4, + VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCreateFlagBits; +typedef enum VkPrimitiveTopology { + VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST = 1, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP = 2, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST = 3, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP = 4, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN = 5, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY = 6, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 7, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 8, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 9, + VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 10, + VK_PRIMITIVE_TOPOLOGY_MAX_ENUM = 0x7FFFFFFF +} VkPrimitiveTopology; +typedef enum VkQueryControlFlagBits { + VK_QUERY_CONTROL_PRECISE_BIT = 1, + VK_QUERY_CONTROL_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueryControlFlagBits; +typedef enum VkQueryPipelineStatisticFlagBits { + VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT = 1, + VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT = 2, + VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT = 4, + VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT = 8, + VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT = 16, + VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT = 32, + VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT = 64, + VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT = 128, + VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT = 256, + VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT = 512, + VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT = 1024, + VK_QUERY_PIPELINE_STATISTIC_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueryPipelineStatisticFlagBits; +typedef enum VkQueryResultFlagBits { + VK_QUERY_RESULT_64_BIT = 1, + VK_QUERY_RESULT_WAIT_BIT = 2, + VK_QUERY_RESULT_WITH_AVAILABILITY_BIT = 4, + VK_QUERY_RESULT_PARTIAL_BIT = 8, + VK_QUERY_RESULT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueryResultFlagBits; +typedef enum VkQueryType { + VK_QUERY_TYPE_OCCLUSION = 0, + VK_QUERY_TYPE_PIPELINE_STATISTICS = 1, + VK_QUERY_TYPE_TIMESTAMP = 2, + VK_QUERY_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkQueryType; +typedef enum VkQueueFlagBits { + VK_QUEUE_GRAPHICS_BIT = 1, + VK_QUEUE_COMPUTE_BIT = 2, + VK_QUEUE_TRANSFER_BIT = 4, + VK_QUEUE_SPARSE_BINDING_BIT = 8, + VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueueFlagBits; +typedef enum VkSubpassContents { + VK_SUBPASS_CONTENTS_INLINE = 0, + VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS = 1, + VK_SUBPASS_CONTENTS_MAX_ENUM = 0x7FFFFFFF +} VkSubpassContents; +typedef enum VkResult { + VK_SUCCESS = 0, + VK_NOT_READY = 1, + VK_TIMEOUT = 2, + VK_EVENT_SET = 3, + VK_EVENT_RESET = 4, + VK_INCOMPLETE = 5, + VK_ERROR_OUT_OF_HOST_MEMORY = -1, + VK_ERROR_OUT_OF_DEVICE_MEMORY = -2, + VK_ERROR_INITIALIZATION_FAILED = -3, + VK_ERROR_DEVICE_LOST = -4, + VK_ERROR_MEMORY_MAP_FAILED = -5, + VK_ERROR_LAYER_NOT_PRESENT = -6, + VK_ERROR_EXTENSION_NOT_PRESENT = -7, + VK_ERROR_FEATURE_NOT_PRESENT = -8, + VK_ERROR_INCOMPATIBLE_DRIVER = -9, + VK_ERROR_TOO_MANY_OBJECTS = -10, + VK_ERROR_FORMAT_NOT_SUPPORTED = -11, + VK_ERROR_FRAGMENTED_POOL = -12, + VK_ERROR_UNKNOWN = -13, + VK_ERROR_SURFACE_LOST_KHR = -1000000000, + VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, + VK_SUBOPTIMAL_KHR = 1000001003, + VK_ERROR_OUT_OF_DATE_KHR = -1000001004, + VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, + VK_RESULT_MAX_ENUM = 0x7FFFFFFF +} VkResult; +typedef enum VkShaderStageFlagBits { + VK_SHADER_STAGE_VERTEX_BIT = 1, + VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT = 2, + VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT = 4, + VK_SHADER_STAGE_GEOMETRY_BIT = 8, + VK_SHADER_STAGE_FRAGMENT_BIT = 16, + VK_SHADER_STAGE_COMPUTE_BIT = 32, + VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F, + VK_SHADER_STAGE_ALL = 0x7FFFFFFF, + VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkShaderStageFlagBits; +typedef enum VkSparseMemoryBindFlagBits { + VK_SPARSE_MEMORY_BIND_METADATA_BIT = 1, + VK_SPARSE_MEMORY_BIND_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSparseMemoryBindFlagBits; +typedef enum VkStencilFaceFlagBits { + VK_STENCIL_FACE_FRONT_BIT = 1, + VK_STENCIL_FACE_BACK_BIT = 2, + VK_STENCIL_FACE_FRONT_AND_BACK = 0x00000003, + VK_STENCIL_FRONT_AND_BACK = VK_STENCIL_FACE_FRONT_AND_BACK, + VK_STENCIL_FACE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkStencilFaceFlagBits; +typedef enum VkStencilOp { + VK_STENCIL_OP_KEEP = 0, + VK_STENCIL_OP_ZERO = 1, + VK_STENCIL_OP_REPLACE = 2, + VK_STENCIL_OP_INCREMENT_AND_CLAMP = 3, + VK_STENCIL_OP_DECREMENT_AND_CLAMP = 4, + VK_STENCIL_OP_INVERT = 5, + VK_STENCIL_OP_INCREMENT_AND_WRAP = 6, + VK_STENCIL_OP_DECREMENT_AND_WRAP = 7, + VK_STENCIL_OP_MAX_ENUM = 0x7FFFFFFF +} VkStencilOp; +typedef enum VkStructureType { + VK_STRUCTURE_TYPE_APPLICATION_INFO = 0, + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO = 1, + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO = 2, + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO = 3, + VK_STRUCTURE_TYPE_SUBMIT_INFO = 4, + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO = 5, + VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE = 6, + VK_STRUCTURE_TYPE_BIND_SPARSE_INFO = 7, + VK_STRUCTURE_TYPE_FENCE_CREATE_INFO = 8, + VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO = 9, + VK_STRUCTURE_TYPE_EVENT_CREATE_INFO = 10, + VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO = 11, + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO = 12, + VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO = 13, + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO = 14, + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO = 15, + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO = 16, + VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO = 17, + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO = 18, + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO = 19, + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO = 20, + VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO = 21, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO = 22, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO = 23, + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO = 24, + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO = 25, + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO = 26, + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO = 27, + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO = 28, + VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO = 29, + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO = 30, + VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO = 31, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO = 32, + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO = 33, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO = 34, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET = 35, + VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET = 36, + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO = 37, + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO = 38, + VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO = 39, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO = 40, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO = 41, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO = 42, + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO = 43, + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER = 44, + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER = 45, + VK_STRUCTURE_TYPE_MEMORY_BARRIER = 46, + VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO = 47, + VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO = 48, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 = 1000059000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 = 1000059001, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2 = 1000059002, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2 = 1000059003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2 = 1000059004, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2 = 1000059005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2 = 1000059006, + VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2 = 1000059007, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2 = 1000059008, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES = 1000221000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES = 1000175000, + VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR = 1000001000, + VK_STRUCTURE_TYPE_PRESENT_INFO_KHR = 1000001001, + VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR = 1000060007, + VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR = 1000060008, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR = 1000060009, + VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHR = 1000060010, + VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHR = 1000060011, + VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHR = 1000060012, + VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, + VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, + VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2, + VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2, + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT = 1000128000, + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT = 1000128001, + VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT = 1000128002, + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT = 1000128003, + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000128004, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES, + VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT = 1000247000, + VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkStructureType; +typedef enum VkSystemAllocationScope { + VK_SYSTEM_ALLOCATION_SCOPE_COMMAND = 0, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT = 1, + VK_SYSTEM_ALLOCATION_SCOPE_CACHE = 2, + VK_SYSTEM_ALLOCATION_SCOPE_DEVICE = 3, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE = 4, + VK_SYSTEM_ALLOCATION_SCOPE_MAX_ENUM = 0x7FFFFFFF +} VkSystemAllocationScope; +typedef enum VkInternalAllocationType { + VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE = 0, + VK_INTERNAL_ALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkInternalAllocationType; +typedef enum VkSamplerAddressMode { + VK_SAMPLER_ADDRESS_MODE_REPEAT = 0, + VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT = 1, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 2, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3, + VK_SAMPLER_ADDRESS_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerAddressMode; +typedef enum VkFilter { + VK_FILTER_NEAREST = 0, + VK_FILTER_LINEAR = 1, + VK_FILTER_MAX_ENUM = 0x7FFFFFFF +} VkFilter; +typedef enum VkSamplerMipmapMode { + VK_SAMPLER_MIPMAP_MODE_NEAREST = 0, + VK_SAMPLER_MIPMAP_MODE_LINEAR = 1, + VK_SAMPLER_MIPMAP_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerMipmapMode; +typedef enum VkVertexInputRate { + VK_VERTEX_INPUT_RATE_VERTEX = 0, + VK_VERTEX_INPUT_RATE_INSTANCE = 1, + VK_VERTEX_INPUT_RATE_MAX_ENUM = 0x7FFFFFFF +} VkVertexInputRate; +typedef enum VkPipelineStageFlagBits { + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT = 1, + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT = 2, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT = 4, + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT = 8, + VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT = 16, + VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT = 32, + VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT = 64, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT = 128, + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT = 256, + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT = 512, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT = 1024, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT = 2048, + VK_PIPELINE_STAGE_TRANSFER_BIT = 4096, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT = 8192, + VK_PIPELINE_STAGE_HOST_BIT = 16384, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 32768, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 65536, + VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineStageFlagBits; +typedef enum VkSparseImageFormatFlagBits { + VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT = 1, + VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT = 2, + VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT = 4, + VK_SPARSE_IMAGE_FORMAT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSparseImageFormatFlagBits; +typedef enum VkSampleCountFlagBits { + VK_SAMPLE_COUNT_1_BIT = 1, + VK_SAMPLE_COUNT_2_BIT = 2, + VK_SAMPLE_COUNT_4_BIT = 4, + VK_SAMPLE_COUNT_8_BIT = 8, + VK_SAMPLE_COUNT_16_BIT = 16, + VK_SAMPLE_COUNT_32_BIT = 32, + VK_SAMPLE_COUNT_64_BIT = 64, + VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSampleCountFlagBits; +typedef enum VkAttachmentDescriptionFlagBits { + VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 1, + VK_ATTACHMENT_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkAttachmentDescriptionFlagBits; +typedef enum VkDescriptorPoolCreateFlagBits { + VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 1, + VK_DESCRIPTOR_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorPoolCreateFlagBits; +typedef enum VkDependencyFlagBits { + VK_DEPENDENCY_BY_REGION_BIT = 1, + VK_DEPENDENCY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDependencyFlagBits; +typedef enum VkObjectType { + VK_OBJECT_TYPE_UNKNOWN = 0, + VK_OBJECT_TYPE_INSTANCE = 1, + VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2, + VK_OBJECT_TYPE_DEVICE = 3, + VK_OBJECT_TYPE_QUEUE = 4, + VK_OBJECT_TYPE_SEMAPHORE = 5, + VK_OBJECT_TYPE_COMMAND_BUFFER = 6, + VK_OBJECT_TYPE_FENCE = 7, + VK_OBJECT_TYPE_DEVICE_MEMORY = 8, + VK_OBJECT_TYPE_BUFFER = 9, + VK_OBJECT_TYPE_IMAGE = 10, + VK_OBJECT_TYPE_EVENT = 11, + VK_OBJECT_TYPE_QUERY_POOL = 12, + VK_OBJECT_TYPE_BUFFER_VIEW = 13, + VK_OBJECT_TYPE_IMAGE_VIEW = 14, + VK_OBJECT_TYPE_SHADER_MODULE = 15, + VK_OBJECT_TYPE_PIPELINE_CACHE = 16, + VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17, + VK_OBJECT_TYPE_RENDER_PASS = 18, + VK_OBJECT_TYPE_PIPELINE = 19, + VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20, + VK_OBJECT_TYPE_SAMPLER = 21, + VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22, + VK_OBJECT_TYPE_DESCRIPTOR_SET = 23, + VK_OBJECT_TYPE_FRAMEBUFFER = 24, + VK_OBJECT_TYPE_COMMAND_POOL = 25, + VK_OBJECT_TYPE_SURFACE_KHR = 1000000000, + VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000, + VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000, + VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000, + VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkObjectType; +typedef enum VkColorSpaceKHR { + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0, + VK_COLORSPACE_SRGB_NONLINEAR_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkColorSpaceKHR; +typedef enum VkCompositeAlphaFlagBitsKHR { + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR = 1, + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR = 2, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR = 4, + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR = 8, + VK_COMPOSITE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkCompositeAlphaFlagBitsKHR; +typedef enum VkPresentModeKHR { + VK_PRESENT_MODE_IMMEDIATE_KHR = 0, + VK_PRESENT_MODE_MAILBOX_KHR = 1, + VK_PRESENT_MODE_FIFO_KHR = 2, + VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3, + VK_PRESENT_MODE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPresentModeKHR; +typedef enum VkSurfaceTransformFlagBitsKHR { + VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR = 1, + VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR = 2, + VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR = 4, + VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR = 8, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR = 16, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR = 32, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR = 64, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR = 128, + VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 256, + VK_SURFACE_TRANSFORM_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkSurfaceTransformFlagBitsKHR; +typedef enum VkDebugReportFlagBitsEXT { + VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 1, + VK_DEBUG_REPORT_WARNING_BIT_EXT = 2, + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 4, + VK_DEBUG_REPORT_ERROR_BIT_EXT = 8, + VK_DEBUG_REPORT_DEBUG_BIT_EXT = 16, + VK_DEBUG_REPORT_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugReportFlagBitsEXT; +typedef enum VkDebugReportObjectTypeEXT { + VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0, + VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1, + VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2, + VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3, + VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4, + VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6, + VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7, + VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9, + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10, + VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11, + VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13, + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14, + VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17, + VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20, + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23, + VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25, + VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26, + VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27, + VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT = 28, + VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29, + VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30, + VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT = 33, + VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT = 1000156000, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT = 1000085000, + VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugReportObjectTypeEXT; +typedef enum VkValidationFeatureEnableEXT { + VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT = 0, + VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT = 1, + VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT = 2, + VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT = 3, + VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT = 4, + VK_VALIDATION_FEATURE_ENABLE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationFeatureEnableEXT; +typedef enum VkValidationFeatureDisableEXT { + VK_VALIDATION_FEATURE_DISABLE_ALL_EXT = 0, + VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT = 1, + VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT = 2, + VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT = 3, + VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT = 4, + VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT = 5, + VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT = 6, + VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT = 7, + VK_VALIDATION_FEATURE_DISABLE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationFeatureDisableEXT; +typedef enum VkDeviceGroupPresentModeFlagBitsKHR { + VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHR = 1, + VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHR = 2, + VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHR = 4, + VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHR = 8, + VK_DEVICE_GROUP_PRESENT_MODE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkDeviceGroupPresentModeFlagBitsKHR; +typedef enum VkSwapchainCreateFlagBitsKHR { + VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR = 1, + VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR = 2, + VK_SWAPCHAIN_CREATE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkSwapchainCreateFlagBitsKHR; +typedef enum VkDebugUtilsMessageSeverityFlagBitsEXT { + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT = 1, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT = 16, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT = 256, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT = 4096, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugUtilsMessageSeverityFlagBitsEXT; +typedef enum VkDebugUtilsMessageTypeFlagBitsEXT { + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT = 1, + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT = 2, + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT = 4, + VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugUtilsMessageTypeFlagBitsEXT; +typedef enum VkVendorId { + VK_VENDOR_ID_VIV = 0x10001, + VK_VENDOR_ID_VSI = 0x10002, + VK_VENDOR_ID_KAZAN = 0x10003, + VK_VENDOR_ID_CODEPLAY = 0x10004, + VK_VENDOR_ID_MESA = 0x10005, + VK_VENDOR_ID_POCL = 0x10006, + VK_VENDOR_ID_MAX_ENUM = 0x7FFFFFFF +} VkVendorId; +typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)( + void* pUserData, + size_t size, + VkInternalAllocationType allocationType, + VkSystemAllocationScope allocationScope); +typedef void (VKAPI_PTR *PFN_vkInternalFreeNotification)( + void* pUserData, + size_t size, + VkInternalAllocationType allocationType, + VkSystemAllocationScope allocationScope); +typedef void* (VKAPI_PTR *PFN_vkReallocationFunction)( + void* pUserData, + void* pOriginal, + size_t size, + size_t alignment, + VkSystemAllocationScope allocationScope); +typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)( + void* pUserData, + size_t size, + size_t alignment, + VkSystemAllocationScope allocationScope); +typedef void (VKAPI_PTR *PFN_vkFreeFunction)( + void* pUserData, + void* pMemory); +typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void); +typedef struct VkBaseOutStructure { + VkStructureType sType; + struct VkBaseOutStructure * pNext; +} VkBaseOutStructure; + +typedef struct VkBaseInStructure { + VkStructureType sType; + const struct VkBaseInStructure * pNext; +} VkBaseInStructure; + +typedef struct VkOffset2D { + int32_t x; + int32_t y; +} VkOffset2D; + +typedef struct VkOffset3D { + int32_t x; + int32_t y; + int32_t z; +} VkOffset3D; + +typedef struct VkExtent2D { + uint32_t width; + uint32_t height; +} VkExtent2D; + +typedef struct VkExtent3D { + uint32_t width; + uint32_t height; + uint32_t depth; +} VkExtent3D; + +typedef struct VkViewport { + float x; + float y; + float width; + float height; + float minDepth; + float maxDepth; +} VkViewport; + +typedef struct VkRect2D { + VkOffset2D offset; + VkExtent2D extent; +} VkRect2D; + +typedef struct VkClearRect { + VkRect2D rect; + uint32_t baseArrayLayer; + uint32_t layerCount; +} VkClearRect; + +typedef struct VkComponentMapping { + VkComponentSwizzle r; + VkComponentSwizzle g; + VkComponentSwizzle b; + VkComponentSwizzle a; +} VkComponentMapping; + +typedef struct VkExtensionProperties { + char extensionName [ VK_MAX_EXTENSION_NAME_SIZE ]; + uint32_t specVersion; +} VkExtensionProperties; + +typedef struct VkLayerProperties { + char layerName [ VK_MAX_EXTENSION_NAME_SIZE ]; + uint32_t specVersion; + uint32_t implementationVersion; + char description [ VK_MAX_DESCRIPTION_SIZE ]; +} VkLayerProperties; + +typedef struct VkApplicationInfo { + VkStructureType sType; + const void * pNext; + const char * pApplicationName; + uint32_t applicationVersion; + const char * pEngineName; + uint32_t engineVersion; + uint32_t apiVersion; +} VkApplicationInfo; + +typedef struct VkAllocationCallbacks { + void * pUserData; + PFN_vkAllocationFunction pfnAllocation; + PFN_vkReallocationFunction pfnReallocation; + PFN_vkFreeFunction pfnFree; + PFN_vkInternalAllocationNotification pfnInternalAllocation; + PFN_vkInternalFreeNotification pfnInternalFree; +} VkAllocationCallbacks; + +typedef struct VkDescriptorImageInfo { + VkSampler sampler; + VkImageView imageView; + VkImageLayout imageLayout; +} VkDescriptorImageInfo; + +typedef struct VkCopyDescriptorSet { + VkStructureType sType; + const void * pNext; + VkDescriptorSet srcSet; + uint32_t srcBinding; + uint32_t srcArrayElement; + VkDescriptorSet dstSet; + uint32_t dstBinding; + uint32_t dstArrayElement; + uint32_t descriptorCount; +} VkCopyDescriptorSet; + +typedef struct VkDescriptorPoolSize { + VkDescriptorType type; + uint32_t descriptorCount; +} VkDescriptorPoolSize; + +typedef struct VkDescriptorSetAllocateInfo { + VkStructureType sType; + const void * pNext; + VkDescriptorPool descriptorPool; + uint32_t descriptorSetCount; + const VkDescriptorSetLayout * pSetLayouts; +} VkDescriptorSetAllocateInfo; + +typedef struct VkSpecializationMapEntry { + uint32_t constantID; + uint32_t offset; + size_t size; +} VkSpecializationMapEntry; + +typedef struct VkSpecializationInfo { + uint32_t mapEntryCount; + const VkSpecializationMapEntry * pMapEntries; + size_t dataSize; + const void * pData; +} VkSpecializationInfo; + +typedef struct VkVertexInputBindingDescription { + uint32_t binding; + uint32_t stride; + VkVertexInputRate inputRate; +} VkVertexInputBindingDescription; + +typedef struct VkVertexInputAttributeDescription { + uint32_t location; + uint32_t binding; + VkFormat format; + uint32_t offset; +} VkVertexInputAttributeDescription; + +typedef struct VkStencilOpState { + VkStencilOp failOp; + VkStencilOp passOp; + VkStencilOp depthFailOp; + VkCompareOp compareOp; + uint32_t compareMask; + uint32_t writeMask; + uint32_t reference; +} VkStencilOpState; + +typedef struct VkPipelineCacheHeaderVersionOne { + uint32_t headerSize; + VkPipelineCacheHeaderVersion headerVersion; + uint32_t vendorID; + uint32_t deviceID; + uint8_t pipelineCacheUUID [ VK_UUID_SIZE ]; +} VkPipelineCacheHeaderVersionOne; + +typedef struct VkCommandBufferAllocateInfo { + VkStructureType sType; + const void * pNext; + VkCommandPool commandPool; + VkCommandBufferLevel level; + uint32_t commandBufferCount; +} VkCommandBufferAllocateInfo; + +typedef union VkClearColorValue { + float float32 [4]; + int32_t int32 [4]; + uint32_t uint32 [4]; +} VkClearColorValue; + +typedef struct VkClearDepthStencilValue { + float depth; + uint32_t stencil; +} VkClearDepthStencilValue; + +typedef union VkClearValue { + VkClearColorValue color; + VkClearDepthStencilValue depthStencil; +} VkClearValue; + +typedef struct VkAttachmentReference { + uint32_t attachment; + VkImageLayout layout; +} VkAttachmentReference; + +typedef struct VkDrawIndirectCommand { + uint32_t vertexCount; + uint32_t instanceCount; + uint32_t firstVertex; + uint32_t firstInstance; +} VkDrawIndirectCommand; + +typedef struct VkDrawIndexedIndirectCommand { + uint32_t indexCount; + uint32_t instanceCount; + uint32_t firstIndex; + int32_t vertexOffset; + uint32_t firstInstance; +} VkDrawIndexedIndirectCommand; + +typedef struct VkDispatchIndirectCommand { + uint32_t x; + uint32_t y; + uint32_t z; +} VkDispatchIndirectCommand; + +typedef struct VkSurfaceFormatKHR { + VkFormat format; + VkColorSpaceKHR colorSpace; +} VkSurfaceFormatKHR; + +typedef struct VkPresentInfoKHR { + VkStructureType sType; + const void * pNext; + uint32_t waitSemaphoreCount; + const VkSemaphore * pWaitSemaphores; + uint32_t swapchainCount; + const VkSwapchainKHR * pSwapchains; + const uint32_t * pImageIndices; + VkResult * pResults; +} VkPresentInfoKHR; + +typedef struct VkValidationFeaturesEXT { + VkStructureType sType; + const void * pNext; + uint32_t enabledValidationFeatureCount; + const VkValidationFeatureEnableEXT * pEnabledValidationFeatures; + uint32_t disabledValidationFeatureCount; + const VkValidationFeatureDisableEXT * pDisabledValidationFeatures; +} VkValidationFeaturesEXT; + +typedef struct VkImageSwapchainCreateInfoKHR { + VkStructureType sType; + const void * pNext; + VkSwapchainKHR swapchain; +} VkImageSwapchainCreateInfoKHR; + +typedef struct VkBindImageMemorySwapchainInfoKHR { + VkStructureType sType; + const void * pNext; + VkSwapchainKHR swapchain; + uint32_t imageIndex; +} VkBindImageMemorySwapchainInfoKHR; + +typedef struct VkAcquireNextImageInfoKHR { + VkStructureType sType; + const void * pNext; + VkSwapchainKHR swapchain; + uint64_t timeout; + VkSemaphore semaphore; + VkFence fence; + uint32_t deviceMask; +} VkAcquireNextImageInfoKHR; + +typedef struct VkDeviceGroupPresentInfoKHR { + VkStructureType sType; + const void * pNext; + uint32_t swapchainCount; + const uint32_t * pDeviceMasks; + VkDeviceGroupPresentModeFlagBitsKHR mode; +} VkDeviceGroupPresentInfoKHR; + +typedef struct VkDebugUtilsObjectNameInfoEXT { + VkStructureType sType; + const void * pNext; + VkObjectType objectType; + uint64_t objectHandle; + const char * pObjectName; +} VkDebugUtilsObjectNameInfoEXT; + +typedef struct VkDebugUtilsObjectTagInfoEXT { + VkStructureType sType; + const void * pNext; + VkObjectType objectType; + uint64_t objectHandle; + uint64_t tagName; + size_t tagSize; + const void * pTag; +} VkDebugUtilsObjectTagInfoEXT; + +typedef struct VkDebugUtilsLabelEXT { + VkStructureType sType; + const void * pNext; + const char * pLabelName; + float color [4]; +} VkDebugUtilsLabelEXT; + +typedef uint32_t VkSampleMask; +typedef uint32_t VkBool32; +typedef uint32_t VkFlags; +typedef uint64_t VkDeviceSize; +typedef uint64_t VkDeviceAddress; +typedef VkFlags VkFramebufferCreateFlags; +typedef VkFlags VkQueryPoolCreateFlags; +typedef VkFlags VkRenderPassCreateFlags; +typedef VkFlags VkSamplerCreateFlags; +typedef VkFlags VkPipelineLayoutCreateFlags; +typedef VkFlags VkPipelineCacheCreateFlags; +typedef VkFlags VkPipelineDepthStencilStateCreateFlags; +typedef VkFlags VkPipelineDynamicStateCreateFlags; +typedef VkFlags VkPipelineColorBlendStateCreateFlags; +typedef VkFlags VkPipelineMultisampleStateCreateFlags; +typedef VkFlags VkPipelineRasterizationStateCreateFlags; +typedef VkFlags VkPipelineViewportStateCreateFlags; +typedef VkFlags VkPipelineTessellationStateCreateFlags; +typedef VkFlags VkPipelineInputAssemblyStateCreateFlags; +typedef VkFlags VkPipelineVertexInputStateCreateFlags; +typedef VkFlags VkPipelineShaderStageCreateFlags; +typedef VkFlags VkDescriptorSetLayoutCreateFlags; +typedef VkFlags VkBufferViewCreateFlags; +typedef VkFlags VkInstanceCreateFlags; +typedef VkFlags VkDeviceCreateFlags; +typedef VkFlags VkDeviceQueueCreateFlags; +typedef VkFlags VkQueueFlags; +typedef VkFlags VkMemoryPropertyFlags; +typedef VkFlags VkMemoryHeapFlags; +typedef VkFlags VkAccessFlags; +typedef VkFlags VkBufferUsageFlags; +typedef VkFlags VkBufferCreateFlags; +typedef VkFlags VkShaderStageFlags; +typedef VkFlags VkImageUsageFlags; +typedef VkFlags VkImageCreateFlags; +typedef VkFlags VkImageViewCreateFlags; +typedef VkFlags VkPipelineCreateFlags; +typedef VkFlags VkColorComponentFlags; +typedef VkFlags VkFenceCreateFlags; +typedef VkFlags VkSemaphoreCreateFlags; +typedef VkFlags VkFormatFeatureFlags; +typedef VkFlags VkQueryControlFlags; +typedef VkFlags VkQueryResultFlags; +typedef VkFlags VkShaderModuleCreateFlags; +typedef VkFlags VkEventCreateFlags; +typedef VkFlags VkCommandPoolCreateFlags; +typedef VkFlags VkCommandPoolResetFlags; +typedef VkFlags VkCommandBufferResetFlags; +typedef VkFlags VkCommandBufferUsageFlags; +typedef VkFlags VkQueryPipelineStatisticFlags; +typedef VkFlags VkMemoryMapFlags; +typedef VkFlags VkImageAspectFlags; +typedef VkFlags VkSparseMemoryBindFlags; +typedef VkFlags VkSparseImageFormatFlags; +typedef VkFlags VkSubpassDescriptionFlags; +typedef VkFlags VkPipelineStageFlags; +typedef VkFlags VkSampleCountFlags; +typedef VkFlags VkAttachmentDescriptionFlags; +typedef VkFlags VkStencilFaceFlags; +typedef VkFlags VkCullModeFlags; +typedef VkFlags VkDescriptorPoolCreateFlags; +typedef VkFlags VkDescriptorPoolResetFlags; +typedef VkFlags VkDependencyFlags; +typedef VkFlags VkCompositeAlphaFlagsKHR; +typedef VkFlags VkSurfaceTransformFlagsKHR; +typedef VkFlags VkSwapchainCreateFlagsKHR; +#if defined(VK_USE_PLATFORM_WIN32_KHR) +typedef VkFlags VkWin32SurfaceCreateFlagsKHR; +#endif + +#if defined(VK_USE_PLATFORM_XCB_KHR) +typedef VkFlags VkXcbSurfaceCreateFlagsKHR; +#endif + +typedef VkFlags VkDeviceGroupPresentModeFlagsKHR; +typedef VkFlags VkDebugReportFlagsEXT; +typedef VkFlags VkDebugUtilsMessageSeverityFlagsEXT; +typedef VkFlags VkDebugUtilsMessageTypeFlagsEXT; +typedef VkFlags VkDebugUtilsMessengerCreateFlagsEXT; +typedef VkFlags VkDebugUtilsMessengerCallbackDataFlagsEXT; +typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage, + void* pUserData); +typedef struct VkDeviceQueueCreateInfo { + VkStructureType sType; + const void * pNext; + VkDeviceQueueCreateFlags flags; + uint32_t queueFamilyIndex; + uint32_t queueCount; + const float * pQueuePriorities; +} VkDeviceQueueCreateInfo; + +typedef struct VkInstanceCreateInfo { + VkStructureType sType; + const void * pNext; + VkInstanceCreateFlags flags; + const VkApplicationInfo * pApplicationInfo; + uint32_t enabledLayerCount; + const char * const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char * const* ppEnabledExtensionNames; +} VkInstanceCreateInfo; + +typedef struct VkQueueFamilyProperties { + VkQueueFlags queueFlags; + uint32_t queueCount; + uint32_t timestampValidBits; + VkExtent3D minImageTransferGranularity; +} VkQueueFamilyProperties; + +typedef struct VkMemoryAllocateInfo { + VkStructureType sType; + const void * pNext; + VkDeviceSize allocationSize; + uint32_t memoryTypeIndex; +} VkMemoryAllocateInfo; + +typedef struct VkMemoryRequirements { + VkDeviceSize size; + VkDeviceSize alignment; + uint32_t memoryTypeBits; +} VkMemoryRequirements; + +typedef struct VkSparseImageFormatProperties { + VkImageAspectFlags aspectMask; + VkExtent3D imageGranularity; + VkSparseImageFormatFlags flags; +} VkSparseImageFormatProperties; + +typedef struct VkSparseImageMemoryRequirements { + VkSparseImageFormatProperties formatProperties; + uint32_t imageMipTailFirstLod; + VkDeviceSize imageMipTailSize; + VkDeviceSize imageMipTailOffset; + VkDeviceSize imageMipTailStride; +} VkSparseImageMemoryRequirements; + +typedef struct VkMemoryType { + VkMemoryPropertyFlags propertyFlags; + uint32_t heapIndex; +} VkMemoryType; + +typedef struct VkMemoryHeap { + VkDeviceSize size; + VkMemoryHeapFlags flags; +} VkMemoryHeap; + +typedef struct VkMappedMemoryRange { + VkStructureType sType; + const void * pNext; + VkDeviceMemory memory; + VkDeviceSize offset; + VkDeviceSize size; +} VkMappedMemoryRange; + +typedef struct VkFormatProperties { + VkFormatFeatureFlags linearTilingFeatures; + VkFormatFeatureFlags optimalTilingFeatures; + VkFormatFeatureFlags bufferFeatures; +} VkFormatProperties; + +typedef struct VkImageFormatProperties { + VkExtent3D maxExtent; + uint32_t maxMipLevels; + uint32_t maxArrayLayers; + VkSampleCountFlags sampleCounts; + VkDeviceSize maxResourceSize; +} VkImageFormatProperties; + +typedef struct VkDescriptorBufferInfo { + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize range; +} VkDescriptorBufferInfo; + +typedef struct VkWriteDescriptorSet { + VkStructureType sType; + const void * pNext; + VkDescriptorSet dstSet; + uint32_t dstBinding; + uint32_t dstArrayElement; + uint32_t descriptorCount; + VkDescriptorType descriptorType; + const VkDescriptorImageInfo * pImageInfo; + const VkDescriptorBufferInfo * pBufferInfo; + const VkBufferView * pTexelBufferView; +} VkWriteDescriptorSet; + +typedef struct VkBufferCreateInfo { + VkStructureType sType; + const void * pNext; + VkBufferCreateFlags flags; + VkDeviceSize size; + VkBufferUsageFlags usage; + VkSharingMode sharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t * pQueueFamilyIndices; +} VkBufferCreateInfo; + +typedef struct VkBufferViewCreateInfo { + VkStructureType sType; + const void * pNext; + VkBufferViewCreateFlags flags; + VkBuffer buffer; + VkFormat format; + VkDeviceSize offset; + VkDeviceSize range; +} VkBufferViewCreateInfo; + +typedef struct VkImageSubresource { + VkImageAspectFlags aspectMask; + uint32_t mipLevel; + uint32_t arrayLayer; +} VkImageSubresource; + +typedef struct VkImageSubresourceLayers { + VkImageAspectFlags aspectMask; + uint32_t mipLevel; + uint32_t baseArrayLayer; + uint32_t layerCount; +} VkImageSubresourceLayers; + +typedef struct VkImageSubresourceRange { + VkImageAspectFlags aspectMask; + uint32_t baseMipLevel; + uint32_t levelCount; + uint32_t baseArrayLayer; + uint32_t layerCount; +} VkImageSubresourceRange; + +typedef struct VkMemoryBarrier { + VkStructureType sType; + const void * pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; +} VkMemoryBarrier; + +typedef struct VkBufferMemoryBarrier { + VkStructureType sType; + const void * pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize size; +} VkBufferMemoryBarrier; + +typedef struct VkImageMemoryBarrier { + VkStructureType sType; + const void * pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkImageLayout oldLayout; + VkImageLayout newLayout; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkImage image; + VkImageSubresourceRange subresourceRange; +} VkImageMemoryBarrier; + +typedef struct VkImageCreateInfo { + VkStructureType sType; + const void * pNext; + VkImageCreateFlags flags; + VkImageType imageType; + VkFormat format; + VkExtent3D extent; + uint32_t mipLevels; + uint32_t arrayLayers; + VkSampleCountFlagBits samples; + VkImageTiling tiling; + VkImageUsageFlags usage; + VkSharingMode sharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t * pQueueFamilyIndices; + VkImageLayout initialLayout; +} VkImageCreateInfo; + +typedef struct VkSubresourceLayout { + VkDeviceSize offset; + VkDeviceSize size; + VkDeviceSize rowPitch; + VkDeviceSize arrayPitch; + VkDeviceSize depthPitch; +} VkSubresourceLayout; + +typedef struct VkImageViewCreateInfo { + VkStructureType sType; + const void * pNext; + VkImageViewCreateFlags flags; + VkImage image; + VkImageViewType viewType; + VkFormat format; + VkComponentMapping components; + VkImageSubresourceRange subresourceRange; +} VkImageViewCreateInfo; + +typedef struct VkBufferCopy { + VkDeviceSize srcOffset; + VkDeviceSize dstOffset; + VkDeviceSize size; +} VkBufferCopy; + +typedef struct VkSparseMemoryBind { + VkDeviceSize resourceOffset; + VkDeviceSize size; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; + VkSparseMemoryBindFlags flags; +} VkSparseMemoryBind; + +typedef struct VkSparseImageMemoryBind { + VkImageSubresource subresource; + VkOffset3D offset; + VkExtent3D extent; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; + VkSparseMemoryBindFlags flags; +} VkSparseImageMemoryBind; + +typedef struct VkSparseBufferMemoryBindInfo { + VkBuffer buffer; + uint32_t bindCount; + const VkSparseMemoryBind * pBinds; +} VkSparseBufferMemoryBindInfo; + +typedef struct VkSparseImageOpaqueMemoryBindInfo { + VkImage image; + uint32_t bindCount; + const VkSparseMemoryBind * pBinds; +} VkSparseImageOpaqueMemoryBindInfo; + +typedef struct VkSparseImageMemoryBindInfo { + VkImage image; + uint32_t bindCount; + const VkSparseImageMemoryBind * pBinds; +} VkSparseImageMemoryBindInfo; + +typedef struct VkBindSparseInfo { + VkStructureType sType; + const void * pNext; + uint32_t waitSemaphoreCount; + const VkSemaphore * pWaitSemaphores; + uint32_t bufferBindCount; + const VkSparseBufferMemoryBindInfo * pBufferBinds; + uint32_t imageOpaqueBindCount; + const VkSparseImageOpaqueMemoryBindInfo * pImageOpaqueBinds; + uint32_t imageBindCount; + const VkSparseImageMemoryBindInfo * pImageBinds; + uint32_t signalSemaphoreCount; + const VkSemaphore * pSignalSemaphores; +} VkBindSparseInfo; + +typedef struct VkImageCopy { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageCopy; + +typedef struct VkImageBlit { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffsets [2]; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffsets [2]; +} VkImageBlit; + +typedef struct VkBufferImageCopy { + VkDeviceSize bufferOffset; + uint32_t bufferRowLength; + uint32_t bufferImageHeight; + VkImageSubresourceLayers imageSubresource; + VkOffset3D imageOffset; + VkExtent3D imageExtent; +} VkBufferImageCopy; + +typedef struct VkImageResolve { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageResolve; + +typedef struct VkShaderModuleCreateInfo { + VkStructureType sType; + const void * pNext; + VkShaderModuleCreateFlags flags; + size_t codeSize; + const uint32_t * pCode; +} VkShaderModuleCreateInfo; + +typedef struct VkDescriptorSetLayoutBinding { + uint32_t binding; + VkDescriptorType descriptorType; + uint32_t descriptorCount; + VkShaderStageFlags stageFlags; + const VkSampler * pImmutableSamplers; +} VkDescriptorSetLayoutBinding; + +typedef struct VkDescriptorSetLayoutCreateInfo { + VkStructureType sType; + const void * pNext; + VkDescriptorSetLayoutCreateFlags flags; + uint32_t bindingCount; + const VkDescriptorSetLayoutBinding * pBindings; +} VkDescriptorSetLayoutCreateInfo; + +typedef struct VkDescriptorPoolCreateInfo { + VkStructureType sType; + const void * pNext; + VkDescriptorPoolCreateFlags flags; + uint32_t maxSets; + uint32_t poolSizeCount; + const VkDescriptorPoolSize * pPoolSizes; +} VkDescriptorPoolCreateInfo; + +typedef struct VkPipelineShaderStageCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineShaderStageCreateFlags flags; + VkShaderStageFlagBits stage; + VkShaderModule module; + const char * pName; + const VkSpecializationInfo * pSpecializationInfo; +} VkPipelineShaderStageCreateInfo; + +typedef struct VkComputePipelineCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineCreateFlags flags; + VkPipelineShaderStageCreateInfo stage; + VkPipelineLayout layout; + VkPipeline basePipelineHandle; + int32_t basePipelineIndex; +} VkComputePipelineCreateInfo; + +typedef struct VkPipelineVertexInputStateCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineVertexInputStateCreateFlags flags; + uint32_t vertexBindingDescriptionCount; + const VkVertexInputBindingDescription * pVertexBindingDescriptions; + uint32_t vertexAttributeDescriptionCount; + const VkVertexInputAttributeDescription * pVertexAttributeDescriptions; +} VkPipelineVertexInputStateCreateInfo; + +typedef struct VkPipelineInputAssemblyStateCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineInputAssemblyStateCreateFlags flags; + VkPrimitiveTopology topology; + VkBool32 primitiveRestartEnable; +} VkPipelineInputAssemblyStateCreateInfo; + +typedef struct VkPipelineTessellationStateCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineTessellationStateCreateFlags flags; + uint32_t patchControlPoints; +} VkPipelineTessellationStateCreateInfo; + +typedef struct VkPipelineViewportStateCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineViewportStateCreateFlags flags; + uint32_t viewportCount; + const VkViewport * pViewports; + uint32_t scissorCount; + const VkRect2D * pScissors; +} VkPipelineViewportStateCreateInfo; + +typedef struct VkPipelineRasterizationStateCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineRasterizationStateCreateFlags flags; + VkBool32 depthClampEnable; + VkBool32 rasterizerDiscardEnable; + VkPolygonMode polygonMode; + VkCullModeFlags cullMode; + VkFrontFace frontFace; + VkBool32 depthBiasEnable; + float depthBiasConstantFactor; + float depthBiasClamp; + float depthBiasSlopeFactor; + float lineWidth; +} VkPipelineRasterizationStateCreateInfo; + +typedef struct VkPipelineMultisampleStateCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineMultisampleStateCreateFlags flags; + VkSampleCountFlagBits rasterizationSamples; + VkBool32 sampleShadingEnable; + float minSampleShading; + const VkSampleMask * pSampleMask; + VkBool32 alphaToCoverageEnable; + VkBool32 alphaToOneEnable; +} VkPipelineMultisampleStateCreateInfo; + +typedef struct VkPipelineColorBlendAttachmentState { + VkBool32 blendEnable; + VkBlendFactor srcColorBlendFactor; + VkBlendFactor dstColorBlendFactor; + VkBlendOp colorBlendOp; + VkBlendFactor srcAlphaBlendFactor; + VkBlendFactor dstAlphaBlendFactor; + VkBlendOp alphaBlendOp; + VkColorComponentFlags colorWriteMask; +} VkPipelineColorBlendAttachmentState; + +typedef struct VkPipelineColorBlendStateCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineColorBlendStateCreateFlags flags; + VkBool32 logicOpEnable; + VkLogicOp logicOp; + uint32_t attachmentCount; + const VkPipelineColorBlendAttachmentState * pAttachments; + float blendConstants [4]; +} VkPipelineColorBlendStateCreateInfo; + +typedef struct VkPipelineDynamicStateCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineDynamicStateCreateFlags flags; + uint32_t dynamicStateCount; + const VkDynamicState * pDynamicStates; +} VkPipelineDynamicStateCreateInfo; + +typedef struct VkPipelineDepthStencilStateCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineDepthStencilStateCreateFlags flags; + VkBool32 depthTestEnable; + VkBool32 depthWriteEnable; + VkCompareOp depthCompareOp; + VkBool32 depthBoundsTestEnable; + VkBool32 stencilTestEnable; + VkStencilOpState front; + VkStencilOpState back; + float minDepthBounds; + float maxDepthBounds; +} VkPipelineDepthStencilStateCreateInfo; + +typedef struct VkGraphicsPipelineCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineCreateFlags flags; + uint32_t stageCount; + const VkPipelineShaderStageCreateInfo * pStages; + const VkPipelineVertexInputStateCreateInfo * pVertexInputState; + const VkPipelineInputAssemblyStateCreateInfo * pInputAssemblyState; + const VkPipelineTessellationStateCreateInfo * pTessellationState; + const VkPipelineViewportStateCreateInfo * pViewportState; + const VkPipelineRasterizationStateCreateInfo * pRasterizationState; + const VkPipelineMultisampleStateCreateInfo * pMultisampleState; + const VkPipelineDepthStencilStateCreateInfo * pDepthStencilState; + const VkPipelineColorBlendStateCreateInfo * pColorBlendState; + const VkPipelineDynamicStateCreateInfo * pDynamicState; + VkPipelineLayout layout; + VkRenderPass renderPass; + uint32_t subpass; + VkPipeline basePipelineHandle; + int32_t basePipelineIndex; +} VkGraphicsPipelineCreateInfo; + +typedef struct VkPipelineCacheCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineCacheCreateFlags flags; + size_t initialDataSize; + const void * pInitialData; +} VkPipelineCacheCreateInfo; + +typedef struct VkPushConstantRange { + VkShaderStageFlags stageFlags; + uint32_t offset; + uint32_t size; +} VkPushConstantRange; + +typedef struct VkPipelineLayoutCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineLayoutCreateFlags flags; + uint32_t setLayoutCount; + const VkDescriptorSetLayout * pSetLayouts; + uint32_t pushConstantRangeCount; + const VkPushConstantRange * pPushConstantRanges; +} VkPipelineLayoutCreateInfo; + +typedef struct VkSamplerCreateInfo { + VkStructureType sType; + const void * pNext; + VkSamplerCreateFlags flags; + VkFilter magFilter; + VkFilter minFilter; + VkSamplerMipmapMode mipmapMode; + VkSamplerAddressMode addressModeU; + VkSamplerAddressMode addressModeV; + VkSamplerAddressMode addressModeW; + float mipLodBias; + VkBool32 anisotropyEnable; + float maxAnisotropy; + VkBool32 compareEnable; + VkCompareOp compareOp; + float minLod; + float maxLod; + VkBorderColor borderColor; + VkBool32 unnormalizedCoordinates; +} VkSamplerCreateInfo; + +typedef struct VkCommandPoolCreateInfo { + VkStructureType sType; + const void * pNext; + VkCommandPoolCreateFlags flags; + uint32_t queueFamilyIndex; +} VkCommandPoolCreateInfo; + +typedef struct VkCommandBufferInheritanceInfo { + VkStructureType sType; + const void * pNext; + VkRenderPass renderPass; + uint32_t subpass; + VkFramebuffer framebuffer; + VkBool32 occlusionQueryEnable; + VkQueryControlFlags queryFlags; + VkQueryPipelineStatisticFlags pipelineStatistics; +} VkCommandBufferInheritanceInfo; + +typedef struct VkCommandBufferBeginInfo { + VkStructureType sType; + const void * pNext; + VkCommandBufferUsageFlags flags; + const VkCommandBufferInheritanceInfo * pInheritanceInfo; +} VkCommandBufferBeginInfo; + +typedef struct VkRenderPassBeginInfo { + VkStructureType sType; + const void * pNext; + VkRenderPass renderPass; + VkFramebuffer framebuffer; + VkRect2D renderArea; + uint32_t clearValueCount; + const VkClearValue * pClearValues; +} VkRenderPassBeginInfo; + +typedef struct VkClearAttachment { + VkImageAspectFlags aspectMask; + uint32_t colorAttachment; + VkClearValue clearValue; +} VkClearAttachment; + +typedef struct VkAttachmentDescription { + VkAttachmentDescriptionFlags flags; + VkFormat format; + VkSampleCountFlagBits samples; + VkAttachmentLoadOp loadOp; + VkAttachmentStoreOp storeOp; + VkAttachmentLoadOp stencilLoadOp; + VkAttachmentStoreOp stencilStoreOp; + VkImageLayout initialLayout; + VkImageLayout finalLayout; +} VkAttachmentDescription; + +typedef struct VkSubpassDescription { + VkSubpassDescriptionFlags flags; + VkPipelineBindPoint pipelineBindPoint; + uint32_t inputAttachmentCount; + const VkAttachmentReference * pInputAttachments; + uint32_t colorAttachmentCount; + const VkAttachmentReference * pColorAttachments; + const VkAttachmentReference * pResolveAttachments; + const VkAttachmentReference * pDepthStencilAttachment; + uint32_t preserveAttachmentCount; + const uint32_t * pPreserveAttachments; +} VkSubpassDescription; + +typedef struct VkSubpassDependency { + uint32_t srcSubpass; + uint32_t dstSubpass; + VkPipelineStageFlags srcStageMask; + VkPipelineStageFlags dstStageMask; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkDependencyFlags dependencyFlags; +} VkSubpassDependency; + +typedef struct VkRenderPassCreateInfo { + VkStructureType sType; + const void * pNext; + VkRenderPassCreateFlags flags; + uint32_t attachmentCount; + const VkAttachmentDescription * pAttachments; + uint32_t subpassCount; + const VkSubpassDescription * pSubpasses; + uint32_t dependencyCount; + const VkSubpassDependency * pDependencies; +} VkRenderPassCreateInfo; + +typedef struct VkEventCreateInfo { + VkStructureType sType; + const void * pNext; + VkEventCreateFlags flags; +} VkEventCreateInfo; + +typedef struct VkFenceCreateInfo { + VkStructureType sType; + const void * pNext; + VkFenceCreateFlags flags; +} VkFenceCreateInfo; + +typedef struct VkPhysicalDeviceFeatures { + VkBool32 robustBufferAccess; + VkBool32 fullDrawIndexUint32; + VkBool32 imageCubeArray; + VkBool32 independentBlend; + VkBool32 geometryShader; + VkBool32 tessellationShader; + VkBool32 sampleRateShading; + VkBool32 dualSrcBlend; + VkBool32 logicOp; + VkBool32 multiDrawIndirect; + VkBool32 drawIndirectFirstInstance; + VkBool32 depthClamp; + VkBool32 depthBiasClamp; + VkBool32 fillModeNonSolid; + VkBool32 depthBounds; + VkBool32 wideLines; + VkBool32 largePoints; + VkBool32 alphaToOne; + VkBool32 multiViewport; + VkBool32 samplerAnisotropy; + VkBool32 textureCompressionETC2; + VkBool32 textureCompressionASTC_LDR; + VkBool32 textureCompressionBC; + VkBool32 occlusionQueryPrecise; + VkBool32 pipelineStatisticsQuery; + VkBool32 vertexPipelineStoresAndAtomics; + VkBool32 fragmentStoresAndAtomics; + VkBool32 shaderTessellationAndGeometryPointSize; + VkBool32 shaderImageGatherExtended; + VkBool32 shaderStorageImageExtendedFormats; + VkBool32 shaderStorageImageMultisample; + VkBool32 shaderStorageImageReadWithoutFormat; + VkBool32 shaderStorageImageWriteWithoutFormat; + VkBool32 shaderUniformBufferArrayDynamicIndexing; + VkBool32 shaderSampledImageArrayDynamicIndexing; + VkBool32 shaderStorageBufferArrayDynamicIndexing; + VkBool32 shaderStorageImageArrayDynamicIndexing; + VkBool32 shaderClipDistance; + VkBool32 shaderCullDistance; + VkBool32 shaderFloat64; + VkBool32 shaderInt64; + VkBool32 shaderInt16; + VkBool32 shaderResourceResidency; + VkBool32 shaderResourceMinLod; + VkBool32 sparseBinding; + VkBool32 sparseResidencyBuffer; + VkBool32 sparseResidencyImage2D; + VkBool32 sparseResidencyImage3D; + VkBool32 sparseResidency2Samples; + VkBool32 sparseResidency4Samples; + VkBool32 sparseResidency8Samples; + VkBool32 sparseResidency16Samples; + VkBool32 sparseResidencyAliased; + VkBool32 variableMultisampleRate; + VkBool32 inheritedQueries; +} VkPhysicalDeviceFeatures; + +typedef struct VkPhysicalDeviceSparseProperties { + VkBool32 residencyStandard2DBlockShape; + VkBool32 residencyStandard2DMultisampleBlockShape; + VkBool32 residencyStandard3DBlockShape; + VkBool32 residencyAlignedMipSize; + VkBool32 residencyNonResidentStrict; +} VkPhysicalDeviceSparseProperties; + +typedef struct VkPhysicalDeviceLimits { + uint32_t maxImageDimension1D; + uint32_t maxImageDimension2D; + uint32_t maxImageDimension3D; + uint32_t maxImageDimensionCube; + uint32_t maxImageArrayLayers; + uint32_t maxTexelBufferElements; + uint32_t maxUniformBufferRange; + uint32_t maxStorageBufferRange; + uint32_t maxPushConstantsSize; + uint32_t maxMemoryAllocationCount; + uint32_t maxSamplerAllocationCount; + VkDeviceSize bufferImageGranularity; + VkDeviceSize sparseAddressSpaceSize; + uint32_t maxBoundDescriptorSets; + uint32_t maxPerStageDescriptorSamplers; + uint32_t maxPerStageDescriptorUniformBuffers; + uint32_t maxPerStageDescriptorStorageBuffers; + uint32_t maxPerStageDescriptorSampledImages; + uint32_t maxPerStageDescriptorStorageImages; + uint32_t maxPerStageDescriptorInputAttachments; + uint32_t maxPerStageResources; + uint32_t maxDescriptorSetSamplers; + uint32_t maxDescriptorSetUniformBuffers; + uint32_t maxDescriptorSetUniformBuffersDynamic; + uint32_t maxDescriptorSetStorageBuffers; + uint32_t maxDescriptorSetStorageBuffersDynamic; + uint32_t maxDescriptorSetSampledImages; + uint32_t maxDescriptorSetStorageImages; + uint32_t maxDescriptorSetInputAttachments; + uint32_t maxVertexInputAttributes; + uint32_t maxVertexInputBindings; + uint32_t maxVertexInputAttributeOffset; + uint32_t maxVertexInputBindingStride; + uint32_t maxVertexOutputComponents; + uint32_t maxTessellationGenerationLevel; + uint32_t maxTessellationPatchSize; + uint32_t maxTessellationControlPerVertexInputComponents; + uint32_t maxTessellationControlPerVertexOutputComponents; + uint32_t maxTessellationControlPerPatchOutputComponents; + uint32_t maxTessellationControlTotalOutputComponents; + uint32_t maxTessellationEvaluationInputComponents; + uint32_t maxTessellationEvaluationOutputComponents; + uint32_t maxGeometryShaderInvocations; + uint32_t maxGeometryInputComponents; + uint32_t maxGeometryOutputComponents; + uint32_t maxGeometryOutputVertices; + uint32_t maxGeometryTotalOutputComponents; + uint32_t maxFragmentInputComponents; + uint32_t maxFragmentOutputAttachments; + uint32_t maxFragmentDualSrcAttachments; + uint32_t maxFragmentCombinedOutputResources; + uint32_t maxComputeSharedMemorySize; + uint32_t maxComputeWorkGroupCount [3]; + uint32_t maxComputeWorkGroupInvocations; + uint32_t maxComputeWorkGroupSize [3]; + uint32_t subPixelPrecisionBits; + uint32_t subTexelPrecisionBits; + uint32_t mipmapPrecisionBits; + uint32_t maxDrawIndexedIndexValue; + uint32_t maxDrawIndirectCount; + float maxSamplerLodBias; + float maxSamplerAnisotropy; + uint32_t maxViewports; + uint32_t maxViewportDimensions [2]; + float viewportBoundsRange [2]; + uint32_t viewportSubPixelBits; + size_t minMemoryMapAlignment; + VkDeviceSize minTexelBufferOffsetAlignment; + VkDeviceSize minUniformBufferOffsetAlignment; + VkDeviceSize minStorageBufferOffsetAlignment; + int32_t minTexelOffset; + uint32_t maxTexelOffset; + int32_t minTexelGatherOffset; + uint32_t maxTexelGatherOffset; + float minInterpolationOffset; + float maxInterpolationOffset; + uint32_t subPixelInterpolationOffsetBits; + uint32_t maxFramebufferWidth; + uint32_t maxFramebufferHeight; + uint32_t maxFramebufferLayers; + VkSampleCountFlags framebufferColorSampleCounts; + VkSampleCountFlags framebufferDepthSampleCounts; + VkSampleCountFlags framebufferStencilSampleCounts; + VkSampleCountFlags framebufferNoAttachmentsSampleCounts; + uint32_t maxColorAttachments; + VkSampleCountFlags sampledImageColorSampleCounts; + VkSampleCountFlags sampledImageIntegerSampleCounts; + VkSampleCountFlags sampledImageDepthSampleCounts; + VkSampleCountFlags sampledImageStencilSampleCounts; + VkSampleCountFlags storageImageSampleCounts; + uint32_t maxSampleMaskWords; + VkBool32 timestampComputeAndGraphics; + float timestampPeriod; + uint32_t maxClipDistances; + uint32_t maxCullDistances; + uint32_t maxCombinedClipAndCullDistances; + uint32_t discreteQueuePriorities; + float pointSizeRange [2]; + float lineWidthRange [2]; + float pointSizeGranularity; + float lineWidthGranularity; + VkBool32 strictLines; + VkBool32 standardSampleLocations; + VkDeviceSize optimalBufferCopyOffsetAlignment; + VkDeviceSize optimalBufferCopyRowPitchAlignment; + VkDeviceSize nonCoherentAtomSize; +} VkPhysicalDeviceLimits; + +typedef struct VkSemaphoreCreateInfo { + VkStructureType sType; + const void * pNext; + VkSemaphoreCreateFlags flags; +} VkSemaphoreCreateInfo; + +typedef struct VkQueryPoolCreateInfo { + VkStructureType sType; + const void * pNext; + VkQueryPoolCreateFlags flags; + VkQueryType queryType; + uint32_t queryCount; + VkQueryPipelineStatisticFlags pipelineStatistics; +} VkQueryPoolCreateInfo; + +typedef struct VkFramebufferCreateInfo { + VkStructureType sType; + const void * pNext; + VkFramebufferCreateFlags flags; + VkRenderPass renderPass; + uint32_t attachmentCount; + const VkImageView * pAttachments; + uint32_t width; + uint32_t height; + uint32_t layers; +} VkFramebufferCreateInfo; + +typedef struct VkSubmitInfo { + VkStructureType sType; + const void * pNext; + uint32_t waitSemaphoreCount; + const VkSemaphore * pWaitSemaphores; + const VkPipelineStageFlags * pWaitDstStageMask; + uint32_t commandBufferCount; + const VkCommandBuffer * pCommandBuffers; + uint32_t signalSemaphoreCount; + const VkSemaphore * pSignalSemaphores; +} VkSubmitInfo; + +typedef struct VkSurfaceCapabilitiesKHR { + uint32_t minImageCount; + uint32_t maxImageCount; + VkExtent2D currentExtent; + VkExtent2D minImageExtent; + VkExtent2D maxImageExtent; + uint32_t maxImageArrayLayers; + VkSurfaceTransformFlagsKHR supportedTransforms; + VkSurfaceTransformFlagBitsKHR currentTransform; + VkCompositeAlphaFlagsKHR supportedCompositeAlpha; + VkImageUsageFlags supportedUsageFlags; +} VkSurfaceCapabilitiesKHR; + +#if defined(VK_USE_PLATFORM_WIN32_KHR) +typedef struct VkWin32SurfaceCreateInfoKHR { + VkStructureType sType; + const void * pNext; + VkWin32SurfaceCreateFlagsKHR flags; + HINSTANCE hinstance; + HWND hwnd; +} VkWin32SurfaceCreateInfoKHR; + +#endif + +#if defined(VK_USE_PLATFORM_XCB_KHR) +typedef struct VkXcbSurfaceCreateInfoKHR { + VkStructureType sType; + const void * pNext; + VkXcbSurfaceCreateFlagsKHR flags; + xcb_connection_t * connection; + xcb_window_t window; +} VkXcbSurfaceCreateInfoKHR; + +#endif + +typedef struct VkSwapchainCreateInfoKHR { + VkStructureType sType; + const void * pNext; + VkSwapchainCreateFlagsKHR flags; + VkSurfaceKHR surface; + uint32_t minImageCount; + VkFormat imageFormat; + VkColorSpaceKHR imageColorSpace; + VkExtent2D imageExtent; + uint32_t imageArrayLayers; + VkImageUsageFlags imageUsage; + VkSharingMode imageSharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t * pQueueFamilyIndices; + VkSurfaceTransformFlagBitsKHR preTransform; + VkCompositeAlphaFlagBitsKHR compositeAlpha; + VkPresentModeKHR presentMode; + VkBool32 clipped; + VkSwapchainKHR oldSwapchain; +} VkSwapchainCreateInfoKHR; + +typedef struct VkDebugReportCallbackCreateInfoEXT { + VkStructureType sType; + const void * pNext; + VkDebugReportFlagsEXT flags; + PFN_vkDebugReportCallbackEXT pfnCallback; + void * pUserData; +} VkDebugReportCallbackCreateInfoEXT; + +typedef struct VkPhysicalDeviceFeatures2 { + VkStructureType sType; + void * pNext; + VkPhysicalDeviceFeatures features; +} VkPhysicalDeviceFeatures2; + +typedef struct VkPhysicalDeviceFeatures2 VkPhysicalDeviceFeatures2KHR; + +typedef struct VkFormatProperties2 { + VkStructureType sType; + void * pNext; + VkFormatProperties formatProperties; +} VkFormatProperties2; + +typedef struct VkFormatProperties2 VkFormatProperties2KHR; + +typedef struct VkImageFormatProperties2 { + VkStructureType sType; + void * pNext; + VkImageFormatProperties imageFormatProperties; +} VkImageFormatProperties2; + +typedef struct VkImageFormatProperties2 VkImageFormatProperties2KHR; + +typedef struct VkPhysicalDeviceImageFormatInfo2 { + VkStructureType sType; + const void * pNext; + VkFormat format; + VkImageType type; + VkImageTiling tiling; + VkImageUsageFlags usage; + VkImageCreateFlags flags; +} VkPhysicalDeviceImageFormatInfo2; + +typedef struct VkPhysicalDeviceImageFormatInfo2 VkPhysicalDeviceImageFormatInfo2KHR; + +typedef struct VkQueueFamilyProperties2 { + VkStructureType sType; + void * pNext; + VkQueueFamilyProperties queueFamilyProperties; +} VkQueueFamilyProperties2; + +typedef struct VkQueueFamilyProperties2 VkQueueFamilyProperties2KHR; + +typedef struct VkSparseImageFormatProperties2 { + VkStructureType sType; + void * pNext; + VkSparseImageFormatProperties properties; +} VkSparseImageFormatProperties2; + +typedef struct VkSparseImageFormatProperties2 VkSparseImageFormatProperties2KHR; + +typedef struct VkPhysicalDeviceSparseImageFormatInfo2 { + VkStructureType sType; + const void * pNext; + VkFormat format; + VkImageType type; + VkSampleCountFlagBits samples; + VkImageUsageFlags usage; + VkImageTiling tiling; +} VkPhysicalDeviceSparseImageFormatInfo2; + +typedef struct VkPhysicalDeviceSparseImageFormatInfo2 VkPhysicalDeviceSparseImageFormatInfo2KHR; + +typedef struct VkDeviceGroupPresentCapabilitiesKHR { + VkStructureType sType; + void * pNext; + uint32_t presentMask [ VK_MAX_DEVICE_GROUP_SIZE ]; + VkDeviceGroupPresentModeFlagsKHR modes; +} VkDeviceGroupPresentCapabilitiesKHR; + +typedef struct VkDeviceGroupSwapchainCreateInfoKHR { + VkStructureType sType; + const void * pNext; + VkDeviceGroupPresentModeFlagsKHR modes; +} VkDeviceGroupSwapchainCreateInfoKHR; + +typedef struct VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures { + VkStructureType sType; + void * pNext; + VkBool32 shaderSubgroupExtendedTypes; +} VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures; + +typedef struct VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR; + +typedef struct VkDebugUtilsMessengerCallbackDataEXT { + VkStructureType sType; + const void * pNext; + VkDebugUtilsMessengerCallbackDataFlagsEXT flags; + const char * pMessageIdName; + int32_t messageIdNumber; + const char * pMessage; + uint32_t queueLabelCount; + const VkDebugUtilsLabelEXT * pQueueLabels; + uint32_t cmdBufLabelCount; + const VkDebugUtilsLabelEXT * pCmdBufLabels; + uint32_t objectCount; + const VkDebugUtilsObjectNameInfoEXT * pObjects; +} VkDebugUtilsMessengerCallbackDataEXT; + +typedef struct VkPhysicalDeviceScalarBlockLayoutFeatures { + VkStructureType sType; + void * pNext; + VkBool32 scalarBlockLayout; +} VkPhysicalDeviceScalarBlockLayoutFeatures; + +typedef struct VkPhysicalDeviceScalarBlockLayoutFeatures VkPhysicalDeviceScalarBlockLayoutFeaturesEXT; + +typedef VkBool32 (VKAPI_PTR *PFN_vkDebugUtilsMessengerCallbackEXT)( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); +typedef struct VkPhysicalDeviceProperties { + uint32_t apiVersion; + uint32_t driverVersion; + uint32_t vendorID; + uint32_t deviceID; + VkPhysicalDeviceType deviceType; + char deviceName [ VK_MAX_PHYSICAL_DEVICE_NAME_SIZE ]; + uint8_t pipelineCacheUUID [ VK_UUID_SIZE ]; + VkPhysicalDeviceLimits limits; + VkPhysicalDeviceSparseProperties sparseProperties; +} VkPhysicalDeviceProperties; + +typedef struct VkDeviceCreateInfo { + VkStructureType sType; + const void * pNext; + VkDeviceCreateFlags flags; + uint32_t queueCreateInfoCount; + const VkDeviceQueueCreateInfo * pQueueCreateInfos; + uint32_t enabledLayerCount; + const char * const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char * const* ppEnabledExtensionNames; + const VkPhysicalDeviceFeatures * pEnabledFeatures; +} VkDeviceCreateInfo; + +typedef struct VkPhysicalDeviceMemoryProperties { + uint32_t memoryTypeCount; + VkMemoryType memoryTypes [ VK_MAX_MEMORY_TYPES ]; + uint32_t memoryHeapCount; + VkMemoryHeap memoryHeaps [ VK_MAX_MEMORY_HEAPS ]; +} VkPhysicalDeviceMemoryProperties; + +typedef struct VkPhysicalDeviceProperties2 { + VkStructureType sType; + void * pNext; + VkPhysicalDeviceProperties properties; +} VkPhysicalDeviceProperties2; + +typedef struct VkPhysicalDeviceProperties2 VkPhysicalDeviceProperties2KHR; + +typedef struct VkPhysicalDeviceMemoryProperties2 { + VkStructureType sType; + void * pNext; + VkPhysicalDeviceMemoryProperties memoryProperties; +} VkPhysicalDeviceMemoryProperties2; + +typedef struct VkPhysicalDeviceMemoryProperties2 VkPhysicalDeviceMemoryProperties2KHR; + +typedef struct VkDebugUtilsMessengerCreateInfoEXT { + VkStructureType sType; + const void * pNext; + VkDebugUtilsMessengerCreateFlagsEXT flags; + VkDebugUtilsMessageSeverityFlagsEXT messageSeverity; + VkDebugUtilsMessageTypeFlagsEXT messageType; + PFN_vkDebugUtilsMessengerCallbackEXT pfnUserCallback; + void * pUserData; +} VkDebugUtilsMessengerCreateInfoEXT; + + + +#define VK_VERSION_1_0 1 +GLAD_API_CALL int GLAD_VK_VERSION_1_0; +#define VK_EXT_debug_report 1 +GLAD_API_CALL int GLAD_VK_EXT_debug_report; +#define VK_EXT_debug_utils 1 +GLAD_API_CALL int GLAD_VK_EXT_debug_utils; +#define VK_EXT_scalar_block_layout 1 +GLAD_API_CALL int GLAD_VK_EXT_scalar_block_layout; +#define VK_EXT_validation_features 1 +GLAD_API_CALL int GLAD_VK_EXT_validation_features; +#define VK_KHR_get_physical_device_properties2 1 +GLAD_API_CALL int GLAD_VK_KHR_get_physical_device_properties2; +#define VK_KHR_portability_enumeration 1 +GLAD_API_CALL int GLAD_VK_KHR_portability_enumeration; +#define VK_KHR_shader_subgroup_extended_types 1 +GLAD_API_CALL int GLAD_VK_KHR_shader_subgroup_extended_types; +#define VK_KHR_surface 1 +GLAD_API_CALL int GLAD_VK_KHR_surface; +#define VK_KHR_swapchain 1 +GLAD_API_CALL int GLAD_VK_KHR_swapchain; +#if defined(VK_USE_PLATFORM_WIN32_KHR) +#define VK_KHR_win32_surface 1 +GLAD_API_CALL int GLAD_VK_KHR_win32_surface; + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +#define VK_KHR_xcb_surface 1 +GLAD_API_CALL int GLAD_VK_KHR_xcb_surface; + +#endif + + +typedef VkResult (GLAD_API_PTR *PFN_vkAcquireNextImage2KHR)(VkDevice device, const VkAcquireNextImageInfoKHR * pAcquireInfo, uint32_t * pImageIndex); +typedef VkResult (GLAD_API_PTR *PFN_vkAcquireNextImageKHR)(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t * pImageIndex); +typedef VkResult (GLAD_API_PTR *PFN_vkAllocateCommandBuffers)(VkDevice device, const VkCommandBufferAllocateInfo * pAllocateInfo, VkCommandBuffer * pCommandBuffers); +typedef VkResult (GLAD_API_PTR *PFN_vkAllocateDescriptorSets)(VkDevice device, const VkDescriptorSetAllocateInfo * pAllocateInfo, VkDescriptorSet * pDescriptorSets); +typedef VkResult (GLAD_API_PTR *PFN_vkAllocateMemory)(VkDevice device, const VkMemoryAllocateInfo * pAllocateInfo, const VkAllocationCallbacks * pAllocator, VkDeviceMemory * pMemory); +typedef VkResult (GLAD_API_PTR *PFN_vkBeginCommandBuffer)(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo * pBeginInfo); +typedef VkResult (GLAD_API_PTR *PFN_vkBindBufferMemory)(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset); +typedef VkResult (GLAD_API_PTR *PFN_vkBindImageMemory)(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset); +typedef void (GLAD_API_PTR *PFN_vkCmdBeginDebugUtilsLabelEXT)(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT * pLabelInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdBeginQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags); +typedef void (GLAD_API_PTR *PFN_vkCmdBeginRenderPass)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo * pRenderPassBegin, VkSubpassContents contents); +typedef void (GLAD_API_PTR *PFN_vkCmdBindDescriptorSets)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet * pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t * pDynamicOffsets); +typedef void (GLAD_API_PTR *PFN_vkCmdBindIndexBuffer)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType); +typedef void (GLAD_API_PTR *PFN_vkCmdBindPipeline)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline); +typedef void (GLAD_API_PTR *PFN_vkCmdBindVertexBuffers)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer * pBuffers, const VkDeviceSize * pOffsets); +typedef void (GLAD_API_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit * pRegions, VkFilter filter); +typedef void (GLAD_API_PTR *PFN_vkCmdClearAttachments)(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment * pAttachments, uint32_t rectCount, const VkClearRect * pRects); +typedef void (GLAD_API_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue * pColor, uint32_t rangeCount, const VkImageSubresourceRange * pRanges); +typedef void (GLAD_API_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue * pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange * pRanges); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyBuffer)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy * pRegions); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy * pRegions); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy * pRegions); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy * pRegions); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyQueryPoolResults)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags); +typedef void (GLAD_API_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); +typedef void (GLAD_API_PTR *PFN_vkCmdDispatchIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset); +typedef void (GLAD_API_PTR *PFN_vkCmdDraw)(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance); +typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndexed)(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance); +typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndexedIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); +typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); +typedef void (GLAD_API_PTR *PFN_vkCmdEndDebugUtilsLabelEXT)(VkCommandBuffer commandBuffer); +typedef void (GLAD_API_PTR *PFN_vkCmdEndQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query); +typedef void (GLAD_API_PTR *PFN_vkCmdEndRenderPass)(VkCommandBuffer commandBuffer); +typedef void (GLAD_API_PTR *PFN_vkCmdExecuteCommands)(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer * pCommandBuffers); +typedef void (GLAD_API_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data); +typedef void (GLAD_API_PTR *PFN_vkCmdInsertDebugUtilsLabelEXT)(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT * pLabelInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdNextSubpass)(VkCommandBuffer commandBuffer, VkSubpassContents contents); +typedef void (GLAD_API_PTR *PFN_vkCmdPipelineBarrier)(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier * pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier * pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier * pImageMemoryBarriers); +typedef void (GLAD_API_PTR *PFN_vkCmdPushConstants)(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void * pValues); +typedef void (GLAD_API_PTR *PFN_vkCmdResetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); +typedef void (GLAD_API_PTR *PFN_vkCmdResetQueryPool)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); +typedef void (GLAD_API_PTR *PFN_vkCmdResolveImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve * pRegions); +typedef void (GLAD_API_PTR *PFN_vkCmdSetBlendConstants)(VkCommandBuffer commandBuffer, const float blendConstants [4]); +typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthBias)(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor); +typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthBounds)(VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds); +typedef void (GLAD_API_PTR *PFN_vkCmdSetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); +typedef void (GLAD_API_PTR *PFN_vkCmdSetLineWidth)(VkCommandBuffer commandBuffer, float lineWidth); +typedef void (GLAD_API_PTR *PFN_vkCmdSetScissor)(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D * pScissors); +typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilCompareMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask); +typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilReference)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference); +typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilWriteMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t writeMask); +typedef void (GLAD_API_PTR *PFN_vkCmdSetViewport)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport * pViewports); +typedef void (GLAD_API_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void * pData); +typedef void (GLAD_API_PTR *PFN_vkCmdWaitEvents)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent * pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier * pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier * pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier * pImageMemoryBarriers); +typedef void (GLAD_API_PTR *PFN_vkCmdWriteTimestamp)(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateBuffer)(VkDevice device, const VkBufferCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkBuffer * pBuffer); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateBufferView)(VkDevice device, const VkBufferViewCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkBufferView * pView); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateCommandPool)(VkDevice device, const VkCommandPoolCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkCommandPool * pCommandPool); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateComputePipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo * pCreateInfos, const VkAllocationCallbacks * pAllocator, VkPipeline * pPipelines); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDebugReportCallbackEXT * pCallback); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateDebugUtilsMessengerEXT)(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDebugUtilsMessengerEXT * pMessenger); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateDescriptorPool)(VkDevice device, const VkDescriptorPoolCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDescriptorPool * pDescriptorPool); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateDescriptorSetLayout)(VkDevice device, const VkDescriptorSetLayoutCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDescriptorSetLayout * pSetLayout); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateDevice)(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDevice * pDevice); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateEvent)(VkDevice device, const VkEventCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkEvent * pEvent); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateFence)(VkDevice device, const VkFenceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkFence * pFence); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateFramebuffer)(VkDevice device, const VkFramebufferCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkFramebuffer * pFramebuffer); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateGraphicsPipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo * pCreateInfos, const VkAllocationCallbacks * pAllocator, VkPipeline * pPipelines); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateImage)(VkDevice device, const VkImageCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkImage * pImage); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateImageView)(VkDevice device, const VkImageViewCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkImageView * pView); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateInstance)(const VkInstanceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkInstance * pInstance); +typedef VkResult (GLAD_API_PTR *PFN_vkCreatePipelineCache)(VkDevice device, const VkPipelineCacheCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkPipelineCache * pPipelineCache); +typedef VkResult (GLAD_API_PTR *PFN_vkCreatePipelineLayout)(VkDevice device, const VkPipelineLayoutCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkPipelineLayout * pPipelineLayout); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateQueryPool)(VkDevice device, const VkQueryPoolCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkQueryPool * pQueryPool); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateRenderPass)(VkDevice device, const VkRenderPassCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkRenderPass * pRenderPass); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateSampler)(VkDevice device, const VkSamplerCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSampler * pSampler); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateSemaphore)(VkDevice device, const VkSemaphoreCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSemaphore * pSemaphore); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateShaderModule)(VkDevice device, const VkShaderModuleCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkShaderModule * pShaderModule); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateSwapchainKHR)(VkDevice device, const VkSwapchainCreateInfoKHR * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSwapchainKHR * pSwapchain); +#if defined(VK_USE_PLATFORM_WIN32_KHR) +typedef VkResult (GLAD_API_PTR *PFN_vkCreateWin32SurfaceKHR)(VkInstance instance, const VkWin32SurfaceCreateInfoKHR * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSurfaceKHR * pSurface); + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +typedef VkResult (GLAD_API_PTR *PFN_vkCreateXcbSurfaceKHR)(VkInstance instance, const VkXcbSurfaceCreateInfoKHR * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSurfaceKHR * pSurface); + +#endif +typedef void (GLAD_API_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char * pLayerPrefix, const char * pMessage); +typedef void (GLAD_API_PTR *PFN_vkDestroyBuffer)(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyBufferView)(VkDevice device, VkBufferView bufferView, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyCommandPool)(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyDebugUtilsMessengerEXT)(VkInstance instance, VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyDescriptorSetLayout)(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyDevice)(VkDevice device, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyEvent)(VkDevice device, VkEvent event, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyFramebuffer)(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyImage)(VkDevice device, VkImage image, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyImageView)(VkDevice device, VkImageView imageView, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyInstance)(VkInstance instance, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyPipeline)(VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyPipelineCache)(VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyPipelineLayout)(VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyQueryPool)(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyRenderPass)(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroySampler)(VkDevice device, VkSampler sampler, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroySemaphore)(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyShaderModule)(VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroySurfaceKHR)(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroySwapchainKHR)(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks * pAllocator); +typedef VkResult (GLAD_API_PTR *PFN_vkDeviceWaitIdle)(VkDevice device); +typedef VkResult (GLAD_API_PTR *PFN_vkEndCommandBuffer)(VkCommandBuffer commandBuffer); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateDeviceExtensionProperties)(VkPhysicalDevice physicalDevice, const char * pLayerName, uint32_t * pPropertyCount, VkExtensionProperties * pProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateDeviceLayerProperties)(VkPhysicalDevice physicalDevice, uint32_t * pPropertyCount, VkLayerProperties * pProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateInstanceExtensionProperties)(const char * pLayerName, uint32_t * pPropertyCount, VkExtensionProperties * pProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateInstanceLayerProperties)(uint32_t * pPropertyCount, VkLayerProperties * pProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumeratePhysicalDevices)(VkInstance instance, uint32_t * pPhysicalDeviceCount, VkPhysicalDevice * pPhysicalDevices); +typedef VkResult (GLAD_API_PTR *PFN_vkFlushMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange * pMemoryRanges); +typedef void (GLAD_API_PTR *PFN_vkFreeCommandBuffers)(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer * pCommandBuffers); +typedef VkResult (GLAD_API_PTR *PFN_vkFreeDescriptorSets)(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet * pDescriptorSets); +typedef void (GLAD_API_PTR *PFN_vkFreeMemory)(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkGetBufferMemoryRequirements)(VkDevice device, VkBuffer buffer, VkMemoryRequirements * pMemoryRequirements); +typedef VkResult (GLAD_API_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHR)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR * pDeviceGroupPresentCapabilities); +typedef VkResult (GLAD_API_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHR)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR * pModes); +typedef void (GLAD_API_PTR *PFN_vkGetDeviceMemoryCommitment)(VkDevice device, VkDeviceMemory memory, VkDeviceSize * pCommittedMemoryInBytes); +typedef PFN_vkVoidFunction (GLAD_API_PTR *PFN_vkGetDeviceProcAddr)(VkDevice device, const char * pName); +typedef void (GLAD_API_PTR *PFN_vkGetDeviceQueue)(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue * pQueue); +typedef VkResult (GLAD_API_PTR *PFN_vkGetEventStatus)(VkDevice device, VkEvent event); +typedef VkResult (GLAD_API_PTR *PFN_vkGetFenceStatus)(VkDevice device, VkFence fence); +typedef void (GLAD_API_PTR *PFN_vkGetImageMemoryRequirements)(VkDevice device, VkImage image, VkMemoryRequirements * pMemoryRequirements); +typedef void (GLAD_API_PTR *PFN_vkGetImageSparseMemoryRequirements)(VkDevice device, VkImage image, uint32_t * pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements * pSparseMemoryRequirements); +typedef void (GLAD_API_PTR *PFN_vkGetImageSubresourceLayout)(VkDevice device, VkImage image, const VkImageSubresource * pSubresource, VkSubresourceLayout * pLayout); +typedef PFN_vkVoidFunction (GLAD_API_PTR *PFN_vkGetInstanceProcAddr)(VkInstance instance, const char * pName); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFeatures)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures * pFeatures); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFeatures2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2 * pFeatures); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties * pFormatProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFormatProperties2KHR)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2 * pFormatProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties * pImageFormatProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2 * pImageFormatInfo, VkImageFormatProperties2 * pImageFormatProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceMemoryProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties * pMemoryProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2 * pMemoryProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t * pRectCount, VkRect2D * pRects); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties * pProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 * pProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties)(VkPhysicalDevice physicalDevice, uint32_t * pQueueFamilyPropertyCount, VkQueueFamilyProperties * pQueueFamilyProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t * pQueueFamilyPropertyCount, VkQueueFamilyProperties2 * pQueueFamilyProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t * pPropertyCount, VkSparseImageFormatProperties * pProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2 * pFormatInfo, uint32_t * pPropertyCount, VkSparseImageFormatProperties2 * pProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR * pSurfaceCapabilities); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t * pSurfaceFormatCount, VkSurfaceFormatKHR * pSurfaceFormats); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t * pPresentModeCount, VkPresentModeKHR * pPresentModes); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32 * pSupported); +#if defined(VK_USE_PLATFORM_WIN32_KHR) +typedef VkBool32 (GLAD_API_PTR *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex); + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +typedef VkBool32 (GLAD_API_PTR *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, xcb_connection_t * connection, xcb_visualid_t visual_id); + +#endif +typedef VkResult (GLAD_API_PTR *PFN_vkGetPipelineCacheData)(VkDevice device, VkPipelineCache pipelineCache, size_t * pDataSize, void * pData); +typedef VkResult (GLAD_API_PTR *PFN_vkGetQueryPoolResults)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void * pData, VkDeviceSize stride, VkQueryResultFlags flags); +typedef void (GLAD_API_PTR *PFN_vkGetRenderAreaGranularity)(VkDevice device, VkRenderPass renderPass, VkExtent2D * pGranularity); +typedef VkResult (GLAD_API_PTR *PFN_vkGetSwapchainImagesKHR)(VkDevice device, VkSwapchainKHR swapchain, uint32_t * pSwapchainImageCount, VkImage * pSwapchainImages); +typedef VkResult (GLAD_API_PTR *PFN_vkInvalidateMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange * pMemoryRanges); +typedef VkResult (GLAD_API_PTR *PFN_vkMapMemory)(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void ** ppData); +typedef VkResult (GLAD_API_PTR *PFN_vkMergePipelineCaches)(VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount, const VkPipelineCache * pSrcCaches); +typedef void (GLAD_API_PTR *PFN_vkQueueBeginDebugUtilsLabelEXT)(VkQueue queue, const VkDebugUtilsLabelEXT * pLabelInfo); +typedef VkResult (GLAD_API_PTR *PFN_vkQueueBindSparse)(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo * pBindInfo, VkFence fence); +typedef void (GLAD_API_PTR *PFN_vkQueueEndDebugUtilsLabelEXT)(VkQueue queue); +typedef void (GLAD_API_PTR *PFN_vkQueueInsertDebugUtilsLabelEXT)(VkQueue queue, const VkDebugUtilsLabelEXT * pLabelInfo); +typedef VkResult (GLAD_API_PTR *PFN_vkQueuePresentKHR)(VkQueue queue, const VkPresentInfoKHR * pPresentInfo); +typedef VkResult (GLAD_API_PTR *PFN_vkQueueSubmit)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo * pSubmits, VkFence fence); +typedef VkResult (GLAD_API_PTR *PFN_vkQueueWaitIdle)(VkQueue queue); +typedef VkResult (GLAD_API_PTR *PFN_vkResetCommandBuffer)(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags); +typedef VkResult (GLAD_API_PTR *PFN_vkResetCommandPool)(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags); +typedef VkResult (GLAD_API_PTR *PFN_vkResetDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags); +typedef VkResult (GLAD_API_PTR *PFN_vkResetEvent)(VkDevice device, VkEvent event); +typedef VkResult (GLAD_API_PTR *PFN_vkResetFences)(VkDevice device, uint32_t fenceCount, const VkFence * pFences); +typedef VkResult (GLAD_API_PTR *PFN_vkSetDebugUtilsObjectNameEXT)(VkDevice device, const VkDebugUtilsObjectNameInfoEXT * pNameInfo); +typedef VkResult (GLAD_API_PTR *PFN_vkSetDebugUtilsObjectTagEXT)(VkDevice device, const VkDebugUtilsObjectTagInfoEXT * pTagInfo); +typedef VkResult (GLAD_API_PTR *PFN_vkSetEvent)(VkDevice device, VkEvent event); +typedef void (GLAD_API_PTR *PFN_vkSubmitDebugUtilsMessageEXT)(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT * pCallbackData); +typedef void (GLAD_API_PTR *PFN_vkUnmapMemory)(VkDevice device, VkDeviceMemory memory); +typedef void (GLAD_API_PTR *PFN_vkUpdateDescriptorSets)(VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet * pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet * pDescriptorCopies); +typedef VkResult (GLAD_API_PTR *PFN_vkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence * pFences, VkBool32 waitAll, uint64_t timeout); + +GLAD_API_CALL PFN_vkAcquireNextImage2KHR glad_vkAcquireNextImage2KHR; +#define vkAcquireNextImage2KHR glad_vkAcquireNextImage2KHR +GLAD_API_CALL PFN_vkAcquireNextImageKHR glad_vkAcquireNextImageKHR; +#define vkAcquireNextImageKHR glad_vkAcquireNextImageKHR +GLAD_API_CALL PFN_vkAllocateCommandBuffers glad_vkAllocateCommandBuffers; +#define vkAllocateCommandBuffers glad_vkAllocateCommandBuffers +GLAD_API_CALL PFN_vkAllocateDescriptorSets glad_vkAllocateDescriptorSets; +#define vkAllocateDescriptorSets glad_vkAllocateDescriptorSets +GLAD_API_CALL PFN_vkAllocateMemory glad_vkAllocateMemory; +#define vkAllocateMemory glad_vkAllocateMemory +GLAD_API_CALL PFN_vkBeginCommandBuffer glad_vkBeginCommandBuffer; +#define vkBeginCommandBuffer glad_vkBeginCommandBuffer +GLAD_API_CALL PFN_vkBindBufferMemory glad_vkBindBufferMemory; +#define vkBindBufferMemory glad_vkBindBufferMemory +GLAD_API_CALL PFN_vkBindImageMemory glad_vkBindImageMemory; +#define vkBindImageMemory glad_vkBindImageMemory +GLAD_API_CALL PFN_vkCmdBeginDebugUtilsLabelEXT glad_vkCmdBeginDebugUtilsLabelEXT; +#define vkCmdBeginDebugUtilsLabelEXT glad_vkCmdBeginDebugUtilsLabelEXT +GLAD_API_CALL PFN_vkCmdBeginQuery glad_vkCmdBeginQuery; +#define vkCmdBeginQuery glad_vkCmdBeginQuery +GLAD_API_CALL PFN_vkCmdBeginRenderPass glad_vkCmdBeginRenderPass; +#define vkCmdBeginRenderPass glad_vkCmdBeginRenderPass +GLAD_API_CALL PFN_vkCmdBindDescriptorSets glad_vkCmdBindDescriptorSets; +#define vkCmdBindDescriptorSets glad_vkCmdBindDescriptorSets +GLAD_API_CALL PFN_vkCmdBindIndexBuffer glad_vkCmdBindIndexBuffer; +#define vkCmdBindIndexBuffer glad_vkCmdBindIndexBuffer +GLAD_API_CALL PFN_vkCmdBindPipeline glad_vkCmdBindPipeline; +#define vkCmdBindPipeline glad_vkCmdBindPipeline +GLAD_API_CALL PFN_vkCmdBindVertexBuffers glad_vkCmdBindVertexBuffers; +#define vkCmdBindVertexBuffers glad_vkCmdBindVertexBuffers +GLAD_API_CALL PFN_vkCmdBlitImage glad_vkCmdBlitImage; +#define vkCmdBlitImage glad_vkCmdBlitImage +GLAD_API_CALL PFN_vkCmdClearAttachments glad_vkCmdClearAttachments; +#define vkCmdClearAttachments glad_vkCmdClearAttachments +GLAD_API_CALL PFN_vkCmdClearColorImage glad_vkCmdClearColorImage; +#define vkCmdClearColorImage glad_vkCmdClearColorImage +GLAD_API_CALL PFN_vkCmdClearDepthStencilImage glad_vkCmdClearDepthStencilImage; +#define vkCmdClearDepthStencilImage glad_vkCmdClearDepthStencilImage +GLAD_API_CALL PFN_vkCmdCopyBuffer glad_vkCmdCopyBuffer; +#define vkCmdCopyBuffer glad_vkCmdCopyBuffer +GLAD_API_CALL PFN_vkCmdCopyBufferToImage glad_vkCmdCopyBufferToImage; +#define vkCmdCopyBufferToImage glad_vkCmdCopyBufferToImage +GLAD_API_CALL PFN_vkCmdCopyImage glad_vkCmdCopyImage; +#define vkCmdCopyImage glad_vkCmdCopyImage +GLAD_API_CALL PFN_vkCmdCopyImageToBuffer glad_vkCmdCopyImageToBuffer; +#define vkCmdCopyImageToBuffer glad_vkCmdCopyImageToBuffer +GLAD_API_CALL PFN_vkCmdCopyQueryPoolResults glad_vkCmdCopyQueryPoolResults; +#define vkCmdCopyQueryPoolResults glad_vkCmdCopyQueryPoolResults +GLAD_API_CALL PFN_vkCmdDispatch glad_vkCmdDispatch; +#define vkCmdDispatch glad_vkCmdDispatch +GLAD_API_CALL PFN_vkCmdDispatchIndirect glad_vkCmdDispatchIndirect; +#define vkCmdDispatchIndirect glad_vkCmdDispatchIndirect +GLAD_API_CALL PFN_vkCmdDraw glad_vkCmdDraw; +#define vkCmdDraw glad_vkCmdDraw +GLAD_API_CALL PFN_vkCmdDrawIndexed glad_vkCmdDrawIndexed; +#define vkCmdDrawIndexed glad_vkCmdDrawIndexed +GLAD_API_CALL PFN_vkCmdDrawIndexedIndirect glad_vkCmdDrawIndexedIndirect; +#define vkCmdDrawIndexedIndirect glad_vkCmdDrawIndexedIndirect +GLAD_API_CALL PFN_vkCmdDrawIndirect glad_vkCmdDrawIndirect; +#define vkCmdDrawIndirect glad_vkCmdDrawIndirect +GLAD_API_CALL PFN_vkCmdEndDebugUtilsLabelEXT glad_vkCmdEndDebugUtilsLabelEXT; +#define vkCmdEndDebugUtilsLabelEXT glad_vkCmdEndDebugUtilsLabelEXT +GLAD_API_CALL PFN_vkCmdEndQuery glad_vkCmdEndQuery; +#define vkCmdEndQuery glad_vkCmdEndQuery +GLAD_API_CALL PFN_vkCmdEndRenderPass glad_vkCmdEndRenderPass; +#define vkCmdEndRenderPass glad_vkCmdEndRenderPass +GLAD_API_CALL PFN_vkCmdExecuteCommands glad_vkCmdExecuteCommands; +#define vkCmdExecuteCommands glad_vkCmdExecuteCommands +GLAD_API_CALL PFN_vkCmdFillBuffer glad_vkCmdFillBuffer; +#define vkCmdFillBuffer glad_vkCmdFillBuffer +GLAD_API_CALL PFN_vkCmdInsertDebugUtilsLabelEXT glad_vkCmdInsertDebugUtilsLabelEXT; +#define vkCmdInsertDebugUtilsLabelEXT glad_vkCmdInsertDebugUtilsLabelEXT +GLAD_API_CALL PFN_vkCmdNextSubpass glad_vkCmdNextSubpass; +#define vkCmdNextSubpass glad_vkCmdNextSubpass +GLAD_API_CALL PFN_vkCmdPipelineBarrier glad_vkCmdPipelineBarrier; +#define vkCmdPipelineBarrier glad_vkCmdPipelineBarrier +GLAD_API_CALL PFN_vkCmdPushConstants glad_vkCmdPushConstants; +#define vkCmdPushConstants glad_vkCmdPushConstants +GLAD_API_CALL PFN_vkCmdResetEvent glad_vkCmdResetEvent; +#define vkCmdResetEvent glad_vkCmdResetEvent +GLAD_API_CALL PFN_vkCmdResetQueryPool glad_vkCmdResetQueryPool; +#define vkCmdResetQueryPool glad_vkCmdResetQueryPool +GLAD_API_CALL PFN_vkCmdResolveImage glad_vkCmdResolveImage; +#define vkCmdResolveImage glad_vkCmdResolveImage +GLAD_API_CALL PFN_vkCmdSetBlendConstants glad_vkCmdSetBlendConstants; +#define vkCmdSetBlendConstants glad_vkCmdSetBlendConstants +GLAD_API_CALL PFN_vkCmdSetDepthBias glad_vkCmdSetDepthBias; +#define vkCmdSetDepthBias glad_vkCmdSetDepthBias +GLAD_API_CALL PFN_vkCmdSetDepthBounds glad_vkCmdSetDepthBounds; +#define vkCmdSetDepthBounds glad_vkCmdSetDepthBounds +GLAD_API_CALL PFN_vkCmdSetEvent glad_vkCmdSetEvent; +#define vkCmdSetEvent glad_vkCmdSetEvent +GLAD_API_CALL PFN_vkCmdSetLineWidth glad_vkCmdSetLineWidth; +#define vkCmdSetLineWidth glad_vkCmdSetLineWidth +GLAD_API_CALL PFN_vkCmdSetScissor glad_vkCmdSetScissor; +#define vkCmdSetScissor glad_vkCmdSetScissor +GLAD_API_CALL PFN_vkCmdSetStencilCompareMask glad_vkCmdSetStencilCompareMask; +#define vkCmdSetStencilCompareMask glad_vkCmdSetStencilCompareMask +GLAD_API_CALL PFN_vkCmdSetStencilReference glad_vkCmdSetStencilReference; +#define vkCmdSetStencilReference glad_vkCmdSetStencilReference +GLAD_API_CALL PFN_vkCmdSetStencilWriteMask glad_vkCmdSetStencilWriteMask; +#define vkCmdSetStencilWriteMask glad_vkCmdSetStencilWriteMask +GLAD_API_CALL PFN_vkCmdSetViewport glad_vkCmdSetViewport; +#define vkCmdSetViewport glad_vkCmdSetViewport +GLAD_API_CALL PFN_vkCmdUpdateBuffer glad_vkCmdUpdateBuffer; +#define vkCmdUpdateBuffer glad_vkCmdUpdateBuffer +GLAD_API_CALL PFN_vkCmdWaitEvents glad_vkCmdWaitEvents; +#define vkCmdWaitEvents glad_vkCmdWaitEvents +GLAD_API_CALL PFN_vkCmdWriteTimestamp glad_vkCmdWriteTimestamp; +#define vkCmdWriteTimestamp glad_vkCmdWriteTimestamp +GLAD_API_CALL PFN_vkCreateBuffer glad_vkCreateBuffer; +#define vkCreateBuffer glad_vkCreateBuffer +GLAD_API_CALL PFN_vkCreateBufferView glad_vkCreateBufferView; +#define vkCreateBufferView glad_vkCreateBufferView +GLAD_API_CALL PFN_vkCreateCommandPool glad_vkCreateCommandPool; +#define vkCreateCommandPool glad_vkCreateCommandPool +GLAD_API_CALL PFN_vkCreateComputePipelines glad_vkCreateComputePipelines; +#define vkCreateComputePipelines glad_vkCreateComputePipelines +GLAD_API_CALL PFN_vkCreateDebugReportCallbackEXT glad_vkCreateDebugReportCallbackEXT; +#define vkCreateDebugReportCallbackEXT glad_vkCreateDebugReportCallbackEXT +GLAD_API_CALL PFN_vkCreateDebugUtilsMessengerEXT glad_vkCreateDebugUtilsMessengerEXT; +#define vkCreateDebugUtilsMessengerEXT glad_vkCreateDebugUtilsMessengerEXT +GLAD_API_CALL PFN_vkCreateDescriptorPool glad_vkCreateDescriptorPool; +#define vkCreateDescriptorPool glad_vkCreateDescriptorPool +GLAD_API_CALL PFN_vkCreateDescriptorSetLayout glad_vkCreateDescriptorSetLayout; +#define vkCreateDescriptorSetLayout glad_vkCreateDescriptorSetLayout +GLAD_API_CALL PFN_vkCreateDevice glad_vkCreateDevice; +#define vkCreateDevice glad_vkCreateDevice +GLAD_API_CALL PFN_vkCreateEvent glad_vkCreateEvent; +#define vkCreateEvent glad_vkCreateEvent +GLAD_API_CALL PFN_vkCreateFence glad_vkCreateFence; +#define vkCreateFence glad_vkCreateFence +GLAD_API_CALL PFN_vkCreateFramebuffer glad_vkCreateFramebuffer; +#define vkCreateFramebuffer glad_vkCreateFramebuffer +GLAD_API_CALL PFN_vkCreateGraphicsPipelines glad_vkCreateGraphicsPipelines; +#define vkCreateGraphicsPipelines glad_vkCreateGraphicsPipelines +GLAD_API_CALL PFN_vkCreateImage glad_vkCreateImage; +#define vkCreateImage glad_vkCreateImage +GLAD_API_CALL PFN_vkCreateImageView glad_vkCreateImageView; +#define vkCreateImageView glad_vkCreateImageView +GLAD_API_CALL PFN_vkCreateInstance glad_vkCreateInstance; +#define vkCreateInstance glad_vkCreateInstance +GLAD_API_CALL PFN_vkCreatePipelineCache glad_vkCreatePipelineCache; +#define vkCreatePipelineCache glad_vkCreatePipelineCache +GLAD_API_CALL PFN_vkCreatePipelineLayout glad_vkCreatePipelineLayout; +#define vkCreatePipelineLayout glad_vkCreatePipelineLayout +GLAD_API_CALL PFN_vkCreateQueryPool glad_vkCreateQueryPool; +#define vkCreateQueryPool glad_vkCreateQueryPool +GLAD_API_CALL PFN_vkCreateRenderPass glad_vkCreateRenderPass; +#define vkCreateRenderPass glad_vkCreateRenderPass +GLAD_API_CALL PFN_vkCreateSampler glad_vkCreateSampler; +#define vkCreateSampler glad_vkCreateSampler +GLAD_API_CALL PFN_vkCreateSemaphore glad_vkCreateSemaphore; +#define vkCreateSemaphore glad_vkCreateSemaphore +GLAD_API_CALL PFN_vkCreateShaderModule glad_vkCreateShaderModule; +#define vkCreateShaderModule glad_vkCreateShaderModule +GLAD_API_CALL PFN_vkCreateSwapchainKHR glad_vkCreateSwapchainKHR; +#define vkCreateSwapchainKHR glad_vkCreateSwapchainKHR +#if defined(VK_USE_PLATFORM_WIN32_KHR) +GLAD_API_CALL PFN_vkCreateWin32SurfaceKHR glad_vkCreateWin32SurfaceKHR; +#define vkCreateWin32SurfaceKHR glad_vkCreateWin32SurfaceKHR + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +GLAD_API_CALL PFN_vkCreateXcbSurfaceKHR glad_vkCreateXcbSurfaceKHR; +#define vkCreateXcbSurfaceKHR glad_vkCreateXcbSurfaceKHR + +#endif +GLAD_API_CALL PFN_vkDebugReportMessageEXT glad_vkDebugReportMessageEXT; +#define vkDebugReportMessageEXT glad_vkDebugReportMessageEXT +GLAD_API_CALL PFN_vkDestroyBuffer glad_vkDestroyBuffer; +#define vkDestroyBuffer glad_vkDestroyBuffer +GLAD_API_CALL PFN_vkDestroyBufferView glad_vkDestroyBufferView; +#define vkDestroyBufferView glad_vkDestroyBufferView +GLAD_API_CALL PFN_vkDestroyCommandPool glad_vkDestroyCommandPool; +#define vkDestroyCommandPool glad_vkDestroyCommandPool +GLAD_API_CALL PFN_vkDestroyDebugReportCallbackEXT glad_vkDestroyDebugReportCallbackEXT; +#define vkDestroyDebugReportCallbackEXT glad_vkDestroyDebugReportCallbackEXT +GLAD_API_CALL PFN_vkDestroyDebugUtilsMessengerEXT glad_vkDestroyDebugUtilsMessengerEXT; +#define vkDestroyDebugUtilsMessengerEXT glad_vkDestroyDebugUtilsMessengerEXT +GLAD_API_CALL PFN_vkDestroyDescriptorPool glad_vkDestroyDescriptorPool; +#define vkDestroyDescriptorPool glad_vkDestroyDescriptorPool +GLAD_API_CALL PFN_vkDestroyDescriptorSetLayout glad_vkDestroyDescriptorSetLayout; +#define vkDestroyDescriptorSetLayout glad_vkDestroyDescriptorSetLayout +GLAD_API_CALL PFN_vkDestroyDevice glad_vkDestroyDevice; +#define vkDestroyDevice glad_vkDestroyDevice +GLAD_API_CALL PFN_vkDestroyEvent glad_vkDestroyEvent; +#define vkDestroyEvent glad_vkDestroyEvent +GLAD_API_CALL PFN_vkDestroyFence glad_vkDestroyFence; +#define vkDestroyFence glad_vkDestroyFence +GLAD_API_CALL PFN_vkDestroyFramebuffer glad_vkDestroyFramebuffer; +#define vkDestroyFramebuffer glad_vkDestroyFramebuffer +GLAD_API_CALL PFN_vkDestroyImage glad_vkDestroyImage; +#define vkDestroyImage glad_vkDestroyImage +GLAD_API_CALL PFN_vkDestroyImageView glad_vkDestroyImageView; +#define vkDestroyImageView glad_vkDestroyImageView +GLAD_API_CALL PFN_vkDestroyInstance glad_vkDestroyInstance; +#define vkDestroyInstance glad_vkDestroyInstance +GLAD_API_CALL PFN_vkDestroyPipeline glad_vkDestroyPipeline; +#define vkDestroyPipeline glad_vkDestroyPipeline +GLAD_API_CALL PFN_vkDestroyPipelineCache glad_vkDestroyPipelineCache; +#define vkDestroyPipelineCache glad_vkDestroyPipelineCache +GLAD_API_CALL PFN_vkDestroyPipelineLayout glad_vkDestroyPipelineLayout; +#define vkDestroyPipelineLayout glad_vkDestroyPipelineLayout +GLAD_API_CALL PFN_vkDestroyQueryPool glad_vkDestroyQueryPool; +#define vkDestroyQueryPool glad_vkDestroyQueryPool +GLAD_API_CALL PFN_vkDestroyRenderPass glad_vkDestroyRenderPass; +#define vkDestroyRenderPass glad_vkDestroyRenderPass +GLAD_API_CALL PFN_vkDestroySampler glad_vkDestroySampler; +#define vkDestroySampler glad_vkDestroySampler +GLAD_API_CALL PFN_vkDestroySemaphore glad_vkDestroySemaphore; +#define vkDestroySemaphore glad_vkDestroySemaphore +GLAD_API_CALL PFN_vkDestroyShaderModule glad_vkDestroyShaderModule; +#define vkDestroyShaderModule glad_vkDestroyShaderModule +GLAD_API_CALL PFN_vkDestroySurfaceKHR glad_vkDestroySurfaceKHR; +#define vkDestroySurfaceKHR glad_vkDestroySurfaceKHR +GLAD_API_CALL PFN_vkDestroySwapchainKHR glad_vkDestroySwapchainKHR; +#define vkDestroySwapchainKHR glad_vkDestroySwapchainKHR +GLAD_API_CALL PFN_vkDeviceWaitIdle glad_vkDeviceWaitIdle; +#define vkDeviceWaitIdle glad_vkDeviceWaitIdle +GLAD_API_CALL PFN_vkEndCommandBuffer glad_vkEndCommandBuffer; +#define vkEndCommandBuffer glad_vkEndCommandBuffer +GLAD_API_CALL PFN_vkEnumerateDeviceExtensionProperties glad_vkEnumerateDeviceExtensionProperties; +#define vkEnumerateDeviceExtensionProperties glad_vkEnumerateDeviceExtensionProperties +GLAD_API_CALL PFN_vkEnumerateDeviceLayerProperties glad_vkEnumerateDeviceLayerProperties; +#define vkEnumerateDeviceLayerProperties glad_vkEnumerateDeviceLayerProperties +GLAD_API_CALL PFN_vkEnumerateInstanceExtensionProperties glad_vkEnumerateInstanceExtensionProperties; +#define vkEnumerateInstanceExtensionProperties glad_vkEnumerateInstanceExtensionProperties +GLAD_API_CALL PFN_vkEnumerateInstanceLayerProperties glad_vkEnumerateInstanceLayerProperties; +#define vkEnumerateInstanceLayerProperties glad_vkEnumerateInstanceLayerProperties +GLAD_API_CALL PFN_vkEnumeratePhysicalDevices glad_vkEnumeratePhysicalDevices; +#define vkEnumeratePhysicalDevices glad_vkEnumeratePhysicalDevices +GLAD_API_CALL PFN_vkFlushMappedMemoryRanges glad_vkFlushMappedMemoryRanges; +#define vkFlushMappedMemoryRanges glad_vkFlushMappedMemoryRanges +GLAD_API_CALL PFN_vkFreeCommandBuffers glad_vkFreeCommandBuffers; +#define vkFreeCommandBuffers glad_vkFreeCommandBuffers +GLAD_API_CALL PFN_vkFreeDescriptorSets glad_vkFreeDescriptorSets; +#define vkFreeDescriptorSets glad_vkFreeDescriptorSets +GLAD_API_CALL PFN_vkFreeMemory glad_vkFreeMemory; +#define vkFreeMemory glad_vkFreeMemory +GLAD_API_CALL PFN_vkGetBufferMemoryRequirements glad_vkGetBufferMemoryRequirements; +#define vkGetBufferMemoryRequirements glad_vkGetBufferMemoryRequirements +GLAD_API_CALL PFN_vkGetDeviceGroupPresentCapabilitiesKHR glad_vkGetDeviceGroupPresentCapabilitiesKHR; +#define vkGetDeviceGroupPresentCapabilitiesKHR glad_vkGetDeviceGroupPresentCapabilitiesKHR +GLAD_API_CALL PFN_vkGetDeviceGroupSurfacePresentModesKHR glad_vkGetDeviceGroupSurfacePresentModesKHR; +#define vkGetDeviceGroupSurfacePresentModesKHR glad_vkGetDeviceGroupSurfacePresentModesKHR +GLAD_API_CALL PFN_vkGetDeviceMemoryCommitment glad_vkGetDeviceMemoryCommitment; +#define vkGetDeviceMemoryCommitment glad_vkGetDeviceMemoryCommitment +GLAD_API_CALL PFN_vkGetDeviceProcAddr glad_vkGetDeviceProcAddr; +#define vkGetDeviceProcAddr glad_vkGetDeviceProcAddr +GLAD_API_CALL PFN_vkGetDeviceQueue glad_vkGetDeviceQueue; +#define vkGetDeviceQueue glad_vkGetDeviceQueue +GLAD_API_CALL PFN_vkGetEventStatus glad_vkGetEventStatus; +#define vkGetEventStatus glad_vkGetEventStatus +GLAD_API_CALL PFN_vkGetFenceStatus glad_vkGetFenceStatus; +#define vkGetFenceStatus glad_vkGetFenceStatus +GLAD_API_CALL PFN_vkGetImageMemoryRequirements glad_vkGetImageMemoryRequirements; +#define vkGetImageMemoryRequirements glad_vkGetImageMemoryRequirements +GLAD_API_CALL PFN_vkGetImageSparseMemoryRequirements glad_vkGetImageSparseMemoryRequirements; +#define vkGetImageSparseMemoryRequirements glad_vkGetImageSparseMemoryRequirements +GLAD_API_CALL PFN_vkGetImageSubresourceLayout glad_vkGetImageSubresourceLayout; +#define vkGetImageSubresourceLayout glad_vkGetImageSubresourceLayout +GLAD_API_CALL PFN_vkGetInstanceProcAddr glad_vkGetInstanceProcAddr; +#define vkGetInstanceProcAddr glad_vkGetInstanceProcAddr +GLAD_API_CALL PFN_vkGetPhysicalDeviceFeatures glad_vkGetPhysicalDeviceFeatures; +#define vkGetPhysicalDeviceFeatures glad_vkGetPhysicalDeviceFeatures +GLAD_API_CALL PFN_vkGetPhysicalDeviceFeatures2KHR glad_vkGetPhysicalDeviceFeatures2KHR; +#define vkGetPhysicalDeviceFeatures2KHR glad_vkGetPhysicalDeviceFeatures2KHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceFormatProperties glad_vkGetPhysicalDeviceFormatProperties; +#define vkGetPhysicalDeviceFormatProperties glad_vkGetPhysicalDeviceFormatProperties +GLAD_API_CALL PFN_vkGetPhysicalDeviceFormatProperties2KHR glad_vkGetPhysicalDeviceFormatProperties2KHR; +#define vkGetPhysicalDeviceFormatProperties2KHR glad_vkGetPhysicalDeviceFormatProperties2KHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceImageFormatProperties glad_vkGetPhysicalDeviceImageFormatProperties; +#define vkGetPhysicalDeviceImageFormatProperties glad_vkGetPhysicalDeviceImageFormatProperties +GLAD_API_CALL PFN_vkGetPhysicalDeviceImageFormatProperties2KHR glad_vkGetPhysicalDeviceImageFormatProperties2KHR; +#define vkGetPhysicalDeviceImageFormatProperties2KHR glad_vkGetPhysicalDeviceImageFormatProperties2KHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceMemoryProperties glad_vkGetPhysicalDeviceMemoryProperties; +#define vkGetPhysicalDeviceMemoryProperties glad_vkGetPhysicalDeviceMemoryProperties +GLAD_API_CALL PFN_vkGetPhysicalDeviceMemoryProperties2KHR glad_vkGetPhysicalDeviceMemoryProperties2KHR; +#define vkGetPhysicalDeviceMemoryProperties2KHR glad_vkGetPhysicalDeviceMemoryProperties2KHR +GLAD_API_CALL PFN_vkGetPhysicalDevicePresentRectanglesKHR glad_vkGetPhysicalDevicePresentRectanglesKHR; +#define vkGetPhysicalDevicePresentRectanglesKHR glad_vkGetPhysicalDevicePresentRectanglesKHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceProperties glad_vkGetPhysicalDeviceProperties; +#define vkGetPhysicalDeviceProperties glad_vkGetPhysicalDeviceProperties +GLAD_API_CALL PFN_vkGetPhysicalDeviceProperties2KHR glad_vkGetPhysicalDeviceProperties2KHR; +#define vkGetPhysicalDeviceProperties2KHR glad_vkGetPhysicalDeviceProperties2KHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceQueueFamilyProperties glad_vkGetPhysicalDeviceQueueFamilyProperties; +#define vkGetPhysicalDeviceQueueFamilyProperties glad_vkGetPhysicalDeviceQueueFamilyProperties +GLAD_API_CALL PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR glad_vkGetPhysicalDeviceQueueFamilyProperties2KHR; +#define vkGetPhysicalDeviceQueueFamilyProperties2KHR glad_vkGetPhysicalDeviceQueueFamilyProperties2KHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceSparseImageFormatProperties glad_vkGetPhysicalDeviceSparseImageFormatProperties; +#define vkGetPhysicalDeviceSparseImageFormatProperties glad_vkGetPhysicalDeviceSparseImageFormatProperties +GLAD_API_CALL PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR glad_vkGetPhysicalDeviceSparseImageFormatProperties2KHR; +#define vkGetPhysicalDeviceSparseImageFormatProperties2KHR glad_vkGetPhysicalDeviceSparseImageFormatProperties2KHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR glad_vkGetPhysicalDeviceSurfaceCapabilitiesKHR; +#define vkGetPhysicalDeviceSurfaceCapabilitiesKHR glad_vkGetPhysicalDeviceSurfaceCapabilitiesKHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceSurfaceFormatsKHR glad_vkGetPhysicalDeviceSurfaceFormatsKHR; +#define vkGetPhysicalDeviceSurfaceFormatsKHR glad_vkGetPhysicalDeviceSurfaceFormatsKHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceSurfacePresentModesKHR glad_vkGetPhysicalDeviceSurfacePresentModesKHR; +#define vkGetPhysicalDeviceSurfacePresentModesKHR glad_vkGetPhysicalDeviceSurfacePresentModesKHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceSurfaceSupportKHR glad_vkGetPhysicalDeviceSurfaceSupportKHR; +#define vkGetPhysicalDeviceSurfaceSupportKHR glad_vkGetPhysicalDeviceSurfaceSupportKHR +#if defined(VK_USE_PLATFORM_WIN32_KHR) +GLAD_API_CALL PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR glad_vkGetPhysicalDeviceWin32PresentationSupportKHR; +#define vkGetPhysicalDeviceWin32PresentationSupportKHR glad_vkGetPhysicalDeviceWin32PresentationSupportKHR + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +GLAD_API_CALL PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR glad_vkGetPhysicalDeviceXcbPresentationSupportKHR; +#define vkGetPhysicalDeviceXcbPresentationSupportKHR glad_vkGetPhysicalDeviceXcbPresentationSupportKHR + +#endif +GLAD_API_CALL PFN_vkGetPipelineCacheData glad_vkGetPipelineCacheData; +#define vkGetPipelineCacheData glad_vkGetPipelineCacheData +GLAD_API_CALL PFN_vkGetQueryPoolResults glad_vkGetQueryPoolResults; +#define vkGetQueryPoolResults glad_vkGetQueryPoolResults +GLAD_API_CALL PFN_vkGetRenderAreaGranularity glad_vkGetRenderAreaGranularity; +#define vkGetRenderAreaGranularity glad_vkGetRenderAreaGranularity +GLAD_API_CALL PFN_vkGetSwapchainImagesKHR glad_vkGetSwapchainImagesKHR; +#define vkGetSwapchainImagesKHR glad_vkGetSwapchainImagesKHR +GLAD_API_CALL PFN_vkInvalidateMappedMemoryRanges glad_vkInvalidateMappedMemoryRanges; +#define vkInvalidateMappedMemoryRanges glad_vkInvalidateMappedMemoryRanges +GLAD_API_CALL PFN_vkMapMemory glad_vkMapMemory; +#define vkMapMemory glad_vkMapMemory +GLAD_API_CALL PFN_vkMergePipelineCaches glad_vkMergePipelineCaches; +#define vkMergePipelineCaches glad_vkMergePipelineCaches +GLAD_API_CALL PFN_vkQueueBeginDebugUtilsLabelEXT glad_vkQueueBeginDebugUtilsLabelEXT; +#define vkQueueBeginDebugUtilsLabelEXT glad_vkQueueBeginDebugUtilsLabelEXT +GLAD_API_CALL PFN_vkQueueBindSparse glad_vkQueueBindSparse; +#define vkQueueBindSparse glad_vkQueueBindSparse +GLAD_API_CALL PFN_vkQueueEndDebugUtilsLabelEXT glad_vkQueueEndDebugUtilsLabelEXT; +#define vkQueueEndDebugUtilsLabelEXT glad_vkQueueEndDebugUtilsLabelEXT +GLAD_API_CALL PFN_vkQueueInsertDebugUtilsLabelEXT glad_vkQueueInsertDebugUtilsLabelEXT; +#define vkQueueInsertDebugUtilsLabelEXT glad_vkQueueInsertDebugUtilsLabelEXT +GLAD_API_CALL PFN_vkQueuePresentKHR glad_vkQueuePresentKHR; +#define vkQueuePresentKHR glad_vkQueuePresentKHR +GLAD_API_CALL PFN_vkQueueSubmit glad_vkQueueSubmit; +#define vkQueueSubmit glad_vkQueueSubmit +GLAD_API_CALL PFN_vkQueueWaitIdle glad_vkQueueWaitIdle; +#define vkQueueWaitIdle glad_vkQueueWaitIdle +GLAD_API_CALL PFN_vkResetCommandBuffer glad_vkResetCommandBuffer; +#define vkResetCommandBuffer glad_vkResetCommandBuffer +GLAD_API_CALL PFN_vkResetCommandPool glad_vkResetCommandPool; +#define vkResetCommandPool glad_vkResetCommandPool +GLAD_API_CALL PFN_vkResetDescriptorPool glad_vkResetDescriptorPool; +#define vkResetDescriptorPool glad_vkResetDescriptorPool +GLAD_API_CALL PFN_vkResetEvent glad_vkResetEvent; +#define vkResetEvent glad_vkResetEvent +GLAD_API_CALL PFN_vkResetFences glad_vkResetFences; +#define vkResetFences glad_vkResetFences +GLAD_API_CALL PFN_vkSetDebugUtilsObjectNameEXT glad_vkSetDebugUtilsObjectNameEXT; +#define vkSetDebugUtilsObjectNameEXT glad_vkSetDebugUtilsObjectNameEXT +GLAD_API_CALL PFN_vkSetDebugUtilsObjectTagEXT glad_vkSetDebugUtilsObjectTagEXT; +#define vkSetDebugUtilsObjectTagEXT glad_vkSetDebugUtilsObjectTagEXT +GLAD_API_CALL PFN_vkSetEvent glad_vkSetEvent; +#define vkSetEvent glad_vkSetEvent +GLAD_API_CALL PFN_vkSubmitDebugUtilsMessageEXT glad_vkSubmitDebugUtilsMessageEXT; +#define vkSubmitDebugUtilsMessageEXT glad_vkSubmitDebugUtilsMessageEXT +GLAD_API_CALL PFN_vkUnmapMemory glad_vkUnmapMemory; +#define vkUnmapMemory glad_vkUnmapMemory +GLAD_API_CALL PFN_vkUpdateDescriptorSets glad_vkUpdateDescriptorSets; +#define vkUpdateDescriptorSets glad_vkUpdateDescriptorSets +GLAD_API_CALL PFN_vkWaitForFences glad_vkWaitForFences; +#define vkWaitForFences glad_vkWaitForFences + + + + + +GLAD_API_CALL int gladLoadVulkanUserPtr( VkPhysicalDevice physical_device, GLADuserptrloadfunc load, void *userptr); +GLAD_API_CALL int gladLoadVulkan( VkPhysicalDevice physical_device, GLADloadfunc load); + + +#ifdef GLAD_VULKAN + +GLAD_API_CALL int gladLoaderLoadVulkan( VkInstance instance, VkPhysicalDevice physical_device, VkDevice device); +GLAD_API_CALL void gladLoaderUnloadVulkan(void); + + + +#endif + +#ifdef __cplusplus +} +#endif +#endif + +/* Source */ +#ifdef GLAD_VULKAN_IMPLEMENTATION +#include +#include +#include + +#ifndef GLAD_IMPL_UTIL_C_ +#define GLAD_IMPL_UTIL_C_ + +#ifdef _MSC_VER +#define GLAD_IMPL_UTIL_SSCANF sscanf_s +#else +#define GLAD_IMPL_UTIL_SSCANF sscanf +#endif + +#endif /* GLAD_IMPL_UTIL_C_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +int GLAD_VK_VERSION_1_0 = 0; +int GLAD_VK_EXT_debug_report = 0; +int GLAD_VK_EXT_debug_utils = 0; +int GLAD_VK_EXT_scalar_block_layout = 0; +int GLAD_VK_EXT_validation_features = 0; +int GLAD_VK_KHR_get_physical_device_properties2 = 0; +int GLAD_VK_KHR_portability_enumeration = 0; +int GLAD_VK_KHR_shader_subgroup_extended_types = 0; +int GLAD_VK_KHR_surface = 0; +int GLAD_VK_KHR_swapchain = 0; +#if defined(VK_USE_PLATFORM_WIN32_KHR) +int GLAD_VK_KHR_win32_surface = 0; + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +int GLAD_VK_KHR_xcb_surface = 0; + +#endif + + + +PFN_vkAcquireNextImage2KHR glad_vkAcquireNextImage2KHR = NULL; +PFN_vkAcquireNextImageKHR glad_vkAcquireNextImageKHR = NULL; +PFN_vkAllocateCommandBuffers glad_vkAllocateCommandBuffers = NULL; +PFN_vkAllocateDescriptorSets glad_vkAllocateDescriptorSets = NULL; +PFN_vkAllocateMemory glad_vkAllocateMemory = NULL; +PFN_vkBeginCommandBuffer glad_vkBeginCommandBuffer = NULL; +PFN_vkBindBufferMemory glad_vkBindBufferMemory = NULL; +PFN_vkBindImageMemory glad_vkBindImageMemory = NULL; +PFN_vkCmdBeginDebugUtilsLabelEXT glad_vkCmdBeginDebugUtilsLabelEXT = NULL; +PFN_vkCmdBeginQuery glad_vkCmdBeginQuery = NULL; +PFN_vkCmdBeginRenderPass glad_vkCmdBeginRenderPass = NULL; +PFN_vkCmdBindDescriptorSets glad_vkCmdBindDescriptorSets = NULL; +PFN_vkCmdBindIndexBuffer glad_vkCmdBindIndexBuffer = NULL; +PFN_vkCmdBindPipeline glad_vkCmdBindPipeline = NULL; +PFN_vkCmdBindVertexBuffers glad_vkCmdBindVertexBuffers = NULL; +PFN_vkCmdBlitImage glad_vkCmdBlitImage = NULL; +PFN_vkCmdClearAttachments glad_vkCmdClearAttachments = NULL; +PFN_vkCmdClearColorImage glad_vkCmdClearColorImage = NULL; +PFN_vkCmdClearDepthStencilImage glad_vkCmdClearDepthStencilImage = NULL; +PFN_vkCmdCopyBuffer glad_vkCmdCopyBuffer = NULL; +PFN_vkCmdCopyBufferToImage glad_vkCmdCopyBufferToImage = NULL; +PFN_vkCmdCopyImage glad_vkCmdCopyImage = NULL; +PFN_vkCmdCopyImageToBuffer glad_vkCmdCopyImageToBuffer = NULL; +PFN_vkCmdCopyQueryPoolResults glad_vkCmdCopyQueryPoolResults = NULL; +PFN_vkCmdDispatch glad_vkCmdDispatch = NULL; +PFN_vkCmdDispatchIndirect glad_vkCmdDispatchIndirect = NULL; +PFN_vkCmdDraw glad_vkCmdDraw = NULL; +PFN_vkCmdDrawIndexed glad_vkCmdDrawIndexed = NULL; +PFN_vkCmdDrawIndexedIndirect glad_vkCmdDrawIndexedIndirect = NULL; +PFN_vkCmdDrawIndirect glad_vkCmdDrawIndirect = NULL; +PFN_vkCmdEndDebugUtilsLabelEXT glad_vkCmdEndDebugUtilsLabelEXT = NULL; +PFN_vkCmdEndQuery glad_vkCmdEndQuery = NULL; +PFN_vkCmdEndRenderPass glad_vkCmdEndRenderPass = NULL; +PFN_vkCmdExecuteCommands glad_vkCmdExecuteCommands = NULL; +PFN_vkCmdFillBuffer glad_vkCmdFillBuffer = NULL; +PFN_vkCmdInsertDebugUtilsLabelEXT glad_vkCmdInsertDebugUtilsLabelEXT = NULL; +PFN_vkCmdNextSubpass glad_vkCmdNextSubpass = NULL; +PFN_vkCmdPipelineBarrier glad_vkCmdPipelineBarrier = NULL; +PFN_vkCmdPushConstants glad_vkCmdPushConstants = NULL; +PFN_vkCmdResetEvent glad_vkCmdResetEvent = NULL; +PFN_vkCmdResetQueryPool glad_vkCmdResetQueryPool = NULL; +PFN_vkCmdResolveImage glad_vkCmdResolveImage = NULL; +PFN_vkCmdSetBlendConstants glad_vkCmdSetBlendConstants = NULL; +PFN_vkCmdSetDepthBias glad_vkCmdSetDepthBias = NULL; +PFN_vkCmdSetDepthBounds glad_vkCmdSetDepthBounds = NULL; +PFN_vkCmdSetEvent glad_vkCmdSetEvent = NULL; +PFN_vkCmdSetLineWidth glad_vkCmdSetLineWidth = NULL; +PFN_vkCmdSetScissor glad_vkCmdSetScissor = NULL; +PFN_vkCmdSetStencilCompareMask glad_vkCmdSetStencilCompareMask = NULL; +PFN_vkCmdSetStencilReference glad_vkCmdSetStencilReference = NULL; +PFN_vkCmdSetStencilWriteMask glad_vkCmdSetStencilWriteMask = NULL; +PFN_vkCmdSetViewport glad_vkCmdSetViewport = NULL; +PFN_vkCmdUpdateBuffer glad_vkCmdUpdateBuffer = NULL; +PFN_vkCmdWaitEvents glad_vkCmdWaitEvents = NULL; +PFN_vkCmdWriteTimestamp glad_vkCmdWriteTimestamp = NULL; +PFN_vkCreateBuffer glad_vkCreateBuffer = NULL; +PFN_vkCreateBufferView glad_vkCreateBufferView = NULL; +PFN_vkCreateCommandPool glad_vkCreateCommandPool = NULL; +PFN_vkCreateComputePipelines glad_vkCreateComputePipelines = NULL; +PFN_vkCreateDebugReportCallbackEXT glad_vkCreateDebugReportCallbackEXT = NULL; +PFN_vkCreateDebugUtilsMessengerEXT glad_vkCreateDebugUtilsMessengerEXT = NULL; +PFN_vkCreateDescriptorPool glad_vkCreateDescriptorPool = NULL; +PFN_vkCreateDescriptorSetLayout glad_vkCreateDescriptorSetLayout = NULL; +PFN_vkCreateDevice glad_vkCreateDevice = NULL; +PFN_vkCreateEvent glad_vkCreateEvent = NULL; +PFN_vkCreateFence glad_vkCreateFence = NULL; +PFN_vkCreateFramebuffer glad_vkCreateFramebuffer = NULL; +PFN_vkCreateGraphicsPipelines glad_vkCreateGraphicsPipelines = NULL; +PFN_vkCreateImage glad_vkCreateImage = NULL; +PFN_vkCreateImageView glad_vkCreateImageView = NULL; +PFN_vkCreateInstance glad_vkCreateInstance = NULL; +PFN_vkCreatePipelineCache glad_vkCreatePipelineCache = NULL; +PFN_vkCreatePipelineLayout glad_vkCreatePipelineLayout = NULL; +PFN_vkCreateQueryPool glad_vkCreateQueryPool = NULL; +PFN_vkCreateRenderPass glad_vkCreateRenderPass = NULL; +PFN_vkCreateSampler glad_vkCreateSampler = NULL; +PFN_vkCreateSemaphore glad_vkCreateSemaphore = NULL; +PFN_vkCreateShaderModule glad_vkCreateShaderModule = NULL; +PFN_vkCreateSwapchainKHR glad_vkCreateSwapchainKHR = NULL; +#if defined(VK_USE_PLATFORM_WIN32_KHR) +PFN_vkCreateWin32SurfaceKHR glad_vkCreateWin32SurfaceKHR = NULL; + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +PFN_vkCreateXcbSurfaceKHR glad_vkCreateXcbSurfaceKHR = NULL; + +#endif +PFN_vkDebugReportMessageEXT glad_vkDebugReportMessageEXT = NULL; +PFN_vkDestroyBuffer glad_vkDestroyBuffer = NULL; +PFN_vkDestroyBufferView glad_vkDestroyBufferView = NULL; +PFN_vkDestroyCommandPool glad_vkDestroyCommandPool = NULL; +PFN_vkDestroyDebugReportCallbackEXT glad_vkDestroyDebugReportCallbackEXT = NULL; +PFN_vkDestroyDebugUtilsMessengerEXT glad_vkDestroyDebugUtilsMessengerEXT = NULL; +PFN_vkDestroyDescriptorPool glad_vkDestroyDescriptorPool = NULL; +PFN_vkDestroyDescriptorSetLayout glad_vkDestroyDescriptorSetLayout = NULL; +PFN_vkDestroyDevice glad_vkDestroyDevice = NULL; +PFN_vkDestroyEvent glad_vkDestroyEvent = NULL; +PFN_vkDestroyFence glad_vkDestroyFence = NULL; +PFN_vkDestroyFramebuffer glad_vkDestroyFramebuffer = NULL; +PFN_vkDestroyImage glad_vkDestroyImage = NULL; +PFN_vkDestroyImageView glad_vkDestroyImageView = NULL; +PFN_vkDestroyInstance glad_vkDestroyInstance = NULL; +PFN_vkDestroyPipeline glad_vkDestroyPipeline = NULL; +PFN_vkDestroyPipelineCache glad_vkDestroyPipelineCache = NULL; +PFN_vkDestroyPipelineLayout glad_vkDestroyPipelineLayout = NULL; +PFN_vkDestroyQueryPool glad_vkDestroyQueryPool = NULL; +PFN_vkDestroyRenderPass glad_vkDestroyRenderPass = NULL; +PFN_vkDestroySampler glad_vkDestroySampler = NULL; +PFN_vkDestroySemaphore glad_vkDestroySemaphore = NULL; +PFN_vkDestroyShaderModule glad_vkDestroyShaderModule = NULL; +PFN_vkDestroySurfaceKHR glad_vkDestroySurfaceKHR = NULL; +PFN_vkDestroySwapchainKHR glad_vkDestroySwapchainKHR = NULL; +PFN_vkDeviceWaitIdle glad_vkDeviceWaitIdle = NULL; +PFN_vkEndCommandBuffer glad_vkEndCommandBuffer = NULL; +PFN_vkEnumerateDeviceExtensionProperties glad_vkEnumerateDeviceExtensionProperties = NULL; +PFN_vkEnumerateDeviceLayerProperties glad_vkEnumerateDeviceLayerProperties = NULL; +PFN_vkEnumerateInstanceExtensionProperties glad_vkEnumerateInstanceExtensionProperties = NULL; +PFN_vkEnumerateInstanceLayerProperties glad_vkEnumerateInstanceLayerProperties = NULL; +PFN_vkEnumeratePhysicalDevices glad_vkEnumeratePhysicalDevices = NULL; +PFN_vkFlushMappedMemoryRanges glad_vkFlushMappedMemoryRanges = NULL; +PFN_vkFreeCommandBuffers glad_vkFreeCommandBuffers = NULL; +PFN_vkFreeDescriptorSets glad_vkFreeDescriptorSets = NULL; +PFN_vkFreeMemory glad_vkFreeMemory = NULL; +PFN_vkGetBufferMemoryRequirements glad_vkGetBufferMemoryRequirements = NULL; +PFN_vkGetDeviceGroupPresentCapabilitiesKHR glad_vkGetDeviceGroupPresentCapabilitiesKHR = NULL; +PFN_vkGetDeviceGroupSurfacePresentModesKHR glad_vkGetDeviceGroupSurfacePresentModesKHR = NULL; +PFN_vkGetDeviceMemoryCommitment glad_vkGetDeviceMemoryCommitment = NULL; +PFN_vkGetDeviceProcAddr glad_vkGetDeviceProcAddr = NULL; +PFN_vkGetDeviceQueue glad_vkGetDeviceQueue = NULL; +PFN_vkGetEventStatus glad_vkGetEventStatus = NULL; +PFN_vkGetFenceStatus glad_vkGetFenceStatus = NULL; +PFN_vkGetImageMemoryRequirements glad_vkGetImageMemoryRequirements = NULL; +PFN_vkGetImageSparseMemoryRequirements glad_vkGetImageSparseMemoryRequirements = NULL; +PFN_vkGetImageSubresourceLayout glad_vkGetImageSubresourceLayout = NULL; +PFN_vkGetInstanceProcAddr glad_vkGetInstanceProcAddr = NULL; +PFN_vkGetPhysicalDeviceFeatures glad_vkGetPhysicalDeviceFeatures = NULL; +PFN_vkGetPhysicalDeviceFeatures2KHR glad_vkGetPhysicalDeviceFeatures2KHR = NULL; +PFN_vkGetPhysicalDeviceFormatProperties glad_vkGetPhysicalDeviceFormatProperties = NULL; +PFN_vkGetPhysicalDeviceFormatProperties2KHR glad_vkGetPhysicalDeviceFormatProperties2KHR = NULL; +PFN_vkGetPhysicalDeviceImageFormatProperties glad_vkGetPhysicalDeviceImageFormatProperties = NULL; +PFN_vkGetPhysicalDeviceImageFormatProperties2KHR glad_vkGetPhysicalDeviceImageFormatProperties2KHR = NULL; +PFN_vkGetPhysicalDeviceMemoryProperties glad_vkGetPhysicalDeviceMemoryProperties = NULL; +PFN_vkGetPhysicalDeviceMemoryProperties2KHR glad_vkGetPhysicalDeviceMemoryProperties2KHR = NULL; +PFN_vkGetPhysicalDevicePresentRectanglesKHR glad_vkGetPhysicalDevicePresentRectanglesKHR = NULL; +PFN_vkGetPhysicalDeviceProperties glad_vkGetPhysicalDeviceProperties = NULL; +PFN_vkGetPhysicalDeviceProperties2KHR glad_vkGetPhysicalDeviceProperties2KHR = NULL; +PFN_vkGetPhysicalDeviceQueueFamilyProperties glad_vkGetPhysicalDeviceQueueFamilyProperties = NULL; +PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR glad_vkGetPhysicalDeviceQueueFamilyProperties2KHR = NULL; +PFN_vkGetPhysicalDeviceSparseImageFormatProperties glad_vkGetPhysicalDeviceSparseImageFormatProperties = NULL; +PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR glad_vkGetPhysicalDeviceSparseImageFormatProperties2KHR = NULL; +PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR glad_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = NULL; +PFN_vkGetPhysicalDeviceSurfaceFormatsKHR glad_vkGetPhysicalDeviceSurfaceFormatsKHR = NULL; +PFN_vkGetPhysicalDeviceSurfacePresentModesKHR glad_vkGetPhysicalDeviceSurfacePresentModesKHR = NULL; +PFN_vkGetPhysicalDeviceSurfaceSupportKHR glad_vkGetPhysicalDeviceSurfaceSupportKHR = NULL; +#if defined(VK_USE_PLATFORM_WIN32_KHR) +PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR glad_vkGetPhysicalDeviceWin32PresentationSupportKHR = NULL; + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR glad_vkGetPhysicalDeviceXcbPresentationSupportKHR = NULL; + +#endif +PFN_vkGetPipelineCacheData glad_vkGetPipelineCacheData = NULL; +PFN_vkGetQueryPoolResults glad_vkGetQueryPoolResults = NULL; +PFN_vkGetRenderAreaGranularity glad_vkGetRenderAreaGranularity = NULL; +PFN_vkGetSwapchainImagesKHR glad_vkGetSwapchainImagesKHR = NULL; +PFN_vkInvalidateMappedMemoryRanges glad_vkInvalidateMappedMemoryRanges = NULL; +PFN_vkMapMemory glad_vkMapMemory = NULL; +PFN_vkMergePipelineCaches glad_vkMergePipelineCaches = NULL; +PFN_vkQueueBeginDebugUtilsLabelEXT glad_vkQueueBeginDebugUtilsLabelEXT = NULL; +PFN_vkQueueBindSparse glad_vkQueueBindSparse = NULL; +PFN_vkQueueEndDebugUtilsLabelEXT glad_vkQueueEndDebugUtilsLabelEXT = NULL; +PFN_vkQueueInsertDebugUtilsLabelEXT glad_vkQueueInsertDebugUtilsLabelEXT = NULL; +PFN_vkQueuePresentKHR glad_vkQueuePresentKHR = NULL; +PFN_vkQueueSubmit glad_vkQueueSubmit = NULL; +PFN_vkQueueWaitIdle glad_vkQueueWaitIdle = NULL; +PFN_vkResetCommandBuffer glad_vkResetCommandBuffer = NULL; +PFN_vkResetCommandPool glad_vkResetCommandPool = NULL; +PFN_vkResetDescriptorPool glad_vkResetDescriptorPool = NULL; +PFN_vkResetEvent glad_vkResetEvent = NULL; +PFN_vkResetFences glad_vkResetFences = NULL; +PFN_vkSetDebugUtilsObjectNameEXT glad_vkSetDebugUtilsObjectNameEXT = NULL; +PFN_vkSetDebugUtilsObjectTagEXT glad_vkSetDebugUtilsObjectTagEXT = NULL; +PFN_vkSetEvent glad_vkSetEvent = NULL; +PFN_vkSubmitDebugUtilsMessageEXT glad_vkSubmitDebugUtilsMessageEXT = NULL; +PFN_vkUnmapMemory glad_vkUnmapMemory = NULL; +PFN_vkUpdateDescriptorSets glad_vkUpdateDescriptorSets = NULL; +PFN_vkWaitForFences glad_vkWaitForFences = NULL; + + +static void glad_vk_load_VK_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_VERSION_1_0) return; + glad_vkAllocateCommandBuffers = (PFN_vkAllocateCommandBuffers) load(userptr, "vkAllocateCommandBuffers"); + glad_vkAllocateDescriptorSets = (PFN_vkAllocateDescriptorSets) load(userptr, "vkAllocateDescriptorSets"); + glad_vkAllocateMemory = (PFN_vkAllocateMemory) load(userptr, "vkAllocateMemory"); + glad_vkBeginCommandBuffer = (PFN_vkBeginCommandBuffer) load(userptr, "vkBeginCommandBuffer"); + glad_vkBindBufferMemory = (PFN_vkBindBufferMemory) load(userptr, "vkBindBufferMemory"); + glad_vkBindImageMemory = (PFN_vkBindImageMemory) load(userptr, "vkBindImageMemory"); + glad_vkCmdBeginQuery = (PFN_vkCmdBeginQuery) load(userptr, "vkCmdBeginQuery"); + glad_vkCmdBeginRenderPass = (PFN_vkCmdBeginRenderPass) load(userptr, "vkCmdBeginRenderPass"); + glad_vkCmdBindDescriptorSets = (PFN_vkCmdBindDescriptorSets) load(userptr, "vkCmdBindDescriptorSets"); + glad_vkCmdBindIndexBuffer = (PFN_vkCmdBindIndexBuffer) load(userptr, "vkCmdBindIndexBuffer"); + glad_vkCmdBindPipeline = (PFN_vkCmdBindPipeline) load(userptr, "vkCmdBindPipeline"); + glad_vkCmdBindVertexBuffers = (PFN_vkCmdBindVertexBuffers) load(userptr, "vkCmdBindVertexBuffers"); + glad_vkCmdBlitImage = (PFN_vkCmdBlitImage) load(userptr, "vkCmdBlitImage"); + glad_vkCmdClearAttachments = (PFN_vkCmdClearAttachments) load(userptr, "vkCmdClearAttachments"); + glad_vkCmdClearColorImage = (PFN_vkCmdClearColorImage) load(userptr, "vkCmdClearColorImage"); + glad_vkCmdClearDepthStencilImage = (PFN_vkCmdClearDepthStencilImage) load(userptr, "vkCmdClearDepthStencilImage"); + glad_vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer) load(userptr, "vkCmdCopyBuffer"); + glad_vkCmdCopyBufferToImage = (PFN_vkCmdCopyBufferToImage) load(userptr, "vkCmdCopyBufferToImage"); + glad_vkCmdCopyImage = (PFN_vkCmdCopyImage) load(userptr, "vkCmdCopyImage"); + glad_vkCmdCopyImageToBuffer = (PFN_vkCmdCopyImageToBuffer) load(userptr, "vkCmdCopyImageToBuffer"); + glad_vkCmdCopyQueryPoolResults = (PFN_vkCmdCopyQueryPoolResults) load(userptr, "vkCmdCopyQueryPoolResults"); + glad_vkCmdDispatch = (PFN_vkCmdDispatch) load(userptr, "vkCmdDispatch"); + glad_vkCmdDispatchIndirect = (PFN_vkCmdDispatchIndirect) load(userptr, "vkCmdDispatchIndirect"); + glad_vkCmdDraw = (PFN_vkCmdDraw) load(userptr, "vkCmdDraw"); + glad_vkCmdDrawIndexed = (PFN_vkCmdDrawIndexed) load(userptr, "vkCmdDrawIndexed"); + glad_vkCmdDrawIndexedIndirect = (PFN_vkCmdDrawIndexedIndirect) load(userptr, "vkCmdDrawIndexedIndirect"); + glad_vkCmdDrawIndirect = (PFN_vkCmdDrawIndirect) load(userptr, "vkCmdDrawIndirect"); + glad_vkCmdEndQuery = (PFN_vkCmdEndQuery) load(userptr, "vkCmdEndQuery"); + glad_vkCmdEndRenderPass = (PFN_vkCmdEndRenderPass) load(userptr, "vkCmdEndRenderPass"); + glad_vkCmdExecuteCommands = (PFN_vkCmdExecuteCommands) load(userptr, "vkCmdExecuteCommands"); + glad_vkCmdFillBuffer = (PFN_vkCmdFillBuffer) load(userptr, "vkCmdFillBuffer"); + glad_vkCmdNextSubpass = (PFN_vkCmdNextSubpass) load(userptr, "vkCmdNextSubpass"); + glad_vkCmdPipelineBarrier = (PFN_vkCmdPipelineBarrier) load(userptr, "vkCmdPipelineBarrier"); + glad_vkCmdPushConstants = (PFN_vkCmdPushConstants) load(userptr, "vkCmdPushConstants"); + glad_vkCmdResetEvent = (PFN_vkCmdResetEvent) load(userptr, "vkCmdResetEvent"); + glad_vkCmdResetQueryPool = (PFN_vkCmdResetQueryPool) load(userptr, "vkCmdResetQueryPool"); + glad_vkCmdResolveImage = (PFN_vkCmdResolveImage) load(userptr, "vkCmdResolveImage"); + glad_vkCmdSetBlendConstants = (PFN_vkCmdSetBlendConstants) load(userptr, "vkCmdSetBlendConstants"); + glad_vkCmdSetDepthBias = (PFN_vkCmdSetDepthBias) load(userptr, "vkCmdSetDepthBias"); + glad_vkCmdSetDepthBounds = (PFN_vkCmdSetDepthBounds) load(userptr, "vkCmdSetDepthBounds"); + glad_vkCmdSetEvent = (PFN_vkCmdSetEvent) load(userptr, "vkCmdSetEvent"); + glad_vkCmdSetLineWidth = (PFN_vkCmdSetLineWidth) load(userptr, "vkCmdSetLineWidth"); + glad_vkCmdSetScissor = (PFN_vkCmdSetScissor) load(userptr, "vkCmdSetScissor"); + glad_vkCmdSetStencilCompareMask = (PFN_vkCmdSetStencilCompareMask) load(userptr, "vkCmdSetStencilCompareMask"); + glad_vkCmdSetStencilReference = (PFN_vkCmdSetStencilReference) load(userptr, "vkCmdSetStencilReference"); + glad_vkCmdSetStencilWriteMask = (PFN_vkCmdSetStencilWriteMask) load(userptr, "vkCmdSetStencilWriteMask"); + glad_vkCmdSetViewport = (PFN_vkCmdSetViewport) load(userptr, "vkCmdSetViewport"); + glad_vkCmdUpdateBuffer = (PFN_vkCmdUpdateBuffer) load(userptr, "vkCmdUpdateBuffer"); + glad_vkCmdWaitEvents = (PFN_vkCmdWaitEvents) load(userptr, "vkCmdWaitEvents"); + glad_vkCmdWriteTimestamp = (PFN_vkCmdWriteTimestamp) load(userptr, "vkCmdWriteTimestamp"); + glad_vkCreateBuffer = (PFN_vkCreateBuffer) load(userptr, "vkCreateBuffer"); + glad_vkCreateBufferView = (PFN_vkCreateBufferView) load(userptr, "vkCreateBufferView"); + glad_vkCreateCommandPool = (PFN_vkCreateCommandPool) load(userptr, "vkCreateCommandPool"); + glad_vkCreateComputePipelines = (PFN_vkCreateComputePipelines) load(userptr, "vkCreateComputePipelines"); + glad_vkCreateDescriptorPool = (PFN_vkCreateDescriptorPool) load(userptr, "vkCreateDescriptorPool"); + glad_vkCreateDescriptorSetLayout = (PFN_vkCreateDescriptorSetLayout) load(userptr, "vkCreateDescriptorSetLayout"); + glad_vkCreateDevice = (PFN_vkCreateDevice) load(userptr, "vkCreateDevice"); + glad_vkCreateEvent = (PFN_vkCreateEvent) load(userptr, "vkCreateEvent"); + glad_vkCreateFence = (PFN_vkCreateFence) load(userptr, "vkCreateFence"); + glad_vkCreateFramebuffer = (PFN_vkCreateFramebuffer) load(userptr, "vkCreateFramebuffer"); + glad_vkCreateGraphicsPipelines = (PFN_vkCreateGraphicsPipelines) load(userptr, "vkCreateGraphicsPipelines"); + glad_vkCreateImage = (PFN_vkCreateImage) load(userptr, "vkCreateImage"); + glad_vkCreateImageView = (PFN_vkCreateImageView) load(userptr, "vkCreateImageView"); + glad_vkCreateInstance = (PFN_vkCreateInstance) load(userptr, "vkCreateInstance"); + glad_vkCreatePipelineCache = (PFN_vkCreatePipelineCache) load(userptr, "vkCreatePipelineCache"); + glad_vkCreatePipelineLayout = (PFN_vkCreatePipelineLayout) load(userptr, "vkCreatePipelineLayout"); + glad_vkCreateQueryPool = (PFN_vkCreateQueryPool) load(userptr, "vkCreateQueryPool"); + glad_vkCreateRenderPass = (PFN_vkCreateRenderPass) load(userptr, "vkCreateRenderPass"); + glad_vkCreateSampler = (PFN_vkCreateSampler) load(userptr, "vkCreateSampler"); + glad_vkCreateSemaphore = (PFN_vkCreateSemaphore) load(userptr, "vkCreateSemaphore"); + glad_vkCreateShaderModule = (PFN_vkCreateShaderModule) load(userptr, "vkCreateShaderModule"); + glad_vkDestroyBuffer = (PFN_vkDestroyBuffer) load(userptr, "vkDestroyBuffer"); + glad_vkDestroyBufferView = (PFN_vkDestroyBufferView) load(userptr, "vkDestroyBufferView"); + glad_vkDestroyCommandPool = (PFN_vkDestroyCommandPool) load(userptr, "vkDestroyCommandPool"); + glad_vkDestroyDescriptorPool = (PFN_vkDestroyDescriptorPool) load(userptr, "vkDestroyDescriptorPool"); + glad_vkDestroyDescriptorSetLayout = (PFN_vkDestroyDescriptorSetLayout) load(userptr, "vkDestroyDescriptorSetLayout"); + glad_vkDestroyDevice = (PFN_vkDestroyDevice) load(userptr, "vkDestroyDevice"); + glad_vkDestroyEvent = (PFN_vkDestroyEvent) load(userptr, "vkDestroyEvent"); + glad_vkDestroyFence = (PFN_vkDestroyFence) load(userptr, "vkDestroyFence"); + glad_vkDestroyFramebuffer = (PFN_vkDestroyFramebuffer) load(userptr, "vkDestroyFramebuffer"); + glad_vkDestroyImage = (PFN_vkDestroyImage) load(userptr, "vkDestroyImage"); + glad_vkDestroyImageView = (PFN_vkDestroyImageView) load(userptr, "vkDestroyImageView"); + glad_vkDestroyInstance = (PFN_vkDestroyInstance) load(userptr, "vkDestroyInstance"); + glad_vkDestroyPipeline = (PFN_vkDestroyPipeline) load(userptr, "vkDestroyPipeline"); + glad_vkDestroyPipelineCache = (PFN_vkDestroyPipelineCache) load(userptr, "vkDestroyPipelineCache"); + glad_vkDestroyPipelineLayout = (PFN_vkDestroyPipelineLayout) load(userptr, "vkDestroyPipelineLayout"); + glad_vkDestroyQueryPool = (PFN_vkDestroyQueryPool) load(userptr, "vkDestroyQueryPool"); + glad_vkDestroyRenderPass = (PFN_vkDestroyRenderPass) load(userptr, "vkDestroyRenderPass"); + glad_vkDestroySampler = (PFN_vkDestroySampler) load(userptr, "vkDestroySampler"); + glad_vkDestroySemaphore = (PFN_vkDestroySemaphore) load(userptr, "vkDestroySemaphore"); + glad_vkDestroyShaderModule = (PFN_vkDestroyShaderModule) load(userptr, "vkDestroyShaderModule"); + glad_vkDeviceWaitIdle = (PFN_vkDeviceWaitIdle) load(userptr, "vkDeviceWaitIdle"); + glad_vkEndCommandBuffer = (PFN_vkEndCommandBuffer) load(userptr, "vkEndCommandBuffer"); + glad_vkEnumerateDeviceExtensionProperties = (PFN_vkEnumerateDeviceExtensionProperties) load(userptr, "vkEnumerateDeviceExtensionProperties"); + glad_vkEnumerateDeviceLayerProperties = (PFN_vkEnumerateDeviceLayerProperties) load(userptr, "vkEnumerateDeviceLayerProperties"); + glad_vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) load(userptr, "vkEnumerateInstanceExtensionProperties"); + glad_vkEnumerateInstanceLayerProperties = (PFN_vkEnumerateInstanceLayerProperties) load(userptr, "vkEnumerateInstanceLayerProperties"); + glad_vkEnumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices) load(userptr, "vkEnumeratePhysicalDevices"); + glad_vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges) load(userptr, "vkFlushMappedMemoryRanges"); + glad_vkFreeCommandBuffers = (PFN_vkFreeCommandBuffers) load(userptr, "vkFreeCommandBuffers"); + glad_vkFreeDescriptorSets = (PFN_vkFreeDescriptorSets) load(userptr, "vkFreeDescriptorSets"); + glad_vkFreeMemory = (PFN_vkFreeMemory) load(userptr, "vkFreeMemory"); + glad_vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements) load(userptr, "vkGetBufferMemoryRequirements"); + glad_vkGetDeviceMemoryCommitment = (PFN_vkGetDeviceMemoryCommitment) load(userptr, "vkGetDeviceMemoryCommitment"); + glad_vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr) load(userptr, "vkGetDeviceProcAddr"); + glad_vkGetDeviceQueue = (PFN_vkGetDeviceQueue) load(userptr, "vkGetDeviceQueue"); + glad_vkGetEventStatus = (PFN_vkGetEventStatus) load(userptr, "vkGetEventStatus"); + glad_vkGetFenceStatus = (PFN_vkGetFenceStatus) load(userptr, "vkGetFenceStatus"); + glad_vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements) load(userptr, "vkGetImageMemoryRequirements"); + glad_vkGetImageSparseMemoryRequirements = (PFN_vkGetImageSparseMemoryRequirements) load(userptr, "vkGetImageSparseMemoryRequirements"); + glad_vkGetImageSubresourceLayout = (PFN_vkGetImageSubresourceLayout) load(userptr, "vkGetImageSubresourceLayout"); + glad_vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) load(userptr, "vkGetInstanceProcAddr"); + glad_vkGetPhysicalDeviceFeatures = (PFN_vkGetPhysicalDeviceFeatures) load(userptr, "vkGetPhysicalDeviceFeatures"); + glad_vkGetPhysicalDeviceFormatProperties = (PFN_vkGetPhysicalDeviceFormatProperties) load(userptr, "vkGetPhysicalDeviceFormatProperties"); + glad_vkGetPhysicalDeviceImageFormatProperties = (PFN_vkGetPhysicalDeviceImageFormatProperties) load(userptr, "vkGetPhysicalDeviceImageFormatProperties"); + glad_vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties) load(userptr, "vkGetPhysicalDeviceMemoryProperties"); + glad_vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties) load(userptr, "vkGetPhysicalDeviceProperties"); + glad_vkGetPhysicalDeviceQueueFamilyProperties = (PFN_vkGetPhysicalDeviceQueueFamilyProperties) load(userptr, "vkGetPhysicalDeviceQueueFamilyProperties"); + glad_vkGetPhysicalDeviceSparseImageFormatProperties = (PFN_vkGetPhysicalDeviceSparseImageFormatProperties) load(userptr, "vkGetPhysicalDeviceSparseImageFormatProperties"); + glad_vkGetPipelineCacheData = (PFN_vkGetPipelineCacheData) load(userptr, "vkGetPipelineCacheData"); + glad_vkGetQueryPoolResults = (PFN_vkGetQueryPoolResults) load(userptr, "vkGetQueryPoolResults"); + glad_vkGetRenderAreaGranularity = (PFN_vkGetRenderAreaGranularity) load(userptr, "vkGetRenderAreaGranularity"); + glad_vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges) load(userptr, "vkInvalidateMappedMemoryRanges"); + glad_vkMapMemory = (PFN_vkMapMemory) load(userptr, "vkMapMemory"); + glad_vkMergePipelineCaches = (PFN_vkMergePipelineCaches) load(userptr, "vkMergePipelineCaches"); + glad_vkQueueBindSparse = (PFN_vkQueueBindSparse) load(userptr, "vkQueueBindSparse"); + glad_vkQueueSubmit = (PFN_vkQueueSubmit) load(userptr, "vkQueueSubmit"); + glad_vkQueueWaitIdle = (PFN_vkQueueWaitIdle) load(userptr, "vkQueueWaitIdle"); + glad_vkResetCommandBuffer = (PFN_vkResetCommandBuffer) load(userptr, "vkResetCommandBuffer"); + glad_vkResetCommandPool = (PFN_vkResetCommandPool) load(userptr, "vkResetCommandPool"); + glad_vkResetDescriptorPool = (PFN_vkResetDescriptorPool) load(userptr, "vkResetDescriptorPool"); + glad_vkResetEvent = (PFN_vkResetEvent) load(userptr, "vkResetEvent"); + glad_vkResetFences = (PFN_vkResetFences) load(userptr, "vkResetFences"); + glad_vkSetEvent = (PFN_vkSetEvent) load(userptr, "vkSetEvent"); + glad_vkUnmapMemory = (PFN_vkUnmapMemory) load(userptr, "vkUnmapMemory"); + glad_vkUpdateDescriptorSets = (PFN_vkUpdateDescriptorSets) load(userptr, "vkUpdateDescriptorSets"); + glad_vkWaitForFences = (PFN_vkWaitForFences) load(userptr, "vkWaitForFences"); +} +static void glad_vk_load_VK_EXT_debug_report( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_EXT_debug_report) return; + glad_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT) load(userptr, "vkCreateDebugReportCallbackEXT"); + glad_vkDebugReportMessageEXT = (PFN_vkDebugReportMessageEXT) load(userptr, "vkDebugReportMessageEXT"); + glad_vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT) load(userptr, "vkDestroyDebugReportCallbackEXT"); +} +static void glad_vk_load_VK_EXT_debug_utils( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_EXT_debug_utils) return; + glad_vkCmdBeginDebugUtilsLabelEXT = (PFN_vkCmdBeginDebugUtilsLabelEXT) load(userptr, "vkCmdBeginDebugUtilsLabelEXT"); + glad_vkCmdEndDebugUtilsLabelEXT = (PFN_vkCmdEndDebugUtilsLabelEXT) load(userptr, "vkCmdEndDebugUtilsLabelEXT"); + glad_vkCmdInsertDebugUtilsLabelEXT = (PFN_vkCmdInsertDebugUtilsLabelEXT) load(userptr, "vkCmdInsertDebugUtilsLabelEXT"); + glad_vkCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT) load(userptr, "vkCreateDebugUtilsMessengerEXT"); + glad_vkDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT) load(userptr, "vkDestroyDebugUtilsMessengerEXT"); + glad_vkQueueBeginDebugUtilsLabelEXT = (PFN_vkQueueBeginDebugUtilsLabelEXT) load(userptr, "vkQueueBeginDebugUtilsLabelEXT"); + glad_vkQueueEndDebugUtilsLabelEXT = (PFN_vkQueueEndDebugUtilsLabelEXT) load(userptr, "vkQueueEndDebugUtilsLabelEXT"); + glad_vkQueueInsertDebugUtilsLabelEXT = (PFN_vkQueueInsertDebugUtilsLabelEXT) load(userptr, "vkQueueInsertDebugUtilsLabelEXT"); + glad_vkSetDebugUtilsObjectNameEXT = (PFN_vkSetDebugUtilsObjectNameEXT) load(userptr, "vkSetDebugUtilsObjectNameEXT"); + glad_vkSetDebugUtilsObjectTagEXT = (PFN_vkSetDebugUtilsObjectTagEXT) load(userptr, "vkSetDebugUtilsObjectTagEXT"); + glad_vkSubmitDebugUtilsMessageEXT = (PFN_vkSubmitDebugUtilsMessageEXT) load(userptr, "vkSubmitDebugUtilsMessageEXT"); +} +static void glad_vk_load_VK_KHR_get_physical_device_properties2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_KHR_get_physical_device_properties2) return; + glad_vkGetPhysicalDeviceFeatures2KHR = (PFN_vkGetPhysicalDeviceFeatures2KHR) load(userptr, "vkGetPhysicalDeviceFeatures2KHR"); + glad_vkGetPhysicalDeviceFormatProperties2KHR = (PFN_vkGetPhysicalDeviceFormatProperties2KHR) load(userptr, "vkGetPhysicalDeviceFormatProperties2KHR"); + glad_vkGetPhysicalDeviceImageFormatProperties2KHR = (PFN_vkGetPhysicalDeviceImageFormatProperties2KHR) load(userptr, "vkGetPhysicalDeviceImageFormatProperties2KHR"); + glad_vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2KHR) load(userptr, "vkGetPhysicalDeviceMemoryProperties2KHR"); + glad_vkGetPhysicalDeviceProperties2KHR = (PFN_vkGetPhysicalDeviceProperties2KHR) load(userptr, "vkGetPhysicalDeviceProperties2KHR"); + glad_vkGetPhysicalDeviceQueueFamilyProperties2KHR = (PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR) load(userptr, "vkGetPhysicalDeviceQueueFamilyProperties2KHR"); + glad_vkGetPhysicalDeviceSparseImageFormatProperties2KHR = (PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR) load(userptr, "vkGetPhysicalDeviceSparseImageFormatProperties2KHR"); +} +static void glad_vk_load_VK_KHR_surface( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_KHR_surface) return; + glad_vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR) load(userptr, "vkDestroySurfaceKHR"); + glad_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR) load(userptr, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); + glad_vkGetPhysicalDeviceSurfaceFormatsKHR = (PFN_vkGetPhysicalDeviceSurfaceFormatsKHR) load(userptr, "vkGetPhysicalDeviceSurfaceFormatsKHR"); + glad_vkGetPhysicalDeviceSurfacePresentModesKHR = (PFN_vkGetPhysicalDeviceSurfacePresentModesKHR) load(userptr, "vkGetPhysicalDeviceSurfacePresentModesKHR"); + glad_vkGetPhysicalDeviceSurfaceSupportKHR = (PFN_vkGetPhysicalDeviceSurfaceSupportKHR) load(userptr, "vkGetPhysicalDeviceSurfaceSupportKHR"); +} +static void glad_vk_load_VK_KHR_swapchain( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_KHR_swapchain) return; + glad_vkAcquireNextImage2KHR = (PFN_vkAcquireNextImage2KHR) load(userptr, "vkAcquireNextImage2KHR"); + glad_vkAcquireNextImageKHR = (PFN_vkAcquireNextImageKHR) load(userptr, "vkAcquireNextImageKHR"); + glad_vkCreateSwapchainKHR = (PFN_vkCreateSwapchainKHR) load(userptr, "vkCreateSwapchainKHR"); + glad_vkDestroySwapchainKHR = (PFN_vkDestroySwapchainKHR) load(userptr, "vkDestroySwapchainKHR"); + glad_vkGetDeviceGroupPresentCapabilitiesKHR = (PFN_vkGetDeviceGroupPresentCapabilitiesKHR) load(userptr, "vkGetDeviceGroupPresentCapabilitiesKHR"); + glad_vkGetDeviceGroupSurfacePresentModesKHR = (PFN_vkGetDeviceGroupSurfacePresentModesKHR) load(userptr, "vkGetDeviceGroupSurfacePresentModesKHR"); + glad_vkGetPhysicalDevicePresentRectanglesKHR = (PFN_vkGetPhysicalDevicePresentRectanglesKHR) load(userptr, "vkGetPhysicalDevicePresentRectanglesKHR"); + glad_vkGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR) load(userptr, "vkGetSwapchainImagesKHR"); + glad_vkQueuePresentKHR = (PFN_vkQueuePresentKHR) load(userptr, "vkQueuePresentKHR"); +} +#if defined(VK_USE_PLATFORM_WIN32_KHR) +static void glad_vk_load_VK_KHR_win32_surface( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_KHR_win32_surface) return; + glad_vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) load(userptr, "vkCreateWin32SurfaceKHR"); + glad_vkGetPhysicalDeviceWin32PresentationSupportKHR = (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) load(userptr, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); +} + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) +static void glad_vk_load_VK_KHR_xcb_surface( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_KHR_xcb_surface) return; + glad_vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR) load(userptr, "vkCreateXcbSurfaceKHR"); + glad_vkGetPhysicalDeviceXcbPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) load(userptr, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); +} + +#endif + + + +static int glad_vk_get_extensions( VkPhysicalDevice physical_device, uint32_t *out_extension_count, char ***out_extensions) { + uint32_t i; + uint32_t instance_extension_count = 0; + uint32_t device_extension_count = 0; + uint32_t max_extension_count = 0; + uint32_t total_extension_count = 0; + char **extensions = NULL; + VkExtensionProperties *ext_properties = NULL; + VkResult result; + + if (glad_vkEnumerateInstanceExtensionProperties == NULL || (physical_device != NULL && glad_vkEnumerateDeviceExtensionProperties == NULL)) { + return 0; + } + + result = glad_vkEnumerateInstanceExtensionProperties(NULL, &instance_extension_count, NULL); + if (result != VK_SUCCESS) { + return 0; + } + + if (physical_device != NULL) { + result = glad_vkEnumerateDeviceExtensionProperties(physical_device, NULL, &device_extension_count, NULL); + if (result != VK_SUCCESS) { + return 0; + } + } + + total_extension_count = instance_extension_count + device_extension_count; + if (total_extension_count <= 0) { + return 0; + } + + max_extension_count = instance_extension_count > device_extension_count + ? instance_extension_count : device_extension_count; + + ext_properties = (VkExtensionProperties*) malloc(max_extension_count * sizeof(VkExtensionProperties)); + if (ext_properties == NULL) { + goto glad_vk_get_extensions_error; + } + + result = glad_vkEnumerateInstanceExtensionProperties(NULL, &instance_extension_count, ext_properties); + if (result != VK_SUCCESS) { + goto glad_vk_get_extensions_error; + } + + extensions = (char**) calloc(total_extension_count, sizeof(char*)); + if (extensions == NULL) { + goto glad_vk_get_extensions_error; + } + + for (i = 0; i < instance_extension_count; ++i) { + VkExtensionProperties ext = ext_properties[i]; + + size_t extension_name_length = strlen(ext.extensionName) + 1; + extensions[i] = (char*) malloc(extension_name_length * sizeof(char)); + if (extensions[i] == NULL) { + goto glad_vk_get_extensions_error; + } + memcpy(extensions[i], ext.extensionName, extension_name_length * sizeof(char)); + } + + if (physical_device != NULL) { + result = glad_vkEnumerateDeviceExtensionProperties(physical_device, NULL, &device_extension_count, ext_properties); + if (result != VK_SUCCESS) { + goto glad_vk_get_extensions_error; + } + + for (i = 0; i < device_extension_count; ++i) { + VkExtensionProperties ext = ext_properties[i]; + + size_t extension_name_length = strlen(ext.extensionName) + 1; + extensions[instance_extension_count + i] = (char*) malloc(extension_name_length * sizeof(char)); + if (extensions[instance_extension_count + i] == NULL) { + goto glad_vk_get_extensions_error; + } + memcpy(extensions[instance_extension_count + i], ext.extensionName, extension_name_length * sizeof(char)); + } + } + + free((void*) ext_properties); + + *out_extension_count = total_extension_count; + *out_extensions = extensions; + + return 1; + +glad_vk_get_extensions_error: + free((void*) ext_properties); + if (extensions != NULL) { + for (i = 0; i < total_extension_count; ++i) { + free((void*) extensions[i]); + } + free(extensions); + } + return 0; +} + +static void glad_vk_free_extensions(uint32_t extension_count, char **extensions) { + uint32_t i; + + for(i = 0; i < extension_count ; ++i) { + free((void*) (extensions[i])); + } + + free((void*) extensions); +} + +static int glad_vk_has_extension(const char *name, uint32_t extension_count, char **extensions) { + uint32_t i; + + for (i = 0; i < extension_count; ++i) { + if(extensions[i] != NULL && strcmp(name, extensions[i]) == 0) { + return 1; + } + } + + return 0; +} + +static GLADapiproc glad_vk_get_proc_from_userptr(void *userptr, const char* name) { + return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); +} + +static int glad_vk_find_extensions_vulkan( VkPhysicalDevice physical_device) { + uint32_t extension_count = 0; + char **extensions = NULL; + if (!glad_vk_get_extensions(physical_device, &extension_count, &extensions)) return 0; + + GLAD_VK_EXT_debug_report = glad_vk_has_extension("VK_EXT_debug_report", extension_count, extensions); + GLAD_VK_EXT_debug_utils = glad_vk_has_extension("VK_EXT_debug_utils", extension_count, extensions); + GLAD_VK_EXT_scalar_block_layout = glad_vk_has_extension("VK_EXT_scalar_block_layout", extension_count, extensions); + GLAD_VK_EXT_validation_features = glad_vk_has_extension("VK_EXT_validation_features", extension_count, extensions); + GLAD_VK_KHR_get_physical_device_properties2 = glad_vk_has_extension("VK_KHR_get_physical_device_properties2", extension_count, extensions); + GLAD_VK_KHR_portability_enumeration = glad_vk_has_extension("VK_KHR_portability_enumeration", extension_count, extensions); + GLAD_VK_KHR_shader_subgroup_extended_types = glad_vk_has_extension("VK_KHR_shader_subgroup_extended_types", extension_count, extensions); + GLAD_VK_KHR_surface = glad_vk_has_extension("VK_KHR_surface", extension_count, extensions); + GLAD_VK_KHR_swapchain = glad_vk_has_extension("VK_KHR_swapchain", extension_count, extensions); +#if defined(VK_USE_PLATFORM_WIN32_KHR) + GLAD_VK_KHR_win32_surface = glad_vk_has_extension("VK_KHR_win32_surface", extension_count, extensions); + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) + GLAD_VK_KHR_xcb_surface = glad_vk_has_extension("VK_KHR_xcb_surface", extension_count, extensions); + +#endif + + (void) glad_vk_has_extension; + + glad_vk_free_extensions(extension_count, extensions); + + return 1; +} + +static int glad_vk_find_core_vulkan( VkPhysicalDevice physical_device) { + int major = 1; + int minor = 0; + +#ifdef VK_VERSION_1_1 + if (glad_vkEnumerateInstanceVersion != NULL) { + uint32_t version; + VkResult result; + + result = glad_vkEnumerateInstanceVersion(&version); + if (result == VK_SUCCESS) { + major = (int) VK_VERSION_MAJOR(version); + minor = (int) VK_VERSION_MINOR(version); + } + } +#endif + + if (physical_device != NULL && glad_vkGetPhysicalDeviceProperties != NULL) { + VkPhysicalDeviceProperties properties; + glad_vkGetPhysicalDeviceProperties(physical_device, &properties); + + major = (int) VK_VERSION_MAJOR(properties.apiVersion); + minor = (int) VK_VERSION_MINOR(properties.apiVersion); + } + + GLAD_VK_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; + + return GLAD_MAKE_VERSION(major, minor); +} + +int gladLoadVulkanUserPtr( VkPhysicalDevice physical_device, GLADuserptrloadfunc load, void *userptr) { + int version; + +#ifdef VK_VERSION_1_1 + glad_vkEnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion) load(userptr, "vkEnumerateInstanceVersion"); +#endif + version = glad_vk_find_core_vulkan( physical_device); + if (!version) { + return 0; + } + + glad_vk_load_VK_VERSION_1_0(load, userptr); + + if (!glad_vk_find_extensions_vulkan( physical_device)) return 0; + glad_vk_load_VK_EXT_debug_report(load, userptr); + glad_vk_load_VK_EXT_debug_utils(load, userptr); + glad_vk_load_VK_KHR_get_physical_device_properties2(load, userptr); + glad_vk_load_VK_KHR_surface(load, userptr); + glad_vk_load_VK_KHR_swapchain(load, userptr); +#if defined(VK_USE_PLATFORM_WIN32_KHR) + glad_vk_load_VK_KHR_win32_surface(load, userptr); + +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) + glad_vk_load_VK_KHR_xcb_surface(load, userptr); + +#endif + + + return version; +} + + +int gladLoadVulkan( VkPhysicalDevice physical_device, GLADloadfunc load) { + return gladLoadVulkanUserPtr( physical_device, glad_vk_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); +} + + + + + +#ifdef GLAD_VULKAN + +#ifndef GLAD_LOADER_LIBRARY_C_ +#define GLAD_LOADER_LIBRARY_C_ + +#include +#include + +#if GLAD_PLATFORM_WIN32 +#include +#else +#include +#endif + + +static void* glad_get_dlopen_handle(const char *lib_names[], int length) { + void *handle = NULL; + int i; + + for (i = 0; i < length; ++i) { +#if GLAD_PLATFORM_WIN32 + #if GLAD_PLATFORM_UWP + size_t buffer_size = (strlen(lib_names[i]) + 1) * sizeof(WCHAR); + LPWSTR buffer = (LPWSTR) malloc(buffer_size); + if (buffer != NULL) { + int ret = MultiByteToWideChar(CP_ACP, 0, lib_names[i], -1, buffer, buffer_size); + if (ret != 0) { + handle = (void*) LoadPackagedLibrary(buffer, 0); + } + free((void*) buffer); + } + #else + handle = (void*) LoadLibraryA(lib_names[i]); + #endif +#else + handle = dlopen(lib_names[i], RTLD_LAZY | RTLD_LOCAL); +#endif + if (handle != NULL) { + return handle; + } + } + + return NULL; +} + +static void glad_close_dlopen_handle(void* handle) { + if (handle != NULL) { +#if GLAD_PLATFORM_WIN32 + FreeLibrary((HMODULE) handle); +#else + dlclose(handle); +#endif + } +} + +static GLADapiproc glad_dlsym_handle(void* handle, const char *name) { + if (handle == NULL) { + return NULL; + } + +#if GLAD_PLATFORM_WIN32 + return (GLADapiproc) GetProcAddress((HMODULE) handle, name); +#else + return GLAD_GNUC_EXTENSION (GLADapiproc) dlsym(handle, name); +#endif +} + +#endif /* GLAD_LOADER_LIBRARY_C_ */ + + +static const char* DEVICE_FUNCTIONS[] = { + "vkAcquireNextImage2KHR", + "vkAcquireNextImageKHR", + "vkAllocateCommandBuffers", + "vkAllocateDescriptorSets", + "vkAllocateMemory", + "vkBeginCommandBuffer", + "vkBindBufferMemory", + "vkBindImageMemory", + "vkCmdBeginDebugUtilsLabelEXT", + "vkCmdBeginQuery", + "vkCmdBeginRenderPass", + "vkCmdBindDescriptorSets", + "vkCmdBindIndexBuffer", + "vkCmdBindPipeline", + "vkCmdBindVertexBuffers", + "vkCmdBlitImage", + "vkCmdClearAttachments", + "vkCmdClearColorImage", + "vkCmdClearDepthStencilImage", + "vkCmdCopyBuffer", + "vkCmdCopyBufferToImage", + "vkCmdCopyImage", + "vkCmdCopyImageToBuffer", + "vkCmdCopyQueryPoolResults", + "vkCmdDispatch", + "vkCmdDispatchIndirect", + "vkCmdDraw", + "vkCmdDrawIndexed", + "vkCmdDrawIndexedIndirect", + "vkCmdDrawIndirect", + "vkCmdEndDebugUtilsLabelEXT", + "vkCmdEndQuery", + "vkCmdEndRenderPass", + "vkCmdExecuteCommands", + "vkCmdFillBuffer", + "vkCmdInsertDebugUtilsLabelEXT", + "vkCmdNextSubpass", + "vkCmdPipelineBarrier", + "vkCmdPushConstants", + "vkCmdResetEvent", + "vkCmdResetQueryPool", + "vkCmdResolveImage", + "vkCmdSetBlendConstants", + "vkCmdSetDepthBias", + "vkCmdSetDepthBounds", + "vkCmdSetEvent", + "vkCmdSetLineWidth", + "vkCmdSetScissor", + "vkCmdSetStencilCompareMask", + "vkCmdSetStencilReference", + "vkCmdSetStencilWriteMask", + "vkCmdSetViewport", + "vkCmdUpdateBuffer", + "vkCmdWaitEvents", + "vkCmdWriteTimestamp", + "vkCreateBuffer", + "vkCreateBufferView", + "vkCreateCommandPool", + "vkCreateComputePipelines", + "vkCreateDescriptorPool", + "vkCreateDescriptorSetLayout", + "vkCreateEvent", + "vkCreateFence", + "vkCreateFramebuffer", + "vkCreateGraphicsPipelines", + "vkCreateImage", + "vkCreateImageView", + "vkCreatePipelineCache", + "vkCreatePipelineLayout", + "vkCreateQueryPool", + "vkCreateRenderPass", + "vkCreateSampler", + "vkCreateSemaphore", + "vkCreateShaderModule", + "vkCreateSwapchainKHR", + "vkDestroyBuffer", + "vkDestroyBufferView", + "vkDestroyCommandPool", + "vkDestroyDescriptorPool", + "vkDestroyDescriptorSetLayout", + "vkDestroyDevice", + "vkDestroyEvent", + "vkDestroyFence", + "vkDestroyFramebuffer", + "vkDestroyImage", + "vkDestroyImageView", + "vkDestroyPipeline", + "vkDestroyPipelineCache", + "vkDestroyPipelineLayout", + "vkDestroyQueryPool", + "vkDestroyRenderPass", + "vkDestroySampler", + "vkDestroySemaphore", + "vkDestroyShaderModule", + "vkDestroySwapchainKHR", + "vkDeviceWaitIdle", + "vkEndCommandBuffer", + "vkFlushMappedMemoryRanges", + "vkFreeCommandBuffers", + "vkFreeDescriptorSets", + "vkFreeMemory", + "vkGetBufferMemoryRequirements", + "vkGetDeviceGroupPresentCapabilitiesKHR", + "vkGetDeviceGroupSurfacePresentModesKHR", + "vkGetDeviceMemoryCommitment", + "vkGetDeviceProcAddr", + "vkGetDeviceQueue", + "vkGetEventStatus", + "vkGetFenceStatus", + "vkGetImageMemoryRequirements", + "vkGetImageSparseMemoryRequirements", + "vkGetImageSubresourceLayout", + "vkGetPipelineCacheData", + "vkGetQueryPoolResults", + "vkGetRenderAreaGranularity", + "vkGetSwapchainImagesKHR", + "vkInvalidateMappedMemoryRanges", + "vkMapMemory", + "vkMergePipelineCaches", + "vkQueueBeginDebugUtilsLabelEXT", + "vkQueueBindSparse", + "vkQueueEndDebugUtilsLabelEXT", + "vkQueueInsertDebugUtilsLabelEXT", + "vkQueuePresentKHR", + "vkQueueSubmit", + "vkQueueWaitIdle", + "vkResetCommandBuffer", + "vkResetCommandPool", + "vkResetDescriptorPool", + "vkResetEvent", + "vkResetFences", + "vkSetDebugUtilsObjectNameEXT", + "vkSetDebugUtilsObjectTagEXT", + "vkSetEvent", + "vkUnmapMemory", + "vkUpdateDescriptorSets", + "vkWaitForFences", +}; + +static int glad_vulkan_is_device_function(const char *name) { + /* Exists as a workaround for: + * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/2323 + * + * `vkGetDeviceProcAddr` does not return NULL for non-device functions. + */ + int i; + int length = sizeof(DEVICE_FUNCTIONS) / sizeof(DEVICE_FUNCTIONS[0]); + + for (i=0; i < length; ++i) { + if (strcmp(DEVICE_FUNCTIONS[i], name) == 0) { + return 1; + } + } + + return 0; +} + +struct _glad_vulkan_userptr { + void *vk_handle; + VkInstance vk_instance; + VkDevice vk_device; + PFN_vkGetInstanceProcAddr get_instance_proc_addr; + PFN_vkGetDeviceProcAddr get_device_proc_addr; +}; + +static GLADapiproc glad_vulkan_get_proc(void *vuserptr, const char *name) { + struct _glad_vulkan_userptr userptr = *(struct _glad_vulkan_userptr*) vuserptr; + PFN_vkVoidFunction result = NULL; + + if (userptr.vk_device != NULL && glad_vulkan_is_device_function(name)) { + result = userptr.get_device_proc_addr(userptr.vk_device, name); + } + + if (result == NULL && userptr.vk_instance != NULL) { + result = userptr.get_instance_proc_addr(userptr.vk_instance, name); + } + + if(result == NULL) { + result = (PFN_vkVoidFunction) glad_dlsym_handle(userptr.vk_handle, name); + } + + return (GLADapiproc) result; +} + + +static void* _vulkan_handle; + +static void* glad_vulkan_dlopen_handle(void) { + static const char *NAMES[] = { +#if GLAD_PLATFORM_APPLE + "libvulkan.1.dylib", +#elif GLAD_PLATFORM_WIN32 + "vulkan-1.dll", + "vulkan.dll", +#else + "libvulkan.so.1", + "libvulkan.so", +#endif + }; + + if (_vulkan_handle == NULL) { + _vulkan_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0])); + } + + return _vulkan_handle; +} + +static struct _glad_vulkan_userptr glad_vulkan_build_userptr(void *handle, VkInstance instance, VkDevice device) { + struct _glad_vulkan_userptr userptr; + userptr.vk_handle = handle; + userptr.vk_instance = instance; + userptr.vk_device = device; + userptr.get_instance_proc_addr = (PFN_vkGetInstanceProcAddr) glad_dlsym_handle(handle, "vkGetInstanceProcAddr"); + userptr.get_device_proc_addr = (PFN_vkGetDeviceProcAddr) glad_dlsym_handle(handle, "vkGetDeviceProcAddr"); + return userptr; +} + +int gladLoaderLoadVulkan( VkInstance instance, VkPhysicalDevice physical_device, VkDevice device) { + int version = 0; + void *handle = NULL; + int did_load = 0; + struct _glad_vulkan_userptr userptr; + + did_load = _vulkan_handle == NULL; + handle = glad_vulkan_dlopen_handle(); + if (handle != NULL) { + userptr = glad_vulkan_build_userptr(handle, instance, device); + + if (userptr.get_instance_proc_addr != NULL && userptr.get_device_proc_addr != NULL) { + version = gladLoadVulkanUserPtr( physical_device, glad_vulkan_get_proc, &userptr); + } + + if (!version && did_load) { + gladLoaderUnloadVulkan(); + } + } + + return version; +} + + + +void gladLoaderUnloadVulkan(void) { + if (_vulkan_handle != NULL) { + glad_close_dlopen_handle(_vulkan_handle); + _vulkan_handle = NULL; + } +} + +#endif /* GLAD_VULKAN */ + +#ifdef __cplusplus +} +#endif + +#endif /* GLAD_VULKAN_IMPLEMENTATION */ +