Răsfoiți Sursa

Add armorcore

luboslenco 10 luni în urmă
părinte
comite
f4f909522f
100 a modificat fișierele cu 16940 adăugiri și 0 ștergeri
  1. 5 0
      armorcore/make
  2. 1 0
      armorcore/make.bat
  3. 392 0
      armorcore/project.js
  4. 5 0
      armorcore/readme.md
  5. 8 0
      armorcore/shaders/g2_colored.frag.glsl
  6. 12 0
      armorcore/shaders/g2_colored.vert.glsl
  7. 13 0
      armorcore/shaders/g2_image.frag.glsl
  8. 15 0
      armorcore/shaders/g2_image.vert.glsl
  9. 11 0
      armorcore/shaders/g2_text.frag.glsl
  10. 15 0
      armorcore/shaders/g2_text.vert.glsl
  11. 84 0
      armorcore/sources/.clang-format
  12. 441 0
      armorcore/sources/backends/android/android_native_app_glue.c
  13. 349 0
      armorcore/sources/backends/android/android_native_app_glue.h
  14. 31 0
      armorcore/sources/backends/android/java/arm/AndroidHttpRequest.java
  15. 267 0
      armorcore/sources/backends/android/java/tech/kinc/KincActivity.kt
  16. 50 0
      armorcore/sources/backends/android/java/tech/kinc/KincMoviePlayer.kt
  17. 62 0
      armorcore/sources/backends/android/java/tech/kinc/KincMovieTexture.kt
  18. 18 0
      armorcore/sources/backends/android/kinc/backend/Android.h
  19. 111 0
      armorcore/sources/backends/android/kinc/backend/android_file_dialog.c
  20. 15 0
      armorcore/sources/backends/android/kinc/backend/android_file_dialog.h
  21. 28 0
      armorcore/sources/backends/android/kinc/backend/android_http_request.c
  22. 16 0
      armorcore/sources/backends/android/kinc/backend/android_http_request.h
  23. 5 0
      armorcore/sources/backends/android/kinc/backend/androidunit.c
  24. 133 0
      armorcore/sources/backends/android/kinc/backend/audio.c.h
  25. 106 0
      armorcore/sources/backends/android/kinc/backend/display.c.h
  26. 1298 0
      armorcore/sources/backends/android/kinc/backend/system.c.h
  27. 581 0
      armorcore/sources/backends/android/kinc/backend/video.c.h
  28. 49 0
      armorcore/sources/backends/android/kinc/backend/video.h
  29. 79 0
      armorcore/sources/backends/android/kinc/backend/window.c.h
  30. 1 0
      armorcore/sources/backends/android/kinc/backend/windowdata.h
  31. 4 0
      armorcore/sources/backends/apple/kinc/backend/appleunit.m
  32. 53 0
      armorcore/sources/backends/apple/kinc/backend/http.m.h
  33. 24 0
      armorcore/sources/backends/apple/kinc/backend/system.m.h
  34. 50 0
      armorcore/sources/backends/apple/kinc/backend/thread.m.h
  35. 56 0
      armorcore/sources/backends/apple/kinc/backend/video.h
  36. 311 0
      armorcore/sources/backends/apple/kinc/backend/video.m.h
  37. 21 0
      armorcore/sources/backends/data/android/app/CMakeLists.txt
  38. 61 0
      armorcore/sources/backends/data/android/app/build.gradle.kts
  39. 34 0
      armorcore/sources/backends/data/android/app/proguard-rules.pro
  40. 5 0
      armorcore/sources/backends/data/android/build.gradle.kts
  41. 23 0
      armorcore/sources/backends/data/android/gradle.properties
  42. BIN
      armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.jar
  43. 6 0
      armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.properties
  44. 185 0
      armorcore/sources/backends/data/android/gradlew
  45. 89 0
      armorcore/sources/backends/data/android/gradlew.bat
  46. 26 0
      armorcore/sources/backends/data/android/main/AndroidManifest.xml
  47. 3 0
      armorcore/sources/backends/data/android/main/res/values/strings.xml
  48. 18 0
      armorcore/sources/backends/data/android/settings.gradle.kts
  49. 202 0
      armorcore/sources/backends/data/wasm/JS-Sources/audio-thread.js
  50. 6 0
      armorcore/sources/backends/data/wasm/JS-Sources/index.html
  51. 541 0
      armorcore/sources/backends/data/wasm/JS-Sources/start.js
  52. 24 0
      armorcore/sources/backends/data/wasm/JS-Sources/thread_starter.js
  53. 1226 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.c.h
  54. 41 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.h
  55. 11 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.c.h
  56. 19 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.h
  57. 195 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.c.h
  58. 43 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.h
  59. 107 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/d3d11unit.c
  60. 89 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.c.h
  61. 13 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.h
  62. 866 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.c.h
  63. 37 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.h
  64. 456 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.c.h
  65. 27 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.h
  66. 112 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.c.h
  67. 60 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.h
  68. 371 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.c.h
  69. 24 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.h
  70. 95 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.c.h
  71. 13 0
      armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.h
  72. 2654 0
      armorcore/sources/backends/direct3d12/d3dx12.h
  73. 622 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/Direct3D12.c.h
  74. 11 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.c.h
  75. 19 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.h
  76. 611 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.c.h
  77. 46 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.h
  78. 167 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.c.h
  79. 30 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.h
  80. 69 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.c.h
  81. 21 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.h
  82. 56 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12mini.h
  83. 106 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12unit.cpp
  84. 6 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/graphics.h
  85. 130 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.c.h
  86. 32 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.h
  87. 559 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.c.h
  88. 77 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.h
  89. 752 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.c.h
  90. 24 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.h
  91. 287 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.c.h
  92. 28 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.h
  93. 145 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.c.h
  94. 7 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.h
  95. 87 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.c.h
  96. 36 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.h
  97. 425 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.c.h
  98. 32 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.h
  99. 123 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.c.h
  100. 30 0
      armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.h

+ 5 - 0
armorcore/make

@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+. `dirname "$0"`/tools/platform.sh
+MAKE="`dirname "$0"`/tools/bin/$KINC_PLATFORM/amake"
+exec $MAKE `dirname "$0"`/tools/make.js "$@"

+ 1 - 0
armorcore/make.bat

@@ -0,0 +1 @@
+@call "%~dp0tools\bin\windows_x64\amake.exe" "%~dp0tools\make.js" %*

+ 392 - 0
armorcore/project.js

@@ -0,0 +1,392 @@
+
+let project = new Project(flags.name);
+
+{
+	project.add_define("KINC_A1");
+	project.add_define("KINC_A2");
+	project.add_define("KINC_G1");
+	project.add_define("KINC_G2");
+	let g5 = false;
+
+	project.add_cfiles("sources/kinc/**");
+	project.add_include_dir("sources");
+
+	function add_backend(name) {
+		project.add_cfiles("sources/backends/" + name + "/**");
+		project.add_include_dir("sources/backends/" + name);
+	}
+
+	if (platform === "windows") {
+		add_backend("windows");
+		add_backend("microsoft");
+		project.add_lib("dxguid");
+		project.add_lib("dsound");
+		project.add_lib("dinput8");
+		project.add_define("_CRT_SECURE_NO_WARNINGS");
+		project.add_define("_WINSOCK_DEPRECATED_NO_WARNINGS");
+		project.add_lib("ws2_32");
+		project.add_lib("Winhttp");
+		project.add_lib("wbemuuid");
+
+		if (graphics === "direct3d11") {
+			add_backend("direct3d11");
+			project.add_define("KINC_DIRECT3D");
+			project.add_define("KINC_DIRECT3D11");
+			project.add_lib("d3d11");
+		}
+		else if (graphics === "direct3d12" || graphics === "default") {
+			g5 = true;
+			add_backend("direct3d12");
+			project.add_define("KINC_DIRECT3D");
+			project.add_define("KINC_DIRECT3D12");
+			project.add_lib("dxgi");
+			project.add_lib("d3d12");
+		}
+		else {
+			throw new Error("Graphics API " + graphics + " is not available for Windows.");
+		}
+
+		add_backend("wasapi");
+	}
+	else if (platform === "macos") {
+		add_backend("apple");
+		add_backend("macos");
+		add_backend("posix");
+		if (graphics === "metal" || graphics === "default") {
+			g5 = true;
+			add_backend("metal");
+			project.add_define("KINC_METAL");
+			project.add_lib("Metal");
+			project.add_lib("MetalKit");
+		}
+		else {
+			throw new Error("Graphics API " + graphics + " is not available for macOS.");
+		}
+		project.add_lib("IOKit");
+		project.add_lib("Cocoa");
+		project.add_lib("AppKit");
+		project.add_lib("CoreAudio");
+		project.add_lib("CoreData");
+		project.add_lib("CoreMedia");
+		project.add_lib("CoreVideo");
+		project.add_lib("AVFoundation");
+		project.add_lib("Foundation");
+	}
+	else if (platform === "ios") {
+		add_backend("apple");
+		add_backend("ios");
+		add_backend("posix");
+		if (graphics === "metal" || graphics === "default") {
+			g5 = true;
+			add_backend("metal");
+			project.add_define("KINC_METAL");
+			project.add_lib("Metal");
+		}
+		else {
+			throw new Error("Graphics API " + graphics + " is not available for iOS.");
+		}
+		project.add_lib("UIKit");
+		project.add_lib("Foundation");
+		project.add_lib("CoreGraphics");
+		project.add_lib("QuartzCore");
+		project.add_lib("CoreAudio");
+		project.add_lib("AudioToolbox");
+		project.add_lib("CoreMotion");
+		project.add_lib("AVFoundation");
+		project.add_lib("CoreFoundation");
+		project.add_lib("CoreVideo");
+		project.add_lib("CoreMedia");
+	}
+	else if (platform === "android") {
+		project.add_define("KINC_ANDROID");
+		add_backend("android");
+		add_backend("posix");
+		if (graphics === "vulkan") {
+			g5 = true;
+			add_backend("vulkan");
+			project.add_define("KINC_VULKAN");
+			project.add_define("VK_USE_PLATFORM_ANDROID_KHR");
+			project.add_lib("vulkan");
+			project.add_define("KINC_ANDROID_API=24");
+		}
+		else if (graphics === "opengl" || graphics === "default") {
+			add_backend("opengl");
+			project.add_define("KINC_OPENGL");
+			project.add_define("KINC_OPENGL_ES");
+			project.add_define("KINC_ANDROID_API=19");
+			project.add_define("KINC_EGL");
+		}
+		else {
+			throw new Error("Graphics API " + graphics + " is not available for Android.");
+		}
+		project.add_lib("log");
+		project.add_lib("android");
+		project.add_lib("EGL");
+		project.add_lib("GLESv3");
+		project.add_lib("OpenSLES");
+		project.add_lib("OpenMAXAL");
+	}
+	else if (platform === "wasm") {
+		project.add_define("KINC_WASM");
+		add_backend("wasm");
+		project.add_include_dir("miniClib");
+		project.add_cfiles("sources/libs/miniClib/**");
+		if (graphics === "webgpu") {
+			g5 = true;
+			add_backend("webgpu");
+			project.add_define("KINC_WEBGPU");
+		}
+		else if (graphics === "opengl" || graphics === "default") {
+			add_backend("opengl");
+			project.add_define("KINC_OPENGL");
+			project.add_define("KINC_OPENGL_ES");
+		}
+		else {
+			throw new Error("Graphics API " + graphics + " is not available for Wasm.");
+		}
+	}
+	else if (platform === "linux") {
+		add_backend("linux");
+		add_backend("posix");
+		project.add_lib("asound");
+		project.add_lib("dl");
+		project.add_lib("udev");
+
+		// try {
+		// 	if (!fs_exists("build")) {
+		// 		fs_mkdir("build");
+		// 	}
+		// 	if (!fs_exists(path_join("build", "wayland"))) {
+		// 		fs_mkdir(path_join("build", "wayland"));
+		// 	}
+		// 	const waylandDir = path_join("build", "wayland", "wayland-generated");
+		// 	if (!fs_exists(waylandDir)) {
+		// 		fs_mkdir(waylandDir);
+		// 	}
+
+		// 	let good_wayland = false;
+
+		// 	const wayland_call = os_exec("wayland-scanner", ["--version"]);
+		// 	if (wayland_call.status !== 0) {
+		// 		throw "Could not run wayland-scanner to ask for its version";
+		// 	}
+		// 	const wayland_version = wayland_call.stderr;
+
+		// 	try {
+		// 		const scanner_versions = wayland_version.split(" ")[1].split(".");
+		// 		const w_x = parseInt(scanner_versions[0]);
+		// 		const w_y = parseInt(scanner_versions[1]);
+		// 		const w_z = parseInt(scanner_versions[2]);
+
+		// 		if (w_x > 1) {
+		// 			good_wayland = true;
+		// 		}
+		// 		else if (w_x === 1) {
+		// 			if (w_y > 17) {
+		// 				good_wayland = true;
+		// 			}
+		// 			else if (w_y === 17) {
+		// 				if (w_z >= 91) {
+		// 					good_wayland = true;
+		// 				}
+		// 			}
+		// 		}
+		// 	}
+		// 	catch (err) {
+		// 		console.log("Could not parse wayland-version " + wayland_version);
+		// 	}
+
+		// 	let c_ending = ".c";
+		// 	if (good_wayland) {
+		// 		c_ending = ".c.h";
+		// 	}
+
+		// 	let chfiles = [];
+
+		// 	function wl_protocol(protocol, file) {
+		// 		chfiles.push(file);
+		// 		const backend_path = path_resolve(waylandDir);
+		// 		const protocol_path = path_resolve("/usr/share/wayland-protocols", protocol);
+		// 		if (os_exec("wayland-scanner", ["private-code", protocol_path, path_resolve(backend_path, file + c_ending)]).status !== 0) {
+		// 			throw "Failed to generate wayland protocol files for" + protocol;
+		// 		}
+		// 		if (os_exec("wayland-scanner", ["client-header", protocol_path, path_resolve(backend_path, file + ".h")]).status !== 0) {
+		// 			throw "Failed to generate wayland protocol header for" + protocol;
+		// 		}
+		// 	}
+
+		// 	if (os_exec("wayland-scanner", ["private-code", "/usr/share/wayland/wayland.xml", path_resolve(waylandDir, "wayland-protocol" + c_ending)]).status !== 0) {
+		// 		throw "Failed to generate wayland protocol files for /usr/share/wayland/wayland.xml";
+		// 	}
+		// 	if (os_exec("wayland-scanner", ["client-header", "/usr/share/wayland/wayland.xml", path_resolve(waylandDir, "wayland-protocol.h")]).status !== 0) {
+		// 		throw "Failed to generate wayland protocol header for /usr/share/wayland/wayland.xml";
+		// 	}
+		// 	wl_protocol("stable/viewporter/viewporter.xml", "wayland-viewporter");
+		// 	wl_protocol("stable/xdg-shell/xdg-shell.xml", "xdg-shell");
+		// 	wl_protocol("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml", "xdg-decoration");
+		// 	wl_protocol("unstable/tablet/tablet-unstable-v2.xml", "wayland-tablet");
+		// 	wl_protocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml", "wayland-pointer-constraint");
+		// 	wl_protocol("unstable/relative-pointer/relative-pointer-unstable-v1.xml", "wayland-relative-pointer");
+
+		// 	if (good_wayland) {
+		// 		let cfile = "#include \"wayland-protocol.c.h\"\n";
+		// 		for (const chfile of chfiles) {
+		// 			cfile += "#include \"" + chfile + ".c.h\"\n";
+		// 		}
+		// 		fs_writefile(path_resolve(waylandDir, "waylandunit.c"), cfile);
+		// 	}
+
+		// 	project.add_include_dir(path_join("build", "wayland"));
+		// 	project.add_cfiles(path_resolve(waylandDir, "**"));
+		// }
+		// catch (err) {
+		// 	console.log("Failed to include wayland-support, setting KINC_NO_WAYLAND.");
+		// 	console.log("Wayland error was: " + err);
+		// 	project.add_define("KINC_NO_WAYLAND");
+		// }
+
+		if (graphics === "vulkan" || graphics === "default") {
+			g5 = true;
+			add_backend("vulkan");
+			project.add_lib("vulkan");
+			project.add_define("KINC_VULKAN");
+		}
+		else if (graphics === "opengl") {
+			add_backend("opengl");
+			project.add_lib("GL");
+			project.add_define("KINC_OPENGL");
+			project.add_lib("EGL");
+			project.add_define("KINC_EGL");
+		}
+		else {
+			throw new Error("Graphics API " + graphics + " is not available for Linux.");
+		}
+		project.add_define("_POSIX_C_SOURCE=200112L");
+		project.add_define("_XOPEN_SOURCE=600");
+	}
+
+	project.add_define("KINC_G4");
+
+	if (g5) {
+		project.add_define("KINC_G5");
+		project.add_define("KINC_G4ONG5");
+		add_backend("g4ong5");
+	}
+	else {
+		project.add_define("KINC_G5");
+		project.add_define("KINC_G5ONG4");
+		add_backend("g5ong4");
+	}
+}
+
+if (fs_exists(os_cwd() + "/icon.png")) {
+	project.icon = "icon.png";
+	if (platform === "macos" && fs_exists(os_cwd() + "/icon_macos.png")) {
+		project.icon = "icon_macos.png";
+	}
+}
+
+project.add_include_dir("sources/libs");
+project.add_cfiles("sources/libs/gc.c");
+project.add_cfiles("sources/libs/dir.c");
+project.add_include_dir("sources");
+project.add_cfiles("sources/iron.c");
+project.add_define("IRON_C_PATH=\"" + os_cwd() + "/build/iron.c" + "\"");
+project.add_define("EMBED_H_PATH=\"" + os_cwd() + "/build/embed.h" + "\"");
+
+if (flags.with_audio) {
+	project.add_define("WITH_AUDIO");
+	project.add_define("arm_audio");
+}
+
+if (flags.with_eval) {
+	project.add_define("WITH_EVAL");
+	project.add_cfiles("sources/libs/quickjs/*.c");
+	if (platform === "linux") {
+		project.add_lib("m");
+		project.add_define("_GNU_SOURCE");
+		project.add_define("environ=__environ");
+		project.add_define("sighandler_t=__sighandler_t");
+	}
+	else if (platform === "windows") {
+		project.add_define("WIN32_LEAN_AND_MEAN");
+		project.add_define("_WIN32_WINNT=0x0602");
+	}
+}
+
+if (flags.with_iron) {
+	project.add_define("WITH_IRON");
+	project.add_cfiles("sources/*.c");
+}
+
+if (platform === "windows") {
+	project.add_lib("Dbghelp"); // Stack walk
+	project.add_lib("Dwmapi"); // DWMWA_USE_IMMERSIVE_DARK_MODE
+	if (flags.with_d3dcompiler) {
+		project.add_define("WITH_D3DCOMPILER");
+		project.add_lib("d3d11");
+		project.add_lib("d3dcompiler");
+	}
+}
+else if (platform === "linux") {
+	project.add_define("KINC_NO_WAYLAND"); // TODO: kinc_wayland_display_init() not implemented
+}
+else if (platform === "android") {
+	// In app/build.gradle:
+	//   android - defaultconfig - ndk.abiFilters "arm64-v8a"
+	project.add_define("IDLE_SLEEP");
+	project.target_options.android.package = flags.package;
+	project.target_options.android.permissions = ["android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE", "android.permission.INTERNET"];
+	project.target_options.android.screenOrientation = ["sensorLandscape"];
+	project.target_options.android.minSdkVersion = 30;
+	project.target_options.android.targetSdkVersion = 33;
+	project.target_options.android.versionCode = 240000;
+	project.target_options.android.versionName = "1.0 alpha";
+}
+else if (platform === "ios") {
+	project.add_define("IDLE_SLEEP");
+}
+
+if (flags.with_nfd && (platform === "windows" || platform === "linux" || platform === "macos")) {
+	project.add_define("WITH_NFD");
+	project.add_include_dir("sources/libs/nfd");
+	project.add_cfiles("sources/libs/nfd/nfd_common.c");
+
+	if (platform === "windows") {
+		project.add_cfiles("sources/libs/nfd/nfd_win.cpp");
+	}
+	else if (platform === "linux") {
+		project.add_cfiles("sources/libs/nfd/nfd_gtk.c");
+		project.add_include_dir("/usr/include/gtk-3.0");
+		project.add_include_dir("/usr/include/glib-2.0");
+		project.add_include_dir("/usr/lib/x86_64-linux-gnu/glib-2.0/include");
+		project.add_include_dir("/usr/include/pango-1.0");
+		project.add_include_dir("/usr/include/cairo");
+		project.add_include_dir("/usr/include/gdk-pixbuf-2.0");
+		project.add_include_dir("/usr/include/atk-1.0");
+		project.add_include_dir("/usr/lib64/glib-2.0/include");
+		project.add_include_dir("/usr/lib/glib-2.0/include");
+		project.add_include_dir("/usr/include/harfbuzz");
+		project.add_lib("gtk-3");
+		project.add_lib("gobject-2.0");
+		project.add_lib("glib-2.0");
+	}
+	else {
+		project.add_cfiles("sources/libs/nfd/nfd_cocoa.m");
+	}
+}
+
+if (flags.with_compress) {
+	project.add_define("WITH_COMPRESS");
+}
+
+if (flags.with_image_write) {
+	project.add_define("WITH_IMAGE_WRITE");
+}
+
+if (flags.with_mpeg_write) {
+	project.add_define("WITH_MPEG_WRITE");
+}
+
+project.flatten();
+return project;

+ 5 - 0
armorcore/readme.md

@@ -0,0 +1,5 @@
+# armorcore
+
+3D engine core for C with JS scripting. ArmorCore targets Direct3D12, Vulkan, Metal and WebGPU.
+
+Powered by [Kinc](https://github.com/Kode/Kinc) - low-level hardware abstraction library.

+ 8 - 0
armorcore/shaders/g2_colored.frag.glsl

@@ -0,0 +1,8 @@
+#version 450
+
+in vec4 fragment_color;
+out vec4 frag_color;
+
+void main() {
+	frag_color = fragment_color;
+}

+ 12 - 0
armorcore/shaders/g2_colored.vert.glsl

@@ -0,0 +1,12 @@
+#version 450
+
+uniform mat4 P;
+
+in vec3 pos;
+in vec4 col;
+out vec4 fragment_color;
+
+void main() {
+	gl_Position = mul(vec4(pos, 1.0), P);
+	fragment_color = col;
+}

+ 13 - 0
armorcore/shaders/g2_image.frag.glsl

@@ -0,0 +1,13 @@
+#version 450
+
+uniform sampler2D tex;
+
+in vec2 tex_coord;
+in vec4 color;
+out vec4 frag_color;
+
+void main() {
+	vec4 texcolor = texture(tex, tex_coord) * color;
+	texcolor.rgb = texcolor.rgb * texcolor.a * color.a;
+	frag_color = texcolor;
+}

+ 15 - 0
armorcore/shaders/g2_image.vert.glsl

@@ -0,0 +1,15 @@
+#version 450
+
+uniform mat4 P;
+
+in vec3 pos;
+in vec2 tex;
+in vec4 col;
+out vec2 tex_coord;
+out vec4 color;
+
+void main() {
+	gl_Position = mul(vec4(pos, 1.0), P);
+	tex_coord = tex;
+	color = col;
+}

+ 11 - 0
armorcore/shaders/g2_text.frag.glsl

@@ -0,0 +1,11 @@
+#version 450
+
+uniform sampler2D tex;
+
+in vec2 tex_coord;
+in vec4 fragment_color;
+out vec4 frag_color;
+
+void main() {
+	frag_color = vec4(fragment_color.rgb, texture(tex, tex_coord).r * fragment_color.a);
+}

+ 15 - 0
armorcore/shaders/g2_text.vert.glsl

@@ -0,0 +1,15 @@
+#version 450
+
+uniform mat4 P;
+
+in vec3 pos;
+in vec2 tex;
+in vec4 col;
+out vec2 tex_coord;
+out vec4 fragment_color;
+
+void main() {
+	gl_Position = mul(vec4(pos, 1.0), P);
+	tex_coord = tex;
+	fragment_color = col;
+}

+ 84 - 0
armorcore/sources/.clang-format

@@ -0,0 +1,84 @@
+BasedOnStyle:  LLVM
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands:   true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:   
+  AfterClass:      false
+  AfterControlStatement: false
+  AfterEnum:       false
+  AfterFunction:   false
+  AfterNamespace:  false
+  AfterObjCDeclaration: false
+  AfterStruct:     false
+  AfterUnion:      false
+  BeforeCatch:     false
+  BeforeElse:      true
+  IndentBraces:    false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+ColumnLimit:     160
+CommentPragmas:  '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DeriveLineEnding: false
+DerivePointerAlignment: false
+DisableFormat:   false
+FixNamespaceComments: false
+IndentCaseLabels: false
+IndentWidth:     4
+IndentWrappedFunctionNames: false
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: All
+ObjCBlockIndentWidth: 4
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments:  true
+SortIncludes:    true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp03
+TabWidth: 4
+UseCRLF: false
+UseTab: ForIndentation
+---
+Language:        Cpp
+---
+Language:        ObjC

+ 441 - 0
armorcore/sources/backends/android/android_native_app_glue.c

@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <jni.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#include "android_native_app_glue.h"
+#include <android/log.h>
+
+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
+#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
+
+/* For debug builds, always enable the debug traces in this library */
+#ifndef NDEBUG
+#  define LOGV(...)  ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__))
+#else
+#  define LOGV(...)  ((void)0)
+#endif
+
+static void free_saved_state(struct android_app* android_app) {
+    pthread_mutex_lock(&android_app->mutex);
+    if (android_app->savedState != NULL) {
+        free(android_app->savedState);
+        android_app->savedState = NULL;
+        android_app->savedStateSize = 0;
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+}
+
+int8_t android_app_read_cmd(struct android_app* android_app) {
+    int8_t cmd;
+    if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
+        switch (cmd) {
+            case APP_CMD_SAVE_STATE:
+                free_saved_state(android_app);
+                break;
+        }
+        return cmd;
+    } else {
+        LOGE("No data on command pipe!");
+    }
+    return -1;
+}
+
+static void print_cur_config(struct android_app* android_app) {
+    char lang[2], country[2];
+    AConfiguration_getLanguage(android_app->config, lang);
+    AConfiguration_getCountry(android_app->config, country);
+
+    LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
+            "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
+            "modetype=%d modenight=%d",
+            AConfiguration_getMcc(android_app->config),
+            AConfiguration_getMnc(android_app->config),
+            lang[0], lang[1], country[0], country[1],
+            AConfiguration_getOrientation(android_app->config),
+            AConfiguration_getTouchscreen(android_app->config),
+            AConfiguration_getDensity(android_app->config),
+            AConfiguration_getKeyboard(android_app->config),
+            AConfiguration_getNavigation(android_app->config),
+            AConfiguration_getKeysHidden(android_app->config),
+            AConfiguration_getNavHidden(android_app->config),
+            AConfiguration_getSdkVersion(android_app->config),
+            AConfiguration_getScreenSize(android_app->config),
+            AConfiguration_getScreenLong(android_app->config),
+            AConfiguration_getUiModeType(android_app->config),
+            AConfiguration_getUiModeNight(android_app->config));
+}
+
+void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
+    switch (cmd) {
+        case APP_CMD_INPUT_CHANGED:
+            LOGV("APP_CMD_INPUT_CHANGED\n");
+            pthread_mutex_lock(&android_app->mutex);
+            if (android_app->inputQueue != NULL) {
+                AInputQueue_detachLooper(android_app->inputQueue);
+            }
+            android_app->inputQueue = android_app->pendingInputQueue;
+            if (android_app->inputQueue != NULL) {
+                LOGV("Attaching input queue to looper");
+                AInputQueue_attachLooper(android_app->inputQueue,
+                        android_app->looper, LOOPER_ID_INPUT, NULL,
+                        &android_app->inputPollSource);
+            }
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+            break;
+
+        case APP_CMD_INIT_WINDOW:
+            LOGV("APP_CMD_INIT_WINDOW\n");
+            pthread_mutex_lock(&android_app->mutex);
+            android_app->window = android_app->pendingWindow;
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+            break;
+
+        case APP_CMD_TERM_WINDOW:
+            LOGV("APP_CMD_TERM_WINDOW\n");
+            pthread_cond_broadcast(&android_app->cond);
+            break;
+
+        case APP_CMD_RESUME:
+        case APP_CMD_START:
+        case APP_CMD_PAUSE:
+        case APP_CMD_STOP:
+            LOGV("activityState=%d\n", cmd);
+            pthread_mutex_lock(&android_app->mutex);
+            android_app->activityState = cmd;
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+            break;
+
+        case APP_CMD_CONFIG_CHANGED:
+            LOGV("APP_CMD_CONFIG_CHANGED\n");
+            AConfiguration_fromAssetManager(android_app->config,
+                    android_app->activity->assetManager);
+            print_cur_config(android_app);
+            break;
+
+        case APP_CMD_DESTROY:
+            LOGV("APP_CMD_DESTROY\n");
+            android_app->destroyRequested = 1;
+            break;
+    }
+}
+
+void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
+    switch (cmd) {
+        case APP_CMD_TERM_WINDOW:
+            LOGV("APP_CMD_TERM_WINDOW\n");
+            pthread_mutex_lock(&android_app->mutex);
+            android_app->window = NULL;
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+            break;
+
+        case APP_CMD_SAVE_STATE:
+            LOGV("APP_CMD_SAVE_STATE\n");
+            pthread_mutex_lock(&android_app->mutex);
+            android_app->stateSaved = 1;
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+            break;
+
+        case APP_CMD_RESUME:
+            free_saved_state(android_app);
+            break;
+    }
+}
+
+void app_dummy() {
+
+}
+
+static void android_app_destroy(struct android_app* android_app) {
+    LOGV("android_app_destroy!");
+    free_saved_state(android_app);
+    pthread_mutex_lock(&android_app->mutex);
+    if (android_app->inputQueue != NULL) {
+        AInputQueue_detachLooper(android_app->inputQueue);
+    }
+    AConfiguration_delete(android_app->config);
+    android_app->destroyed = 1;
+    pthread_cond_broadcast(&android_app->cond);
+    pthread_mutex_unlock(&android_app->mutex);
+    // Can't touch android_app object after this.
+}
+
+static void process_input(struct android_app* app, struct android_poll_source* source) {
+    AInputEvent* event = NULL;
+    while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
+        LOGV("New input event: type=%d\n", AInputEvent_getType(event));
+        if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
+            continue;
+        }
+        int32_t handled = 0;
+        if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
+        AInputQueue_finishEvent(app->inputQueue, event, handled);
+    }
+}
+
+static void process_cmd(struct android_app* app, struct android_poll_source* source) {
+    int8_t cmd = android_app_read_cmd(app);
+    android_app_pre_exec_cmd(app, cmd);
+    if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
+    android_app_post_exec_cmd(app, cmd);
+}
+
+static void* android_app_entry(void* param) {
+    struct android_app* android_app = (struct android_app*)param;
+
+    android_app->config = AConfiguration_new();
+    AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
+
+    print_cur_config(android_app);
+
+    android_app->cmdPollSource.id = LOOPER_ID_MAIN;
+    android_app->cmdPollSource.app = android_app;
+    android_app->cmdPollSource.process = process_cmd;
+    android_app->inputPollSource.id = LOOPER_ID_INPUT;
+    android_app->inputPollSource.app = android_app;
+    android_app->inputPollSource.process = process_input;
+
+    ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+    ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
+            &android_app->cmdPollSource);
+    android_app->looper = looper;
+
+    pthread_mutex_lock(&android_app->mutex);
+    android_app->running = 1;
+    pthread_cond_broadcast(&android_app->cond);
+    pthread_mutex_unlock(&android_app->mutex);
+
+    android_main(android_app);
+
+    android_app_destroy(android_app);
+    return NULL;
+}
+
+// --------------------------------------------------------------------
+// Native activity interaction (called from main thread)
+// --------------------------------------------------------------------
+
+static struct android_app* android_app_create(ANativeActivity* activity,
+        void* savedState, size_t savedStateSize) {
+    struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
+    memset(android_app, 0, sizeof(struct android_app));
+    android_app->activity = activity;
+
+    pthread_mutex_init(&android_app->mutex, NULL);
+    pthread_cond_init(&android_app->cond, NULL);
+
+    if (savedState != NULL) {
+        android_app->savedState = malloc(savedStateSize);
+        android_app->savedStateSize = savedStateSize;
+        memcpy(android_app->savedState, savedState, savedStateSize);
+    }
+
+    int msgpipe[2];
+    if (pipe(msgpipe)) {
+        LOGE("could not create pipe: %s", strerror(errno));
+        return NULL;
+    }
+    android_app->msgread = msgpipe[0];
+    android_app->msgwrite = msgpipe[1];
+
+    pthread_attr_t attr; 
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+    pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
+
+    // Wait for thread to start.
+    pthread_mutex_lock(&android_app->mutex);
+    while (!android_app->running) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+
+    return android_app;
+}
+
+static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
+    if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
+        LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
+    }
+}
+
+static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
+    pthread_mutex_lock(&android_app->mutex);
+    android_app->pendingInputQueue = inputQueue;
+    android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
+    while (android_app->inputQueue != android_app->pendingInputQueue) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+}
+
+static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
+    pthread_mutex_lock(&android_app->mutex);
+    if (android_app->pendingWindow != NULL) {
+        android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
+    }
+    android_app->pendingWindow = window;
+    if (window != NULL) {
+        android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
+    }
+    while (android_app->window != android_app->pendingWindow) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+}
+
+static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
+    pthread_mutex_lock(&android_app->mutex);
+    android_app_write_cmd(android_app, cmd);
+    while (android_app->activityState != cmd) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+}
+
+static void android_app_free(struct android_app* android_app) {
+    pthread_mutex_lock(&android_app->mutex);
+    android_app_write_cmd(android_app, APP_CMD_DESTROY);
+    while (!android_app->destroyed) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+
+    close(android_app->msgread);
+    close(android_app->msgwrite);
+    pthread_cond_destroy(&android_app->cond);
+    pthread_mutex_destroy(&android_app->mutex);
+    free(android_app);
+}
+
+static void onDestroy(ANativeActivity* activity) {
+    LOGV("Destroy: %p\n", activity);
+    android_app_free((struct android_app*)activity->instance);
+}
+
+static void onStart(ANativeActivity* activity) {
+    LOGV("Start: %p\n", activity);
+    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
+}
+
+static void onResume(ANativeActivity* activity) {
+    LOGV("Resume: %p\n", activity);
+    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
+}
+
+static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
+    struct android_app* android_app = (struct android_app*)activity->instance;
+    void* savedState = NULL;
+
+    LOGV("SaveInstanceState: %p\n", activity);
+    pthread_mutex_lock(&android_app->mutex);
+    android_app->stateSaved = 0;
+    android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
+    while (!android_app->stateSaved) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+
+    if (android_app->savedState != NULL) {
+        savedState = android_app->savedState;
+        *outLen = android_app->savedStateSize;
+        android_app->savedState = NULL;
+        android_app->savedStateSize = 0;
+    }
+
+    pthread_mutex_unlock(&android_app->mutex);
+
+    return savedState;
+}
+
+static void onPause(ANativeActivity* activity) {
+    LOGV("Pause: %p\n", activity);
+    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
+}
+
+static void onStop(ANativeActivity* activity) {
+    LOGV("Stop: %p\n", activity);
+    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
+}
+
+static void onConfigurationChanged(ANativeActivity* activity) {
+    struct android_app* android_app = (struct android_app*)activity->instance;
+    LOGV("ConfigurationChanged: %p\n", activity);
+    android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
+}
+
+static void onLowMemory(ANativeActivity* activity) {
+    struct android_app* android_app = (struct android_app*)activity->instance;
+    LOGV("LowMemory: %p\n", activity);
+    android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
+}
+
+static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
+    LOGV("WindowFocusChanged: %p -- %d\n", activity, focused);
+    android_app_write_cmd((struct android_app*)activity->instance,
+            focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
+}
+
+static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
+    LOGV("NativeWindowCreated: %p -- %p\n", activity, window);
+    android_app_set_window((struct android_app*)activity->instance, window);
+}
+
+static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
+    LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window);
+    android_app_set_window((struct android_app*)activity->instance, NULL);
+}
+
+static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
+    LOGV("InputQueueCreated: %p -- %p\n", activity, queue);
+    android_app_set_input((struct android_app*)activity->instance, queue);
+}
+
+static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
+    LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue);
+    android_app_set_input((struct android_app*)activity->instance, NULL);
+}
+
+void ANativeActivity_onCreate(ANativeActivity* activity,
+        void* savedState, size_t savedStateSize) {
+    LOGV("Creating: %p\n", activity);
+    activity->callbacks->onDestroy = onDestroy;
+    activity->callbacks->onStart = onStart;
+    activity->callbacks->onResume = onResume;
+    activity->callbacks->onSaveInstanceState = onSaveInstanceState;
+    activity->callbacks->onPause = onPause;
+    activity->callbacks->onStop = onStop;
+    activity->callbacks->onConfigurationChanged = onConfigurationChanged;
+    activity->callbacks->onLowMemory = onLowMemory;
+    activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
+    activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
+    activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
+    activity->callbacks->onInputQueueCreated = onInputQueueCreated;
+    activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
+
+    activity->instance = android_app_create(activity, savedState, savedStateSize);
+}

+ 349 - 0
armorcore/sources/backends/android/android_native_app_glue.h

@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef _ANDROID_NATIVE_APP_GLUE_H
+#define _ANDROID_NATIVE_APP_GLUE_H
+
+#include <poll.h>
+#include <pthread.h>
+#include <sched.h>
+
+#include <android/configuration.h>
+#include <android/looper.h>
+#include <android/native_activity.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The native activity interface provided by <android/native_activity.h>
+ * is based on a set of application-provided callbacks that will be called
+ * by the Activity's main thread when certain events occur.
+ *
+ * This means that each one of this callbacks _should_ _not_ block, or they
+ * risk having the system force-close the application. This programming
+ * model is direct, lightweight, but constraining.
+ *
+ * The 'android_native_app_glue' static library is used to provide a different
+ * execution model where the application can implement its own main event
+ * loop in a different thread instead. Here's how it works:
+ *
+ * 1/ The application must provide a function named "android_main()" that
+ *    will be called when the activity is created, in a new thread that is
+ *    distinct from the activity's main thread.
+ *
+ * 2/ android_main() receives a pointer to a valid "android_app" structure
+ *    that contains references to other important objects, e.g. the
+ *    ANativeActivity obejct instance the application is running in.
+ *
+ * 3/ the "android_app" object holds an ALooper instance that already
+ *    listens to two important things:
+ *
+ *      - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
+ *        declarations below.
+ *
+ *      - input events coming from the AInputQueue attached to the activity.
+ *
+ *    Each of these correspond to an ALooper identifier returned by
+ *    ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
+ *    respectively.
+ *
+ *    Your application can use the same ALooper to listen to additional
+ *    file-descriptors.  They can either be callback based, or with return
+ *    identifiers starting with LOOPER_ID_USER.
+ *
+ * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
+ *    the returned data will point to an android_poll_source structure.  You
+ *    can call the process() function on it, and fill in android_app->onAppCmd
+ *    and android_app->onInputEvent to be called for your own processing
+ *    of the event.
+ *
+ *    Alternatively, you can call the low-level functions to read and process
+ *    the data directly...  look at the process_cmd() and process_input()
+ *    implementations in the glue to see how to do this.
+ *
+ * See the sample named "native-activity" that comes with the NDK with a
+ * full usage example.  Also look at the JavaDoc of NativeActivity.
+ */
+
+struct android_app;
+
+/**
+ * Data associated with an ALooper fd that will be returned as the "outData"
+ * when that source has data ready.
+ */
+struct android_poll_source {
+    // The identifier of this source.  May be LOOPER_ID_MAIN or
+    // LOOPER_ID_INPUT.
+    int32_t id;
+
+    // The android_app this ident is associated with.
+    struct android_app* app;
+
+    // Function to call to perform the standard processing of data from
+    // this source.
+    void (*process)(struct android_app* app, struct android_poll_source* source);
+};
+
+/**
+ * This is the interface for the standard glue code of a threaded
+ * application.  In this model, the application's code is running
+ * in its own thread separate from the main thread of the process.
+ * It is not required that this thread be associated with the Java
+ * VM, although it will need to be in order to make JNI calls any
+ * Java objects.
+ */
+struct android_app {
+    // The application can place a pointer to its own state object
+    // here if it likes.
+    void* userData;
+
+    // Fill this in with the function to process main app commands (APP_CMD_*)
+    void (*onAppCmd)(struct android_app* app, int32_t cmd);
+
+    // Fill this in with the function to process input events.  At this point
+    // the event has already been pre-dispatched, and it will be finished upon
+    // return.  Return 1 if you have handled the event, 0 for any default
+    // dispatching.
+    int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);
+
+    // The ANativeActivity object instance that this app is running in.
+    ANativeActivity* activity;
+
+    // The current configuration the app is running in.
+    AConfiguration* config;
+
+    // This is the last instance's saved state, as provided at creation time.
+    // It is NULL if there was no state.  You can use this as you need; the
+    // memory will remain around until you call android_app_exec_cmd() for
+    // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL.
+    // These variables should only be changed when processing a APP_CMD_SAVE_STATE,
+    // at which point they will be initialized to NULL and you can malloc your
+    // state and place the information here.  In that case the memory will be
+    // freed for you later.
+    void* savedState;
+    size_t savedStateSize;
+
+    // The ALooper associated with the app's thread.
+    ALooper* looper;
+
+    // When non-NULL, this is the input queue from which the app will
+    // receive user input events.
+    AInputQueue* inputQueue;
+
+    // When non-NULL, this is the window surface that the app can draw in.
+    ANativeWindow* window;
+
+    // Current content rectangle of the window; this is the area where the
+    // window's content should be placed to be seen by the user.
+    ARect contentRect;
+
+    // Current state of the app's activity.  May be either APP_CMD_START,
+    // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below.
+    int activityState;
+
+    // This is non-zero when the application's NativeActivity is being
+    // destroyed and waiting for the app thread to complete.
+    int destroyRequested;
+
+    // -------------------------------------------------
+    // Below are "private" implementation of the glue code.
+
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+
+    int msgread;
+    int msgwrite;
+
+    pthread_t thread;
+
+    struct android_poll_source cmdPollSource;
+    struct android_poll_source inputPollSource;
+
+    int running;
+    int stateSaved;
+    int destroyed;
+    int redrawNeeded;
+    AInputQueue* pendingInputQueue;
+    ANativeWindow* pendingWindow;
+    ARect pendingContentRect;
+};
+
+enum {
+    /**
+     * Looper data ID of commands coming from the app's main thread, which
+     * is returned as an identifier from ALooper_pollOnce().  The data for this
+     * identifier is a pointer to an android_poll_source structure.
+     * These can be retrieved and processed with android_app_read_cmd()
+     * and android_app_exec_cmd().
+     */
+    LOOPER_ID_MAIN = 1,
+
+    /**
+     * Looper data ID of events coming from the AInputQueue of the
+     * application's window, which is returned as an identifier from
+     * ALooper_pollOnce().  The data for this identifier is a pointer to an
+     * android_poll_source structure.  These can be read via the inputQueue
+     * object of android_app.
+     */
+    LOOPER_ID_INPUT = 2,
+
+    /**
+     * Start of user-defined ALooper identifiers.
+     */
+    LOOPER_ID_USER = 3,
+};
+
+enum {
+    /**
+     * Command from main thread: the AInputQueue has changed.  Upon processing
+     * this command, android_app->inputQueue will be updated to the new queue
+     * (or NULL).
+     */
+    APP_CMD_INPUT_CHANGED,
+
+    /**
+     * Command from main thread: a new ANativeWindow is ready for use.  Upon
+     * receiving this command, android_app->window will contain the new window
+     * surface.
+     */
+    APP_CMD_INIT_WINDOW,
+
+    /**
+     * Command from main thread: the existing ANativeWindow needs to be
+     * terminated.  Upon receiving this command, android_app->window still
+     * contains the existing window; after calling android_app_exec_cmd
+     * it will be set to NULL.
+     */
+    APP_CMD_TERM_WINDOW,
+
+    /**
+     * Command from main thread: the current ANativeWindow has been resized.
+     * Please redraw with its new size.
+     */
+    APP_CMD_WINDOW_RESIZED,
+
+    /**
+     * Command from main thread: the system needs that the current ANativeWindow
+     * be redrawn.  You should redraw the window before handing this to
+     * android_app_exec_cmd() in order to avoid transient drawing glitches.
+     */
+    APP_CMD_WINDOW_REDRAW_NEEDED,
+
+    /**
+     * Command from main thread: the content area of the window has changed,
+     * such as from the soft input window being shown or hidden.  You can
+     * find the new content rect in android_app::contentRect.
+     */
+    APP_CMD_CONTENT_RECT_CHANGED,
+
+    /**
+     * Command from main thread: the app's activity window has gained
+     * input focus.
+     */
+    APP_CMD_GAINED_FOCUS,
+
+    /**
+     * Command from main thread: the app's activity window has lost
+     * input focus.
+     */
+    APP_CMD_LOST_FOCUS,
+
+    /**
+     * Command from main thread: the current device configuration has changed.
+     */
+    APP_CMD_CONFIG_CHANGED,
+
+    /**
+     * Command from main thread: the system is running low on memory.
+     * Try to reduce your memory use.
+     */
+    APP_CMD_LOW_MEMORY,
+
+    /**
+     * Command from main thread: the app's activity has been started.
+     */
+    APP_CMD_START,
+
+    /**
+     * Command from main thread: the app's activity has been resumed.
+     */
+    APP_CMD_RESUME,
+
+    /**
+     * Command from main thread: the app should generate a new saved state
+     * for itself, to restore from later if needed.  If you have saved state,
+     * allocate it with malloc and place it in android_app.savedState with
+     * the size in android_app.savedStateSize.  The will be freed for you
+     * later.
+     */
+    APP_CMD_SAVE_STATE,
+
+    /**
+     * Command from main thread: the app's activity has been paused.
+     */
+    APP_CMD_PAUSE,
+
+    /**
+     * Command from main thread: the app's activity has been stopped.
+     */
+    APP_CMD_STOP,
+
+    /**
+     * Command from main thread: the app's activity is being destroyed,
+     * and waiting for the app thread to clean up and exit before proceeding.
+     */
+    APP_CMD_DESTROY,
+};
+
+/**
+ * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
+ * app command message.
+ */
+int8_t android_app_read_cmd(struct android_app* android_app);
+
+/**
+ * Call with the command returned by android_app_read_cmd() to do the
+ * initial pre-processing of the given command.  You can perform your own
+ * actions for the command after calling this function.
+ */
+void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
+
+/**
+ * Call with the command returned by android_app_read_cmd() to do the
+ * final post-processing of the given command.  You must have done your own
+ * actions for the command before calling this function.
+ */
+void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);
+
+/**
+ * Dummy function you can call to ensure glue code isn't stripped.
+ */
+void app_dummy();
+
+/**
+ * This is the function that application code must implement, representing
+ * the main entry to the app.
+ */
+extern void android_main(struct android_app* app);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ANDROID_NATIVE_APP_GLUE_H */

+ 31 - 0
armorcore/sources/backends/android/java/arm/AndroidHttpRequest.java

@@ -0,0 +1,31 @@
+package arm;
+
+// TODO: Move to Kinc
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+class AndroidHttpRequest {
+
+	public static byte[] androidHttpRequest(String address) throws Exception {
+		// https://developer.android.com/reference/java/net/HttpURLConnection.html
+		URL url = new URL(address);
+		HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
+		InputStream in = new BufferedInputStream(urlConnection.getInputStream());
+
+		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+		int i;
+		byte[] data = new byte[4];
+		while ((i = in.read(data, 0, data.length)) != -1) {
+			buffer.write(data, 0, i);
+		}
+		buffer.flush();
+		byte[] result = buffer.toByteArray();
+
+		urlConnection.disconnect();
+		return result;
+	}
+}

+ 267 - 0
armorcore/sources/backends/android/java/tech/kinc/KincActivity.kt

@@ -0,0 +1,267 @@
+package tech.kinc
+
+import android.app.NativeActivity
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.Message
+import android.os.Vibrator
+import android.os.VibrationEffect
+import android.os.Build
+import android.view.KeyEvent
+import android.view.View
+import android.view.WindowManager
+import android.view.inputmethod.InputMethodManager
+import kotlin.system.exitProcess
+import android.content.ContentResolver;
+import android.util.Log
+import android.view.DragAndDropPermissions;
+import android.view.DragEvent;
+import android.webkit.MimeTypeMap;
+import android.view.DragEvent.ACTION_DRAG_STARTED;
+import android.view.DragEvent.ACTION_DROP;
+import androidx.core.database.getStringOrNull
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import kotlin.math.log
+
+class KincActivity: NativeActivity(), KeyEvent.Callback {
+	companion object {
+		var instance: KincActivity? = null
+
+		@JvmStatic
+		fun showKeyboard() {
+			instance!!.inputManager!!.showSoftInput(instance!!.window.decorView, 0)
+		}
+
+		@JvmStatic
+		fun hideKeyboard() {
+			instance!!.inputManager!!.hideSoftInputFromWindow(instance!!.window.decorView.windowToken, 0)
+			instance!!.delayedHideSystemUI()
+		}
+
+		@JvmStatic
+		fun loadURL(url: String) {
+			val i = Intent(Intent.ACTION_VIEW, Uri.parse(url))
+			instance!!.startActivity(i)
+		}
+
+		@JvmStatic
+		fun getLanguage(): String {
+			return java.util.Locale.getDefault().language
+		}
+
+		@JvmStatic
+		fun vibrate(ms: Int) {
+			val v: Vibrator = instance!!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+				v.vibrate(VibrationEffect.createOneShot(ms.toLong(), VibrationEffect.DEFAULT_AMPLITUDE))
+			}
+			else {
+				// deprecated in API 26
+				v.vibrate(ms.toLong())
+			}
+		}
+
+		@JvmStatic
+		fun getRotation(): Int {
+			val context: Context = instance!!.applicationContext
+			val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+			return manager.defaultDisplay.rotation
+		}
+
+		@JvmStatic
+		fun getScreenDpi(): Int {
+			val context: Context = instance!!.applicationContext
+			val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+			val metrics: android.util.DisplayMetrics = android.util.DisplayMetrics()
+			manager.defaultDisplay.getMetrics(metrics)
+			return metrics.xdpi.toInt()
+		}
+
+		@JvmStatic
+		fun getRefreshRate(): Int {
+			val context: Context = instance!!.applicationContext
+			val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+			return manager.defaultDisplay.refreshRate.toInt()
+		}
+
+		@JvmStatic
+		fun getDisplayWidth(): Int {
+			val context: Context = instance!!.applicationContext
+			val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+			val size: android.graphics.Point = android.graphics.Point()
+			manager.defaultDisplay.getRealSize(size)
+			return size.x
+		}
+
+		@JvmStatic
+		fun getDisplayHeight(): Int {
+			val context: Context = instance!!.applicationContext
+			val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+			val size: android.graphics.Point = android.graphics.Point()
+			manager.defaultDisplay.getRealSize(size)
+			return size.y
+		}
+
+		@JvmStatic
+		fun stop() {
+			instance!!.runOnUiThread {
+				fun run() {
+					instance!!.finish()
+					exitProcess(0)
+				}
+			}
+		}
+
+		class MyHandler(private val kincActivity: KincActivity) : Handler() {
+			override fun handleMessage(msg: Message) {
+				kincActivity.hideSystemUI()
+			}
+		}
+
+		@JvmStatic
+		public fun pickFile() {
+			val intent: Intent = Intent(Intent.ACTION_GET_CONTENT)
+			intent.type = "*/*"
+			instance!!.startActivityForResult(Intent.createChooser(intent, "Select File"), 1)
+		}
+	}
+
+	var inputManager: InputMethodManager? = null
+	private var isDisabledStickyImmersiveMode = false
+
+	private val hideSystemUIHandler = MyHandler(this)
+
+	override fun onCreate(state: Bundle?) {
+		super.onCreate(state)
+		hideSystemUI()
+		instance = this
+		inputManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+		isDisabledStickyImmersiveMode = try {
+			val ai: ApplicationInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
+			val bundle: Bundle = ai.metaData
+			bundle.getBoolean("disableStickyImmersiveMode")
+		} catch (e: PackageManager.NameNotFoundException) {
+			false
+		} catch (e: NullPointerException) {
+			false
+		}
+
+		window.decorView.setOnDragListener(
+				fun (view: View, dragEvent: DragEvent): Boolean {
+					if (dragEvent.action == ACTION_DRAG_STARTED) return true
+					if (dragEvent.action == ACTION_DROP) {
+						val dropPermissions = requestDragAndDropPermissions(dragEvent)
+						importFile(dragEvent.clipData.getItemAt(0).uri)
+						dropPermissions.release()
+						return true
+					}
+					return false
+				}
+		);
+	}
+
+    private fun hideSystemUI() {
+        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+    }
+
+    private fun delayedHideSystemUI() {
+        hideSystemUIHandler.removeMessages(0)
+        if (!isDisabledStickyImmersiveMode) {
+            hideSystemUIHandler.sendEmptyMessageDelayed(0, 300)
+        }
+    }
+
+    override fun onWindowFocusChanged(hasFocus: Boolean) {
+        super.onWindowFocusChanged(hasFocus)
+        if (hasFocus) {
+            delayedHideSystemUI()
+        }
+        else {
+            hideSystemUIHandler.removeMessages(0)
+        }
+    }
+
+	override fun onKeyMultiple(keyCode: Int, count: Int, event: KeyEvent): Boolean {
+		this.nativeKincKeyPress(event.characters)
+		return false
+	}
+
+	private external fun nativeKincKeyPress(chars: String)
+
+	private external fun onAndroidFilePicked(pickedPath: String)
+	private external fun getMobileTitle(): String
+
+	override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
+		super.onActivityResult(requestCode, resultCode, data)
+		if (requestCode == 1 && resultCode == RESULT_OK) {
+			importFile(data.data!!)
+		}
+	}
+
+	private fun importFile(pickedFile: Uri) {
+		val resolver: ContentResolver = applicationContext.contentResolver
+		val inps: InputStream = resolver.openInputStream(pickedFile)!!
+		try {
+			val bis: BufferedInputStream = BufferedInputStream(inps)
+			val dir: File = File(filesDir.absolutePath + "/" + getMobileTitle())
+			dir.mkdirs()
+			var path: List<String> = pickedFile.path!!.split("/")
+
+			// Samsung files app removes extension from fileName
+			val filePath: Array<String> = arrayOf(android.provider.MediaStore.Images.Media.DATA)
+			val cursor: android.database.Cursor = contentResolver.query(pickedFile, filePath, null, null, null)!!
+			cursor.moveToFirst()
+			val pickedPath: String? = cursor.getStringOrNull(cursor.getColumnIndex(filePath[0]))
+			if (pickedPath != null) {
+				path = pickedPath.split("/")
+			}
+			cursor.close()
+
+			var fileName: String = path[path.size - 1]
+
+			// Extension still unknown
+			if (!fileName.contains(".")) {
+				var ext: String = MimeTypeMap.getSingleton().getExtensionFromMimeType(contentResolver.getType(pickedFile))!!
+				// Note: for obj/fbx file, the extension returned is bin..
+				if (ext == "bin") {
+					bis.mark(0)
+					val header: StringBuilder = StringBuilder()
+					for (i in 0..17) {
+						val c: Int = bis.read()
+						if (c == -1) break
+						header.append(c.toChar())
+					}
+					ext = if (header.toString() == "Kaydara FBX Binary") "fbx" else "obj"
+					bis.reset()
+				}
+				fileName += "." + ext
+			}
+
+			val dst: String = filesDir.absolutePath + "/" + getMobileTitle() + "/" + fileName
+			val os: OutputStream = FileOutputStream(dst)
+			try {
+				val buf = ByteArray(1024)
+				var len = bis.read(buf)
+				while (len > 0) {
+					os.write(buf, 0, len)
+					len = bis.read(buf)
+				}
+				onAndroidFilePicked(dst)
+			}
+			catch (e: IOException) {}
+		}
+		catch (e: FileNotFoundException) {}
+		catch (e: IOException) {}
+	}
+}

+ 50 - 0
armorcore/sources/backends/android/java/tech/kinc/KincMoviePlayer.kt

@@ -0,0 +1,50 @@
+package tech.kinc
+
+import java.util.ArrayList
+
+import android.view.Surface
+
+class KincMoviePlayer(var path: String) {
+	companion object {
+		var players = ArrayList<KincMoviePlayer?>()
+
+		@JvmStatic
+		fun updateAll() {
+			for (player in KincMoviePlayer.players) {
+				player!!.update()
+			}
+		}
+
+		fun remove(id: Int) {
+			players[id] = null
+		}
+	}
+
+	private var movieTexture: KincMovieTexture? = null
+	var id: Int = players.size
+
+	init {
+		players.add(this)
+	}
+	
+	fun init() {
+		movieTexture = KincMovieTexture()
+		val surface = Surface(movieTexture!!.surfaceTexture)
+		nativeCreate(path, surface, id)
+		surface.release()
+	}
+
+	fun getMovieTexture(): KincMovieTexture? {
+		return movieTexture
+	}
+
+	fun update(): Boolean {
+		return movieTexture!!.update()
+	}
+	
+	fun getTextureId(): Int {
+		return movieTexture!!.textureId
+	}
+
+	private external fun nativeCreate(path: String, surface: Surface, id: Int)
+}

+ 62 - 0
armorcore/sources/backends/android/java/tech/kinc/KincMovieTexture.kt

@@ -0,0 +1,62 @@
+package tech.kinc
+
+import android.graphics.SurfaceTexture
+import android.graphics.SurfaceTexture.OnFrameAvailableListener
+import android.opengl.GLES20
+
+class KincMovieTexture: OnFrameAvailableListener {
+	private val GL_TEXTURE_EXTERNAL_OES: Int = 0x8D65
+
+	var textureId: Int = 0
+
+	init {
+		val textures = IntArray(1)
+		GLES20.glGenTextures(1, textures, 0)
+		textureId = textures[0]
+
+		GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId)
+		GLES20.glTexParameteri(
+			GL_TEXTURE_EXTERNAL_OES,
+			GLES20.GL_TEXTURE_MIN_FILTER,
+			GLES20.GL_NEAREST
+		)
+		GLES20.glTexParameteri(
+			GL_TEXTURE_EXTERNAL_OES,
+			GLES20.GL_TEXTURE_MAG_FILTER,
+			GLES20.GL_LINEAR
+		)
+		GLES20.glTexParameteri(
+			GL_TEXTURE_EXTERNAL_OES,
+			GLES20.GL_TEXTURE_WRAP_S,
+			GLES20.GL_CLAMP_TO_EDGE
+		)
+		GLES20.glTexParameteri(
+			GL_TEXTURE_EXTERNAL_OES,
+			GLES20.GL_TEXTURE_WRAP_T,
+			GLES20.GL_CLAMP_TO_EDGE
+		)
+	}
+
+	var surfaceTexture = SurfaceTexture(textureId)
+
+	init {
+		surfaceTexture.setOnFrameAvailableListener(this)
+	}
+
+	private var updateTexture = false
+
+	fun update(): Boolean {
+		val ret = updateTexture
+		if (updateTexture) {
+			surfaceTexture.updateTexImage()
+			updateTexture = false
+		}
+		return ret
+	}
+	
+	override fun onFrameAvailable(surface: SurfaceTexture) {
+		if (surfaceTexture == surface) {
+			updateTexture = true
+		}
+	}
+}

+ 18 - 0
armorcore/sources/backends/android/kinc/backend/Android.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include <android_native_app_glue.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// name in usual Java syntax (points, no slashes)
+jclass kinc_android_find_class(JNIEnv *env, const char *name);
+
+ANativeActivity *kinc_android_get_activity(void);
+
+AAssetManager *kinc_android_get_asset_manager(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 111 - 0
armorcore/sources/backends/android/kinc/backend/android_file_dialog.c

@@ -0,0 +1,111 @@
+#include "android_file_dialog.h"
+#include <kinc/system.h>
+#include <jni.h>
+#include <string.h>
+#include <stdlib.h>
+#include <android_native_app_glue.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+extern char mobile_title[1024];
+
+ANativeActivity *kinc_android_get_activity(void);
+jclass kinc_android_find_class(JNIEnv *env, const char *name);
+
+JNIEXPORT void JNICALL Java_tech_kinc_KincActivity_onAndroidFilePicked(JNIEnv *env, jobject jobj, jstring jstr) {
+	if (jstr == NULL) return;
+	const char *str = (*env)->GetStringUTFChars(env, jstr, 0);
+	size_t len = strlen(str);
+	wchar_t filePath[len + 1];
+	mbstowcs(filePath, (char *)str, len);
+	filePath[len] = 0;
+
+	kinc_internal_drop_files_callback(filePath);
+	(*env)->ReleaseStringUTFChars(env, jstr, str);
+}
+
+JNIEXPORT jstring JNICALL Java_tech_kinc_KincActivity_getMobileTitle(JNIEnv *env, jobject jobj) {
+	jstring result = (*env)->NewStringUTF(env, mobile_title);
+	return result;
+}
+
+void AndroidFileDialogOpen() {
+	ANativeActivity *activity = kinc_android_get_activity();
+	JNIEnv *env;
+	JavaVM *vm = kinc_android_get_activity()->vm;
+	(*vm)->AttachCurrentThread(vm, &env, NULL);
+	jclass kincActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	(*env)->CallStaticVoidMethod(env, kincActivityClass, (*env)->GetStaticMethodID(env, kincActivityClass, "pickFile", "()V"));
+	(*vm)->DetachCurrentThread(vm);
+}
+
+wchar_t *AndroidFileDialogSave() {
+	// kinc_android_get_activity()->externalDataPath; // /storage/emulated/0/Android/data/org.armorpaint/files
+	mkdir("/storage/emulated/0/Pictures/ArmorPaint", 0777);
+	return L"/storage/emulated/0/Pictures/ArmorPaint/untitled";
+}
+
+jstring android_permission_name(JNIEnv *env, const char *perm_name) {
+	jclass ClassManifestpermission = (*env)->FindClass(env, "android/Manifest$permission");
+	jfieldID lid_PERM = (*env)->GetStaticFieldID(env, ClassManifestpermission, perm_name, "Ljava/lang/String;");
+	jstring ls_PERM = (jstring)((*env)->GetStaticObjectField(env, ClassManifestpermission, lid_PERM));
+	return ls_PERM;
+}
+
+bool android_has_permission(struct android_app *app, const char *perm_name) {
+	ANativeActivity *activity = kinc_android_get_activity();
+	JNIEnv *env;
+	JavaVM *vm = kinc_android_get_activity()->vm;
+	(*vm)->AttachCurrentThread(vm, &env, NULL);
+	bool result = false;
+	jstring ls_PERM = android_permission_name(env, perm_name);
+	jint PERMISSION_GRANTED = (jint)(-1);
+	{
+		jclass ClassPackageManager = (*env)->FindClass(env, "android/content/pm/PackageManager");
+		jfieldID lid_PERMISSION_GRANTED = (*env)->GetStaticFieldID(env, ClassPackageManager, "PERMISSION_GRANTED", "I");
+		PERMISSION_GRANTED = (*env)->GetStaticIntField(env, ClassPackageManager, lid_PERMISSION_GRANTED);
+	}
+	{
+		jobject activity = app->activity->clazz;
+		jclass ClassContext = (*env)->FindClass(env, "android/content/Context");
+		jmethodID MethodcheckSelfPermission = (*env)->GetMethodID(env, ClassContext, "checkSelfPermission", "(Ljava/lang/String;)I");
+		jint int_result = (*env)->CallIntMethod(env, activity, MethodcheckSelfPermission, ls_PERM);
+		result = (int_result == PERMISSION_GRANTED);
+	}
+	(*vm)->DetachCurrentThread(vm);
+	return result;
+}
+
+void android_request_file_permissions(struct android_app *app) {
+	ANativeActivity *activity = kinc_android_get_activity();
+	JNIEnv *env;
+	JavaVM *vm = kinc_android_get_activity()->vm;
+	(*vm)->AttachCurrentThread(vm, &env, NULL);
+	jobjectArray perm_array = (*env)->NewObjectArray(env, 2, (*env)->FindClass(env, "java/lang/String"), (*env)->NewStringUTF(env, ""));
+	(*env)->SetObjectArrayElement(env, perm_array, 0, android_permission_name(env, "READ_EXTERNAL_STORAGE"));
+	(*env)->SetObjectArrayElement(env, perm_array, 1, android_permission_name(env, "WRITE_EXTERNAL_STORAGE"));
+	jobject jactivity = app->activity->clazz;
+	jclass ClassActivity = (*env)->FindClass(env, "android/app/Activity");
+	jmethodID MethodrequestPermissions = (*env)->GetMethodID(env, ClassActivity, "requestPermissions", "([Ljava/lang/String;I)V");
+	(*env)->CallVoidMethod(env, jactivity, MethodrequestPermissions, perm_array, 0);
+	(*vm)->DetachCurrentThread(vm);
+}
+
+void android_check_permissions() {
+	ANativeActivity *activity = kinc_android_get_activity();
+	struct android_app *app = (struct android_app *)activity->instance;
+	bool hasPermissions = android_has_permission(app, "READ_EXTERNAL_STORAGE") && android_has_permission(app, "WRITE_EXTERNAL_STORAGE");
+	if (!hasPermissions) android_request_file_permissions(app);
+
+	JNIEnv *env;
+	JavaVM *vm = kinc_android_get_activity()->vm;
+	(*vm)->AttachCurrentThread(vm, &env, NULL);
+	jclass kincActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	JNINativeMethod methodTable[] = {
+		{"onAndroidFilePicked", "(Ljava/lang/String;)V", (void *)Java_tech_kinc_KincActivity_onAndroidFilePicked},
+		{"getMobileTitle", "()Ljava/lang/String;", (void *)Java_tech_kinc_KincActivity_getMobileTitle}
+	};
+	int methodTableSize = sizeof(methodTable) / sizeof(methodTable[0]);
+	(*env)->RegisterNatives(env, kincActivityClass, methodTable, methodTableSize);
+	(*vm)->DetachCurrentThread(vm);
+}

+ 15 - 0
armorcore/sources/backends/android/kinc/backend/android_file_dialog.h

@@ -0,0 +1,15 @@
+#pragma once
+
+#include <wchar.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void AndroidFileDialogOpen();
+wchar_t *AndroidFileDialogSave();
+void android_check_permissions();
+
+#ifdef __cplusplus
+}
+#endif

+ 28 - 0
armorcore/sources/backends/android/kinc/backend/android_http_request.c

@@ -0,0 +1,28 @@
+#include "android_http_request.h"
+#include <kinc/system.h>
+#include <jni.h>
+#include <string.h>
+#include <android_native_app_glue.h>
+
+ANativeActivity *kinc_android_get_activity(void);
+jclass kinc_android_find_class(JNIEnv *env, const char *name);
+
+void android_http_request(const char *url, const char *path, const char *data, int port, bool secure, int method, const char *header,
+                          kinc_http_callback_t callback, void *callbackdata) {
+    ANativeActivity *activity = kinc_android_get_activity();
+    JNIEnv *env;
+    JavaVM *vm = kinc_android_get_activity()->vm;
+    (*vm)->AttachCurrentThread(vm, &env, NULL);
+    jclass activityClass = kinc_android_find_class(env, "arm.AndroidHttpRequest");
+
+    jstring jstr = (*env)->NewStringUTF(env, url);
+    jbyteArray bytes_array = (jbyteArray)((*env)->CallStaticObjectMethod(env, activityClass, (*env)->GetStaticMethodID(env, activityClass, "androidHttpRequest", "(Ljava/lang/String;)[B"), jstr));
+    jsize num_bytes = (*env)->GetArrayLength(env, bytes_array);
+    jbyte *elements = (*env)->GetByteArrayElements(env, bytes_array, NULL);
+    if (elements != NULL) {
+        callback(0, 200, (char *)elements, callbackdata);
+        // (*env)->ReleaseByteArrayElements(env, bytes_array, elements, JNI_ABORT);
+    }
+
+    (*vm)->DetachCurrentThread(vm);
+}

+ 16 - 0
armorcore/sources/backends/android/kinc/backend/android_http_request.h

@@ -0,0 +1,16 @@
+#pragma once
+
+#include <stdbool.h>
+
+typedef void (*kinc_http_callback_t)(int error, int response, const char *body, void *callbackdata);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void android_http_request(const char *url, const char *path, const char *data, int port, bool secure, int method, const char *header,
+						  kinc_http_callback_t callback, void *callbackdata);
+
+#ifdef __cplusplus
+}
+#endif

+ 5 - 0
armorcore/sources/backends/android/kinc/backend/androidunit.c

@@ -0,0 +1,5 @@
+#include "audio.c.h"
+#include "display.c.h"
+#include "system.c.h"
+#include "window.c.h"
+#include "video.c.h"

+ 133 - 0
armorcore/sources/backends/android/kinc/backend/audio.c.h

@@ -0,0 +1,133 @@
+#include <kinc/audio2/audio.h>
+
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+static kinc_a2_buffer_t a2_buffer;
+
+static SLObjectItf engineObject;
+static SLEngineItf engineEngine;
+static SLObjectItf outputMixObject;
+static SLObjectItf bqPlayerObject;
+static SLPlayItf bqPlayerPlay = NULL;
+static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
+#define AUDIO_BUFFER_SIZE 1 * 1024
+static int16_t tempBuffer[AUDIO_BUFFER_SIZE];
+
+static void copySample(void *buffer) {
+	float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location];
+	float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location];
+	a2_buffer.read_location += 1;
+	if (a2_buffer.read_location >= a2_buffer.data_size) {
+		a2_buffer.read_location = 0;
+	}
+	((int16_t *)buffer)[0] = (int16_t)(left_value * 32767);
+	((int16_t *)buffer)[1] = (int16_t)(right_value * 32767);
+}
+
+static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf caller, void *context) {
+	if (kinc_a2_internal_callback(&a2_buffer, AUDIO_BUFFER_SIZE / 2)) {
+		for (int i = 0; i < AUDIO_BUFFER_SIZE; i += 2) {
+			copySample(&tempBuffer[i]);
+		}
+	}
+	else {
+		memset(tempBuffer, 0, sizeof(tempBuffer));
+	}
+	SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, tempBuffer, AUDIO_BUFFER_SIZE * 2);
+}
+
+static bool initialized = false;
+
+void kinc_a2_init() {
+	if (initialized) {
+		return;
+	}
+
+	kinc_a2_internal_init();
+	initialized = true;
+
+	a2_buffer.read_location = 0;
+	a2_buffer.write_location = 0;
+	a2_buffer.data_size = 128 * 1024;
+	a2_buffer.channel_count = 2;
+	a2_buffer.channels[0] = (float*)malloc(a2_buffer.data_size * sizeof(float));
+	a2_buffer.channels[1] = (float*)malloc(a2_buffer.data_size * sizeof(float));
+
+	SLresult result;
+	result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
+	result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
+	result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
+
+	const SLInterfaceID ids[] = {SL_IID_VOLUME};
+	const SLboolean req[] = {SL_BOOLEAN_FALSE};
+	result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
+	result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
+
+	SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
+	SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,           2,
+	                               SL_SAMPLINGRATE_44_1,        SL_PCMSAMPLEFORMAT_FIXED_16,
+	                               SL_PCMSAMPLEFORMAT_FIXED_16, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
+	                               SL_BYTEORDER_LITTLEENDIAN};
+	SLDataSource audioSrc = {&loc_bufq, &format_pcm};
+
+	SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
+	SLDataSink audioSnk = {&loc_outmix, NULL};
+
+	const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
+	const SLboolean req1[] = {SL_BOOLEAN_TRUE};
+	result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(bqPlayerObject), &audioSrc, &audioSnk, 1, ids1, req1);
+	result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
+
+	result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &(bqPlayerPlay));
+
+	result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(bqPlayerBufferQueue));
+
+	result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
+
+	result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
+
+	memset(tempBuffer, 0, sizeof(tempBuffer));
+	result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, tempBuffer, AUDIO_BUFFER_SIZE * 2);
+}
+
+void pauseAudio() {
+	if (bqPlayerPlay == NULL) {
+		return;
+	}
+	SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
+}
+
+void resumeAudio() {
+	if (bqPlayerPlay == NULL) {
+		return;
+	}
+	SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
+}
+
+void kinc_a2_update() {}
+
+void kinc_a2_shutdown() {
+	if (bqPlayerObject != NULL) {
+		(*bqPlayerObject)->Destroy(bqPlayerObject);
+		bqPlayerObject = NULL;
+		bqPlayerPlay = NULL;
+		bqPlayerBufferQueue = NULL;
+	}
+	if (outputMixObject != NULL) {
+		(*outputMixObject)->Destroy(outputMixObject);
+		outputMixObject = NULL;
+	}
+	if (engineObject != NULL) {
+		(*engineObject)->Destroy(engineObject);
+		engineObject = NULL;
+		engineEngine = NULL;
+	}
+}
+
+uint32_t kinc_a2_samples_per_second(void) {
+	return 44100;
+}

+ 106 - 0
armorcore/sources/backends/android/kinc/backend/display.c.h

@@ -0,0 +1,106 @@
+#include <kinc/backend/Android.h>
+
+#include <kinc/display.h>
+#include <kinc/log.h>
+
+typedef struct {
+	bool available;
+	int x;
+	int y;
+	int width;
+	int height;
+	bool primary;
+	int number;
+} kinc_display_t;
+
+static kinc_display_t display;
+
+int kinc_count_displays(void) {
+	return 1;
+}
+
+int kinc_primary_display(void) {
+	return 0;
+}
+
+static int width() {
+	JNIEnv *env;
+	JavaVM *vm = kinc_android_get_activity()->vm;
+	(*vm)->AttachCurrentThread(vm, &env, NULL);
+	jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getDisplayWidth", "()I");
+	int width = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi);
+	(*vm)->DetachCurrentThread(vm);
+	return width;
+}
+
+static int height() {
+	JNIEnv *env;
+	JavaVM *vm = kinc_android_get_activity()->vm;
+	(*vm)->AttachCurrentThread(vm, &env, NULL);
+	jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getDisplayHeight", "()I");
+	int height = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi);
+	(*vm)->DetachCurrentThread(vm);
+	return height;
+}
+
+static int pixelsPerInch() {
+	JNIEnv *env;
+	JavaVM *vm = kinc_android_get_activity()->vm;
+	(*vm)->AttachCurrentThread(vm, &env, NULL);
+	jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getScreenDpi", "()I");
+	int dpi = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi);
+	(*vm)->DetachCurrentThread(vm);
+	return dpi;
+}
+
+static int refreshRate() {
+	JNIEnv *env;
+	JavaVM *vm = kinc_android_get_activity()->vm;
+	(*vm)->AttachCurrentThread(vm, &env, NULL);
+	jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getRefreshRate", "()I");
+	int dpi = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi);
+	(*vm)->DetachCurrentThread(vm);
+	return dpi;
+}
+
+void kinc_display_init() {}
+
+kinc_display_mode_t kinc_display_available_mode(int display_index, int mode_index) {
+	kinc_display_mode_t mode;
+	mode.x = 0;
+	mode.y = 0;
+	mode.width = width();
+	mode.height = height();
+	mode.frequency = refreshRate();
+	mode.bits_per_pixel = 32;
+	mode.pixels_per_inch = pixelsPerInch();
+	return mode;
+}
+
+int kinc_display_count_available_modes(int display_index) {
+	return 1;
+}
+
+kinc_display_mode_t kinc_display_current_mode(int display) {
+	kinc_display_mode_t mode;
+	mode.x = 0;
+	mode.y = 0;
+	mode.width = width();
+	mode.height = height();
+	mode.frequency = refreshRate();
+	mode.bits_per_pixel = 32;
+	mode.pixels_per_inch = pixelsPerInch();
+	return mode;
+}
+
+const char *kinc_display_name(int display) {
+	return "Display";
+}
+
+bool kinc_display_available(int display) {
+	return display == 0;
+}

+ 1298 - 0
armorcore/sources/backends/android/kinc/backend/system.c.h

@@ -0,0 +1,1298 @@
+#define EGL_NO_PLATFORM_SPECIFIC_TYPES
+#include <EGL/egl.h>
+#include <kinc/error.h>
+// #include <GLContext.h>
+#include <kinc/backend/Android.h>
+#include <kinc/graphics4/graphics.h>
+#include <kinc/input/gamepad.h>
+#include <kinc/input/keyboard.h>
+#include <kinc/input/mouse.h>
+// #include <kinc/input/sensor.h>
+#include <android/sensor.h>
+#include <android/window.h>
+#include <android_native_app_glue.h>
+#include <kinc/input/pen.h>
+#include <kinc/input/surface.h>
+#include <kinc/log.h>
+#include <kinc/system.h>
+#include <kinc/threads/mutex.h>
+#include <kinc/video.h>
+#include <kinc/window.h>
+
+#include <unistd.h>
+
+#include <stdlib.h>
+
+void pauseAudio();
+void resumeAudio();
+
+static struct android_app *app = NULL;
+static ANativeActivity *activity = NULL;
+static ASensorManager *sensorManager = NULL;
+static const ASensor *accelerometerSensor = NULL;
+static const ASensor *gyroSensor = NULL;
+static ASensorEventQueue *sensorEventQueue = NULL;
+
+static bool started = false;
+static bool paused = true;
+static bool displayIsInitialized = false;
+static bool appIsForeground = false;
+static bool activityJustResized = false;
+
+#include <assert.h>
+#include <kinc/log.h>
+
+#ifdef KINC_EGL
+
+EGLDisplay kinc_egl_get_display() {
+	return eglGetDisplay(EGL_DEFAULT_DISPLAY);
+}
+
+EGLNativeWindowType kinc_egl_get_native_window(EGLDisplay display, EGLConfig config, int window) {
+	kinc_affirm(window == 0);
+	EGLint format;
+	eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
+	int e = ANativeWindow_setBuffersGeometry(app->window, 0, 0, format);
+	if (e < 0) {
+		kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to set ANativeWindow buffer geometry.");
+	}
+	return app->window;
+}
+
+#endif
+
+#ifdef KINC_VULKAN
+
+#include <vulkan/vulkan_android.h>
+#include <vulkan/vulkan_core.h>
+
+VkResult kinc_vulkan_create_surface(VkInstance instance, int window_index, VkSurfaceKHR *surface) {
+	assert(app->window != NULL);
+	VkAndroidSurfaceCreateInfoKHR createInfo = {};
+	createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+	createInfo.pNext = NULL;
+	createInfo.flags = 0;
+	createInfo.window = app->window;
+	return vkCreateAndroidSurfaceKHR(instance, &createInfo, NULL, surface);
+}
+
+void kinc_vulkan_get_instance_extensions(const char **names, int *index, int max) {
+	assert(*index + 1 < max);
+	names[(*index)++] = VK_KHR_ANDROID_SURFACE_EXTENSION_NAME;
+}
+
+VkBool32 kinc_vulkan_get_physical_device_presentation_support(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex) {
+	// https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_KHR_android_surface.html#_issues
+	//
+	// 1) Does Android need a way to query for compatibility between a particular physical device (and queue family?)
+	// and a specific Android display?
+	// RESOLVED: No. Currently on Android, any physical device is expected to be able to present to the system compositor,
+	// and all queue families must support the necessary image layout transitions and synchronization operations.
+	return true;
+}
+#endif
+
+#ifndef KINC_VULKAN
+void kinc_egl_init_window(int window);
+void kinc_egl_destroy_window(int window);
+#endif
+#ifdef KINC_VULKAN
+void kinc_vulkan_init_window(int window);
+#endif
+
+static void initDisplay() {
+#ifndef KINC_VULKAN
+	kinc_egl_init_window(0);
+#endif
+#ifdef KINC_VULKAN
+	kinc_vulkan_init_window(0);
+#endif
+}
+
+static void termDisplay() {
+#ifndef KINC_VULKAN
+	kinc_egl_destroy_window(0);
+#endif
+}
+
+static void updateAppForegroundStatus(bool displayIsInitializedValue, bool appIsForegroundValue) {
+	bool oldStatus = displayIsInitialized && appIsForeground;
+	displayIsInitialized = displayIsInitializedValue;
+	appIsForeground = appIsForegroundValue;
+	bool newStatus = displayIsInitialized && appIsForeground;
+	if (oldStatus != newStatus) {
+		if (newStatus) {
+			kinc_internal_foreground_callback();
+		}
+		else {
+			kinc_internal_background_callback();
+		}
+	}
+}
+
+static bool isGamepadEvent(AInputEvent *event) {
+	return ((AInputEvent_getSource(event) & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD ||
+	        (AInputEvent_getSource(event) & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK ||
+	        (AInputEvent_getSource(event) & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD);
+}
+
+static bool isPenEvent(AInputEvent *event) {
+	return (AInputEvent_getSource(event) & AINPUT_SOURCE_STYLUS) == AINPUT_SOURCE_STYLUS;
+}
+
+static void touchInput(AInputEvent *event) {
+	int action = AMotionEvent_getAction(event);
+	int index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+	int id = AMotionEvent_getPointerId(event, index);
+	float x = AMotionEvent_getX(event, index);
+	float y = AMotionEvent_getY(event, index);
+	switch (action & AMOTION_EVENT_ACTION_MASK) {
+	case AMOTION_EVENT_ACTION_DOWN:
+	case AMOTION_EVENT_ACTION_POINTER_DOWN:
+		if (id == 0) {
+			kinc_internal_mouse_trigger_press(0, 0, x, y);
+		}
+		if (isPenEvent(event)) {
+			kinc_internal_pen_trigger_press(0, x, y, AMotionEvent_getPressure(event, index));
+		}
+		kinc_internal_surface_trigger_touch_start(id, x, y);
+		break;
+	case AMOTION_EVENT_ACTION_MOVE:
+	case AMOTION_EVENT_ACTION_HOVER_MOVE: {
+		size_t count = AMotionEvent_getPointerCount(event);
+		for (int i = 0; i < count; ++i) {
+			id = AMotionEvent_getPointerId(event, i);
+			x = AMotionEvent_getX(event, i);
+			y = AMotionEvent_getY(event, i);
+			if (id == 0) {
+				kinc_internal_mouse_trigger_move(0, x, y);
+			}
+			if (isPenEvent(event)) {
+				kinc_internal_pen_trigger_move(0, x, y, AMotionEvent_getPressure(event, index));
+			}
+			kinc_internal_surface_trigger_move(id, x, y);
+		}
+	} break;
+	case AMOTION_EVENT_ACTION_UP:
+	case AMOTION_EVENT_ACTION_CANCEL:
+	case AMOTION_EVENT_ACTION_POINTER_UP:
+		if (id == 0) {
+			kinc_internal_mouse_trigger_release(0, 0, x, y);
+		}
+		if (isPenEvent(event)) {
+			kinc_internal_pen_trigger_release(0, x, y, AMotionEvent_getPressure(event, index));
+		}
+		kinc_internal_surface_trigger_touch_end(id, x, y);
+		break;
+	case AMOTION_EVENT_ACTION_SCROLL:
+		if (id == 0) {
+			float scroll = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_VSCROLL, 0);
+			kinc_internal_mouse_trigger_scroll(0, -(int)scroll);
+		}
+		break;
+	}
+}
+
+static float last_x = 0.0f;
+static float last_y = 0.0f;
+static float last_l = 0.0f;
+static float last_r = 0.0f;
+static bool last_hat_left = false;
+static bool last_hat_right = false;
+static bool last_hat_up = false;
+static bool last_hat_down = false;
+
+static int32_t input(struct android_app *app, AInputEvent *event) {
+	if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
+		int source = AInputEvent_getSource(event);
+		if (((source & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) || ((source & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE)) {
+			touchInput(event);
+			return 1;
+		}
+		else if ((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) {
+			// int id = AInputEvent_getDeviceId(event);
+
+			float x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_X, 0);
+			if (x != last_x) {
+				kinc_internal_gamepad_trigger_axis(0, 0, x);
+				last_x = x;
+			}
+
+			float y = -AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Y, 0);
+			if (y != last_y) {
+				kinc_internal_gamepad_trigger_axis(0, 1, y);
+				last_y = y;
+			}
+
+			float l = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_LTRIGGER, 0);
+			if (l != last_l) {
+				kinc_internal_gamepad_trigger_button(0, 6, l);
+				last_l = l;
+			}
+
+			float r = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_RTRIGGER, 0);
+			if (r != last_r) {
+				kinc_internal_gamepad_trigger_button(0, 7, r);
+				last_r = r;
+			}
+
+			float hat_x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0);
+
+			bool hat_left = false;
+			bool hat_right = false;
+			if (hat_x < -0.5f) {
+				hat_left = true;
+			}
+			else if (hat_x > 0.5f) {
+				hat_right = true;
+			}
+
+			float hat_y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0);
+
+			bool hat_up = false;
+			bool hat_down = false;
+			if (hat_y < -0.5f) {
+				hat_up = true;
+			}
+			else if (hat_y > 0.5f) {
+				hat_down = true;
+			}
+
+			if (hat_left != last_hat_left) {
+				kinc_internal_gamepad_trigger_button(0, 14, hat_left ? 1.0f : 0.0f);
+				last_hat_left = hat_left;
+			}
+
+			if (hat_right != last_hat_right) {
+				kinc_internal_gamepad_trigger_button(0, 15, hat_right ? 1.0f : 0.0f);
+				last_hat_right = hat_right;
+			}
+
+			if (hat_up != last_hat_up) {
+				kinc_internal_gamepad_trigger_button(0, 12, hat_up ? 1.0f : 0.0f);
+				last_hat_up = hat_up;
+			}
+
+			if (hat_down != last_hat_down) {
+				kinc_internal_gamepad_trigger_button(0, 13, hat_down ? 1.0f : 0.0f);
+				last_hat_down = hat_down;
+			}
+
+			return 1;
+		}
+	}
+	else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
+		int32_t code = AKeyEvent_getKeyCode(event);
+
+		if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) {
+			int shift = AKeyEvent_getMetaState(event) & AMETA_SHIFT_ON;
+			if (shift) {
+				switch (code) {
+				case AKEYCODE_1:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_EXCLAMATION);
+					kinc_internal_keyboard_trigger_key_press('!');
+					return 1;
+				case AKEYCODE_4:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_DOLLAR);
+					kinc_internal_keyboard_trigger_key_press('$');
+					return 1;
+				case AKEYCODE_5:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_PERCENT);
+					kinc_internal_keyboard_trigger_key_press('%');
+					return 1;
+				case AKEYCODE_6:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_CIRCUMFLEX);
+					kinc_internal_keyboard_trigger_key_press('^');
+					return 1;
+				case AKEYCODE_7:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_AMPERSAND);
+					kinc_internal_keyboard_trigger_key_press('&');
+					return 1;
+				case AKEYCODE_9:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_OPEN_PAREN);
+					kinc_internal_keyboard_trigger_key_press('(');
+					return 1;
+				case AKEYCODE_0:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_CLOSE_PAREN);
+					kinc_internal_keyboard_trigger_key_press(')');
+					return 1;
+				case AKEYCODE_COMMA:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_LESS_THAN);
+					kinc_internal_keyboard_trigger_key_press('<');
+					return 1;
+				case AKEYCODE_PERIOD:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_GREATER_THAN);
+					kinc_internal_keyboard_trigger_key_press('>');
+					return 1;
+				case AKEYCODE_MINUS:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_UNDERSCORE);
+					kinc_internal_keyboard_trigger_key_press('_');
+					return 1;
+				case AKEYCODE_SLASH:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_QUESTIONMARK);
+					kinc_internal_keyboard_trigger_key_press('?');
+					return 1;
+				case AKEYCODE_BACKSLASH:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_PIPE);
+					kinc_internal_keyboard_trigger_key_press('|');
+					return 1;
+				case AKEYCODE_LEFT_BRACKET:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_OPEN_CURLY_BRACKET);
+					kinc_internal_keyboard_trigger_key_press('{');
+					return 1;
+				case AKEYCODE_RIGHT_BRACKET:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_CLOSE_CURLY_BRACKET);
+					kinc_internal_keyboard_trigger_key_press('}');
+					return 1;
+				case AKEYCODE_SEMICOLON:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_COLON);
+					kinc_internal_keyboard_trigger_key_press(':');
+					return 1;
+				case AKEYCODE_APOSTROPHE:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_DOUBLE_QUOTE);
+					kinc_internal_keyboard_trigger_key_press('"');
+					return 1;
+				case AKEYCODE_GRAVE:
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_TILDE);
+					kinc_internal_keyboard_trigger_key_press('~');
+					return 1;
+				}
+			}
+			switch (code) {
+			case AKEYCODE_SHIFT_LEFT:
+			case AKEYCODE_SHIFT_RIGHT:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_SHIFT);
+				return 1;
+			case AKEYCODE_DEL:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACKSPACE);
+				return 1;
+			case AKEYCODE_ENTER:
+			case AKEYCODE_NUMPAD_ENTER:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_RETURN);
+				return 1;
+			case AKEYCODE_DPAD_CENTER:
+			case AKEYCODE_BUTTON_B:
+				kinc_internal_gamepad_trigger_button(0, 1, 1);
+				return 1;
+			case AKEYCODE_BACK:
+				if (AKeyEvent_getMetaState(event) & AMETA_ALT_ON) { // Xperia Play
+					kinc_internal_gamepad_trigger_button(0, 1, 1);
+					return 1;
+				}
+				else {
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACK);
+					return 1;
+				}
+			case AKEYCODE_BUTTON_A:
+				kinc_internal_gamepad_trigger_button(0, 0, 1);
+				return 1;
+			case AKEYCODE_BUTTON_Y:
+				kinc_internal_gamepad_trigger_button(0, 3, 1);
+				return 1;
+			case AKEYCODE_BUTTON_X:
+				kinc_internal_gamepad_trigger_button(0, 2, 1);
+				return 1;
+			case AKEYCODE_BUTTON_L1:
+				kinc_internal_gamepad_trigger_button(0, 4, 1);
+				return 1;
+			case AKEYCODE_BUTTON_R1:
+				kinc_internal_gamepad_trigger_button(0, 5, 1);
+				return 1;
+			case AKEYCODE_BUTTON_L2:
+				kinc_internal_gamepad_trigger_button(0, 6, 1);
+				return 1;
+			case AKEYCODE_BUTTON_R2:
+				kinc_internal_gamepad_trigger_button(0, 7, 1);
+				return 1;
+			case AKEYCODE_BUTTON_SELECT:
+				kinc_internal_gamepad_trigger_button(0, 8, 1);
+				return 1;
+			case AKEYCODE_BUTTON_START:
+				kinc_internal_gamepad_trigger_button(0, 9, 1);
+				return 1;
+			case AKEYCODE_BUTTON_THUMBL:
+				kinc_internal_gamepad_trigger_button(0, 10, 1);
+				return 1;
+			case AKEYCODE_BUTTON_THUMBR:
+				kinc_internal_gamepad_trigger_button(0, 11, 1);
+				return 1;
+			case AKEYCODE_DPAD_UP:
+				if (isGamepadEvent(event))
+					kinc_internal_gamepad_trigger_button(0, 12, 1);
+				else
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_UP);
+				return 1;
+			case AKEYCODE_DPAD_DOWN:
+				if (isGamepadEvent(event))
+					kinc_internal_gamepad_trigger_button(0, 13, 1);
+				else
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_DOWN);
+				return 1;
+			case AKEYCODE_DPAD_LEFT:
+				if (isGamepadEvent(event))
+					kinc_internal_gamepad_trigger_button(0, 14, 1);
+				else
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_LEFT);
+				return 1;
+			case AKEYCODE_DPAD_RIGHT:
+				if (isGamepadEvent(event))
+					kinc_internal_gamepad_trigger_button(0, 15, 1);
+				else
+					kinc_internal_keyboard_trigger_key_down(KINC_KEY_RIGHT);
+				return 1;
+			case AKEYCODE_BUTTON_MODE:
+				kinc_internal_gamepad_trigger_button(0, 16, 1);
+				return 1;
+			case AKEYCODE_STAR:
+			case AKEYCODE_NUMPAD_MULTIPLY:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_MULTIPLY);
+				kinc_internal_keyboard_trigger_key_press('*');
+				return 1;
+			case AKEYCODE_POUND:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_HASH);
+				kinc_internal_keyboard_trigger_key_press('#');
+				return 1;
+			case AKEYCODE_COMMA:
+			case AKEYCODE_NUMPAD_COMMA:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_COMMA);
+				kinc_internal_keyboard_trigger_key_press(',');
+				return 1;
+			case AKEYCODE_PERIOD:
+			case AKEYCODE_NUMPAD_DOT:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_PERIOD);
+				kinc_internal_keyboard_trigger_key_press('.');
+				return 1;
+			case AKEYCODE_SPACE:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_SPACE);
+				kinc_internal_keyboard_trigger_key_press(' ');
+				return 1;
+			case AKEYCODE_MINUS:
+			case AKEYCODE_NUMPAD_SUBTRACT:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_HYPHEN_MINUS);
+				kinc_internal_keyboard_trigger_key_press('-');
+				return 1;
+			case AKEYCODE_EQUALS:
+			case AKEYCODE_NUMPAD_EQUALS:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_EQUALS);
+				kinc_internal_keyboard_trigger_key_press('=');
+				return 1;
+			case AKEYCODE_LEFT_BRACKET:
+			case AKEYCODE_NUMPAD_LEFT_PAREN:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_OPEN_BRACKET);
+				kinc_internal_keyboard_trigger_key_press('[');
+				return 1;
+			case AKEYCODE_RIGHT_BRACKET:
+			case AKEYCODE_NUMPAD_RIGHT_PAREN:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_CLOSE_BRACKET);
+				kinc_internal_keyboard_trigger_key_press(']');
+				return 1;
+			case AKEYCODE_BACKSLASH:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACK_SLASH);
+				kinc_internal_keyboard_trigger_key_press('\\');
+				return 1;
+			case AKEYCODE_SEMICOLON:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_SEMICOLON);
+				kinc_internal_keyboard_trigger_key_press(';');
+				return 1;
+			case AKEYCODE_APOSTROPHE:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_QUOTE);
+				kinc_internal_keyboard_trigger_key_press('\'');
+				return 1;
+			case AKEYCODE_GRAVE:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACK_QUOTE);
+				kinc_internal_keyboard_trigger_key_press('`');
+				return 1;
+			case AKEYCODE_SLASH:
+			case AKEYCODE_NUMPAD_DIVIDE:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_SLASH);
+				kinc_internal_keyboard_trigger_key_press('/');
+				return 1;
+			case AKEYCODE_AT:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_AT);
+				kinc_internal_keyboard_trigger_key_press('@');
+				return 1;
+			case AKEYCODE_PLUS:
+			case AKEYCODE_NUMPAD_ADD:
+				kinc_internal_keyboard_trigger_key_down(KINC_KEY_PLUS);
+				kinc_internal_keyboard_trigger_key_press('+');
+				return 1;
+			// (DK) Amazon FireTV remote/controller mappings
+			// (DK) TODO handle multiple pads (up to 4 possible)
+			case AKEYCODE_MENU:
+				kinc_internal_gamepad_trigger_button(0, 9, 1);
+				return 1;
+			case AKEYCODE_MEDIA_REWIND:
+				kinc_internal_gamepad_trigger_button(0, 10, 1);
+				return 1;
+			case AKEYCODE_MEDIA_FAST_FORWARD:
+				kinc_internal_gamepad_trigger_button(0, 11, 1);
+				return 1;
+			case AKEYCODE_MEDIA_PLAY_PAUSE:
+				kinc_internal_gamepad_trigger_button(0, 12, 1);
+				return 1;
+			// (DK) /Amazon FireTV remote/controller mappings
+			default:
+				if (code >= AKEYCODE_NUMPAD_0 && code <= AKEYCODE_NUMPAD_9) {
+					kinc_internal_keyboard_trigger_key_down(code + KINC_KEY_NUMPAD_0 - AKEYCODE_NUMPAD_0);
+					kinc_internal_keyboard_trigger_key_press(code + KINC_KEY_NUMPAD_0 - AKEYCODE_NUMPAD_0);
+					return 1;
+				}
+				else if (code >= AKEYCODE_0 && code <= AKEYCODE_9) {
+					kinc_internal_keyboard_trigger_key_down(code + KINC_KEY_0 - AKEYCODE_0);
+					kinc_internal_keyboard_trigger_key_press(code + KINC_KEY_0 - AKEYCODE_0);
+					return 1;
+				}
+				else if (code >= AKEYCODE_A && code <= AKEYCODE_Z) {
+					kinc_internal_keyboard_trigger_key_down(code + KINC_KEY_A - AKEYCODE_A);
+					kinc_internal_keyboard_trigger_key_press(code + (shift ? 'A' : 'a') - AKEYCODE_A);
+					return 1;
+				}
+			}
+		}
+		else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP) {
+			int shift = AKeyEvent_getMetaState(event) & AMETA_SHIFT_ON;
+			if (shift) {
+				switch (code) {
+				case AKEYCODE_1:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_EXCLAMATION);
+					return 1;
+				case AKEYCODE_4:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_DOLLAR);
+					return 1;
+				case AKEYCODE_5:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_PERCENT);
+					return 1;
+				case AKEYCODE_6:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_CIRCUMFLEX);
+					return 1;
+				case AKEYCODE_7:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_AMPERSAND);
+					return 1;
+				case AKEYCODE_9:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_OPEN_PAREN);
+					return 1;
+				case AKEYCODE_0:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_CLOSE_PAREN);
+					return 1;
+				case AKEYCODE_COMMA:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_LESS_THAN);
+					return 1;
+				case AKEYCODE_PERIOD:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_GREATER_THAN);
+					return 1;
+				case AKEYCODE_MINUS:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_UNDERSCORE);
+					return 1;
+				case AKEYCODE_SLASH:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_QUESTIONMARK);
+					return 1;
+				case AKEYCODE_BACKSLASH:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_PIPE);
+					return 1;
+				case AKEYCODE_LEFT_BRACKET:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_OPEN_CURLY_BRACKET);
+					return 1;
+				case AKEYCODE_RIGHT_BRACKET:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_CLOSE_CURLY_BRACKET);
+					return 1;
+				case AKEYCODE_SEMICOLON:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_COLON);
+					return 1;
+				case AKEYCODE_APOSTROPHE:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_DOUBLE_QUOTE);
+					return 1;
+				case AKEYCODE_GRAVE:
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_TILDE);
+					return 1;
+				}
+			}
+			switch (code) {
+			case AKEYCODE_SHIFT_LEFT:
+			case AKEYCODE_SHIFT_RIGHT:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_SHIFT);
+				return 1;
+			case AKEYCODE_DEL:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACKSPACE);
+				return 1;
+			case AKEYCODE_ENTER:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_RETURN);
+				return 1;
+			case AKEYCODE_DPAD_CENTER:
+			case AKEYCODE_BUTTON_B:
+				kinc_internal_gamepad_trigger_button(0, 1, 0);
+				return 1;
+			case AKEYCODE_BACK:
+				if (AKeyEvent_getMetaState(event) & AMETA_ALT_ON) { // Xperia Play
+					kinc_internal_gamepad_trigger_button(0, 1, 0);
+					return 1;
+				}
+				else {
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACK);
+					return 1;
+				}
+			case AKEYCODE_BUTTON_A:
+				kinc_internal_gamepad_trigger_button(0, 0, 0);
+				return 1;
+			case AKEYCODE_BUTTON_Y:
+				kinc_internal_gamepad_trigger_button(0, 3, 0);
+				return 1;
+			case AKEYCODE_BUTTON_X:
+				kinc_internal_gamepad_trigger_button(0, 2, 0);
+				return 1;
+			case AKEYCODE_BUTTON_L1:
+				kinc_internal_gamepad_trigger_button(0, 4, 0);
+				return 1;
+			case AKEYCODE_BUTTON_R1:
+				kinc_internal_gamepad_trigger_button(0, 5, 0);
+				return 1;
+			case AKEYCODE_BUTTON_L2:
+				kinc_internal_gamepad_trigger_button(0, 6, 0);
+				return 1;
+			case AKEYCODE_BUTTON_R2:
+				kinc_internal_gamepad_trigger_button(0, 7, 0);
+				return 1;
+			case AKEYCODE_BUTTON_SELECT:
+				kinc_internal_gamepad_trigger_button(0, 8, 0);
+				return 1;
+			case AKEYCODE_BUTTON_START:
+				kinc_internal_gamepad_trigger_button(0, 9, 0);
+				return 1;
+			case AKEYCODE_BUTTON_THUMBL:
+				kinc_internal_gamepad_trigger_button(0, 10, 0);
+				return 1;
+			case AKEYCODE_BUTTON_THUMBR:
+				kinc_internal_gamepad_trigger_button(0, 11, 0);
+				return 1;
+			case AKEYCODE_DPAD_UP:
+				if (isGamepadEvent(event))
+					kinc_internal_gamepad_trigger_button(0, 12, 0);
+				else
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_UP);
+				return 1;
+			case AKEYCODE_DPAD_DOWN:
+				if (isGamepadEvent(event))
+					kinc_internal_gamepad_trigger_button(0, 13, 0);
+				else
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_DOWN);
+				return 1;
+			case AKEYCODE_DPAD_LEFT:
+				if (isGamepadEvent(event))
+					kinc_internal_gamepad_trigger_button(0, 14, 0);
+				else
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_LEFT);
+				return 1;
+			case AKEYCODE_DPAD_RIGHT:
+				if (isGamepadEvent(event))
+					kinc_internal_gamepad_trigger_button(0, 15, 0);
+				else
+					kinc_internal_keyboard_trigger_key_up(KINC_KEY_RIGHT);
+				return 1;
+			case AKEYCODE_BUTTON_MODE:
+				kinc_internal_gamepad_trigger_button(0, 16, 0);
+				return 1;
+			case AKEYCODE_STAR:
+			case AKEYCODE_NUMPAD_MULTIPLY:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_MULTIPLY);
+				return 1;
+			case AKEYCODE_POUND:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_HASH);
+				return 1;
+			case AKEYCODE_COMMA:
+			case AKEYCODE_NUMPAD_COMMA:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_COMMA);
+				return 1;
+			case AKEYCODE_PERIOD:
+			case AKEYCODE_NUMPAD_DOT:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_PERIOD);
+				return 1;
+			case AKEYCODE_SPACE:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_SPACE);
+				return 1;
+			case AKEYCODE_MINUS:
+			case AKEYCODE_NUMPAD_SUBTRACT:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_HYPHEN_MINUS);
+				return 1;
+			case AKEYCODE_EQUALS:
+			case AKEYCODE_NUMPAD_EQUALS:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_EQUALS);
+				return 1;
+			case AKEYCODE_LEFT_BRACKET:
+			case AKEYCODE_NUMPAD_LEFT_PAREN:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_OPEN_BRACKET);
+				return 1;
+			case AKEYCODE_RIGHT_BRACKET:
+			case AKEYCODE_NUMPAD_RIGHT_PAREN:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_CLOSE_BRACKET);
+				return 1;
+			case AKEYCODE_BACKSLASH:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACK_SLASH);
+				return 1;
+			case AKEYCODE_SEMICOLON:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_SEMICOLON);
+				return 1;
+			case AKEYCODE_APOSTROPHE:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_QUOTE);
+				return 1;
+			case AKEYCODE_GRAVE:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACK_QUOTE);
+				return 1;
+			case AKEYCODE_SLASH:
+			case AKEYCODE_NUMPAD_DIVIDE:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_SLASH);
+				return 1;
+			case AKEYCODE_AT:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_AT);
+				return 1;
+			case AKEYCODE_PLUS:
+			case AKEYCODE_NUMPAD_ADD:
+				kinc_internal_keyboard_trigger_key_up(KINC_KEY_PLUS);
+				return 1;
+			// (DK) Amazon FireTV remote/controller mappings
+			// (DK) TODO handle multiple pads (up to 4 possible)
+			case AKEYCODE_MENU:
+				kinc_internal_gamepad_trigger_button(0, 9, 0);
+				return 1;
+			case AKEYCODE_MEDIA_REWIND:
+				kinc_internal_gamepad_trigger_button(0, 10, 0);
+				return 1;
+			case AKEYCODE_MEDIA_FAST_FORWARD:
+				kinc_internal_gamepad_trigger_button(0, 11, 0);
+				return 1;
+			case AKEYCODE_MEDIA_PLAY_PAUSE:
+				kinc_internal_gamepad_trigger_button(0, 12, 0);
+				return 1;
+			// (DK) /Amazon FireTV remote/controller mappings
+			default:
+				if (code >= AKEYCODE_NUMPAD_0 && code <= AKEYCODE_NUMPAD_9) {
+					kinc_internal_keyboard_trigger_key_up(code + KINC_KEY_NUMPAD_0 - AKEYCODE_NUMPAD_0);
+					return 1;
+				}
+				else if (code >= AKEYCODE_0 && code <= AKEYCODE_9) {
+					kinc_internal_keyboard_trigger_key_up(code + KINC_KEY_0 - AKEYCODE_0);
+					return 1;
+				}
+				else if (code >= AKEYCODE_A && code <= AKEYCODE_Z) {
+					kinc_internal_keyboard_trigger_key_up(code + KINC_KEY_A - AKEYCODE_A);
+					return 1;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static void cmd(struct android_app *app, int32_t cmd) {
+	switch (cmd) {
+	case APP_CMD_SAVE_STATE:
+		break;
+	case APP_CMD_INIT_WINDOW:
+		if (app->window != NULL) {
+			if (!started) {
+				started = true;
+			}
+			else {
+				initDisplay();
+				kinc_g4_swap_buffers();
+			}
+
+			updateAppForegroundStatus(true, appIsForeground);
+		}
+		break;
+	case APP_CMD_TERM_WINDOW:
+		termDisplay();
+		updateAppForegroundStatus(false, appIsForeground);
+		break;
+	case APP_CMD_GAINED_FOCUS:
+		if (accelerometerSensor != NULL) {
+			ASensorEventQueue_enableSensor(sensorEventQueue, accelerometerSensor);
+			ASensorEventQueue_setEventRate(sensorEventQueue, accelerometerSensor, (1000L / 60) * 1000);
+		}
+		if (gyroSensor != NULL) {
+			ASensorEventQueue_enableSensor(sensorEventQueue, gyroSensor);
+			ASensorEventQueue_setEventRate(sensorEventQueue, gyroSensor, (1000L / 60) * 1000);
+		}
+		break;
+	case APP_CMD_LOST_FOCUS:
+		if (accelerometerSensor != NULL) {
+			ASensorEventQueue_disableSensor(sensorEventQueue, accelerometerSensor);
+		}
+		if (gyroSensor != NULL) {
+			ASensorEventQueue_disableSensor(sensorEventQueue, gyroSensor);
+		}
+		break;
+	case APP_CMD_START:
+		updateAppForegroundStatus(displayIsInitialized, true);
+		break;
+	case APP_CMD_RESUME:
+		kinc_internal_resume_callback();
+		resumeAudio();
+		paused = false;
+		break;
+	case APP_CMD_PAUSE:
+		kinc_internal_pause_callback();
+		pauseAudio();
+		paused = true;
+		break;
+	case APP_CMD_STOP:
+		updateAppForegroundStatus(displayIsInitialized, false);
+		break;
+	case APP_CMD_DESTROY:
+		kinc_internal_shutdown_callback();
+		break;
+	case APP_CMD_CONFIG_CHANGED: {
+
+		break;
+	}
+	}
+}
+
+static void resize(ANativeActivity *activity, ANativeWindow *window) {
+	activityJustResized = true;
+}
+
+ANativeActivity *kinc_android_get_activity(void) {
+	return activity;
+}
+
+AAssetManager *kinc_android_get_asset_manager(void) {
+	return activity->assetManager;
+}
+
+jclass kinc_android_find_class(JNIEnv *env, const char *name) {
+	jobject nativeActivity = activity->clazz;
+	jclass acl = (*env)->GetObjectClass(env, nativeActivity);
+	jmethodID getClassLoader = (*env)->GetMethodID(env, acl, "getClassLoader", "()Ljava/lang/ClassLoader;");
+	jobject cls = (*env)->CallObjectMethod(env, nativeActivity, getClassLoader);
+	jclass classLoader = (*env)->FindClass(env, "java/lang/ClassLoader");
+	jmethodID findClass = (*env)->GetMethodID(env, classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+	jstring strClassName = (*env)->NewStringUTF(env, name);
+	jclass clazz = (jclass)((*env)->CallObjectMethod(env, cls, findClass, strClassName));
+	(*env)->DeleteLocalRef(env, strClassName);
+	return clazz;
+}
+
+#define UNICODE_STACK_SIZE 256
+static uint16_t unicode_stack[UNICODE_STACK_SIZE];
+static int unicode_stack_index = 0;
+static kinc_mutex_t unicode_mutex;
+
+JNIEXPORT void JNICALL Java_tech_kinc_KincActivity_nativeKincKeyPress(JNIEnv *env, jobject jobj, jstring chars) {
+	const jchar *text = (*env)->GetStringChars(env, chars, NULL);
+	const jsize length = (*env)->GetStringLength(env, chars);
+
+	kinc_mutex_lock(&unicode_mutex);
+	for (jsize i = 0; i < length && unicode_stack_index < UNICODE_STACK_SIZE; ++i) {
+		unicode_stack[unicode_stack_index++] = text[i];
+	}
+	kinc_mutex_unlock(&unicode_mutex);
+
+	(*env)->ReleaseStringChars(env, chars, text);
+}
+
+void KincAndroidKeyboardInit() {
+	JNIEnv *env;
+	(*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL);
+
+	jclass clazz = kinc_android_find_class(env, "tech.kinc.KincActivity");
+
+	// String chars
+	JNINativeMethod methodTable[] = {{"nativeKincKeyPress", "(Ljava/lang/String;)V", (void *)Java_tech_kinc_KincActivity_nativeKincKeyPress}};
+
+	int methodTableSize = sizeof(methodTable) / sizeof(methodTable[0]);
+
+	int failure = (*env)->RegisterNatives(env, clazz, methodTable, methodTableSize);
+	if (failure != 0) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "Failed to register KincActivity.nativeKincKeyPress");
+	}
+
+	(*activity->vm)->DetachCurrentThread(activity->vm);
+}
+
+static bool keyboard_active = false;
+
+void kinc_keyboard_show() {
+	keyboard_active = true;
+	JNIEnv *env;
+	(*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL);
+	jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	(*env)->CallStaticVoidMethod(env, koreActivityClass, (*env)->GetStaticMethodID(env, koreActivityClass, "showKeyboard", "()V"));
+	(*activity->vm)->DetachCurrentThread(activity->vm);
+}
+
+void kinc_keyboard_hide() {
+	keyboard_active = false;
+	JNIEnv *env;
+	(*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL);
+	jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	(*env)->CallStaticVoidMethod(env, koreActivityClass, (*env)->GetStaticMethodID(env, koreActivityClass, "hideKeyboard", "()V"));
+	(*activity->vm)->DetachCurrentThread(activity->vm);
+}
+
+bool kinc_keyboard_active() {
+	return keyboard_active;
+}
+
+void kinc_load_url(const char *url) {
+	JNIEnv *env;
+	(*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL);
+	jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	jstring jurl = (*env)->NewStringUTF(env, url);
+	(*env)->CallStaticVoidMethod(env, koreActivityClass, (*env)->GetStaticMethodID(env, koreActivityClass, "loadURL", "(Ljava/lang/String;)V"), jurl);
+	(*activity->vm)->DetachCurrentThread(activity->vm);
+}
+
+void kinc_vibrate(int ms) {
+	JNIEnv *env;
+	(*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL);
+	jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	(*env)->CallStaticVoidMethod(env, koreActivityClass, (*env)->GetStaticMethodID(env, koreActivityClass, "vibrate", "(I)V"), ms);
+	(*activity->vm)->DetachCurrentThread(activity->vm);
+}
+
+const char *kinc_language() {
+	JNIEnv *env;
+	(*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL);
+	jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	jstring s = (jstring)(*env)->CallStaticObjectMethod(env, koreActivityClass,
+	                                                    (*env)->GetStaticMethodID(env, koreActivityClass, "getLanguage", "()Ljava/lang/String;"));
+	const char *str = (*env)->GetStringUTFChars(env, s, 0);
+	(*activity->vm)->DetachCurrentThread(activity->vm);
+	return str;
+}
+
+#ifdef KINC_VULKAN
+bool kinc_vulkan_internal_get_size(int *width, int *height);
+#endif
+
+#ifdef KINC_EGL
+extern int kinc_egl_width(int window);
+extern int kinc_egl_height(int window);
+#endif
+
+int kinc_android_width() {
+#if defined(KINC_EGL)
+	return kinc_egl_width(0);
+#elif defined(KINC_VULKAN)
+	int width, height;
+	if (kinc_vulkan_internal_get_size(&width, &height)) {
+		return width;
+	}
+	else
+#endif
+	{ return ANativeWindow_getWidth(app->window); }
+}
+
+int kinc_android_height() {
+#if defined(KINC_EGL)
+	return kinc_egl_height(0);
+#elif defined(KINC_VULKAN)
+	int width, height;
+	if (kinc_vulkan_internal_get_size(&width, &height)) {
+		return height;
+	}
+	else
+#endif
+	{ return ANativeWindow_getHeight(app->window); }
+}
+
+const char *kinc_internal_save_path() {
+	return kinc_android_get_activity()->internalDataPath;
+}
+
+const char *kinc_system_id() {
+	return "Android";
+}
+
+static const char *videoFormats[] = {"ts", NULL};
+
+const char **kinc_video_formats() {
+	return videoFormats;
+}
+
+void kinc_set_keep_screen_on(bool on) {
+	if (on) {
+		ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0);
+	}
+	else {
+		ANativeActivity_setWindowFlags(activity, 0, AWINDOW_FLAG_KEEP_SCREEN_ON);
+	}
+}
+
+#include <kinc/input/acceleration.h>
+#include <kinc/input/rotation.h>
+#include <kinc/window.h>
+#include <sys/time.h>
+#include <time.h>
+
+static __kernel_time_t start_sec = 0;
+
+double kinc_frequency() {
+	return 1000000.0;
+}
+
+kinc_ticks_t kinc_timestamp() {
+	struct timeval now;
+	gettimeofday(&now, NULL);
+	return (kinc_ticks_t)(now.tv_sec - start_sec) * 1000000 + (kinc_ticks_t)(now.tv_usec);
+}
+
+double kinc_time() {
+	struct timeval now;
+	gettimeofday(&now, NULL);
+	return (double)(now.tv_sec - start_sec) + (now.tv_usec / 1000000.0);
+}
+
+void kinc_internal_resize(int window, int width, int height);
+
+bool kinc_internal_handle_messages(void) {
+	kinc_mutex_lock(&unicode_mutex);
+	for (int i = 0; i < unicode_stack_index; ++i) {
+		kinc_internal_keyboard_trigger_key_press(unicode_stack[i]);
+	}
+	unicode_stack_index = 0;
+	kinc_mutex_unlock(&unicode_mutex);
+
+	int ident;
+	int events;
+	struct android_poll_source *source;
+
+	while ((ident = ALooper_pollAll(paused ? -1 : 0, NULL, &events, (void **)&source)) >= 0) {
+		if (source != NULL) {
+			source->process(app, source);
+		}
+
+		if (ident == LOOPER_ID_USER) {
+			if (accelerometerSensor != NULL) {
+				ASensorEvent event;
+				while (ASensorEventQueue_getEvents(sensorEventQueue, &event, 1) > 0) {
+					if (event.type == ASENSOR_TYPE_ACCELEROMETER) {
+						kinc_internal_on_acceleration(event.acceleration.x, event.acceleration.y, event.acceleration.z);
+					}
+					else if (event.type == ASENSOR_TYPE_GYROSCOPE) {
+						kinc_internal_on_rotation(event.vector.x, event.vector.y, event.vector.z);
+					}
+				}
+			}
+		}
+
+		if (app->destroyRequested != 0) {
+			termDisplay();
+			kinc_stop();
+			return true;
+		}
+	}
+
+	if (activityJustResized && app->window != NULL) {
+		activityJustResized = false;
+		int32_t width = kinc_android_width();
+		int32_t height = kinc_android_height();
+#ifdef KINC_VULKAN
+		kinc_internal_resize(0, width, height);
+#endif
+		kinc_internal_call_resize_callback(0, width, height);
+	}
+
+	// Get screen rotation
+	/*
+	JNIEnv* env;
+	(*activity->vm)->AttachCurrentThread(&env, NULL);
+	jclass koreActivityClass = KoreAndroid::findClass(env, "tech.kode.kore.KoreActivity");
+	jmethodID koreActivityGetRotation = (*env)->GetStaticMethodID(koreActivityClass, "getRotation", "()I");
+	screenRotation = (*env)->CallStaticIntMethod(koreActivityClass, koreActivityGetRotation);
+	(*activity->vm)->DetachCurrentThread();
+	*/
+
+	return true;
+}
+
+bool kinc_mouse_can_lock(void) {
+	return false;
+}
+
+void kinc_mouse_show() {}
+
+void kinc_mouse_hide() {}
+
+void kinc_mouse_set_position(int window, int x, int y) {}
+
+void kinc_internal_mouse_lock(int window) {}
+
+void kinc_internal_mouse_unlock(void) {}
+
+void kinc_mouse_get_position(int window, int *x, int *y) {
+	x = 0;
+	y = 0;
+}
+
+void kinc_mouse_set_cursor(int cursor_index) {}
+
+void kinc_login() {}
+
+void kinc_unlock_achievement(int id) {}
+
+bool kinc_gamepad_connected(int num) {
+	return num == 0;
+}
+
+void kinc_gamepad_rumble(int gamepad, float left, float right) {}
+
+void initAndroidFileReader();
+void KoreAndroidVideoInit();
+
+void android_main(struct android_app *application) {
+	app_dummy();
+
+	struct timeval now;
+	gettimeofday(&now, NULL);
+	start_sec = now.tv_sec;
+
+	app = application;
+	activity = application->activity;
+	initAndroidFileReader();
+	KoreAndroidVideoInit();
+	KincAndroidKeyboardInit();
+	application->onAppCmd = cmd;
+	application->onInputEvent = input;
+	activity->callbacks->onNativeWindowResized = resize;
+	// #ifndef KINC_VULKAN
+	// 	glContext = ndk_helper::GLContext::GetInstance();
+	// #endif
+	sensorManager = ASensorManager_getInstance();
+	accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
+	gyroSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_GYROSCOPE);
+	sensorEventQueue = ASensorManager_createEventQueue(sensorManager, application->looper, LOOPER_ID_USER, NULL, NULL);
+
+	JNIEnv *env = NULL;
+	(*kinc_android_get_activity()->vm)->AttachCurrentThread(kinc_android_get_activity()->vm, &env, NULL);
+
+	jclass koreMoviePlayerClass = kinc_android_find_class(env, "tech.kinc.KincMoviePlayer");
+	jmethodID updateAll = (*env)->GetStaticMethodID(env, koreMoviePlayerClass, "updateAll", "()V");
+
+	while (!started) {
+		kinc_internal_handle_messages();
+		(*env)->CallStaticVoidMethod(env, koreMoviePlayerClass, updateAll);
+	}
+	(*kinc_android_get_activity()->vm)->DetachCurrentThread(kinc_android_get_activity()->vm);
+	kickstart(0, NULL);
+
+	(*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL);
+	jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
+	jmethodID FinishHim = (*env)->GetStaticMethodID(env, koreActivityClass, "stop", "()V");
+	(*env)->CallStaticVoidMethod(env, koreActivityClass, FinishHim);
+	(*activity->vm)->DetachCurrentThread(activity->vm);
+}
+
+int kinc_init(const char *name, int width, int height, struct kinc_window_options *win, struct kinc_framebuffer_options *frame) {
+	kinc_mutex_init(&unicode_mutex);
+
+	kinc_window_options_t default_win;
+	if (win == NULL) {
+		kinc_window_options_set_defaults(&default_win);
+		win = &default_win;
+	}
+	win->width = width;
+	win->height = height;
+
+	kinc_framebuffer_options_t default_frame;
+	if (frame == NULL) {
+		kinc_framebuffer_options_set_defaults(&default_frame);
+		frame = &default_frame;
+	}
+
+	kinc_g4_internal_init();
+	kinc_g4_internal_init_window(0, frame->depth_bits, frame->stencil_bits, true);
+
+	kinc_internal_gamepad_trigger_connect(0);
+
+	return 0;
+}
+
+void kinc_internal_shutdown(void) {
+	kinc_internal_gamepad_trigger_disconnect(0);
+}
+
+const char *kinc_gamepad_vendor(int gamepad) {
+	return "Google";
+}
+
+const char *kinc_gamepad_product_name(int gamepad) {
+	return "gamepad";
+}
+
+#include <kinc/io/filereader.h>
+
+#define CLASS_NAME "android/app/NativeActivity"
+
+void initAndroidFileReader(void) {
+	if (activity == NULL) {
+		kinc_log(KINC_LOG_LEVEL_ERROR, "Android activity is NULL");
+		return;
+	}
+
+	JNIEnv *env;
+
+	(*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL);
+
+	jclass android_app_NativeActivity = (*env)->FindClass(env, CLASS_NAME);
+	jmethodID getExternalFilesDir = (*env)->GetMethodID(env, android_app_NativeActivity, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
+	jobject file = (*env)->CallObjectMethod(env, activity->clazz, getExternalFilesDir, NULL);
+	jclass java_io_File = (*env)->FindClass(env, "java/io/File");
+	jmethodID getPath = (*env)->GetMethodID(env, java_io_File, "getPath", "()Ljava/lang/String;");
+	jstring jPath = (*env)->CallObjectMethod(env, file, getPath);
+
+	const char *path = (*env)->GetStringUTFChars(env, jPath, NULL);
+	char *externalFilesDir = malloc(strlen(path) + 1);
+	strcpy(externalFilesDir, path);
+	kinc_internal_set_files_location(externalFilesDir);
+
+	(*env)->ReleaseStringUTFChars(env, jPath, path);
+	(*env)->DeleteLocalRef(env, jPath);
+	(*activity->vm)->DetachCurrentThread(activity->vm);
+}
+
+static bool kinc_aasset_reader_close(kinc_file_reader_t *reader) {
+	AAsset_close((struct AAsset *)reader->data);
+	return true;
+}
+
+static size_t kinc_aasset_reader_read(kinc_file_reader_t *reader, void *data, size_t size) {
+	return AAsset_read((struct AAsset *)reader->data, data, size);
+}
+
+static size_t kinc_aasset_reader_pos(kinc_file_reader_t *reader) {
+	return (size_t)AAsset_seek((struct AAsset *)reader->data, 0, SEEK_CUR);
+}
+
+static bool kinc_aasset_reader_seek(kinc_file_reader_t *reader, size_t pos) {
+	AAsset_seek((struct AAsset *)reader->data, pos, SEEK_SET);
+	return true;
+}
+
+static bool kinc_aasset_reader_open(kinc_file_reader_t *reader, const char *filename, int type) {
+	if (type != KINC_FILE_TYPE_ASSET)
+		return false;
+	reader->data = AAssetManager_open(kinc_android_get_asset_manager(), filename, AASSET_MODE_RANDOM);
+	if (reader->data == NULL)
+		return false;
+	reader->size = AAsset_getLength((struct AAsset *)reader->data);
+	reader->close = kinc_aasset_reader_close;
+	reader->read = kinc_aasset_reader_read;
+	reader->pos = kinc_aasset_reader_pos;
+	reader->seek = kinc_aasset_reader_seek;
+	return true;
+}
+
+bool kinc_file_reader_open(kinc_file_reader_t *reader, const char *filename, int type) {
+	memset(reader, 0, sizeof(*reader));
+	return kinc_internal_file_reader_callback(reader, filename, type) ||
+	       kinc_internal_file_reader_open(reader, filename, type) ||
+	       kinc_aasset_reader_open(reader, filename, type);
+}
+
+int kinc_cpu_cores(void) {
+	return kinc_hardware_threads();
+}
+
+int kinc_hardware_threads(void) {
+	return sysconf(_SC_NPROCESSORS_ONLN);
+}

+ 581 - 0
armorcore/sources/backends/android/kinc/backend/video.c.h

@@ -0,0 +1,581 @@
+#include <kinc/video.h>
+
+#include <kinc/audio1/audio.h>
+#include <kinc/graphics4/texture.h>
+#include <kinc/io/filereader.h>
+#include <kinc/log.h>
+#include <kinc/system.h>
+
+#include <android_native_app_glue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+#include <OMXAL/OpenMAXAL.h>
+#include <OMXAL/OpenMAXAL_Android.h>
+#endif
+#include <assert.h>
+#include <jni.h>
+#include <kinc/backend/Android.h>
+#include <pthread.h>
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
+#include <android/native_window_jni.h>
+#endif
+
+void kinc_video_sound_stream_impl_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) {
+	stream->bufferSize = 1;
+	stream->bufferReadPosition = 0;
+	stream->bufferWritePosition = 0;
+	stream->read = 0;
+	stream->written = 0;
+}
+
+void kinc_video_sound_stream_impl_destroy(kinc_internal_video_sound_stream_t *stream) {}
+
+void kinc_video_sound_stream_impl_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {}
+
+static float samples[2] = {0};
+
+float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) {
+	return samples;
+}
+
+bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) {
+	return false;
+}
+
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+
+#define videosCount 10
+static kinc_video_t *videos[videosCount] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+#define NB_MAXAL_INTERFACES 3 // XAAndroidBufferQueueItf, XAStreamInformationItf and XAPlayItf
+#define NB_BUFFERS 8
+#define MPEG2_TS_PACKET_SIZE 188
+#define PACKETS_PER_BUFFER 10
+#define BUFFER_SIZE (PACKETS_PER_BUFFER * MPEG2_TS_PACKET_SIZE)
+static const int kEosBufferCntxt = 1980; // a magic value we can compare against
+
+typedef struct kinc_android_video {
+	XAObjectItf engineObject;
+	XAEngineItf engineEngine;
+	XAObjectItf outputMixObject;
+	const char *path;
+
+	AAsset *file;
+	XAObjectItf playerObj;
+	XAPlayItf playerPlayItf;
+	XAAndroidBufferQueueItf playerBQItf;
+	XAStreamInformationItf playerStreamInfoItf;
+	XAVolumeItf playerVolItf;
+	char dataCache[BUFFER_SIZE * NB_BUFFERS];
+	ANativeWindow *theNativeWindow;
+	jboolean reachedEof;
+	pthread_mutex_t mutex;
+	pthread_cond_t cond;
+	bool discontinuity;
+} kinc_android_video_t;
+
+void kinc_android_video_init(kinc_android_video_t *video) {
+	video->engineObject = NULL;
+	video->engineEngine = NULL;
+	video->outputMixObject = NULL;
+	video->file = NULL;
+	video->playerObj = NULL;
+	video->playerPlayItf = NULL;
+	video->playerBQItf = NULL;
+	video->playerStreamInfoItf = NULL;
+	video->playerVolItf = NULL;
+	video->theNativeWindow = NULL;
+	video->reachedEof = JNI_FALSE;
+	memset(&video->mutex, 0, sizeof(video->mutex)); // mutex = PTHREAD_MUTEX_INITIALIZER; // simple assign stopped working in Android Studio 2.2
+	memset(&video->cond, 0, sizeof(video->cond));   // cond = PTHREAD_COND_INITIALIZER; // simple assign stopped working in Android Studio 2.2
+	video->discontinuity = false;
+}
+
+bool kinc_android_video_enqueue_initial_buffers(kinc_android_video_t *video, bool discontinuity) {
+	// Fill our cache.
+	// We want to read whole packets (integral multiples of MPEG2_TS_PACKET_SIZE).
+	// fread returns units of "elements" not bytes, so we ask for 1-byte elements
+	// and then check that the number of elements is a multiple of the packet size.
+	//
+	size_t bytesRead;
+	// bytesRead = fread(dataCache, 1, BUFFER_SIZE * NB_BUFFERS, file);
+	bytesRead = AAsset_read(video->file, video->dataCache, BUFFER_SIZE * NB_BUFFERS);
+	if (bytesRead <= 0) {
+		// could be premature EOF or I/O error
+		return false;
+	}
+	if ((bytesRead % MPEG2_TS_PACKET_SIZE) != 0) {
+		kinc_log(KINC_LOG_LEVEL_INFO, "Dropping last packet because it is not whole");
+	}
+	size_t packetsRead = bytesRead / MPEG2_TS_PACKET_SIZE;
+	kinc_log(KINC_LOG_LEVEL_INFO, "Initially queueing %zu packets", packetsRead);
+
+	// Enqueue the content of our cache before starting to play,
+	// we don't want to starve the player
+	size_t i;
+	for (i = 0; i < NB_BUFFERS && packetsRead > 0; i++) {
+		// compute size of this buffer
+		size_t packetsThisBuffer = packetsRead;
+		if (packetsThisBuffer > PACKETS_PER_BUFFER) {
+			packetsThisBuffer = PACKETS_PER_BUFFER;
+		}
+		size_t bufferSize = packetsThisBuffer * MPEG2_TS_PACKET_SIZE;
+		XAresult res;
+		if (discontinuity) {
+			// signal discontinuity
+			XAAndroidBufferItem items[1];
+			items[0].itemKey = XA_ANDROID_ITEMKEY_DISCONTINUITY;
+			items[0].itemSize = 0;
+			// DISCONTINUITY message has no parameters,
+			//   so the total size of the message is the size of the key
+			//   plus the size if itemSize, both XAuint32
+			res = (*video->playerBQItf)
+			          ->Enqueue(video->playerBQItf, NULL /*pBufferContext*/, video->dataCache + i * BUFFER_SIZE, bufferSize, items /*pMsg*/,
+			                    sizeof(XAuint32) * 2 /*msgLength*/);
+			discontinuity = JNI_FALSE;
+		}
+		else {
+			res = (*video->playerBQItf)->Enqueue(video->playerBQItf, NULL /*pBufferContext*/, video->dataCache + i * BUFFER_SIZE, bufferSize, NULL, 0);
+		}
+		assert(XA_RESULT_SUCCESS == res);
+		packetsRead -= packetsThisBuffer;
+	}
+
+	return true;
+}
+
+static XAresult AndroidBufferQueueCallback(XAAndroidBufferQueueItf caller, void *pCallbackContext, /* input */
+                                           void *pBufferContext,                                   /* input */
+                                           void *pBufferData,                                      /* input */
+                                           XAuint32 dataSize,                                      /* input */
+                                           XAuint32 dataUsed,                                      /* input */
+                                           const XAAndroidBufferItem *pItems,                      /* input */
+                                           XAuint32 itemsLength /* input */) {
+	kinc_android_video_t *self = (kinc_android_video_t *)pCallbackContext;
+	XAresult res;
+	int ok;
+
+	// pCallbackContext was specified as NULL at RegisterCallback and is unused here
+	// assert(NULL == pCallbackContext);
+
+	// note there is never any contention on this mutex unless a discontinuity request is active
+	ok = pthread_mutex_lock(&self->mutex);
+	assert(0 == ok);
+
+	// was a discontinuity requested?
+	if (self->discontinuity) {
+		// Note: can't rewind after EOS, which we send when reaching EOF
+		// (don't send EOS if you plan to play more content through the same player)
+		if (!self->reachedEof) {
+			// clear the buffer queue
+			res = (*self->playerBQItf)->Clear(self->playerBQItf);
+			assert(XA_RESULT_SUCCESS == res);
+			// rewind the data source so we are guaranteed to be at an appropriate point
+			// rewind(file);
+			AAsset_seek(self->file, 0, SEEK_SET);
+			// Enqueue the initial buffers, with a discontinuity indicator on first buffer
+			kinc_android_video_enqueue_initial_buffers(self, JNI_TRUE);
+		}
+		// acknowledge the discontinuity request
+		self->discontinuity = JNI_FALSE;
+		ok = pthread_cond_signal(&self->cond);
+		assert(0 == ok);
+		goto exit;
+	}
+
+	if ((pBufferData == NULL) && (pBufferContext != NULL)) {
+		const int processedCommand = *(int *)pBufferContext;
+		if (kEosBufferCntxt == processedCommand) {
+			kinc_log(KINC_LOG_LEVEL_INFO, "EOS was processed");
+			// our buffer with the EOS message has been consumed
+			assert(0 == dataSize);
+			goto exit;
+		}
+	}
+
+	// pBufferData is a pointer to a buffer that we previously Enqueued
+	assert((dataSize > 0) && ((dataSize % MPEG2_TS_PACKET_SIZE) == 0));
+	assert(self->dataCache <= (char *)pBufferData && (char *)pBufferData < &self->dataCache[BUFFER_SIZE * NB_BUFFERS]);
+	assert(0 == (((char *)pBufferData - self->dataCache) % BUFFER_SIZE));
+
+	// don't bother trying to read more data once we've hit EOF
+	if (self->reachedEof) {
+		goto exit;
+	}
+
+	size_t nbRead;
+	// note we do call fread from multiple threads, but never concurrently
+	size_t bytesRead;
+	// bytesRead = fread(pBufferData, 1, BUFFER_SIZE, file);
+	bytesRead = AAsset_read(self->file, pBufferData, BUFFER_SIZE);
+	if (bytesRead > 0) {
+		if ((bytesRead % MPEG2_TS_PACKET_SIZE) != 0) {
+			kinc_log(KINC_LOG_LEVEL_INFO, "Dropping last packet because it is not whole");
+		}
+		size_t packetsRead = bytesRead / MPEG2_TS_PACKET_SIZE;
+		size_t bufferSize = packetsRead * MPEG2_TS_PACKET_SIZE;
+		res = (*caller)->Enqueue(caller, NULL /*pBufferContext*/, pBufferData /*pData*/, bufferSize /*dataLength*/, NULL /*pMsg*/, 0 /*msgLength*/);
+		assert(XA_RESULT_SUCCESS == res);
+	}
+	else {
+		// EOF or I/O error, signal EOS
+		XAAndroidBufferItem msgEos[1];
+		msgEos[0].itemKey = XA_ANDROID_ITEMKEY_EOS;
+		msgEos[0].itemSize = 0;
+		// EOS message has no parameters, so the total size of the message is the size of the key
+		//   plus the size if itemSize, both XAuint32
+		res = (*caller)->Enqueue(caller, (void *)&kEosBufferCntxt /*pBufferContext*/, NULL /*pData*/, 0 /*dataLength*/, msgEos /*pMsg*/,
+		                         sizeof(XAuint32) * 2 /*msgLength*/);
+		assert(XA_RESULT_SUCCESS == res);
+		self->reachedEof = JNI_TRUE;
+	}
+
+exit:
+	ok = pthread_mutex_unlock(&self->mutex);
+	assert(0 == ok);
+	return XA_RESULT_SUCCESS;
+}
+
+static void StreamChangeCallback(XAStreamInformationItf caller, XAuint32 eventId, XAuint32 streamIndex, void *pEventData, void *pContext) {
+	kinc_log(KINC_LOG_LEVEL_INFO, "StreamChangeCallback called for stream %u", streamIndex);
+	kinc_android_video_t *self = (kinc_android_video_t *)pContext;
+	// pContext was specified as NULL at RegisterStreamChangeCallback and is unused here
+	// assert(NULL == pContext);
+	switch (eventId) {
+	case XA_STREAMCBEVENT_PROPERTYCHANGE: {
+		// From spec 1.0.1:
+		//  "This event indicates that stream property change has occurred.
+		//  The streamIndex parameter identifies the stream with the property change.
+		//  The pEventData parameter for this event is not used and shall be ignored."
+		//
+
+		XAresult res;
+		XAuint32 domain;
+		res = (*caller)->QueryStreamType(caller, streamIndex, &domain);
+		assert(XA_RESULT_SUCCESS == res);
+		switch (domain) {
+		case XA_DOMAINTYPE_VIDEO: {
+			XAVideoStreamInformation videoInfo;
+			res = (*caller)->QueryStreamInformation(caller, streamIndex, &videoInfo);
+			assert(XA_RESULT_SUCCESS == res);
+			kinc_log(KINC_LOG_LEVEL_INFO, "Found video size %u x %u, codec ID=%u, frameRate=%u, bitRate=%u, duration=%u ms", videoInfo.width, videoInfo.height,
+			         videoInfo.codecId, videoInfo.frameRate, videoInfo.bitRate, videoInfo.duration);
+		} break;
+		default:
+			kinc_log(KINC_LOG_LEVEL_ERROR, "Unexpected domain %u\n", domain);
+			break;
+		}
+	} break;
+	default:
+		kinc_log(KINC_LOG_LEVEL_ERROR, "Unexpected stream event ID %u\n", eventId);
+		break;
+	}
+}
+
+bool kinc_android_video_open(kinc_android_video_t *video, const char *filename) {
+	XAresult res;
+
+	// create engine
+	res = xaCreateEngine(&video->engineObject, 0, NULL, 0, NULL, NULL);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// realize the engine
+	res = (*video->engineObject)->Realize(video->engineObject, XA_BOOLEAN_FALSE);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// get the engine interface, which is needed in order to create other objects
+	res = (*video->engineObject)->GetInterface(video->engineObject, XA_IID_ENGINE, &video->engineEngine);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// create output mix
+	res = (*video->engineEngine)->CreateOutputMix(video->engineEngine, &video->outputMixObject, 0, NULL, NULL);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// realize the output mix
+	res = (*video->outputMixObject)->Realize(video->outputMixObject, XA_BOOLEAN_FALSE);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// open the file to play
+	video->file = AAssetManager_open(kinc_android_get_asset_manager(), filename, AASSET_MODE_STREAMING);
+	if (video->file == NULL) {
+		kinc_log(KINC_LOG_LEVEL_INFO, "Could not find video file.");
+		return false;
+	}
+
+	// configure data source
+	XADataLocator_AndroidBufferQueue loc_abq = {XA_DATALOCATOR_ANDROIDBUFFERQUEUE, NB_BUFFERS};
+	XADataFormat_MIME format_mime = {XA_DATAFORMAT_MIME, XA_ANDROID_MIME_MP2TS, XA_CONTAINERTYPE_MPEG_TS};
+	XADataSource dataSrc = {&loc_abq, &format_mime};
+
+	// configure audio sink
+	XADataLocator_OutputMix loc_outmix = {XA_DATALOCATOR_OUTPUTMIX, video->outputMixObject};
+	XADataSink audioSnk = {&loc_outmix, NULL};
+
+	// configure image video sink
+	XADataLocator_NativeDisplay loc_nd = {
+	    XA_DATALOCATOR_NATIVEDISPLAY, // locatorType
+	    // the video sink must be an ANativeWindow created from a Surface or SurfaceTexture
+	    (void *)video->theNativeWindow, // hWindow
+	    // must be NULL
+	    NULL // hDisplay
+	};
+	XADataSink imageVideoSink = {&loc_nd, NULL};
+
+	// declare interfaces to use
+	XAboolean required[NB_MAXAL_INTERFACES] = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE};
+	XAInterfaceID iidArray[NB_MAXAL_INTERFACES] = {XA_IID_PLAY, XA_IID_ANDROIDBUFFERQUEUESOURCE, XA_IID_STREAMINFORMATION};
+
+	// create media player
+	res = (*video->engineEngine)
+	          ->CreateMediaPlayer(video->engineEngine, &video->playerObj, &dataSrc, NULL, &audioSnk, &imageVideoSink, NULL, NULL,
+	                              NB_MAXAL_INTERFACES /*XAuint32 numInterfaces*/, iidArray /*const XAInterfaceID *pInterfaceIds*/,
+	                              required /*const XAboolean *pInterfaceRequired*/);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// realize the player
+	res = (*video->playerObj)->Realize(video->playerObj, XA_BOOLEAN_FALSE);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// get the play interface
+	res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_PLAY, &video->playerPlayItf);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// get the stream information interface (for video size)
+	res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_STREAMINFORMATION, &video->playerStreamInfoItf);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// get the volume interface
+	res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_VOLUME, &video->playerVolItf);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// get the Android buffer queue interface
+	res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_ANDROIDBUFFERQUEUESOURCE, &video->playerBQItf);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// specify which events we want to be notified of
+	res = (*video->playerBQItf)->SetCallbackEventsMask(video->playerBQItf, XA_ANDROIDBUFFERQUEUEEVENT_PROCESSED);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// register the callback from which OpenMAX AL can retrieve the data to play
+	res = (*video->playerBQItf)->RegisterCallback(video->playerBQItf, AndroidBufferQueueCallback, video);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// we want to be notified of the video size once it's found, so we register a callback for that
+	res = (*video->playerStreamInfoItf)->RegisterStreamChangeCallback(video->playerStreamInfoItf, StreamChangeCallback, video);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// enqueue the initial buffers
+	if (!kinc_android_video_enqueue_initial_buffers(video, false)) {
+		kinc_log(KINC_LOG_LEVEL_INFO, "Could not enqueue initial buffers for video decoding.");
+		return false;
+	}
+
+	// prepare the player
+	res = (*video->playerPlayItf)->SetPlayState(video->playerPlayItf, XA_PLAYSTATE_PAUSED);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// set the volume
+	res = (*video->playerVolItf)->SetVolumeLevel(video->playerVolItf, 0);
+	assert(XA_RESULT_SUCCESS == res);
+
+	// start the playback
+	res = (*video->playerPlayItf)->SetPlayState(video->playerPlayItf, XA_PLAYSTATE_PLAYING);
+	assert(XA_RESULT_SUCCESS == res);
+
+	kinc_log(KINC_LOG_LEVEL_INFO, "Successfully loaded video.");
+
+	return true;
+}
+
+void kinc_android_video_shutdown(kinc_android_video_t *video) {
+	// destroy streaming media player object, and invalidate all associated interfaces
+	if (video->playerObj != NULL) {
+		(*video->playerObj)->Destroy(video->playerObj);
+		video->playerObj = NULL;
+		video->playerPlayItf = NULL;
+		video->playerBQItf = NULL;
+		video->playerStreamInfoItf = NULL;
+		video->playerVolItf = NULL;
+	}
+
+	// destroy output mix object, and invalidate all associated interfaces
+	if (video->outputMixObject != NULL) {
+		(*video->outputMixObject)->Destroy(video->outputMixObject);
+		video->outputMixObject = NULL;
+	}
+
+	// destroy engine object, and invalidate all associated interfaces
+	if (video->engineObject != NULL) {
+		(*video->engineObject)->Destroy(video->engineObject);
+		video->engineObject = NULL;
+		video->engineEngine = NULL;
+	}
+
+	// close the file
+	if (video->file != NULL) {
+		AAsset_close(video->file);
+		video->file = NULL;
+	}
+
+	// make sure we don't leak native windows
+	if (video->theNativeWindow != NULL) {
+		ANativeWindow_release(video->theNativeWindow);
+		video->theNativeWindow = NULL;
+	}
+}
+
+#endif
+
+JNIEXPORT void JNICALL Java_tech_kinc_KincMoviePlayer_nativeCreate(JNIEnv *env, jobject jobj, jstring jpath, jobject surface, jint id) {
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+	const char *path = (*env)->GetStringUTFChars(env, jpath, NULL);
+	kinc_android_video_t *av = malloc(sizeof *av);
+	kinc_android_video_init(av);
+	av->theNativeWindow = ANativeWindow_fromSurface(env, surface);
+	kinc_android_video_open(av, path);
+	for (int i = 0; i < 10; ++i) {
+		if (videos[i] != NULL && videos[i]->impl.id == id) {
+			videos[i]->impl.androidVideo = av;
+			break;
+		}
+	}
+	(*env)->ReleaseStringUTFChars(env, jpath, path);
+#endif
+}
+
+void KoreAndroidVideoInit() {
+	JNIEnv *env;
+	(*kinc_android_get_activity()->vm)->AttachCurrentThread(kinc_android_get_activity()->vm, &env, NULL);
+
+	jclass clazz = kinc_android_find_class(env, "tech.kinc.KincMoviePlayer");
+
+	// String path, Surface surface, int id
+	JNINativeMethod methodTable[] = {{"nativeCreate", "(Ljava/lang/String;Landroid/view/Surface;I)V", (void *)Java_tech_kinc_KincMoviePlayer_nativeCreate}};
+
+	int methodTableSize = sizeof(methodTable) / sizeof(methodTable[0]);
+
+	int failure = (*env)->RegisterNatives(env, clazz, methodTable, methodTableSize);
+	if (failure != 0) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "Failed to register KincMoviePlayer.nativeCreate");
+	}
+
+	(*kinc_android_get_activity()->vm)->DetachCurrentThread(kinc_android_get_activity()->vm);
+}
+
+void kinc_video_init(kinc_video_t *video, const char *filename) {
+	video->impl.playing = false;
+	video->impl.sound = NULL;
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+	kinc_log(KINC_LOG_LEVEL_INFO, "Opening video %s.", filename);
+	video->impl.myWidth = 1023;
+	video->impl.myHeight = 684;
+
+	video->impl.next = 0;
+	video->impl.audioTime = 0;
+
+	JNIEnv *env = NULL;
+	(*kinc_android_get_activity()->vm)->AttachCurrentThread(kinc_android_get_activity()->vm, &env, NULL);
+	jclass koreMoviePlayerClass = kinc_android_find_class(env, "tech.kinc.KincMoviePlayer");
+	jmethodID constructor = (*env)->GetMethodID(env, koreMoviePlayerClass, "<init>", "(Ljava/lang/String;)V");
+	jobject object = (*env)->NewObject(env, koreMoviePlayerClass, constructor, (*env)->NewStringUTF(env, filename));
+
+	jmethodID getId = (*env)->GetMethodID(env, koreMoviePlayerClass, "getId", "()I");
+	video->impl.id = (*env)->CallIntMethod(env, object, getId);
+
+	for (int i = 0; i < videosCount; ++i) {
+		if (videos[i] == NULL) {
+			videos[i] = video;
+			break;
+		}
+	}
+
+	jmethodID jinit = (*env)->GetMethodID(env, koreMoviePlayerClass, "init", "()V");
+	(*env)->CallVoidMethod(env, object, jinit);
+
+	jmethodID getTextureId = (*env)->GetMethodID(env, koreMoviePlayerClass, "getTextureId", "()I");
+	int texid = (*env)->CallIntMethod(env, object, getTextureId);
+
+	(*kinc_android_get_activity()->vm)->DetachCurrentThread(kinc_android_get_activity()->vm);
+
+	kinc_g4_texture_init_from_id(&video->impl.image, texid);
+#endif
+}
+
+void kinc_video_destroy(kinc_video_t *video) {
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+	kinc_video_stop(video);
+	kinc_android_video_t *av = (kinc_android_video_t *)video->impl.androidVideo;
+	kinc_android_video_shutdown(av);
+	for (int i = 0; i < 10; ++i) {
+		if (videos[i] == video) {
+			videos[i] = NULL;
+			break;
+		}
+	}
+#endif
+}
+
+void kinc_video_play(kinc_video_t *video, bool loop) {
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+	video->impl.playing = true;
+	video->impl.start = kinc_time();
+#endif
+}
+
+void kinc_video_pause(kinc_video_t *video) {
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+	video->impl.playing = false;
+#endif
+}
+
+void kinc_video_stop(kinc_video_t *video) {
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+	kinc_video_pause(video);
+#endif
+}
+
+void kinc_video_update(kinc_video_t *video, double time) {}
+
+int kinc_video_width(kinc_video_t *video) {
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+	return video->impl.myWidth;
+#else
+	return 512;
+#endif
+}
+
+int kinc_video_height(kinc_video_t *video) {
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+	return video->impl.myHeight;
+#else
+	return 512;
+#endif
+}
+
+kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) {
+#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
+	return &video->impl.image;
+#else
+	return NULL;
+#endif
+}
+
+double kinc_video_duration(kinc_video_t *video) {
+	return 0.0;
+}
+
+double kinc_video_position(kinc_video_t *video) {
+	return 0.0;
+}
+
+bool kinc_video_finished(kinc_video_t *video) {
+	return false;
+}
+
+bool kinc_video_paused(kinc_video_t *video) {
+	return !video->impl.playing;
+}

+ 49 - 0
armorcore/sources/backends/android/kinc/backend/video.h

@@ -0,0 +1,49 @@
+#pragma once
+
+#include <kinc/graphics4/texture.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	void *assetReader;
+	void *videoTrackOutput;
+	void *audioTrackOutput;
+	double start;
+	double next;
+	// double audioTime;
+	unsigned long long audioTime;
+	bool playing;
+	void *sound;
+	void *androidVideo;
+	int id;
+	kinc_g4_texture_t image;
+	double lastTime;
+	int myWidth;
+	int myHeight;
+} kinc_video_impl_t;
+
+typedef struct kinc_internal_video_sound_stream {
+	void *audioTrackOutput;
+	float *buffer;
+	int bufferSize;
+	int bufferWritePosition;
+	int bufferReadPosition;
+	uint64_t read;
+	uint64_t written;
+} kinc_internal_video_sound_stream_t;
+
+void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency);
+
+void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream);
+
+void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count);
+
+float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream);
+
+bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream);
+
+#ifdef __cplusplus
+}
+#endif

+ 79 - 0
armorcore/sources/backends/android/kinc/backend/window.c.h

@@ -0,0 +1,79 @@
+#include <kinc/display.h>
+#include <kinc/graphics4/graphics.h>
+#include <kinc/window.h>
+
+static void (*resizeCallback)(int x, int y, void *data) = NULL;
+static void *resizeCallbackData = NULL;
+
+int kinc_count_windows(void) {
+	return 1;
+}
+
+int kinc_window_x(int window_index) {
+	return 0;
+}
+
+int kinc_window_y(int window_index) {
+	return 0;
+}
+
+int kinc_android_width();
+
+int kinc_window_width(int window_index) {
+	return kinc_android_width();
+}
+
+int kinc_android_height();
+
+int kinc_window_height(int window_index) {
+	return kinc_android_height();
+}
+
+void kinc_window_resize(int window_index, int width, int height) {}
+
+void kinc_window_move(int window_index, int x, int y) {}
+
+void kinc_internal_change_framebuffer(int window, struct kinc_framebuffer_options *frame);
+
+void kinc_window_change_framebuffer(int window_index, kinc_framebuffer_options_t *frame) {
+	kinc_internal_change_framebuffer(0, frame);
+}
+
+void kinc_window_change_features(int window_index, int features) {}
+
+void kinc_window_change_mode(int window_index, kinc_window_mode_t mode) {}
+
+void kinc_window_destroy(int window_index) {}
+
+void kinc_window_show(int window_index) {}
+
+void kinc_window_hide(int window_index) {}
+
+void kinc_window_set_title(int window_index, const char *title) {}
+
+int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) {
+	return 0;
+}
+
+void kinc_window_set_resize_callback(int window_index, void (*callback)(int x, int y, void *data), void *data) {
+	resizeCallback = callback;
+	resizeCallbackData = data;
+}
+
+void kinc_internal_call_resize_callback(int window_index, int width, int height) {
+	if (resizeCallback != NULL) {
+		resizeCallback(width, height, resizeCallbackData);
+	}
+}
+
+void kinc_window_set_ppi_changed_callback(int window_index, void (*callback)(int ppi, void *data), void *data) {}
+
+void kinc_window_set_close_callback(int window, bool (*callback)(void *), void *data) {}
+
+kinc_window_mode_t kinc_window_get_mode(int window_index) {
+	return KINC_WINDOW_MODE_FULLSCREEN;
+}
+
+int kinc_window_display(int window) {
+	return 0;
+}

+ 1 - 0
armorcore/sources/backends/android/kinc/backend/windowdata.h

@@ -0,0 +1 @@
+#pragma once

+ 4 - 0
armorcore/sources/backends/apple/kinc/backend/appleunit.m

@@ -0,0 +1,4 @@
+#include "http.m.h"
+#include "system.m.h"
+#include "thread.m.h"
+#include "video.m.h"

+ 53 - 0
armorcore/sources/backends/apple/kinc/backend/http.m.h

@@ -0,0 +1,53 @@
+#include <kinc/network/http.h>
+
+#import <Foundation/Foundation.h>
+
+void kinc_http_request(const char *url, const char *path, const char *data, int port, bool secure, int method, const char *header,
+                       kinc_http_callback_t callback, void *callbackdata) {
+	NSString *urlstring = secure ? @"https://" : @"http://";
+	urlstring = [urlstring stringByAppendingString:[NSString stringWithUTF8String:url]];
+	urlstring = [urlstring stringByAppendingString:@":"];
+	urlstring = [urlstring stringByAppendingString:[[NSNumber numberWithInt:port] stringValue]];
+	urlstring = [urlstring stringByAppendingString:@"/"];
+	urlstring = [urlstring stringByAppendingString:[NSString stringWithUTF8String:path]];
+
+	NSURL *aUrl = [NSURL URLWithString:urlstring];
+
+	NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
+	sessionConfiguration.HTTPAdditionalHeaders = @{@"Content-Type" : @"application/json"};
+	NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
+	NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl];
+	if (data != 0) {
+		// printf("Sending %s\n\n", data);
+		NSString *datastring = [NSString stringWithUTF8String:data];
+		request.HTTPBody = [datastring dataUsingEncoding:NSUTF8StringEncoding];
+	}
+
+	switch (method) {
+	case KINC_HTTP_GET:
+		request.HTTPMethod = @"GET";
+		break;
+	case KINC_HTTP_POST:
+		request.HTTPMethod = @"POST";
+		break;
+	case KINC_HTTP_PUT:
+		request.HTTPMethod = @"PUT";
+		break;
+	case KINC_HTTP_DELETE:
+		request.HTTPMethod = @"DELETE";
+		break;
+	}
+
+	NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
+	                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+		                                            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
+		                                            int statusCode = (int)[httpResponse statusCode];
+
+		                                            NSMutableData *responseData = [[NSMutableData alloc] init];
+		                                            [responseData appendData:data];
+		                                            [responseData appendBytes:"\0" length:1];
+
+		                                            callback(error == nil ? 0 : 1, statusCode, (const char *)[responseData bytes], callbackdata);
+	                                            }];
+	[dataTask resume];
+}

+ 24 - 0
armorcore/sources/backends/apple/kinc/backend/system.m.h

@@ -0,0 +1,24 @@
+#include <kinc/system.h>
+
+int kinc_hardware_threads(void) {
+	return (int)[[NSProcessInfo processInfo] processorCount];
+}
+
+#ifdef KINC_APPLE_SOC
+
+int kinc_cpu_cores(void) {
+	return kinc_hardware_threads();
+}
+
+#else
+
+#include <sys/sysctl.h>
+
+int kinc_cpu_cores(void) {
+	uint32_t proper_cpu_count = 1;
+	size_t count_length = sizeof(count_length);
+	sysctlbyname("hw.physicalcpu", &proper_cpu_count, &count_length, 0, 0);
+	return (int)proper_cpu_count;
+}
+
+#endif

+ 50 - 0
armorcore/sources/backends/apple/kinc/backend/thread.m.h

@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <Foundation/Foundation.h>
+
+#include <kinc/threads/mutex.h>
+#include <kinc/threads/thread.h>
+
+#include <pthread.h>
+#include <stdio.h>
+#include <wchar.h>
+
+static void *ThreadProc(void *arg) {
+	@autoreleasepool {
+		kinc_thread_t *t = (kinc_thread_t *)arg;
+		t->impl.thread(t->impl.param);
+		pthread_exit(NULL);
+		return NULL;
+	}
+}
+
+void kinc_thread_init(kinc_thread_t *t, void (*thread)(void *param), void *param) {
+	t->impl.param = param;
+	t->impl.thread = thread;
+	pthread_attr_t attr;
+	pthread_attr_init(&attr);
+	// pthread_attr_setstacksize(&attr, 1024 * 64);
+	struct sched_param sp;
+	memset(&sp, 0, sizeof(sp));
+	sp.sched_priority = 0;
+	pthread_attr_setschedparam(&attr, &sp);
+	pthread_create(&t->impl.pthread, &attr, &ThreadProc, t);
+	// Kt::affirmD(ret == 0);
+	pthread_attr_destroy(&attr);
+}
+
+void kinc_thread_wait_and_destroy(kinc_thread_t *thread) {
+	int ret;
+	do {
+		ret = pthread_join(thread->impl.pthread, NULL);
+	} while (ret != 0);
+}
+
+bool kinc_thread_try_to_destroy(kinc_thread_t *thread) {
+	return pthread_join(thread->impl.pthread, NULL) == 0;
+}
+
+void kinc_threads_init(void) {}
+
+void kinc_threads_quit(void) {}

+ 56 - 0
armorcore/sources/backends/apple/kinc/backend/video.h

@@ -0,0 +1,56 @@
+#pragma once
+
+#include <objc/runtime.h>
+
+#include <kinc/graphics4/texture.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	double start;
+	double videoStart;
+	double next;
+	// double audioTime;
+	unsigned long long audioTime;
+	bool playing;
+	bool loop;
+	void *sound;
+	bool image_initialized;
+	kinc_g4_texture_t image;
+	double lastTime;
+	float duration;
+	bool finished;
+	int myWidth;
+	int myHeight;
+
+	id videoAsset;
+	id assetReader;
+	id videoTrackOutput;
+	id audioTrackOutput;
+	id url;
+} kinc_video_impl_t;
+
+typedef struct kinc_internal_video_sound_stream {
+	float *buffer;
+	int bufferSize;
+	int bufferWritePosition;
+	int bufferReadPosition;
+	uint64_t read;
+	uint64_t written;
+} kinc_internal_video_sound_stream_t;
+
+void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency);
+
+void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream);
+
+void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count);
+
+float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream);
+
+bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream);
+
+#ifdef __cplusplus
+}
+#endif

+ 311 - 0
armorcore/sources/backends/apple/kinc/backend/video.m.h

@@ -0,0 +1,311 @@
+#include <kinc/video.h>
+
+#import <AVFoundation/AVFoundation.h>
+#include <kinc/audio1/audio.h>
+#include <kinc/graphics4/texture.h>
+#include <kinc/io/filereader.h>
+#include <kinc/log.h>
+#include <kinc/system.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern const char *iphonegetresourcepath(void);
+extern const char *macgetresourcepath(void);
+
+void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) {
+	stream->bufferSize = 1024 * 100;
+	stream->bufferReadPosition = 0;
+	stream->bufferWritePosition = 0;
+	stream->read = 0;
+	stream->written = 0;
+	stream->buffer = (float *)malloc(stream->bufferSize * sizeof(float));
+}
+
+void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream) {
+	free(stream->buffer);
+}
+
+void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {
+	for (int i = 0; i < sample_count; ++i) {
+		float value = data[i]; // / 32767.0;
+		stream->buffer[stream->bufferWritePosition++] = value;
+		++stream->written;
+		if (stream->bufferWritePosition >= stream->bufferSize) {
+			stream->bufferWritePosition = 0;
+		}
+	}
+}
+
+static float samples[2] = {0};
+
+float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) {
+	++stream->read;
+	if (stream->written <= stream->read) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "Out of audio\n");
+		return 0;
+	}
+
+	if (stream->bufferReadPosition >= stream->bufferSize) {
+		stream->bufferReadPosition = 0;
+		kinc_log(KINC_LOG_LEVEL_INFO, "buffer read back - %i\n", (int)(stream->written - stream->read));
+	}
+	samples[0] = stream->buffer[stream->bufferReadPosition++];
+
+	if (stream->bufferReadPosition >= stream->bufferSize) {
+		stream->bufferReadPosition = 0;
+		kinc_log(KINC_LOG_LEVEL_INFO, "buffer read back - %i\n", (int)(stream->written - stream->read));
+	}
+	samples[1] = stream->buffer[stream->bufferReadPosition++];
+
+	return samples;
+}
+
+bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) {
+	return false;
+}
+
+static void load(kinc_video_t *video, double startTime) {
+	video->impl.videoStart = startTime;
+	AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:video->impl.url options:nil];
+	video->impl.videoAsset = asset;
+
+	video->impl.duration = [asset duration].value / [asset duration].timescale;
+
+	AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
+	NSDictionary *videoOutputSettings =
+	    [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey, nil];
+	AVAssetReaderTrackOutput *videoOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:videoOutputSettings];
+	[videoOutput setSupportsRandomAccess:YES];
+
+	bool hasAudio = [[asset tracksWithMediaType:AVMediaTypeAudio] count] > 0;
+	AVAssetReaderAudioMixOutput *audioOutput = NULL;
+	if (hasAudio) {
+		AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
+		NSDictionary *audioOutputSettings = [NSDictionary
+		    dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey, [NSNumber numberWithFloat:44100.0], AVSampleRateKey,
+		                                 [NSNumber numberWithInt:32], AVLinearPCMBitDepthKey, [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
+		                                 [NSNumber numberWithBool:YES], AVLinearPCMIsFloatKey, [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey, nil];
+		audioOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:@[ audioTrack ] audioSettings:audioOutputSettings];
+		[audioOutput setSupportsRandomAccess:YES];
+	}
+
+	AVAssetReader *reader = [AVAssetReader assetReaderWithAsset:asset error:nil];
+
+	if (startTime > 0) {
+		CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(startTime * 1000, 1000), kCMTimePositiveInfinity);
+		reader.timeRange = timeRange;
+	}
+
+	[reader addOutput:videoOutput];
+	if (hasAudio) {
+		[reader addOutput:audioOutput];
+	}
+
+	video->impl.assetReader = reader;
+	video->impl.videoTrackOutput = videoOutput;
+	if (hasAudio) {
+		video->impl.audioTrackOutput = audioOutput;
+	}
+	else {
+		video->impl.audioTrackOutput = NULL;
+	}
+
+	if (video->impl.myWidth < 0)
+		video->impl.myWidth = [videoTrack naturalSize].width;
+	if (video->impl.myHeight < 0)
+		video->impl.myHeight = [videoTrack naturalSize].height;
+	int framerate = [videoTrack nominalFrameRate];
+	kinc_log(KINC_LOG_LEVEL_INFO, "Framerate: %i\n", framerate);
+	video->impl.next = video->impl.videoStart;
+	video->impl.audioTime = video->impl.videoStart * 44100;
+}
+
+void kinc_video_init(kinc_video_t *video, const char *filename) {
+	video->impl.playing = false;
+	video->impl.sound = NULL;
+	video->impl.image_initialized = false;
+	char name[2048];
+#ifdef KINC_IOS
+	strcpy(name, iphonegetresourcepath());
+#else
+	strcpy(name, macgetresourcepath());
+#endif
+	strcat(name, "/");
+	strcat(name, KINC_DEBUGDIR);
+	strcat(name, "/");
+	strcat(name, filename);
+	video->impl.url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:name]];
+	video->impl.myWidth = -1;
+	video->impl.myHeight = -1;
+	video->impl.finished = false;
+	video->impl.duration = 0;
+	load(video, 0);
+}
+
+void kinc_video_destroy(kinc_video_t *video) {
+	kinc_video_stop(video);
+}
+
+#ifdef KINC_IOS
+void iosPlayVideoSoundStream(kinc_internal_video_sound_stream_t *video);
+void iosStopVideoSoundStream(void);
+#else
+void macPlayVideoSoundStream(kinc_internal_video_sound_stream_t *video);
+void macStopVideoSoundStream(void);
+#endif
+
+void kinc_video_play(kinc_video_t *video, bool loop) {
+	AVAssetReader *reader = video->impl.assetReader;
+	[reader startReading];
+
+	kinc_internal_video_sound_stream_t *stream = (kinc_internal_video_sound_stream_t *)malloc(sizeof(kinc_internal_video_sound_stream_t));
+	kinc_internal_video_sound_stream_init(stream, 2, 44100);
+	video->impl.sound = stream;
+#ifdef KINC_IOS
+	iosPlayVideoSoundStream((kinc_internal_video_sound_stream_t *)video->impl.sound);
+#else
+	macPlayVideoSoundStream((kinc_internal_video_sound_stream_t *)video->impl.sound);
+#endif
+
+	video->impl.playing = true;
+	video->impl.start = kinc_time() - video->impl.videoStart;
+	video->impl.loop = loop;
+}
+
+void kinc_video_pause(kinc_video_t *video) {
+	video->impl.playing = false;
+	if (video->impl.sound != NULL) {
+// Mixer::stop(sound);
+#ifdef KINC_IOS
+		iosStopVideoSoundStream();
+#else
+		macStopVideoSoundStream();
+#endif
+		kinc_internal_video_sound_stream_destroy((kinc_internal_video_sound_stream_t *)video->impl.sound);
+		free(video->impl.sound);
+		video->impl.sound = NULL;
+	}
+}
+
+void kinc_video_stop(kinc_video_t *video) {
+	kinc_video_pause(video);
+	video->impl.finished = true;
+}
+
+static void updateImage(kinc_video_t *video) {
+	if (!video->impl.playing)
+		return;
+
+	{
+		AVAssetReaderTrackOutput *videoOutput = video->impl.videoTrackOutput;
+		CMSampleBufferRef buffer = [videoOutput copyNextSampleBuffer];
+		if (!buffer) {
+			if (video->impl.loop) {
+				CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(0, 1000), kCMTimePositiveInfinity);
+				[videoOutput resetForReadingTimeRanges:[NSArray arrayWithObject:[NSValue valueWithCMTimeRange:timeRange]]];
+
+				AVAssetReaderAudioMixOutput *audioOutput = video->impl.audioTrackOutput;
+				CMSampleBufferRef audio_buffer = [audioOutput copyNextSampleBuffer];
+				while (audio_buffer) {
+					audio_buffer = [audioOutput copyNextSampleBuffer];
+				}
+				[audioOutput resetForReadingTimeRanges:[NSArray arrayWithObject:[NSValue valueWithCMTimeRange:timeRange]]];
+
+				buffer = [videoOutput copyNextSampleBuffer];
+
+				video->impl.start = kinc_time() - video->impl.videoStart;
+			}
+			else {
+				kinc_video_stop(video);
+				return;
+			}
+		}
+		video->impl.next = CMTimeGetSeconds(CMSampleBufferGetOutputPresentationTimeStamp(buffer));
+
+		CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(buffer);
+
+		if (!video->impl.image_initialized) {
+			CGSize size = CVImageBufferGetDisplaySize(pixelBuffer);
+			video->impl.myWidth = size.width;
+			video->impl.myHeight = size.height;
+			kinc_g4_texture_init(&video->impl.image, kinc_video_width(video), kinc_video_height(video), KINC_IMAGE_FORMAT_BGRA32);
+			video->impl.image_initialized = true;
+		}
+
+		if (pixelBuffer != NULL) {
+			CVPixelBufferLockBaseAddress(pixelBuffer, 0);
+#ifdef KINC_OPENGL
+			kinc_g4_texture_upload(&video->impl.image, (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer),
+			                       (int)(CVPixelBufferGetBytesPerRow(pixelBuffer) / 4));
+#else
+			kinc_g4_texture_upload(&video->impl.image, (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer), (int)(CVPixelBufferGetBytesPerRow(pixelBuffer)));
+#endif
+			CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
+		}
+		CFRelease(buffer);
+	}
+
+	if (video->impl.audioTrackOutput != NULL) {
+		AVAssetReaderAudioMixOutput *audioOutput = video->impl.audioTrackOutput;
+		while (video->impl.audioTime / 44100.0 < video->impl.next + 0.1) {
+			CMSampleBufferRef buffer = [audioOutput copyNextSampleBuffer];
+			if (!buffer)
+				return;
+			CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(buffer);
+			AudioBufferList audioBufferList;
+			CMBlockBufferRef blockBufferOut = nil;
+			CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(buffer, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL,
+			                                                        kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBufferOut);
+			for (int bufferCount = 0; bufferCount < audioBufferList.mNumberBuffers; ++bufferCount) {
+				float *samples = (float *)audioBufferList.mBuffers[bufferCount].mData;
+				kinc_internal_video_sound_stream_t *sound = (kinc_internal_video_sound_stream_t *)video->impl.sound;
+				if (video->impl.audioTime / 44100.0 > video->impl.next - 0.1) {
+					kinc_internal_video_sound_stream_insert_data(sound, samples, (int)numSamplesInBuffer * 2);
+				}
+				else {
+					// Send some data anyway because the buffers are huge
+					kinc_internal_video_sound_stream_insert_data(sound, samples, (int)numSamplesInBuffer);
+				}
+				video->impl.audioTime += numSamplesInBuffer;
+			}
+			CFRelease(blockBufferOut);
+			CFRelease(buffer);
+		}
+	}
+}
+
+void kinc_video_update(kinc_video_t *video, double time) {
+	if (video->impl.playing && time >= video->impl.start + video->impl.next) {
+		updateImage(video);
+	}
+}
+
+int kinc_video_width(kinc_video_t *video) {
+	return video->impl.myWidth;
+}
+
+int kinc_video_height(kinc_video_t *video) {
+	return video->impl.myHeight;
+}
+
+kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) {
+	kinc_video_update(video, kinc_time());
+	return &video->impl.image;
+}
+
+double kinc_video_duration(kinc_video_t *video) {
+	return video->impl.duration;
+}
+
+bool kinc_video_finished(kinc_video_t *video) {
+	return video->impl.finished;
+}
+
+bool kinc_video_paused(kinc_video_t *video) {
+	return !video->impl.playing;
+}
+
+double kinc_video_position(kinc_video_t *video) {
+	return video->impl.next - video->impl.start;
+}

+ 21 - 0
armorcore/sources/backends/data/android/app/CMakeLists.txt

@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.22.1)
+
+project("Kinc")
+
+set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}{debug_defines}")
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}{debug_defines}")
+set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}{release_defines}")
+set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}{release_defines}")
+
+include_directories(
+{includes})
+
+add_library(
+  kinc
+  SHARED
+{files})
+
+{libraries1}
+target_link_libraries(
+  kinc
+{libraries2})

+ 61 - 0
armorcore/sources/backends/data/android/app/build.gradle.kts

@@ -0,0 +1,61 @@
+plugins {
+    id("com.android.application")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    namespace = "{package}"
+    compileSdk = {compileSdkVersion}
+
+    defaultConfig {
+        applicationId = "{package}"
+        minSdk = {minSdkVersion}
+        targetSdk = {targetSdkVersion}
+        versionCode = {versionCode}
+        versionName = "{versionName}"
+        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+        ndk {
+            abiFilters.add("arm64-v8a")
+        }
+    }
+    sourceSets.getByName("main") {
+        java.setSrcDirs(listOf({javasources}))
+    }
+    buildTypes {
+        release {
+            isMinifyEnabled = true
+            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+            {architecture}
+        }
+        debug {
+            isMinifyEnabled = false
+            {architecture}
+        }
+    }
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_1_8
+        targetCompatibility = JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = "1.8"
+    }
+    externalNativeBuild {
+        cmake {
+            path = file("CMakeLists.txt")
+            version = "3.22.1"
+        }
+    }
+    buildFeatures {
+        viewBinding = true
+    }
+}
+
+dependencies {
+    implementation("androidx.core:core-ktx:1.9.0")
+    implementation("androidx.appcompat:appcompat:1.6.1")
+    implementation("com.google.android.material:material:1.8.0")
+    implementation("androidx.constraintlayout:constraintlayout:2.1.4")
+    testImplementation("junit:junit:4.13.2")
+    androidTestImplementation("androidx.test.ext:junit:1.1.5")
+    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
+}

+ 34 - 0
armorcore/sources/backends/data/android/app/proguard-rules.pro

@@ -0,0 +1,34 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+
+-keep public class tech.kinc.KincActivity {
+    public <methods>;
+}
+-keep public class tech.kinc.KincMoviePlayer {
+    public <methods>;
+}
+-keep public class tech.kinc.KincMovieTexture {
+    public <methods>;
+}
+-keepclasseswithmembernames,includedescriptorclasses class * {
+    native <methods>;
+}

+ 5 - 0
armorcore/sources/backends/data/android/build.gradle.kts

@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+    id("com.android.application") version "8.1.0" apply false
+    id("org.jetbrains.kotlin.android") version "1.8.0" apply false
+}

+ 23 - 0
armorcore/sources/backends/data/android/gradle.properties

@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true

BIN
armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Fri Aug 04 18:00:20 CEST 2023
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists

+ 185 - 0
armorcore/sources/backends/data/android/gradlew

@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"

+ 89 - 0
armorcore/sources/backends/data/android/gradlew.bat

@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 26 - 0
armorcore/sources/backends/data/android/main/AndroidManifest.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:installLocation="{installLocation}">
+{permissions}
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        tools:targetApi="{targetSdkVersion}">
+        <activity
+            android:name="tech.kinc.KincActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|screenLayout|smallestScreenSize"
+            android:exported="true" android:screenOrientation="{screenOrientation}"
+            android:launchMode="singleTask"
+            android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
+            <meta-data android:name="android.app.lib_name" android:value="kinc" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>

+ 3 - 0
armorcore/sources/backends/data/android/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">{name}</string>
+</resources>

+ 18 - 0
armorcore/sources/backends/data/android/settings.gradle.kts

@@ -0,0 +1,18 @@
+pluginManagement {
+    repositories {
+        google()
+        mavenCentral()
+        gradlePluginPortal()
+    }
+}
+dependencyResolutionManagement {
+    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+rootProject.name = "{name}"
+include(":app")
+ 

+ 202 - 0
armorcore/sources/backends/data/wasm/JS-Sources/audio-thread.js

@@ -0,0 +1,202 @@
+// from https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletNode
+
+function create_thread(func) {
+
+}
+
+class AudioThread extends AudioWorkletProcessor {
+  constructor(options) {
+    super();
+
+    const self = this;
+
+    this.port.onmessageerror = (e) => {
+      this.port.postMessage('Error: ' + JSON.stringify(e));
+    };
+
+    const mod = options.processorOptions.mod;
+    const memory = options.processorOptions.memory;
+    
+    const importObject = {
+      env: { memory },
+      imports: {
+        imported_func: arg => console.log('thread: ' + arg),
+        create_thread,
+        glViewport: function(x, y, width, height) { },
+        glScissor: function(x, y, width, height) { },
+        glGetIntegerv: function(pname, data) { },
+        glGetFloatv: function(pname, data) { },
+        glGetString: function(name) { },
+        glDrawElements: function(mode, count, type, offset) { },
+        glDrawElementsInstanced: function(mode, count, type, indices, instancecount) { },
+        glVertexAttribDivisor: function(index, divisor) { },
+        glBindFramebuffer: function(target, framebuffer) { },
+        glFramebufferTexture2D: function(target, attachment, textarget, texture, level) { },
+        glGenFramebuffers: function(n, framebuffers) { },
+        glGenRenderbuffers: function(n, renderbuffers) { },
+        glBindRenderbuffer: function(target, renderbuffer) { },
+        glRenderbufferStorage: function(target, internalformat, width, height) { },
+        glFramebufferRenderbuffer: function(target, attachment, renderbuffertarget, renderbuffer) { },
+        glReadPixels: function(x, y, width, height, format, type, data) { },
+        glTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, type, pixels) { },
+        glEnable: function(cap) { },
+        glDisable: function(cap) { },
+        glColorMask: function(red, green, blue, alpha) { },
+        glClearColor: function(red, green, blue, alpha) { },
+        glDepthMask: function(flag) { },
+        glClearDepthf: function(depth) { },
+        glStencilMask: function(mask) { },
+        glClearStencil: function(s) { },
+        glClear: function(mask) { },
+        glBindBuffer: function(target, buffer) { },
+        glUseProgram: function(program) { },
+        glStencilMaskSeparate: function(face, mask) { },
+        glStencilOpSeparate: function(face, fail, zfail, zpass) { },
+        glStencilFuncSeparate: function(face, func, ref, mask) { },
+        glDepthFunc: function(func) { },
+        glCullFace: function(mode) { },
+        glBlendFuncSeparate: function(src_rgb, dst_rgb, src_alpha, dst_alpha) { },
+        glBlendEquationSeparate: function(mode_rgb, mode_alpha) { },
+        glGenBuffers: function(n, buffers) { },
+        glBufferData: function(target, size, data, usage) { },
+        glCreateProgram: function() { },
+        glAttachShader: function(program, shader) { },
+        glBindAttribLocation: function(program, index, name) { },
+        glLinkProgram: function(program) { },
+        glGetProgramiv: function(program, pname, params) { },
+        glGetProgramInfoLog: function(program) { },
+        glCreateShader: function(type) { },
+        glShaderSource: function(shader, count, source, length) { },
+        glCompileShader: function(shader) { },
+        glGetShaderiv: function(shader, pname, params) { },
+        glGetShaderInfoLog: function(shader) { },
+        glBufferSubData: function(target, offset, size, data) { },
+        glEnableVertexAttribArray: function(index) { },
+        glVertexAttribPointer: function(index, size, type, normalized, stride, offset) { },
+        glDisableVertexAttribArray: function(index) { },
+        glGetUniformLocation: function(program, name) { },
+        glUniform1i: function(location, v0) { },
+        glUniform2i: function(location, v0, v1) { },
+        glUniform3i: function(location, v0, v1, v2) { },
+        glUniform4i: function(location, v0, v1, v2, v3) { },
+        glUniform1iv: function(location, count, value) { },
+        glUniform2iv: function(location, count, value) { },
+        glUniform3iv: function(location, count, value) { },
+        glUniform4iv: function(location, count, value) { },
+        glUniform1f: function(location, v0) { },
+        glUniform2f: function(location, v0, v1) { },
+        glUniform3f: function(location, v0, v1, v2) { },
+        glUniform4f: function(location, v0, v1, v2, v3) { },
+        glUniform1fv: function(location, count, value) { },
+        glUniform2fv: function(location, count, value) { },
+        glUniform3fv: function(location, count, value) { },
+        glUniform4fv: function(location, count, value) { },
+        glUniformMatrix3fv: function(location, count, transpose, value) { },
+        glUniformMatrix4fv: function(location, count, transpose, value) { },
+        glTexParameterf: function(target, pname, param) { },
+        glActiveTexture: function(texture) { },
+        glBindTexture: function(target, texture) { },
+        glTexParameteri: function(target, pname, param) { },
+        glGetActiveUniform: function(program, index, bufSize, length, size, type, name) { },
+        glGenTextures: function(n, textures) { },
+        glTexImage2D: function(target, level, internalformat, width, height, border, format, type, data) { },
+        glPixelStorei: function(pname, param) { },
+        glCompressedTexImage2D: function(target, level, internalformat, width, height, border, imageSize, data) { },
+        glDrawBuffers: function(n, bufs) { },
+        glGenerateMipmap: function(target) { },
+        glFlush: function() { },
+        glDeleteBuffers: function(n, buffers) { },
+        glDeleteTextures: function(n, textures) { },
+        glDeleteFramebuffers: function(n, framebuffers) { },
+        glDeleteProgram: function(program) { },
+        glDeleteShader: function(shader) { },
+        js_fprintf: function(format) {
+          console.log(read_string(format));
+        },
+        js_fopen: function(filename) {
+          return 0;
+        },
+        js_ftell: function(stream) {
+          return 0;
+        },
+        js_fseek: function(stream, offset, origin) {
+          return 0;
+        },
+        js_fread: function(ptr, size, count, stream) {
+          return 0;
+        },
+        js_time: function() {
+          return window.performance.now();
+        },
+        js_pow: function(x) {
+          return Math.pow(x);
+        },
+        js_floor: function(x) {
+          return Math.floor(x);
+        },
+        js_sin: function(x) {
+          return Math.sin(x);
+        },
+        js_cos: function(x) {
+          return Math.cos(x);
+        },
+        js_tan: function(x) {
+          return Math.tan(x);
+        },
+        js_log: function(base, exponent) {
+          return Math.log(base, exponent);
+        },
+        js_exp: function(x) {
+          return Math.exp(x);
+        },
+        js_sqrt: function(x) {
+          return Math.sqrt(x);
+        },
+        js_eval: function(str) { }
+      }
+    };
+
+    WebAssembly.instantiate(mod, importObject).then((instance) => {
+      this.port.postMessage('Running audio thread');
+      self.audio_func = instance.exports.audio_func;
+      self.audio_pointer = instance.exports.malloc(16 * 1024);
+      this.port.postMessage('Audio pointer: ' + self.audio_pointer);
+      this.port.postMessage('Memory byteLength: ' + memory.buffer.byteLength);
+      self.audio_data = new Float32Array(
+        memory.buffer,
+        self.audio_pointer,
+        16 * 256
+      );
+    });
+  }
+
+  process(inputs, outputs, parameters) {
+    const output = outputs[0];
+
+    const data = output[0];
+
+    //for (let i = 0; i < data.length; ++i) {
+    //  data[i] = Math.random() * 2 - 1;
+    //}
+
+    if (this.audio_func) {
+      let offset = 0;
+      for (;;) {
+        const length = Math.min(data.length - offset, this.audio_data.length);
+        this.audio_func(this.audio_pointer + offset, length);
+        for (let i = 0; i < length; ++i) {
+          data[offset + i] = this.audio_data[i];
+        }
+
+        if (offset + this.audio_data.length >= data.length) {
+          break;
+        }
+        offset += this.audio_data.length;
+      }
+    }
+
+    return true;
+  }
+}
+
+registerProcessor('audio-thread', AudioThread);

+ 6 - 0
armorcore/sources/backends/data/wasm/JS-Sources/index.html

@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+
+<body>
+  <canvas id="kanvas" width="800" height="600"></canvas>
+  <script src="start.js"></script>
+</body>

+ 541 - 0
armorcore/sources/backends/data/wasm/JS-Sources/start.js

@@ -0,0 +1,541 @@
+// Includes snippets from https://surma.dev/things/c-to-webassembly/ and https://developer.mozilla.org/en-US/docs/WebAssembly/Using_the_JavaScript_API -->
+
+let memory = null;
+let heapu8 = null;
+let heapu16 = null;
+let heapu32 = null;
+let heapi32 = null;
+let heapf32 = null;
+let mod = null;
+let instance = null;
+let audio_thread_started = false;
+
+function create_thread(func) {
+	console.log('Creating thread');
+	const thread_starter = new Worker('thread_starter.js');
+	const arr = new Uint8Array(memory.buffer, func, 256);
+	let str = '';
+	for (let i = 0; arr[i] != 0; ++i) {
+		str += String.fromCharCode(arr[i]);
+	}
+	thread_starter.postMessage({ mod, memory, func: str });
+}
+
+async function start_audio_thread() {
+	const audioContext = new AudioContext();
+	await audioContext.audioWorklet.addModule('audio-thread.js');
+	const audioThreadNode = new AudioWorkletNode(audioContext, 'audio-thread', { processorOptions: { mod, memory }});
+	audioThreadNode.port.onmessage = (message) => {
+		console.log(message.data);
+	};
+	audioThreadNode.connect(audioContext.destination);
+}
+
+function read_string(ptr) {
+	let str = '';
+	for (let i = 0; heapu8[ptr + i] != 0; ++i) {
+		str += String.fromCharCode(heapu8[ptr + i]);
+	}
+	return str;
+}
+
+function write_string(ptr, str) {
+	for (let i = 0; i < str.length; ++i) {
+		heapu8[ptr + i] = str.charCodeAt(i);
+	}
+	heapu8[ptr + str.length] = 0;
+}
+
+async function init() {
+	let wasm_bytes = null;
+	await fetch("./ShaderTest.wasm").then(res => res.arrayBuffer()).then(buffer => wasm_bytes = new Uint8Array(buffer));
+
+	// Read memory size from wasm file
+	let memory_size = 0;
+	let i = 8;
+	while (i < wasm_bytes.length) {
+		function read_leb() {
+			let result = 0;
+			let shift = 0;
+			while (true) {
+				let byte = wasm_bytes[i++];
+				result |= (byte & 0x7f) << shift;
+				if ((byte & 0x80) == 0) return result;
+				shift += 7;
+			}
+		}
+		let type = read_leb()
+		let length = read_leb()
+		if (type == 6) {
+			read_leb(); // count
+			i++; // gtype
+			i++; // mutable
+			read_leb(); // opcode
+			memory_size = read_leb() / 65536 + 1;
+			break;
+		}
+		i += length;
+	}
+
+	memory = new WebAssembly.Memory({ initial: memory_size, maximum: memory_size, shared: true });
+	heapu8 = new Uint8Array(memory.buffer);
+	heapu16 = new Uint16Array(memory.buffer);
+	heapu32 = new Uint32Array(memory.buffer);
+	heapi32 = new Int32Array(memory.buffer);
+	heapf32 = new Float32Array(memory.buffer);
+
+	const kanvas = document.getElementById('kanvas');
+	const gl = kanvas.getContext('webgl2', { antialias: false, alpha: false });
+	// gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
+	gl.getExtension("EXT_color_buffer_float");
+	gl.getExtension("OES_texture_float_linear");
+	gl.getExtension("OES_texture_half_float_linear");
+	gl.getExtension("EXT_texture_filter_anisotropic");
+
+	let file_buffer = null;
+	let file_buffer_pos = 0;
+	let gl_programs = [null];
+	let gl_shaders = [null];
+	let gl_buffers = [null];
+	let gl_framebuffers = [null];
+	let gl_renderbuffers = [null];
+	let gl_textures = [null];
+	let gl_locations = [null];
+
+	const result = await WebAssembly.instantiate(
+		wasm_bytes, {
+			env: { memory },
+			imports: {
+				create_thread,
+				glViewport: function(x, y, width, height) {
+					gl.viewport(x, y, width, height);
+				},
+				glScissor: function(x, y, width, height) {
+					gl.scissor(x, y, width, height);
+				},
+				glGetIntegerv: function(pname, data) {
+					if (pname == 2) { // GL_MAJOR_VERSION
+						heapu32[data / 4] = 3;
+					}
+					else {
+						heapu32[data / 4] = gl.getParameter(pname);
+					}
+				},
+				glGetFloatv: function(pname, data) {
+					heapf32[data / 4] = gl.getParameter(pname);
+				},
+				glGetString: function(name) {
+					// return gl.getParameter(name);
+				},
+				glDrawElements: function(mode, count, type, offset) {
+					gl.drawElements(mode, count, type, offset);
+				},
+				glDrawElementsInstanced: function(mode, count, type, indices, instancecount) {
+					gl.drawElementsInstanced(mode, count, type, indices, instancecount);
+				},
+				glVertexAttribDivisor: function(index, divisor) {
+					gl.vertexAttribDivisor(index, divisor);
+				},
+				glBindFramebuffer: function(target, framebuffer) {
+					gl.bindFramebuffer(target, gl_framebuffers[framebuffer]);
+				},
+				glFramebufferTexture2D: function(target, attachment, textarget, texture, level) {
+					gl.framebufferTexture2D(target, attachment, textarget, gl_textures[texture], level);
+					if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+						console.log("Incomplete framebuffer");
+					}
+				},
+				glGenFramebuffers: function(n, framebuffers) {
+					for (let i = 0; i < n; ++i) {
+						gl_framebuffers.push(gl.createFramebuffer());
+						heapu32[framebuffers / 4 + i] = gl_framebuffers.length - 1;
+					}
+				},
+				glGenRenderbuffers: function(n, renderbuffers) {
+					for (let i = 0; i < n; ++i) {
+						gl_renderbuffers.push(gl.createRenderbuffer());
+						heapu32[renderbuffers / 4 + i] = gl_renderbuffers.length - 1;
+					}
+				},
+				glBindRenderbuffer: function(target, renderbuffer) {
+					gl.bindRenderbuffer(target, gl_renderbuffers[renderbuffer]);
+				},
+				glRenderbufferStorage: function(target, internalformat, width, height) {
+					gl.renderbufferStorage(target, internalformat, width, height)
+				},
+				glFramebufferRenderbuffer: function(target, attachment, renderbuffertarget, renderbuffer) {
+					gl.framebufferRenderbuffer(target, attachment, renderbuffertarget, gl_renderbuffers[renderbuffer]);
+				},
+				glReadPixels: function(x, y, width, height, format, type, data) {
+					let pixels = type == gl.FLOAT ? heapf32.subarray(data / 4) : heapu8.subarray(data);
+					gl.readPixels(x, y, width, height, format, type, pixels);
+				},
+				glTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, type, pixels) {
+					gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, heapu8.subarray(pixels));
+				},
+				glEnable: function(cap) {
+					gl.enable(cap);
+				},
+				glDisable: function(cap) {
+					gl.disable(cap);
+				},
+				glColorMask: function(red, green, blue, alpha) {
+					gl.colorMask(red, green, blue, alpha);
+				},
+				glClearColor: function(red, green, blue, alpha) {
+					gl.clearColor(red, green, blue, alpha);
+				},
+				glDepthMask: function(flag) {
+					gl.depthMask(flag);
+				},
+				glClearDepthf: function(depth) {
+					gl.clearDepth(depth);
+				},
+				glStencilMask: function(mask) {
+					gl.stencilMask(mask);
+				},
+				glClearStencil: function(s) {
+					gl.clearStencil(s);
+				},
+				glClear: function(mask) {
+					gl.clear(mask);
+				},
+				glBindBuffer: function(target, buffer) {
+					gl.bindBuffer(target, gl_buffers[buffer]);
+				},
+				glUseProgram: function(program) {
+					gl.useProgram(gl_programs[program]);
+				},
+				glStencilMaskSeparate: function(face, mask) {
+					gl.stencilMaskSeparate(face, mask);
+				},
+				glStencilOpSeparate: function(face, fail, zfail, zpass) {
+					gl.stencilOpSeparate(face, fail, zfail, zpass);
+				},
+				glStencilFuncSeparate: function(face, func, ref, mask) {
+					gl.stencilFuncSeparate(face, func, ref, mask);
+				},
+				glDepthFunc: function(func) {
+					gl.depthFunc(func);
+				},
+				glCullFace: function(mode) {
+					gl.cullFace(mode);
+				},
+				glBlendFuncSeparate: function(src_rgb, dst_rgb, src_alpha, dst_alpha) {
+					gl.blendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha);
+				},
+				glBlendEquationSeparate: function(mode_rgb, mode_alpha) {
+					gl.blendEquationSeparate(mode_rgb, mode_alpha);
+				},
+				glGenBuffers: function(n, buffers) {
+					for (let i = 0; i < n; ++i) {
+						gl_buffers.push(gl.createBuffer());
+						heapu32[buffers / 4 + i] = gl_buffers.length - 1;
+					}
+				},
+				glBufferData: function(target, size, data, usage) {
+					gl.bufferData(target, heapu8.subarray(data, data + Number(size)), usage);
+				},
+				glCreateProgram: function() {
+					gl_programs.push(gl.createProgram());
+					return gl_programs.length - 1;
+				},
+				glAttachShader: function(program, shader) {
+					gl.attachShader(gl_programs[program], gl_shaders[shader]);
+				},
+				glBindAttribLocation: function(program, index, name) {
+					gl.bindAttribLocation(gl_programs[program], index, read_string(name));
+				},
+				glLinkProgram: function(program) {
+					gl.linkProgram(gl_programs[program]);
+				},
+				glGetProgramiv: function(program, pname, params) {
+					heapu32[params / 4] = gl.getProgramParameter(gl_programs[program], pname);
+				},
+				glGetProgramInfoLog: function(program) {
+					console.log(gl.getProgramInfoLog(gl_programs[program]));
+				},
+				glCreateShader: function(type) {
+					gl_shaders.push(gl.createShader(type));
+					return gl_shaders.length - 1;
+				},
+				glShaderSource: function(shader, count, source, length) {
+					gl.shaderSource(gl_shaders[shader], read_string(heapu32[source / 4]));
+				},
+				glCompileShader: function(shader) {
+					gl.compileShader(gl_shaders[shader]);
+				},
+				glGetShaderiv: function(shader, pname, params) {
+					heapu32[params / 4] = gl.getShaderParameter(gl_shaders[shader], pname);
+				},
+				glGetShaderInfoLog: function(shader) {
+					console.log(gl.getShaderInfoLog(gl_shaders[shader]));
+				},
+				glBufferSubData: function(target, offset, size, data) {
+					gl.bufferSubData(target, Number(offset), heapu8.subarray(data, data + Number(size)), 0);
+				},
+				glEnableVertexAttribArray: function(index) {
+					gl.enableVertexAttribArray(index);
+				},
+				glVertexAttribPointer: function(index, size, type, normalized, stride, offset) {
+					gl.vertexAttribPointer(index, size, type, normalized, stride, offset);
+				},
+				glDisableVertexAttribArray: function(index) {
+					gl.disableVertexAttribArray(index);
+				},
+				glGetUniformLocation: function(program, name) {
+					gl_locations.push(gl.getUniformLocation(gl_programs[program], read_string(name)));
+					return gl_locations.length - 1;
+				},
+				glUniform1i: function(location, v0) {
+					gl.uniform1i(gl_locations[location], v0);
+				},
+				glUniform2i: function(location, v0, v1) {
+					gl.uniform2i(gl_locations[location], v0, v1);
+				},
+				glUniform3i: function(location, v0, v1, v2) {
+					gl.uniform3i(gl_locations[location], v0, v1, v2);
+				},
+				glUniform4i: function(location, v0, v1, v2, v3) {
+					gl.uniform4i(gl_locations[location], v0, v1, v2, v3);
+				},
+				glUniform1iv: function(location, count, value) {
+					gl.uniform1iv(gl_locations[location], count, heapi32.subarray(value / 4));
+				},
+				glUniform2iv: function(location, count, value) {
+					gl.uniform2iv(gl_locations[location], count, heapi32.subarray(value / 4));
+				},
+				glUniform3iv: function(location, count, value) {
+					gl.uniform3iv(gl_locations[location], count, heapi32.subarray(value / 4));
+				},
+				glUniform4iv: function(location, count, value) {
+					gl.uniform4iv(gl_locations[location], count, heapi32.subarray(value / 4));
+				},
+				glUniform1f: function(location, v0) {
+					gl.uniform1f(gl_locations[location], v0);
+				},
+				glUniform2f: function(location, v0, v1) {
+					gl.uniform2f(gl_locations[location], v0, v1);
+				},
+				glUniform3f: function(location, v0, v1, v2) {
+					gl.uniform3f(gl_locations[location], v0, v1, v2);
+				},
+				glUniform4f: function(location, v0, v1, v2, v3) {
+					gl.uniform4f(gl_locations[location], v0, v1, v2, v3);
+				},
+				glUniform1fv: function(location, count, value) {
+					var f32 = new Float32Array(memory.buffer, value, count);
+					gl.uniform1fv(gl_locations[location], f32);
+				},
+				glUniform2fv: function(location, count, value) {
+					var f32 = new Float32Array(memory.buffer, value, count * 2);
+					gl.uniform2fv(gl_locations[location], f32);
+				},
+				glUniform3fv: function(location, count, value) {
+					var f32 = new Float32Array(memory.buffer, value, count * 3);
+					gl.uniform3fv(gl_locations[location], f32);
+				},
+				glUniform4fv: function(location, count, value) {
+					var f32 = new Float32Array(memory.buffer, value, count * 4);
+					gl.uniform4fv(gl_locations[location], f32);
+				},
+				glUniformMatrix3fv: function(location, count, transpose, value) {
+					var f32 = new Float32Array(memory.buffer, value, 3 * 3);
+					gl.uniformMatrix3fv(gl_locations[location], transpose, f32);
+				},
+				glUniformMatrix4fv: function(location, count, transpose, value) {
+					var f32 = new Float32Array(memory.buffer, value, 4 * 4);
+					gl.uniformMatrix4fv(gl_locations[location], transpose, f32);
+				},
+				glTexParameterf: function(target, pname, param) {
+					gl.texParameterf(target, pname, param);
+				},
+				glActiveTexture: function(texture) {
+					gl.activeTexture(texture);
+				},
+				glBindTexture: function(target, texture) {
+					gl.bindTexture(target, gl_textures[texture]);
+				},
+				glTexParameteri: function(target, pname, param) {
+					gl.texParameteri(target, pname, param);
+				},
+				glGetActiveUniform: function(program, index, bufSize, length, size, type, name) {
+					let u = gl.getActiveUniform(gl_programs[program], index);
+					heapu32[size / 4] = u.size;
+					heapu32[type / 4] = u.type;
+					write_string(name, u.name);
+				},
+				glGenTextures: function(n, textures) {
+					for (let i = 0; i < n; ++i) {
+						gl_textures.push(gl.createTexture());
+						heapu32[textures / 4 + i] = gl_textures.length - 1;
+					}
+				},
+				glTexImage2D: function(target, level, internalformat, width, height, border, format, type, data) {
+					let pixels = type == gl.FLOAT ? heapf32.subarray(data / 4) :
+								 type == gl.UNSIGNED_INT ? heapu32.subarray(data / 4) :
+								 type == gl.UNSIGNED_SHORT ? heapu16.subarray(data / 2) :
+								 type == gl.HALF_FLOAT ? heapu16.subarray(data / 2) : heapu8.subarray(data);
+					gl.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+				},
+				glPixelStorei: function(pname, param) {
+					gl.pixelStorei(pname, param);
+				},
+				glCompressedTexImage2D: function(target, level, internalformat, width, height, border, imageSize, data) {
+					gl.compressedTexImage2D(target, level, internalformat, width, height, border, imageSize, heapu8.subarray(data));
+				},
+				glDrawBuffers: function(n, bufs) {
+					let ar = [];
+					for (let i = 0; i < n; ++i) {
+						ar.push(gl.COLOR_ATTACHMENT0 + i);
+					}
+					gl.drawBuffers(ar);
+				},
+				glGenerateMipmap: function(target) {
+					gl.generateMipmap(target);
+				},
+				glFlush: function() {
+					gl.flush();
+				},
+				glDeleteBuffers: function(n, buffers) {
+					for (let i = 0; i < n; ++i) {
+						gl.deleteBuffer(gl_buffers[heapu32[buffers / 4 + i]]);
+					}
+				},
+				glDeleteTextures: function(n, textures) {
+					for (let i = 0; i < n; ++i) {
+						gl.deleteTexture(gl_textures[heapu32[textures / 4 + i]]);
+					}
+				},
+				glDeleteFramebuffers: function(n, framebuffers) {
+					for (let i = 0; i < n; ++i) {
+						gl.deleteFramebuffer(gl_framebuffers[heapu32[framebuffers / 4 + i]]);
+					}
+				},
+				glDeleteProgram: function(program) {
+					gl.deleteProgram(gl_programs[program]);
+				},
+				glDeleteShader: function(shader) {
+					gl.deleteShader(gl_shaders[shader]);
+				},
+				js_fprintf: function(format) {
+					console.log(read_string(format));
+				},
+				js_fopen: function(filename) {
+					const req = new XMLHttpRequest();
+					req.open("GET", read_string(filename), false);
+					req.overrideMimeType("text/plain; charset=x-user-defined");
+					req.send();
+					let str = req.response;
+					file_buffer_pos = 0;
+					file_buffer = new ArrayBuffer(str.length);
+					let buf_view = new Uint8Array(file_buffer);
+					for (let i = 0; i < str.length; ++i) {
+						buf_view[i] = str.charCodeAt(i);
+					}
+					return 1;
+				},
+				js_ftell: function(stream) {
+					return file_buffer_pos;
+				},
+				js_fseek: function(stream, offset, origin) {
+					file_buffer_pos = offset;
+					if (origin == 1) file_buffer_pos += file_buffer.byteLength; // SEEK_END
+					return 0;
+				},
+				js_fread: function(ptr, size, count, stream) {
+					let buf_view = new Uint8Array(file_buffer);
+					for (let i = 0; i < count; ++i) {
+						heapu8[ptr + i] = buf_view[file_buffer_pos++];
+					}
+					return count;
+				},
+				js_time: function() {
+					return window.performance.now();
+				},
+				js_pow: function(x) {
+					return Math.pow(x);
+				},
+				js_floor: function(x) {
+					return Math.floor(x);
+				},
+				js_sin: function(x) {
+					return Math.sin(x);
+				},
+				js_cos: function(x) {
+					return Math.cos(x);
+				},
+				js_tan: function(x) {
+					return Math.tan(x);
+				},
+				js_log: function(base, exponent) {
+					return Math.log(base, exponent);
+				},
+				js_exp: function(x) {
+					return Math.exp(x);
+				},
+				js_sqrt: function(x) {
+					return Math.sqrt(x);
+				},
+				js_eval: function(str) {
+					(1, eval)(read_string(str));
+				}
+			}
+		}
+	);
+
+	mod = result.module;
+	instance = result.instance;
+	instance.exports._start();
+
+	function update() {
+		instance.exports._update();
+		window.requestAnimationFrame(update);
+	}
+	window.requestAnimationFrame(update);
+
+	kanvas.addEventListener('click', (event) => {
+		if (!audio_thread_started) {
+			start_audio_thread();
+			audio_thread_started = true;
+		}
+	});
+
+	kanvas.addEventListener('contextmenu', (event) => {
+		event.preventDefault();
+	});
+
+	kanvas.addEventListener('mousedown', (event) => {
+		instance.exports._mousedown(event.button, event.clientX, event.clientY);
+	});
+
+	kanvas.addEventListener('mouseup', (event) => {
+		instance.exports._mouseup(event.button, event.clientX, event.clientY);
+	});
+
+	kanvas.addEventListener('mousemove', (event) => {
+		instance.exports._mousemove(event.clientX, event.clientY);
+	});
+
+	kanvas.addEventListener('wheel', (event) => {
+		instance.exports._wheel(event.deltaY);
+	});
+
+	kanvas.addEventListener('keydown', (event) => {
+		if (event.repeat) {
+			event.preventDefault();
+			return;
+		}
+		instance.exports._keydown(event.keyCode);
+	});
+
+	kanvas.addEventListener('keyup', (event) => {
+		if (event.repeat) {
+			event.preventDefault();
+			return;
+		}
+		instance.exports._keyup(event.keyCode);
+	});
+}
+
+init();

+ 24 - 0
armorcore/sources/backends/data/wasm/JS-Sources/thread_starter.js

@@ -0,0 +1,24 @@
+function create_thread(func) {
+
+}
+
+onmessage = function(e) {
+  console.log('onmessage');
+  
+  const mod = e.data.mod;
+  const memory = e.data.memory;
+  const func = e.data.func;
+
+  const importObject = {
+    env: { memory },
+    imports: {
+      imported_func: arg => console.log('thread: ' + arg),
+      create_thread
+    }
+  };
+
+  WebAssembly.instantiate(mod, importObject).then((instance) => {
+    console.log('Running thread');
+    instance.exports[func]();
+  });
+};

+ 1226 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.c.h

@@ -0,0 +1,1226 @@
+#include "Direct3D11.h"
+
+#include <kinc/log.h>
+
+#include <kinc/math/core.h>
+#include <kinc/graphics4/pipeline.h>
+#include <kinc/graphics4/shader.h>
+
+#include <kinc/graphics4/graphics.h>
+#include <kinc/graphics4/indexbuffer.h>
+#include <kinc/graphics4/rendertarget.h>
+#include <kinc/graphics4/vertexbuffer.h>
+#include <kinc/graphics4/texture.h>
+
+#undef CreateWindow
+
+#include <kinc/system.h>
+
+#include <kinc/display.h>
+#include <kinc/window.h>
+
+#ifdef KINC_WINDOWS
+#include <kinc/backend/Windows.h>
+#else
+int antialiasingSamples(void) {
+	return 1;
+}
+#endif
+
+// MinGW workaround for missing defines
+#ifndef D3D11_MIN_DEPTH
+#define D3D11_MIN_DEPTH (0.0f)
+#endif
+
+#ifndef D3D11_MAX_DEPTH
+#define D3D11_MAX_DEPTH (1.0f)
+#endif
+
+extern kinc_g4_pipeline_t *currentPipeline;
+extern float currentBlendFactor[4];
+extern bool needPipelineRebind;
+void kinc_internal_set_constants(void);
+void kinc_internal_pipeline_rebind(void);
+
+bool kinc_internal_scissoring = false;
+
+// static unsigned hz;
+// static bool vsync;
+
+static D3D_FEATURE_LEVEL featureLevel;
+// static IDXGISwapChain *swapChain = NULL;
+static D3D11_SAMPLER_DESC lastSamplers[16];
+
+struct Sampler {
+	D3D11_SAMPLER_DESC desc;
+	ID3D11SamplerState *state;
+};
+
+#define MAX_SAMPLERS 256
+static struct Sampler samplers[MAX_SAMPLERS];
+static uint32_t samplers_size = 0;
+
+static ID3D11SamplerState *getSamplerState(D3D11_SAMPLER_DESC *desc) {
+	for (unsigned i = 0; i < samplers_size; ++i) {
+		if (memcmp(desc, &samplers[i].desc, sizeof(D3D11_SAMPLER_DESC)) == 0) {
+			return samplers[i].state;
+		}
+	}
+	struct Sampler s;
+	s.desc = *desc;
+	dx_ctx.device->lpVtbl->CreateSamplerState(dx_ctx.device, &s.desc, &s.state);
+	assert(samplers_size < MAX_SAMPLERS);
+	samplers[samplers_size++] = s;
+	return s.state;
+}
+
+static void initSamplers(void) {
+	D3D11_SAMPLER_DESC samplerDesc;
+	memset(&samplerDesc, 0, sizeof(samplerDesc));
+	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
+	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
+	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
+	samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
+	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+	samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
+
+	ID3D11SamplerState *state;
+	dx_ctx.device->lpVtbl->CreateSamplerState(dx_ctx.device, &samplerDesc, &state);
+
+	ID3D11SamplerState *states[D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT];
+	for (int i = 0; i < D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT; ++i) {
+		states[i] = state;
+	}
+
+	dx_ctx.context->lpVtbl->VSSetSamplers(dx_ctx.context, 0, D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, states);
+	dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, 0, D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, states);
+}
+
+static ID3D11RenderTargetView *currentRenderTargetViews[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT];
+static int renderTargetCount = 1;
+static ID3D11DepthStencilView *currentDepthStencilView;
+
+void kinc_g4_internal_destroy_window(int window) {}
+
+void kinc_g4_internal_destroy(void) {}
+
+static void createBackbuffer(struct dx_window *window, int antialiasingSamples) {
+	if (window->depthStencil) {
+		window->depthStencil->lpVtbl->Release(window->depthStencil);
+		window->depthStencilView->lpVtbl->Release(window->depthStencilView);
+		window->renderTargetView->lpVtbl->Release(window->renderTargetView);
+		window->backBuffer->lpVtbl->Release(window->backBuffer);
+		window->depthStencil = NULL;
+		window->depthStencilView = NULL;
+		window->renderTargetView = NULL;
+		window->backBuffer = NULL;
+	}
+
+	window->swapChain->lpVtbl->ResizeBuffers(window->swapChain, 1, window->width, window->height, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
+
+	kinc_microsoft_affirm(window->swapChain->lpVtbl->GetBuffer(window->swapChain, 0, &IID_ID3D11Texture2D, (void **)&window->backBuffer));
+
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)window->backBuffer, NULL, &window->renderTargetView));
+
+	D3D11_TEXTURE2D_DESC backBufferDesc;
+	window->backBuffer->lpVtbl->GetDesc(window->backBuffer, &backBufferDesc);
+	window->width = window->new_width = backBufferDesc.Width;
+	window->height = window->new_height = backBufferDesc.Height;
+
+	// TODO (DK) map depth/stencilBufferBits arguments
+	D3D11_TEXTURE2D_DESC depth_stencil_desc;
+	depth_stencil_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
+	depth_stencil_desc.Width = backBufferDesc.Width;
+	depth_stencil_desc.Height = backBufferDesc.Height;
+	depth_stencil_desc.ArraySize = 1;
+	depth_stencil_desc.MipLevels = 1;
+	depth_stencil_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
+	depth_stencil_desc.Usage = D3D11_USAGE_DEFAULT;
+	depth_stencil_desc.CPUAccessFlags = 0;
+	depth_stencil_desc.SampleDesc.Count = antialiasingSamples > 1 ? antialiasingSamples : 1,
+	depth_stencil_desc.SampleDesc.Quality = antialiasingSamples > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0;
+	depth_stencil_desc.MiscFlags = 0;
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &depth_stencil_desc, NULL, &window->depthStencil));
+
+	D3D11_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_desc;
+	depth_stencil_view_desc.ViewDimension = antialiasingSamples > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D;
+	depth_stencil_view_desc.Format = DXGI_FORMAT_UNKNOWN;
+	depth_stencil_view_desc.Flags = 0;
+	if (antialiasingSamples <= 1) {
+		depth_stencil_view_desc.Texture2D.MipSlice = 0;
+	}
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateDepthStencilView(dx_ctx.device, (ID3D11Resource *)window->depthStencil, &depth_stencil_view_desc,
+	                                                                    &window->depthStencilView));
+}
+
+#ifdef KINC_WINDOWS
+static bool isWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) {
+	OSVERSIONINFOEXW osvi = {sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0};
+	DWORDLONG const dwlConditionMask =
+	    VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_GREATER_EQUAL),
+	                        VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+
+	osvi.dwMajorVersion = wMajorVersion;
+	osvi.dwMinorVersion = wMinorVersion;
+	osvi.wServicePackMajor = wServicePackMajor;
+
+	return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
+}
+
+#ifndef _WIN32_WINNT_WIN8
+#define _WIN32_WINNT_WIN8 0x0602
+#endif
+
+#ifndef _WIN32_WINNT_WIN10
+#define _WIN32_WINNT_WIN10 0x0A00
+#endif
+
+static bool isWindows8OrGreater(void) {
+	return isWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);
+}
+
+static bool isWindows10OrGreater(void) {
+	return isWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN10), LOBYTE(_WIN32_WINNT_WIN10), 0);
+}
+#endif
+
+void kinc_g4_internal_init(void) {
+	D3D_FEATURE_LEVEL featureLevels[] = {
+	    D3D_FEATURE_LEVEL_11_0,
+	    D3D_FEATURE_LEVEL_10_1,
+	    D3D_FEATURE_LEVEL_10_0,
+	};
+	UINT flags = 0;
+
+#ifdef _DEBUG
+	flags = D3D11_CREATE_DEVICE_DEBUG;
+#endif
+
+	IDXGIAdapter *adapter = NULL;
+	HRESULT result = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION,
+	                                   &dx_ctx.device, &featureLevel, &dx_ctx.context);
+
+#ifdef _DEBUG
+	if (result == E_FAIL || result == DXGI_ERROR_SDK_COMPONENT_MISSING) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "%s", "Failed to create device with D3D11_CREATE_DEVICE_DEBUG, trying without");
+		flags &= ~D3D11_CREATE_DEVICE_DEBUG;
+		result = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &dx_ctx.device,
+		                           &featureLevel, &dx_ctx.context);
+	}
+#endif
+
+	if (result != S_OK) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "%s", "Falling back to the WARP driver, things will be slow.");
+		kinc_microsoft_affirm(D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_WARP, NULL, flags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION,
+		                                        &dx_ctx.device, &featureLevel, &dx_ctx.context));
+	}
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->QueryInterface(dx_ctx.device, &IID_IDXGIDevice, (void **)&dx_ctx.dxgiDevice));
+	kinc_microsoft_affirm(dx_ctx.dxgiDevice->lpVtbl->GetAdapter(dx_ctx.dxgiDevice, &dx_ctx.dxgiAdapter));
+	kinc_microsoft_affirm(dx_ctx.dxgiAdapter->lpVtbl->GetParent(dx_ctx.dxgiAdapter, &IID_IDXGIFactory, (void **)&dx_ctx.dxgiFactory));
+}
+
+void kinc_g4_internal_init_window(int windowId, int depthBufferBits, int stencilBufferBits, bool vSync) {
+	for (int i = 0; i < 1024 * 4; ++i)
+		vertexConstants[i] = 0;
+	for (int i = 0; i < 1024 * 4; ++i)
+		fragmentConstants[i] = 0;
+
+	struct dx_window *window = &dx_ctx.windows[windowId];
+#ifdef KINC_WINDOWS
+	window->hwnd = kinc_windows_window_handle(windowId);
+#endif
+	window->depth_bits = depthBufferBits;
+	window->stencil_bits = stencilBufferBits;
+	window->vsync = vSync;
+
+	const int _DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL = 3;
+	const int _DXGI_SWAP_EFFECT_FLIP_DISCARD = 4;
+
+	DXGI_SWAP_CHAIN_DESC swapChainDesc = {0};
+	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+	swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; // 60Hz
+	swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
+	swapChainDesc.BufferDesc.Width = kinc_window_width(windowId); // use automatic sizing
+	swapChainDesc.BufferDesc.Height = kinc_window_height(windowId);
+	swapChainDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format
+	// swapChainDesc.Stereo = false;
+	swapChainDesc.SampleDesc.Count = kinc_g4_antialiasing_samples() > 1 ? kinc_g4_antialiasing_samples() : 1;
+	swapChainDesc.SampleDesc.Quality = kinc_g4_antialiasing_samples() > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0;
+	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+	swapChainDesc.BufferCount = 2; // use two buffers to enable flip effect
+	swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+	swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; // DXGI_SCALING_NONE;
+#ifdef KINC_WINDOWS
+	if (isWindows10OrGreater()) {
+		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+		//(DXGI_SWAP_EFFECT) _DXGI_SWAP_EFFECT_FLIP_DISCARD;
+	}
+	else if (isWindows8OrGreater()) {
+		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+		//(DXGI_SWAP_EFFECT) _DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+	}
+	else
+#endif
+	{
+		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+	}
+	swapChainDesc.Flags = 0;
+	swapChainDesc.OutputWindow = window->hwnd;
+	swapChainDesc.Windowed = true;
+
+	kinc_microsoft_affirm(dx_ctx.dxgiFactory->lpVtbl->CreateSwapChain(dx_ctx.dxgiFactory, (IUnknown *)dx_ctx.dxgiDevice, &swapChainDesc, &window->swapChain));
+
+	createBackbuffer(window, kinc_g4_antialiasing_samples());
+	currentRenderTargetViews[0] = window->renderTargetView;
+	currentDepthStencilView = window->depthStencilView;
+	dx_ctx.context->lpVtbl->OMSetRenderTargets(dx_ctx.context, 1, &window->renderTargetView, window->depthStencilView);
+
+	D3D11_VIEWPORT viewPort;
+	viewPort.TopLeftX = 0.0f;
+	viewPort.TopLeftY = 0.0f;
+	viewPort.Width = (float)window->width;
+	viewPort.Height = (float)window->height;
+	viewPort.MinDepth = D3D11_MIN_DEPTH;
+	viewPort.MaxDepth = D3D11_MAX_DEPTH;
+	dx_ctx.context->lpVtbl->RSSetViewports(dx_ctx.context, 1, &viewPort);
+
+	D3D11_SAMPLER_DESC samplerDesc;
+	memset(&samplerDesc, 0, sizeof(samplerDesc));
+	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
+	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
+	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
+	samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
+	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+	samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
+
+	for (int i = 0; i < 16; ++i) {
+		lastSamplers[i] = samplerDesc;
+	}
+
+	initSamplers();
+
+	D3D11_BLEND_DESC blendDesc;
+	memset(&blendDesc, 0, sizeof(blendDesc));
+
+	D3D11_RENDER_TARGET_BLEND_DESC rtbd;
+	memset(&rtbd, 0, sizeof(rtbd));
+
+	rtbd.BlendEnable = true;
+	rtbd.SrcBlend = D3D11_BLEND_SRC_ALPHA;
+	rtbd.DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
+	rtbd.BlendOp = D3D11_BLEND_OP_ADD;
+	rtbd.SrcBlendAlpha = D3D11_BLEND_ONE;
+	rtbd.DestBlendAlpha = D3D11_BLEND_ZERO;
+	rtbd.BlendOpAlpha = D3D11_BLEND_OP_ADD;
+	rtbd.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL;
+
+	blendDesc.AlphaToCoverageEnable = false;
+	blendDesc.RenderTarget[0] = rtbd;
+
+	ID3D11BlendState *blending;
+	dx_ctx.device->lpVtbl->CreateBlendState(dx_ctx.device, &blendDesc, &blending);
+
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBlendState(dx_ctx.device, &blendDesc, &blending));
+	dx_ctx.context->lpVtbl->OMSetBlendState(dx_ctx.context, blending, NULL, 0xffffffff);
+}
+
+void kinc_g4_flush() {}
+
+static ID3D11ShaderResourceView *nullviews[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] = {0};
+
+static kinc_g4_index_buffer_t *currentIndexBuffer = NULL;
+
+void kinc_internal_g4_index_buffer_set(kinc_g4_index_buffer_t *buffer) {
+	currentIndexBuffer = buffer;
+	dx_ctx.context->lpVtbl->IASetIndexBuffer(dx_ctx.context, buffer->impl.ib, buffer->impl.sixteen ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
+}
+
+void kinc_g4_draw_indexed_vertices() {
+	kinc_internal_pipeline_rebind();
+	if (currentPipeline->tessellation_control_shader != NULL) {
+		dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
+	}
+	else {
+		dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+	}
+	kinc_internal_set_constants();
+	dx_ctx.context->lpVtbl->DrawIndexed(dx_ctx.context, currentIndexBuffer->impl.count, 0, 0);
+}
+
+void kinc_g4_draw_indexed_vertices_from_to(int start, int count) {
+	kinc_g4_draw_indexed_vertices_from_to_from(start, count, 0);
+}
+
+void kinc_g4_draw_indexed_vertices_from_to_from(int start, int count, int vertex_offset) {
+	kinc_internal_pipeline_rebind();
+	if (currentPipeline->tessellation_control_shader != NULL) {
+		dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
+	}
+	else {
+		dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+	}
+	kinc_internal_set_constants();
+	dx_ctx.context->lpVtbl->DrawIndexed(dx_ctx.context, count, start, vertex_offset);
+}
+
+void kinc_g4_draw_indexed_vertices_instanced(int instanceCount) {
+	kinc_g4_draw_indexed_vertices_instanced_from_to(instanceCount, 0, currentIndexBuffer->impl.count);
+}
+
+void kinc_g4_draw_indexed_vertices_instanced_from_to(int instanceCount, int start, int count) {
+	kinc_internal_pipeline_rebind();
+	if (currentPipeline->tessellation_control_shader != NULL) {
+		dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
+	}
+	else {
+		dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+	}
+	kinc_internal_set_constants();
+	dx_ctx.context->lpVtbl->DrawIndexedInstanced(dx_ctx.context, count, instanceCount, start, 0, 0);
+
+	dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, 0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullviews);
+}
+
+static D3D11_TEXTURE_ADDRESS_MODE convertAddressing(kinc_g4_texture_addressing_t addressing) {
+	switch (addressing) {
+	default:
+	case KINC_G4_TEXTURE_ADDRESSING_REPEAT:
+		return D3D11_TEXTURE_ADDRESS_WRAP;
+	case KINC_G4_TEXTURE_ADDRESSING_MIRROR:
+		return D3D11_TEXTURE_ADDRESS_MIRROR;
+	case KINC_G4_TEXTURE_ADDRESSING_CLAMP:
+		return D3D11_TEXTURE_ADDRESS_CLAMP;
+	case KINC_G4_TEXTURE_ADDRESSING_BORDER:
+		return D3D11_TEXTURE_ADDRESS_BORDER;
+	}
+}
+
+void kinc_g4_set_texture_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) {
+		return;
+	}
+
+	switch (dir) {
+	case KINC_G4_TEXTURE_DIRECTION_U:
+		lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].AddressU = convertAddressing(addressing);
+		break;
+	case KINC_G4_TEXTURE_DIRECTION_V:
+		lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].AddressV = convertAddressing(addressing);
+		break;
+	case KINC_G4_TEXTURE_DIRECTION_W:
+		lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].AddressW = convertAddressing(addressing);
+		break;
+	}
+
+	ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]);
+	dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler);
+}
+
+void kinc_g4_set_texture3d_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) {
+	kinc_g4_set_texture_addressing(unit, dir, addressing);
+}
+
+int kinc_g4_max_bound_textures(void) {
+	return D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
+}
+
+void kinc_g4_clear(unsigned flags, unsigned color, float depth, int stencil) {
+	const float clearColor[] = {((color & 0x00ff0000) >> 16) / 255.0f, ((color & 0x0000ff00) >> 8) / 255.0f, (color & 0x000000ff) / 255.0f,
+	                            ((color & 0xff000000) >> 24) / 255.0f};
+	for (int i = 0; i < renderTargetCount; ++i) {
+		if (currentRenderTargetViews[i] != NULL && flags & KINC_G4_CLEAR_COLOR) {
+			dx_ctx.context->lpVtbl->ClearRenderTargetView(dx_ctx.context, currentRenderTargetViews[i], clearColor);
+		}
+	}
+	if (currentDepthStencilView != NULL && (flags & KINC_G4_CLEAR_DEPTH) || (flags & KINC_G4_CLEAR_STENCIL)) {
+		unsigned d3dflags = ((flags & KINC_G4_CLEAR_DEPTH) ? D3D11_CLEAR_DEPTH : 0) | ((flags & KINC_G4_CLEAR_STENCIL) ? D3D11_CLEAR_STENCIL : 0);
+		dx_ctx.context->lpVtbl->ClearDepthStencilView(dx_ctx.context, currentDepthStencilView, d3dflags, kinc_clamp(depth, 0.0f, 1.0f), stencil);
+	}
+}
+
+void kinc_g4_begin(int windowId) {
+	dx_ctx.current_window = windowId;
+	struct dx_window *window = &dx_ctx.windows[windowId];
+	if (window->new_width != window->width || window->new_height != window->height) {
+		window->width = window->new_width;
+		window->height = window->new_height;
+		createBackbuffer(window, kinc_g4_antialiasing_samples());
+	}
+	kinc_g4_restore_render_target();
+}
+
+void kinc_g4_viewport(int x, int y, int width, int height) {
+	D3D11_VIEWPORT viewport;
+	viewport.TopLeftX = (float)x;
+	viewport.TopLeftY = (float)y;
+	viewport.Width = (float)width;
+	viewport.Height = (float)height;
+	viewport.MinDepth = 0.0f;
+	viewport.MaxDepth = 1.0f;
+	dx_ctx.context->lpVtbl->RSSetViewports(dx_ctx.context, 1, &viewport);
+}
+
+void kinc_internal_set_rasterizer_state(kinc_g4_pipeline_t *pipeline, bool scissoring);
+
+void kinc_g4_scissor(int x, int y, int width, int height) {
+	D3D11_RECT rect;
+	rect.left = x;
+	rect.top = y;
+	rect.right = x + width;
+	rect.bottom = y + height;
+	dx_ctx.context->lpVtbl->RSSetScissorRects(dx_ctx.context, 1, &rect);
+	kinc_internal_scissoring = true;
+	if (currentPipeline != NULL) {
+		kinc_internal_set_rasterizer_state(currentPipeline, kinc_internal_scissoring);
+	}
+}
+
+void kinc_g4_disable_scissor() {
+	dx_ctx.context->lpVtbl->RSSetScissorRects(dx_ctx.context, 0, NULL);
+	kinc_internal_scissoring = false;
+	if (currentPipeline != NULL) {
+		kinc_internal_set_rasterizer_state(currentPipeline, kinc_internal_scissoring);
+	}
+}
+
+void kinc_g4_set_pipeline(kinc_g4_pipeline_t *pipeline) {
+	if (pipeline != currentPipeline) {
+		currentPipeline = pipeline;
+		needPipelineRebind = true;
+	}
+}
+
+void kinc_g4_set_stencil_reference_value(int value) {
+	if (currentPipeline != NULL) {
+		dx_ctx.context->lpVtbl->OMSetDepthStencilState(dx_ctx.context, currentPipeline->impl.depthStencilState, value);
+	}
+}
+
+void kinc_g4_set_blend_constant(float r, float g, float b, float a) {
+	if (currentBlendFactor[0] != r || currentBlendFactor[1] != g || currentBlendFactor[2] != b || currentBlendFactor[3] != a) {
+		currentBlendFactor[0] = r;
+		currentBlendFactor[1] = g;
+		currentBlendFactor[2] = b;
+		currentBlendFactor[3] = a;
+		needPipelineRebind = true;
+	}
+}
+
+void kinc_g4_end(int windowId) {}
+
+bool kinc_g4_swap_buffers(void) {
+	bool success = true;
+	for (int i = 0; i < MAXIMUM_WINDOWS; i++) {
+		if (dx_ctx.windows[i].swapChain) {
+			if (!SUCCEEDED(dx_ctx.windows[i].swapChain->lpVtbl->Present(dx_ctx.windows[i].swapChain, dx_ctx.windows[i].vsync, 0))) {
+				success = false;
+			}
+		}
+	}
+	// TODO: if (hr == DXGI_STATUS_OCCLUDED)...
+	// http://www.pouet.net/topic.php?which=10454
+	// "Proper handling of DXGI_STATUS_OCCLUDED would be to pause the application,
+	// and periodically call Present with the TEST flag, and when it returns S_OK, resume rendering."
+
+	// if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
+	//	Initialize(m_window);
+	//}
+	// else {
+	return success;
+	//}
+}
+
+static void setInt(uint8_t *constants, uint32_t offset, uint32_t size, int value) {
+	if (size == 0)
+		return;
+	int *ints = (int *)&constants[offset];
+	ints[0] = value;
+}
+
+static void setInt2(uint8_t *constants, uint32_t offset, uint32_t size, int value1, int value2) {
+	if (size == 0)
+		return;
+	int *ints = (int *)&constants[offset];
+	ints[0] = value1;
+	ints[1] = value2;
+}
+
+static void setInt3(uint8_t *constants, uint32_t offset, uint32_t size, int value1, int value2, int value3) {
+	if (size == 0)
+		return;
+	int *ints = (int *)&constants[offset];
+	ints[0] = value1;
+	ints[1] = value2;
+	ints[2] = value3;
+}
+
+static void setInt4(uint8_t *constants, uint32_t offset, uint32_t size, int value1, int value2, int value3, int value4) {
+	if (size == 0)
+		return;
+	int *ints = (int *)&constants[offset];
+	ints[0] = value1;
+	ints[1] = value2;
+	ints[2] = value3;
+	ints[3] = value4;
+}
+
+static void setInts(uint8_t *constants, uint32_t offset, uint32_t size, uint8_t columns, uint8_t rows, int *values, int count) {
+	if (size == 0)
+		return;
+	int *ints = (int *)&constants[offset];
+	if (columns == 4 && rows == 4) {
+		for (int i = 0; i < count / 16 && i < (int)size / 4; ++i) {
+			for (int y = 0; y < 4; ++y) {
+				for (int x = 0; x < 4; ++x) {
+					ints[i * 16 + x + y * 4] = values[i * 16 + y + x * 4];
+				}
+			}
+		}
+	}
+	else if (columns == 3 && rows == 3) {
+		for (int i = 0; i < count / 9 && i < (int)size / 3; ++i) {
+			for (int y = 0; y < 4; ++y) {
+				for (int x = 0; x < 4; ++x) {
+					ints[i * 12 + x + y * 4] = values[i * 9 + y + x * 3];
+				}
+			}
+		}
+	}
+	else if (columns == 2 && rows == 2) {
+		for (int i = 0; i < count / 4 && i < (int)size / 2; ++i) {
+			for (int y = 0; y < 4; ++y) {
+				for (int x = 0; x < 4; ++x) {
+					ints[i * 8 + x + y * 4] = values[i * 4 + y + x * 2];
+				}
+			}
+		}
+	}
+	else {
+		for (int i = 0; i < count && i * 4 < (int)size; ++i) {
+			ints[i] = values[i];
+		}
+	}
+}
+
+static void setFloat(uint8_t *constants, uint32_t offset, uint32_t size, float value) {
+	if (size == 0)
+		return;
+	float *floats = (float *)&constants[offset];
+	floats[0] = value;
+}
+
+static void setFloat2(uint8_t *constants, uint32_t offset, uint32_t size, float value1, float value2) {
+	if (size == 0)
+		return;
+	float *floats = (float *)&constants[offset];
+	floats[0] = value1;
+	floats[1] = value2;
+}
+
+static void setFloat3(uint8_t *constants, uint32_t offset, uint32_t size, float value1, float value2, float value3) {
+	if (size == 0)
+		return;
+	float *floats = (float *)&constants[offset];
+	floats[0] = value1;
+	floats[1] = value2;
+	floats[2] = value3;
+}
+
+static void setFloat4(uint8_t *constants, uint32_t offset, uint32_t size, float value1, float value2, float value3, float value4) {
+	if (size == 0)
+		return;
+	float *floats = (float *)&constants[offset];
+	floats[0] = value1;
+	floats[1] = value2;
+	floats[2] = value3;
+	floats[3] = value4;
+}
+
+static void setFloats(uint8_t *constants, uint32_t offset, uint32_t size, uint8_t columns, uint8_t rows, float *values, int count) {
+	if (size == 0)
+		return;
+	float *floats = (float *)&constants[offset];
+	if (columns == 4 && rows == 4) {
+		for (int i = 0; i < count / 16 && i < (int)size / 4; ++i) {
+			for (int y = 0; y < 4; ++y) {
+				for (int x = 0; x < 4; ++x) {
+					floats[i * 16 + x + y * 4] = values[i * 16 + y + x * 4];
+				}
+			}
+		}
+	}
+	else if (columns == 3 && rows == 3) {
+		for (int i = 0; i < count / 9 && i < (int)size / 3; ++i) {
+			for (int y = 0; y < 4; ++y) {
+				for (int x = 0; x < 4; ++x) {
+					floats[i * 12 + x + y * 4] = values[i * 9 + y + x * 3];
+				}
+			}
+		}
+	}
+	else if (columns == 2 && rows == 2) {
+		for (int i = 0; i < count / 4 && i < (int)size / 2; ++i) {
+			for (int y = 0; y < 4; ++y) {
+				for (int x = 0; x < 4; ++x) {
+					floats[i * 8 + x + y * 4] = values[i * 4 + y + x * 2];
+				}
+			}
+		}
+	}
+	else {
+		for (int i = 0; i < count && i * 4 < (int)size; ++i) {
+			floats[i] = values[i];
+		}
+	}
+}
+
+static void setBool(uint8_t *constants, uint32_t offset, uint32_t size, bool value) {
+	if (size == 0)
+		return;
+	int *ints = (int *)&constants[offset];
+	ints[0] = value ? 1 : 0;
+}
+
+static void setMatrix4(uint8_t *constants, uint32_t offset, uint32_t size, kinc_matrix4x4_t *value) {
+	if (size == 0)
+		return;
+	float *floats = (float *)&constants[offset];
+	for (int y = 0; y < 4; ++y) {
+		for (int x = 0; x < 4; ++x) {
+			floats[x + y * 4] = value->m[y + x * 4];
+		}
+	}
+}
+
+static void setMatrix3(uint8_t *constants, uint32_t offset, uint32_t size, kinc_matrix3x3_t *value) {
+	if (size == 0)
+		return;
+	float *floats = (float *)&constants[offset];
+	for (int y = 0; y < 3; ++y) {
+		for (int x = 0; x < 3; ++x) {
+			floats[x + y * 4] = value->m[y + x * 3];
+		}
+	}
+}
+
+void kinc_g4_set_int(kinc_g4_constant_location_t location, int value) {
+	setInt(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value);
+	setInt(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value);
+	setInt(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value);
+	setInt(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value);
+	setInt(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value);
+	setInt(computeConstants, location.impl.computeOffset, location.impl.computeSize, value);
+}
+
+void kinc_g4_set_int2(kinc_g4_constant_location_t location, int value1, int value2) {
+	setInt2(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2);
+	setInt2(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2);
+	setInt2(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2);
+	setInt2(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2);
+	setInt2(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2);
+	setInt2(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2);
+}
+
+void kinc_g4_set_int3(kinc_g4_constant_location_t location, int value1, int value2, int value3) {
+	setInt3(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2, value3);
+	setInt3(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2, value3);
+	setInt3(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2, value3);
+	setInt3(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2, value3);
+	setInt3(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2, value3);
+	setInt3(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2, value3);
+}
+
+void kinc_g4_set_int4(kinc_g4_constant_location_t location, int value1, int value2, int value3, int value4) {
+	setInt4(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2, value3, value4);
+	setInt4(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2, value3, value4);
+	setInt4(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2, value3, value4);
+	setInt4(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2, value3, value4);
+	setInt4(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2, value3, value4);
+	setInt4(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2, value3, value4);
+}
+
+void kinc_g4_set_ints(kinc_g4_constant_location_t location, int *values, int count) {
+	setInts(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, location.impl.vertexColumns, location.impl.vertexRows, values, count);
+	setInts(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, location.impl.fragmentColumns, location.impl.fragmentRows, values,
+	        count);
+	setInts(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, location.impl.geometryColumns, location.impl.geometryRows, values,
+	        count);
+	setInts(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, location.impl.tessEvalColumns, location.impl.tessEvalRows, values,
+	        count);
+	setInts(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, location.impl.tessControlColumns,
+	        location.impl.tessControlRows, values, count);
+	setInts(computeConstants, location.impl.computeOffset, location.impl.computeSize, location.impl.computeColumns, location.impl.computeRows, values, count);
+}
+
+void kinc_g4_set_float(kinc_g4_constant_location_t location, float value) {
+	setFloat(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value);
+	setFloat(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value);
+	setFloat(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value);
+	setFloat(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value);
+	setFloat(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value);
+	setFloat(computeConstants, location.impl.computeOffset, location.impl.computeSize, value);
+}
+
+void kinc_g4_set_float2(kinc_g4_constant_location_t location, float value1, float value2) {
+	setFloat2(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2);
+	setFloat2(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2);
+	setFloat2(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2);
+	setFloat2(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2);
+	setFloat2(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2);
+	setFloat2(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2);
+}
+
+void kinc_g4_set_float3(kinc_g4_constant_location_t location, float value1, float value2, float value3) {
+	setFloat3(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2, value3);
+	setFloat3(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2, value3);
+	setFloat3(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2, value3);
+	setFloat3(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2, value3);
+	setFloat3(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2, value3);
+	setFloat3(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2, value3);
+}
+
+void kinc_g4_set_float4(kinc_g4_constant_location_t location, float value1, float value2, float value3, float value4) {
+	setFloat4(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2, value3, value4);
+	setFloat4(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2, value3, value4);
+	setFloat4(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2, value3, value4);
+	setFloat4(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2, value3, value4);
+	setFloat4(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2, value3, value4);
+	setFloat4(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2, value3, value4);
+}
+
+void kinc_g4_set_floats(kinc_g4_constant_location_t location, float *values, int count) {
+	setFloats(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, location.impl.vertexColumns, location.impl.vertexRows, values, count);
+	setFloats(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, location.impl.fragmentColumns, location.impl.fragmentRows, values,
+	          count);
+	setFloats(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, location.impl.geometryColumns, location.impl.geometryRows, values,
+	          count);
+	setFloats(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, location.impl.tessEvalColumns, location.impl.tessEvalRows, values,
+	          count);
+	setFloats(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, location.impl.tessControlColumns,
+	          location.impl.tessControlRows, values, count);
+	setFloats(computeConstants, location.impl.computeOffset, location.impl.computeSize, location.impl.computeColumns, location.impl.computeRows, values, count);
+}
+
+void kinc_g4_set_bool(kinc_g4_constant_location_t location, bool value) {
+	setBool(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value);
+	setBool(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value);
+	setBool(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value);
+	setBool(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value);
+	setBool(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value);
+	setBool(computeConstants, location.impl.computeOffset, location.impl.computeSize, value);
+}
+
+void kinc_g4_set_matrix4(kinc_g4_constant_location_t location, kinc_matrix4x4_t *value) {
+	setMatrix4(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value);
+	setMatrix4(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value);
+	setMatrix4(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value);
+	setMatrix4(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value);
+	setMatrix4(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value);
+	setMatrix4(computeConstants, location.impl.computeOffset, location.impl.computeSize, value);
+}
+
+void kinc_g4_set_matrix3(kinc_g4_constant_location_t location, kinc_matrix3x3_t *value) {
+	setMatrix3(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value);
+	setMatrix3(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value);
+	setMatrix3(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value);
+	setMatrix3(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value);
+	setMatrix3(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value);
+	setMatrix3(computeConstants, location.impl.computeOffset, location.impl.computeSize, value);
+}
+
+void kinc_g4_set_texture_magnification_filter(kinc_g4_texture_unit_t unit, kinc_g4_texture_filter_t filter) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
+		return;
+
+	D3D11_FILTER d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+
+	switch (filter) {
+	case KINC_G4_TEXTURE_FILTER_POINT:
+		switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) {
+		case D3D11_FILTER_MIN_MAG_MIP_POINT:
+			d3d11filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR:
+		case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT:
+		case D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
+			break;
+		case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT:
+		case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT:
+			d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
+		case D3D11_FILTER_MIN_MAG_MIP_LINEAR:
+		case D3D11_FILTER_ANISOTROPIC:
+			d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
+			break;
+		default:
+			break;
+		}
+		break;
+	case KINC_G4_TEXTURE_FILTER_LINEAR:
+		switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) {
+		case D3D11_FILTER_MIN_MAG_MIP_POINT:
+		case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT:
+			d3d11filter = D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR;
+			break;
+		case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT:
+		case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT:
+			d3d11filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
+		case D3D11_FILTER_MIN_MAG_MIP_LINEAR:
+		case D3D11_FILTER_ANISOTROPIC:
+			d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+			break;
+		default:
+			break;
+		}
+		break;
+	case KINC_G4_TEXTURE_FILTER_ANISOTROPIC:
+		d3d11filter = D3D11_FILTER_ANISOTROPIC;
+		break;
+	}
+
+	lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter = d3d11filter;
+	lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MaxAnisotropy = d3d11filter == D3D11_FILTER_ANISOTROPIC ? D3D11_REQ_MAXANISOTROPY : 0;
+
+	ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]);
+	dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler);
+}
+
+void kinc_g4_set_texture3d_magnification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) {
+	kinc_g4_set_texture_magnification_filter(texunit, filter);
+}
+
+void kinc_g4_set_texture_minification_filter(kinc_g4_texture_unit_t unit, kinc_g4_texture_filter_t filter) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
+		return;
+
+	D3D11_FILTER d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+
+	switch (filter) {
+	case KINC_G4_TEXTURE_FILTER_POINT:
+		switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) {
+		case D3D11_FILTER_MIN_MAG_MIP_POINT:
+		case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT:
+			d3d11filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR:
+		case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
+			break;
+		case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT:
+		case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT:
+			d3d11filter = D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR:
+		case D3D11_FILTER_MIN_MAG_MIP_LINEAR:
+		case D3D11_FILTER_ANISOTROPIC:
+			d3d11filter = D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR;
+			break;
+		default:
+			break;
+		}
+		break;
+	case KINC_G4_TEXTURE_FILTER_LINEAR:
+		switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) {
+		case D3D11_FILTER_MIN_MAG_MIP_POINT:
+		case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT:
+			d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR:
+		case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
+			break;
+		case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT:
+		case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT:
+			d3d11filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR:
+		case D3D11_FILTER_MIN_MAG_MIP_LINEAR:
+		case D3D11_FILTER_ANISOTROPIC:
+			d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+			break;
+		default:
+			break;
+		}
+		break;
+	case KINC_G4_TEXTURE_FILTER_ANISOTROPIC:
+		d3d11filter = D3D11_FILTER_ANISOTROPIC;
+		break;
+	default:
+		break;
+	}
+
+	lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter = d3d11filter;
+	lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MaxAnisotropy = d3d11filter == D3D11_FILTER_ANISOTROPIC ? D3D11_REQ_MAXANISOTROPY : 0;
+
+	ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]);
+	dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler);
+}
+
+void kinc_g4_set_texture3d_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) {
+	kinc_g4_set_texture_minification_filter(texunit, filter);
+}
+
+void kinc_g4_set_texture_mipmap_filter(kinc_g4_texture_unit_t unit, kinc_g4_mipmap_filter_t filter) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
+		return;
+
+	D3D11_FILTER d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+
+	switch (filter) {
+	case KINC_G4_MIPMAP_FILTER_NONE:
+	case KINC_G4_MIPMAP_FILTER_POINT:
+		switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) {
+		case D3D11_FILTER_MIN_MAG_MIP_POINT:
+		case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT:
+		case D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT:
+		case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
+			break;
+		case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT:
+		case D3D11_FILTER_MIN_MAG_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+			break;
+		case D3D11_FILTER_ANISOTROPIC:
+			d3d11filter = D3D11_FILTER_ANISOTROPIC;
+			break;
+		default:
+			break;
+		}
+		break;
+	case KINC_G4_MIPMAP_FILTER_LINEAR:
+		switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) {
+		case D3D11_FILTER_MIN_MAG_MIP_POINT:
+		case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
+			break;
+		case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT:
+		case D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR;
+			break;
+		case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT:
+		case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
+			break;
+		case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT:
+		case D3D11_FILTER_MIN_MAG_MIP_LINEAR:
+			d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+			break;
+		case D3D11_FILTER_ANISOTROPIC:
+			d3d11filter = D3D11_FILTER_ANISOTROPIC;
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+
+	lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter = d3d11filter;
+	lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MaxAnisotropy = d3d11filter == D3D11_FILTER_ANISOTROPIC ? D3D11_REQ_MAXANISOTROPY : 0;
+
+	ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]);
+	dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler);
+}
+
+void kinc_g4_set_texture3d_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter) {
+	kinc_g4_set_texture_mipmap_filter(texunit, filter);
+}
+
+void kinc_g4_set_texture_compare_mode(kinc_g4_texture_unit_t unit, bool enabled) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
+		return;
+
+	if (enabled) {
+		lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;
+		lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter = D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
+	}
+	else {
+		lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].ComparisonFunc = D3D11_COMPARISON_NEVER;
+	}
+
+	ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]);
+	dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler);
+}
+
+void kinc_g4_set_texture_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
+		return;
+
+	lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].ComparisonFunc = get_comparison(mode);
+
+	ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]);
+	dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler);
+}
+
+void kinc_g4_set_cubemap_compare_mode(kinc_g4_texture_unit_t unit, bool enabled) {
+	kinc_g4_set_texture_compare_mode(unit, enabled);
+}
+
+void kinc_g4_set_cubemap_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode) {
+	kinc_g4_set_texture_compare_func(unit, mode);
+}
+
+void kinc_g4_set_texture_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
+		return;
+
+	lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MaxAnisotropy = max_anisotropy;
+
+	ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]);
+	dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler);
+}
+
+void kinc_g4_set_cubemap_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy) {
+	kinc_g4_set_texture_max_anisotropy(unit, max_anisotropy);
+}
+
+void kinc_g4_set_texture_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
+		return;
+
+	lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MinLOD = lod_min_clamp;
+	lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MaxLOD = lod_max_clamp;
+
+	ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]);
+	dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler);
+}
+
+void kinc_g4_set_cubemap_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp) {
+	kinc_g4_set_texture_lod(unit, lod_min_clamp, lod_max_clamp);
+}
+
+void kinc_g4_restore_render_target(void) {
+	struct dx_window *window = &dx_ctx.windows[dx_ctx.current_window];
+	currentRenderTargetViews[0] = window->renderTargetView;
+	currentDepthStencilView = window->depthStencilView;
+	dx_ctx.context->lpVtbl->OMSetRenderTargets(dx_ctx.context, 1, &window->renderTargetView, window->depthStencilView);
+	renderTargetCount = 1;
+	D3D11_VIEWPORT viewPort;
+	viewPort.TopLeftX = 0.0f;
+	viewPort.TopLeftY = 0.0f;
+	viewPort.Width = (float)window->width;
+	viewPort.Height = (float)window->height;
+	viewPort.MinDepth = D3D11_MIN_DEPTH;
+	viewPort.MaxDepth = D3D11_MAX_DEPTH;
+	dx_ctx.context->lpVtbl->RSSetViewports(dx_ctx.context, 1, &viewPort);
+}
+
+void kinc_g4_set_render_targets(struct kinc_g4_render_target **targets, int count) {
+	currentDepthStencilView = targets[0]->impl.depthStencilView[0];
+
+	renderTargetCount = count;
+	for (int i = 0; i < count; ++i) {
+		currentRenderTargetViews[i] = targets[i]->impl.renderTargetViewRender[0];
+	}
+
+	dx_ctx.context->lpVtbl->OMSetRenderTargets(dx_ctx.context, count, currentRenderTargetViews, currentDepthStencilView);
+	D3D11_VIEWPORT viewPort;
+	viewPort.TopLeftX = 0.0f;
+	viewPort.TopLeftY = 0.0f;
+	viewPort.Width = (float)targets[0]->width;
+	viewPort.Height = (float)targets[0]->height;
+	viewPort.MinDepth = D3D11_MIN_DEPTH;
+	viewPort.MaxDepth = D3D11_MAX_DEPTH;
+	dx_ctx.context->lpVtbl->RSSetViewports(dx_ctx.context, 1, &viewPort);
+}
+
+void kinc_g4_set_render_target_face(struct kinc_g4_render_target *texture, int face) {
+	renderTargetCount = 1;
+	currentRenderTargetViews[0] = texture->impl.renderTargetViewRender[face];
+	currentDepthStencilView = texture->impl.depthStencilView[face];
+	dx_ctx.context->lpVtbl->OMSetRenderTargets(dx_ctx.context, 1, currentRenderTargetViews, currentDepthStencilView);
+	D3D11_VIEWPORT viewPort;
+	viewPort.TopLeftX = 0.0f;
+	viewPort.TopLeftY = 0.0f;
+	viewPort.Width = (float)texture->width;
+	viewPort.Height = (float)texture->height;
+	viewPort.MinDepth = D3D11_MIN_DEPTH;
+	viewPort.MaxDepth = D3D11_MAX_DEPTH;
+	dx_ctx.context->lpVtbl->RSSetViewports(dx_ctx.context, 1, &viewPort);
+}
+
+void kinc_g4_set_vertex_buffers(kinc_g4_vertex_buffer_t **buffers, int count) {
+	kinc_internal_g4_vertex_buffer_set(buffers[0], 0);
+
+	ID3D11Buffer **d3dbuffers = (ID3D11Buffer **)alloca(count * sizeof(ID3D11Buffer *));
+	for (int i = 0; i < count; ++i) {
+		d3dbuffers[i] = buffers[i]->impl.vb;
+	}
+
+	UINT *strides = (UINT *)alloca(count * sizeof(UINT));
+	for (int i = 0; i < count; ++i) {
+		strides[i] = buffers[i]->impl.stride;
+	}
+
+	UINT *internaloffsets = (UINT *)alloca(count * sizeof(UINT));
+	for (int i = 0; i < count; ++i) {
+		internaloffsets[i] = 0;
+	}
+
+	dx_ctx.context->lpVtbl->IASetVertexBuffers(dx_ctx.context, 0, count, d3dbuffers, strides, internaloffsets);
+}
+
+void kinc_g4_set_index_buffer(kinc_g4_index_buffer_t *buffer) {
+	kinc_internal_g4_index_buffer_set(buffer);
+}
+
+void kinc_internal_texture_set(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit);
+
+void kinc_g4_set_texture(kinc_g4_texture_unit_t unit, kinc_g4_texture_t *texture) {
+	kinc_internal_texture_set(texture, unit);
+}
+
+void kinc_internal_texture_set_image(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit);
+
+void kinc_g4_set_image_texture(kinc_g4_texture_unit_t unit, kinc_g4_texture_t *texture) {
+	kinc_internal_texture_set_image(texture, unit);
+}
+
+void kinc_internal_resize(int windowId, int width, int height) {
+	struct dx_window *window = &dx_ctx.windows[windowId];
+	window->new_width = width;
+	window->new_height = height;
+}
+
+void kinc_internal_change_framebuffer(int window_index, kinc_framebuffer_options_t *frame) {
+	struct dx_window *window = &dx_ctx.windows[window_index];
+	kinc_g4_set_antialiasing_samples(frame->samples_per_pixel);
+	window->vsync = frame->vertical_sync;
+}
+
+bool kinc_window_vsynced(int window_index) {
+	struct dx_window *window = &dx_ctx.windows[window_index];
+	return window->vsync;
+}
+
+bool kinc_g4_supports_instanced_rendering(void) {
+	return true;
+}
+
+bool kinc_g4_supports_compute_shaders(void) {
+	return true;
+}
+
+bool kinc_g4_supports_blend_constants(void) {
+	return true;
+}
+
+bool kinc_g4_supports_non_pow2_textures(void) {
+	return true; // we always request feature level >= 10
+}
+
+bool kinc_g4_render_targets_inverted_y(void) {
+	return false;
+}

+ 41 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#include <d3d11.h>
+#include <dxgi.h>
+#include <stdbool.h>
+
+#define MAXIMUM_WINDOWS 16
+
+struct dx_window {
+	HWND hwnd;
+	IDXGISwapChain *swapChain;
+	ID3D11Texture2D *backBuffer;
+	ID3D11RenderTargetView *renderTargetView;
+	ID3D11Texture2D *depthStencil;
+	ID3D11DepthStencilView *depthStencilView;
+
+	int width;
+	int height;
+
+	int new_width;
+	int new_height;
+
+	bool vsync;
+	int depth_bits;
+	int stencil_bits;
+};
+
+struct dx_context {
+	ID3D11Device *device;
+	ID3D11DeviceContext *context;
+	IDXGIDevice *dxgiDevice;
+	IDXGIAdapter *dxgiAdapter;
+	IDXGIFactory *dxgiFactory;
+
+	int current_window;
+	struct dx_window windows[MAXIMUM_WINDOWS];
+};
+
+extern struct dx_context dx_ctx;
+
+#include <kinc/backend/SystemMicrosoft.h>

+ 11 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.c.h

@@ -0,0 +1,11 @@
+#include "ShaderHash.h"
+
+// djb2
+uint32_t kinc_internal_hash_name(unsigned char *str) {
+	unsigned long hash = 5381;
+	int c;
+	while ((c = *str++)) {
+		hash = hash * 33 ^ c;
+	}
+	return hash;
+}

+ 19 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	uint32_t hash;
+	uint32_t index;
+} kinc_internal_hash_index_t;
+
+uint32_t kinc_internal_hash_name(unsigned char *str);
+
+#ifdef __cplusplus
+}
+#endif

+ 195 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.c.h

@@ -0,0 +1,195 @@
+#include "Direct3D11.h"
+
+#include <kinc/graphics4/compute.h>
+#include <kinc/graphics4/texture.h>
+#include <kinc/log.h>
+#include <kinc/math/core.h>
+
+#include <kinc/backend/SystemMicrosoft.h>
+
+#include <assert.h>
+
+static int getMultipleOf16(int value) {
+	int ret = 16;
+	while (ret < value)
+		ret += 16;
+	return ret;
+}
+
+void kinc_g4_compute_shader_init(kinc_g4_compute_shader *shader, void *_data, int length) {
+	unsigned index = 0;
+	uint8_t *data = (uint8_t *)_data;
+
+	memset(&shader->impl.attributes, 0, sizeof(shader->impl.attributes));
+	int attributesCount = data[index++];
+	for (int i = 0; i < attributesCount; ++i) {
+		unsigned char name[256];
+		for (unsigned i2 = 0; i2 < 255; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		shader->impl.attributes[i].hash = kinc_internal_hash_name(name);
+		shader->impl.attributes[i].index = data[index++];
+	}
+
+	memset(&shader->impl.textures, 0, sizeof(shader->impl.textures));
+	uint8_t texCount = data[index++];
+	for (unsigned i = 0; i < texCount; ++i) {
+		unsigned char name[256];
+		for (unsigned i2 = 0; i2 < 255; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		shader->impl.textures[i].hash = kinc_internal_hash_name(name);
+		shader->impl.textures[i].index = data[index++];
+	}
+
+	memset(&shader->impl.constants, 0, sizeof(shader->impl.constants));
+	uint8_t constantCount = data[index++];
+	shader->impl.constantsSize = 0;
+	for (unsigned i = 0; i < constantCount; ++i) {
+		unsigned char name[256];
+		for (unsigned i2 = 0; i2 < 255; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		kinc_g4_compute_internal_shader_constant constant;
+		constant.hash = kinc_internal_hash_name(name);
+		constant.offset = *(uint32_t *)&data[index];
+		index += 4;
+		constant.size = *(uint32_t *)&data[index];
+		index += 4;
+		constant.columns = data[index];
+		index += 1;
+		constant.rows = data[index];
+		index += 1;
+
+		shader->impl.constants[i] = constant;
+		shader->impl.constantsSize = constant.offset + constant.size;
+	}
+
+	shader->impl.length = (int)(length - index);
+	shader->impl.data = (uint8_t *)malloc(shader->impl.length);
+	assert(shader->impl.data != NULL);
+	memcpy(shader->impl.data, &data[index], shader->impl.length);
+
+	HRESULT hr =
+	    dx_ctx.device->lpVtbl->CreateComputeShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL, (ID3D11ComputeShader **)&shader->impl.shader);
+
+	if (hr != S_OK) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize compute shader.");
+		return;
+	}
+
+	D3D11_BUFFER_DESC desc;
+	desc.ByteWidth = getMultipleOf16(shader->impl.constantsSize);
+	desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+	desc.Usage = D3D11_USAGE_DEFAULT;
+	desc.CPUAccessFlags = 0;
+	desc.MiscFlags = 0;
+	desc.StructureByteStride = 0;
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &shader->impl.constantBuffer));
+}
+
+void kinc_g4_compute_shader_destroy(kinc_g4_compute_shader *shader) {}
+
+static kinc_g4_compute_internal_shader_constant *compute_findConstant(kinc_g4_compute_internal_shader_constant *constants, uint32_t hash) {
+	for (int i = 0; i < 64; ++i) {
+		if (constants[i].hash == hash) {
+			return &constants[i];
+		}
+	}
+	return NULL;
+}
+
+static kinc_internal_hash_index_t *compute_findTextureUnit(kinc_internal_hash_index_t *units, uint32_t hash) {
+	for (int i = 0; i < 64; ++i) {
+		if (units[i].hash == hash) {
+			return &units[i];
+		}
+	}
+	return NULL;
+}
+
+kinc_g4_constant_location_t kinc_g4_compute_shader_get_constant_location(kinc_g4_compute_shader *shader, const char *name) {
+	kinc_g4_constant_location_t location = {0};
+
+	uint32_t hash = kinc_internal_hash_name((unsigned char *)name);
+
+	kinc_g4_compute_internal_shader_constant *constant = compute_findConstant(shader->impl.constants, hash);
+	if (constant == NULL) {
+		location.impl.computeOffset = 0;
+		location.impl.computeSize = 0;
+		location.impl.computeColumns = 0;
+		location.impl.computeRows = 0;
+	}
+	else {
+		location.impl.computeOffset = constant->offset;
+		location.impl.computeSize = constant->size;
+		location.impl.computeColumns = constant->columns;
+		location.impl.computeRows = constant->rows;
+	}
+
+	if (location.impl.computeSize == 0) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "Uniform %s not found.", name);
+	}
+
+	return location;
+}
+
+kinc_g4_texture_unit_t kinc_g4_compute_shader_get_texture_unit(kinc_g4_compute_shader *shader, const char *name) {
+	char unitName[64];
+	int unitOffset = 0;
+	size_t len = strlen(name);
+	if (len > 63) {
+		len = 63;
+	}
+	strncpy(unitName, name, len + 1);
+	if (unitName[len - 1] == ']') {                  // Check for array - mySampler[2]
+		unitOffset = (int)(unitName[len - 2] - '0'); // Array index is unit offset
+		unitName[len - 3] = 0;                       // Strip array from name
+	}
+
+	uint32_t hash = kinc_internal_hash_name((unsigned char *)unitName);
+
+	kinc_g4_texture_unit_t unit;
+	for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) {
+		unit.stages[i] = -1;
+	}
+
+	kinc_internal_hash_index_t *compute_unit = compute_findTextureUnit(shader->impl.textures, hash);
+	if (compute_unit == NULL) {
+		unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] = -1;
+#ifndef NDEBUG
+		static int notFoundCount = 0;
+		if (notFoundCount < 10) {
+			kinc_log(KINC_LOG_LEVEL_WARNING, "Sampler %s not found.", unitName);
+			++notFoundCount;
+		}
+		else if (notFoundCount == 10) {
+			kinc_log(KINC_LOG_LEVEL_WARNING, "Giving up on sampler not found messages.", unitName);
+			++notFoundCount;
+		}
+#endif
+	}
+	else {
+		unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] = compute_unit->index + unitOffset;
+	}
+	return unit;
+}
+
+void kinc_g4_set_compute_shader(kinc_g4_compute_shader *shader) {
+	dx_ctx.context->lpVtbl->CSSetShader(dx_ctx.context, (ID3D11ComputeShader *)shader->impl.shader, NULL, 0);
+	dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)shader->impl.constantBuffer, 0, NULL, computeConstants, 0, 0);
+	dx_ctx.context->lpVtbl->CSSetConstantBuffers(dx_ctx.context, 0, 1, &shader->impl.constantBuffer);
+}
+
+void kinc_g4_compute(int x, int y, int z) {
+	dx_ctx.context->lpVtbl->Dispatch(dx_ctx.context, x, y, z);
+
+	ID3D11UnorderedAccessView *nullView = NULL;
+	dx_ctx.context->lpVtbl->CSSetUnorderedAccessViews(dx_ctx.context, 0, 1, &nullView, NULL);
+}

+ 43 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.h

@@ -0,0 +1,43 @@
+#pragma once
+
+#include <kinc/backend/graphics4/ShaderHash.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ID3D11Buffer;
+
+typedef struct kinc_g4_compute_constant_location_impl {
+	uint32_t offset;
+	uint32_t size;
+	uint8_t columns;
+	uint8_t rows;
+} kinc_g4_compute_constant_location_impl;
+
+typedef struct kinc_g4_compute_texture_unit_impl {
+	int unit;
+} kinc_g4_compute_texture_unit_impl;
+
+typedef struct kinc_g4_compute_internal_shader_constant {
+	uint32_t hash;
+	uint32_t offset;
+	uint32_t size;
+	uint8_t columns;
+	uint8_t rows;
+} kinc_g4_compute_internal_shader_constant;
+
+typedef struct kinc_g4_compute_shader_impl {
+	kinc_g4_compute_internal_shader_constant constants[64];
+	int constantsSize;
+	kinc_internal_hash_index_t attributes[64];
+	kinc_internal_hash_index_t textures[64];
+	struct ID3D11Buffer *constantBuffer;
+	void *shader;
+	uint8_t *data;
+	int length;
+} kinc_g4_compute_shader_impl;
+
+#ifdef __cplusplus
+}
+#endif

+ 107 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/d3d11unit.c

@@ -0,0 +1,107 @@
+// Windows 7
+#define WINVER 0x0601
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0601
+
+#define NOATOM
+#define NOCLIPBOARD
+#define NOCOLOR
+#define NOCOMM
+#define NOCTLMGR
+#define NODEFERWINDOWPOS
+#define NODRAWTEXT
+#define NOGDI
+#define NOGDICAPMASKS
+#define NOHELP
+#define NOICONS
+#define NOKANJI
+#define NOKEYSTATES
+#define NOMB
+#define NOMCX
+#define NOMEMMGR
+#define NOMENUS
+#define NOMETAFILE
+#define NOMINMAX
+// #define NOMSG
+#define NONLS
+#define NOOPENFILE
+#define NOPROFILER
+#define NORASTEROPS
+#define NOSCROLL
+#define NOSERVICE
+#define NOSHOWWINDOW
+#define NOSOUND
+#define NOSYSCOMMANDS
+#define NOSYSMETRICS
+#define NOTEXTMETRIC
+// #define NOUSER
+#define NOVIRTUALKEYCODES
+#define NOWH
+#define NOWINMESSAGES
+#define NOWINOFFSETS
+#define NOWINSTYLES
+#define WIN32_LEAN_AND_MEAN
+
+#include <kinc/graphics4/graphics.h>
+
+#include <kinc/backend/SystemMicrosoft.h>
+
+#pragma warning(disable : 4005)
+#include <d3d11.h>
+
+#include "Direct3D11.h"
+
+#include <assert.h>
+#include <malloc.h>
+#include <stdint.h>
+
+struct dx_context dx_ctx = {0};
+
+static uint8_t vertexConstants[1024 * 4];
+static uint8_t fragmentConstants[1024 * 4];
+static uint8_t geometryConstants[1024 * 4];
+static uint8_t tessControlConstants[1024 * 4];
+static uint8_t tessEvalConstants[1024 * 4];
+static uint8_t computeConstants[1024 * 4];
+
+static D3D11_COMPARISON_FUNC get_comparison(kinc_g4_compare_mode_t compare) {
+	switch (compare) {
+	default:
+	case KINC_G4_COMPARE_ALWAYS:
+		return D3D11_COMPARISON_ALWAYS;
+	case KINC_G4_COMPARE_NEVER:
+		return D3D11_COMPARISON_NEVER;
+	case KINC_G4_COMPARE_EQUAL:
+		return D3D11_COMPARISON_EQUAL;
+	case KINC_G4_COMPARE_NOT_EQUAL:
+		return D3D11_COMPARISON_NOT_EQUAL;
+	case KINC_G4_COMPARE_LESS:
+		return D3D11_COMPARISON_LESS;
+	case KINC_G4_COMPARE_LESS_EQUAL:
+		return D3D11_COMPARISON_LESS_EQUAL;
+	case KINC_G4_COMPARE_GREATER:
+		return D3D11_COMPARISON_GREATER;
+	case KINC_G4_COMPARE_GREATER_EQUAL:
+		return D3D11_COMPARISON_GREATER_EQUAL;
+	}
+}
+
+static size_t get_multiple_of_16(size_t value) {
+	size_t ret = 16;
+	while (ret < value) {
+		ret += 16;
+	}
+	return ret;
+}
+
+#include "Direct3D11.c.h"
+#include "ShaderHash.c.h"
+#include "compute.c.h"
+#include "indexbuffer.c.h"
+#include "pipeline.c.h"
+#include "rendertarget.c.h"
+#include "shader.c.h"
+#include "texture.c.h"
+#include "vertexbuffer.c.h"

+ 89 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.c.h

@@ -0,0 +1,89 @@
+#include <kinc/graphics4/indexbuffer.h>
+
+void kinc_g4_index_buffer_init(kinc_g4_index_buffer_t *buffer, int count, kinc_g4_index_buffer_format_t format, kinc_g4_usage_t usage) {
+	buffer->impl.count = count;
+	buffer->impl.sixteen = format == KINC_G4_INDEX_BUFFER_FORMAT_16BIT;
+	buffer->impl.last_start = 0;
+	buffer->impl.last_count = count;
+
+	uint32_t byte_size = buffer->impl.sixteen ? sizeof(uint16_t) * count : sizeof(uint32_t) * count;
+
+	if (usage == KINC_G4_USAGE_DYNAMIC) {
+		buffer->impl.indices = NULL;
+	}
+	else {
+		buffer->impl.indices = malloc(byte_size);
+	}
+
+	D3D11_BUFFER_DESC bufferDesc;
+	bufferDesc.Usage = D3D11_USAGE_DEFAULT;
+	bufferDesc.ByteWidth = byte_size;
+	bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+	bufferDesc.CPUAccessFlags = 0;
+	bufferDesc.MiscFlags = 0;
+	bufferDesc.StructureByteStride = 0;
+
+	buffer->impl.usage = usage;
+	switch (usage) {
+	case KINC_G4_USAGE_STATIC:
+		bufferDesc.Usage = D3D11_USAGE_DEFAULT;
+		break;
+	case KINC_G4_USAGE_DYNAMIC:
+		bufferDesc.Usage = D3D11_USAGE_DYNAMIC;
+		bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+		break;
+	case KINC_G4_USAGE_READABLE:
+		bufferDesc.Usage = D3D11_USAGE_DEFAULT;
+		break;
+	}
+
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &bufferDesc, NULL, &buffer->impl.ib));
+}
+
+void kinc_g4_index_buffer_destroy(kinc_g4_index_buffer_t *buffer) {
+	buffer->impl.ib->lpVtbl->Release(buffer->impl.ib);
+	free(buffer->impl.indices);
+	buffer->impl.indices = NULL;
+}
+
+static int kinc_g4_internal_index_buffer_stride(kinc_g4_index_buffer_t *buffer) {
+	return buffer->impl.sixteen ? 2 : 4;
+}
+
+void *kinc_g4_index_buffer_lock_all(kinc_g4_index_buffer_t *buffer) {
+	return kinc_g4_index_buffer_lock(buffer, 0, kinc_g4_index_buffer_count(buffer));
+}
+
+void *kinc_g4_index_buffer_lock(kinc_g4_index_buffer_t *buffer, int start, int count) {
+	buffer->impl.last_start = start;
+	buffer->impl.last_count = count;
+
+	if (buffer->impl.usage == KINC_G4_USAGE_DYNAMIC) {
+		D3D11_MAPPED_SUBRESOURCE mappedResource;
+		memset(&mappedResource, 0, sizeof(D3D11_MAPPED_SUBRESOURCE));
+		dx_ctx.context->lpVtbl->Map(dx_ctx.context, (ID3D11Resource *)buffer->impl.ib, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
+		uint8_t *data = (uint8_t *)mappedResource.pData;
+		return &data[start * kinc_g4_internal_index_buffer_stride(buffer)];
+	}
+	else {
+		uint8_t *data = (uint8_t *)buffer->impl.indices;
+		return &data[start * kinc_g4_internal_index_buffer_stride(buffer)];
+	}
+}
+
+void kinc_g4_index_buffer_unlock_all(kinc_g4_index_buffer_t *buffer) {
+	kinc_g4_index_buffer_unlock(buffer, buffer->impl.last_count);
+}
+
+void kinc_g4_index_buffer_unlock(kinc_g4_index_buffer_t *buffer, int count) {
+	if (buffer->impl.usage == KINC_G4_USAGE_DYNAMIC) {
+		dx_ctx.context->lpVtbl->Unmap(dx_ctx.context, (ID3D11Resource *)buffer->impl.ib, 0);
+	}
+	else {
+		dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)buffer->impl.ib, 0, NULL, buffer->impl.indices, 0, 0);
+	}
+}
+
+int kinc_g4_index_buffer_count(kinc_g4_index_buffer_t *buffer) {
+	return buffer->impl.count;
+}

+ 13 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.h

@@ -0,0 +1,13 @@
+#pragma once
+
+struct ID3D11Buffer;
+
+typedef struct {
+	struct ID3D11Buffer *ib;
+	void *indices;
+	int count;
+	int usage;
+	bool sixteen;
+	int last_start;
+	int last_count;
+} kinc_g4_index_buffer_impl_t;

+ 866 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.c.h

@@ -0,0 +1,866 @@
+#include <kinc/graphics4/pipeline.h>
+#include <kinc/graphics4/shader.h>
+#include <kinc/graphics4/vertexbuffer.h>
+#include <kinc/libs/stb_sprintf.h>
+#include <kinc/log.h>
+
+kinc_g4_pipeline_t *currentPipeline = NULL;
+float currentBlendFactor[4] = {0, 0, 0, 0};
+bool needPipelineRebind = true;
+
+static D3D11_CULL_MODE convert_cull_mode(kinc_g4_cull_mode_t cullMode) {
+	switch (cullMode) {
+	case KINC_G4_CULL_CLOCKWISE:
+		return D3D11_CULL_BACK;
+	case KINC_G4_CULL_COUNTER_CLOCKWISE:
+		return D3D11_CULL_FRONT;
+	case KINC_G4_CULL_NOTHING:
+		return D3D11_CULL_NONE;
+	default:
+		assert(false);
+		return D3D11_CULL_NONE;
+	}
+}
+
+static D3D11_BLEND convert_blend_factor(kinc_g4_blending_factor_t factor) {
+	switch (factor) {
+	case KINC_G4_BLEND_ONE:
+		return D3D11_BLEND_ONE;
+	case KINC_G4_BLEND_ZERO:
+		return D3D11_BLEND_ZERO;
+	case KINC_G4_BLEND_SOURCE_ALPHA:
+		return D3D11_BLEND_SRC_ALPHA;
+	case KINC_G4_BLEND_DEST_ALPHA:
+		return D3D11_BLEND_DEST_ALPHA;
+	case KINC_G4_BLEND_INV_SOURCE_ALPHA:
+		return D3D11_BLEND_INV_SRC_ALPHA;
+	case KINC_G4_BLEND_INV_DEST_ALPHA:
+		return D3D11_BLEND_INV_DEST_ALPHA;
+	case KINC_G4_BLEND_SOURCE_COLOR:
+		return D3D11_BLEND_SRC_COLOR;
+	case KINC_G4_BLEND_DEST_COLOR:
+		return D3D11_BLEND_DEST_COLOR;
+	case KINC_G4_BLEND_INV_SOURCE_COLOR:
+		return D3D11_BLEND_INV_SRC_COLOR;
+	case KINC_G4_BLEND_INV_DEST_COLOR:
+		return D3D11_BLEND_INV_DEST_COLOR;
+	default:
+		assert(false);
+		return D3D11_BLEND_SRC_ALPHA;
+	}
+}
+
+static D3D11_BLEND_OP convert_blend_operation(kinc_g4_blending_operation_t operation) {
+	switch (operation) {
+	case KINC_G4_BLENDOP_ADD:
+		return D3D11_BLEND_OP_ADD;
+	case KINC_G4_BLENDOP_SUBTRACT:
+		return D3D11_BLEND_OP_SUBTRACT;
+	case KINC_G4_BLENDOP_REVERSE_SUBTRACT:
+		return D3D11_BLEND_OP_REV_SUBTRACT;
+	case KINC_G4_BLENDOP_MIN:
+		return D3D11_BLEND_OP_MIN;
+	case KINC_G4_BLENDOP_MAX:
+		return D3D11_BLEND_OP_MAX;
+	default:
+		assert(false);
+		return D3D11_BLEND_OP_ADD;
+	}
+}
+
+static D3D11_STENCIL_OP get_stencil_action(kinc_g4_stencil_action_t action) {
+	switch (action) {
+	default:
+	case KINC_G4_STENCIL_KEEP:
+		return D3D11_STENCIL_OP_KEEP;
+	case KINC_G4_STENCIL_ZERO:
+		return D3D11_STENCIL_OP_ZERO;
+	case KINC_G4_STENCIL_REPLACE:
+		return D3D11_STENCIL_OP_REPLACE;
+	case KINC_G4_STENCIL_INCREMENT:
+		return D3D11_STENCIL_OP_INCR;
+	case KINC_G4_STENCIL_INCREMENT_WRAP:
+		return D3D11_STENCIL_OP_INCR_SAT;
+	case KINC_G4_STENCIL_DECREMENT:
+		return D3D11_STENCIL_OP_DECR;
+	case KINC_G4_STENCIL_DECREMENT_WRAP:
+		return D3D11_STENCIL_OP_DECR_SAT;
+	case KINC_G4_STENCIL_INVERT:
+		return D3D11_STENCIL_OP_INVERT;
+	}
+}
+
+void kinc_internal_set_constants(void) {
+	if (currentPipeline->vertex_shader->impl.constantsSize > 0) {
+		dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)currentPipeline->impl.vertexConstantBuffer, 0, NULL, vertexConstants, 0, 0);
+		dx_ctx.context->lpVtbl->VSSetConstantBuffers(dx_ctx.context, 0, 1, &currentPipeline->impl.vertexConstantBuffer);
+	}
+	if (currentPipeline->fragment_shader->impl.constantsSize > 0) {
+		dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)currentPipeline->impl.fragmentConstantBuffer, 0, NULL, fragmentConstants, 0,
+		                                          0);
+		dx_ctx.context->lpVtbl->PSSetConstantBuffers(dx_ctx.context, 0, 1, &currentPipeline->impl.fragmentConstantBuffer);
+	}
+	if (currentPipeline->geometry_shader != NULL && currentPipeline->geometry_shader->impl.constantsSize > 0) {
+		dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)currentPipeline->impl.geometryConstantBuffer, 0, NULL, geometryConstants, 0,
+		                                          0);
+		dx_ctx.context->lpVtbl->GSSetConstantBuffers(dx_ctx.context, 0, 1, &currentPipeline->impl.geometryConstantBuffer);
+	}
+	if (currentPipeline->tessellation_control_shader != NULL && currentPipeline->tessellation_control_shader->impl.constantsSize > 0) {
+		dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)currentPipeline->impl.tessControlConstantBuffer, 0, NULL,
+		                                          tessControlConstants, 0, 0);
+		dx_ctx.context->lpVtbl->HSSetConstantBuffers(dx_ctx.context, 0, 1, &currentPipeline->impl.tessControlConstantBuffer);
+	}
+	if (currentPipeline->tessellation_evaluation_shader != NULL && currentPipeline->tessellation_evaluation_shader->impl.constantsSize > 0) {
+		dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)currentPipeline->impl.tessEvalConstantBuffer, 0, NULL, tessEvalConstants, 0,
+		                                          0);
+		dx_ctx.context->lpVtbl->DSSetConstantBuffers(dx_ctx.context, 0, 1, &currentPipeline->impl.tessEvalConstantBuffer);
+	}
+}
+
+void kinc_g4_pipeline_init(struct kinc_g4_pipeline *state) {
+	memset(state, 0, sizeof(struct kinc_g4_pipeline));
+	kinc_g4_internal_pipeline_set_defaults(state);
+	state->impl.d3d11inputLayout = NULL;
+	state->impl.fragmentConstantBuffer = NULL;
+	state->impl.vertexConstantBuffer = NULL;
+	state->impl.geometryConstantBuffer = NULL;
+	state->impl.tessEvalConstantBuffer = NULL;
+	state->impl.tessControlConstantBuffer = NULL;
+	state->impl.depthStencilState = NULL;
+	state->impl.rasterizerState = NULL;
+	state->impl.rasterizerStateScissor = NULL;
+	state->impl.blendState = NULL;
+}
+
+void kinc_g4_pipeline_destroy(struct kinc_g4_pipeline *state) {
+	if (state->impl.d3d11inputLayout != NULL) {
+		state->impl.d3d11inputLayout->lpVtbl->Release(state->impl.d3d11inputLayout);
+		state->impl.d3d11inputLayout = NULL;
+	}
+	if (state->impl.fragmentConstantBuffer != NULL) {
+		state->impl.fragmentConstantBuffer->lpVtbl->Release(state->impl.fragmentConstantBuffer);
+		state->impl.fragmentConstantBuffer = NULL;
+	}
+	if (state->impl.vertexConstantBuffer != NULL) {
+		state->impl.vertexConstantBuffer->lpVtbl->Release(state->impl.vertexConstantBuffer);
+		state->impl.vertexConstantBuffer = NULL;
+	}
+	if (state->impl.geometryConstantBuffer != NULL) {
+		state->impl.geometryConstantBuffer->lpVtbl->Release(state->impl.geometryConstantBuffer);
+		state->impl.geometryConstantBuffer = NULL;
+	}
+	if (state->impl.tessEvalConstantBuffer != NULL) {
+		state->impl.tessEvalConstantBuffer->lpVtbl->Release(state->impl.tessEvalConstantBuffer);
+		state->impl.tessEvalConstantBuffer = NULL;
+	}
+	if (state->impl.tessControlConstantBuffer != NULL) {
+		state->impl.tessControlConstantBuffer->lpVtbl->Release(state->impl.tessControlConstantBuffer);
+		state->impl.tessControlConstantBuffer = NULL;
+	}
+	if (state->impl.depthStencilState != NULL) {
+		state->impl.depthStencilState->lpVtbl->Release(state->impl.depthStencilState);
+		state->impl.depthStencilState = NULL;
+	}
+	if (state->impl.rasterizerState != NULL) {
+		state->impl.rasterizerState->lpVtbl->Release(state->impl.rasterizerState);
+		state->impl.rasterizerState = NULL;
+	}
+	if (state->impl.rasterizerStateScissor != NULL) {
+		state->impl.rasterizerStateScissor->lpVtbl->Release(state->impl.rasterizerStateScissor);
+		state->impl.rasterizerStateScissor = NULL;
+	}
+	if (state->impl.blendState != NULL) {
+		state->impl.blendState->lpVtbl->Release(state->impl.blendState);
+		state->impl.blendState = NULL;
+	}
+}
+
+void kinc_internal_set_rasterizer_state(struct kinc_g4_pipeline *pipeline, bool scissoring) {
+	if (scissoring && pipeline->impl.rasterizerStateScissor != NULL)
+		dx_ctx.context->lpVtbl->RSSetState(dx_ctx.context, pipeline->impl.rasterizerStateScissor);
+	else if (pipeline->impl.rasterizerState != NULL)
+		dx_ctx.context->lpVtbl->RSSetState(dx_ctx.context, pipeline->impl.rasterizerState);
+}
+
+void kinc_internal_set_pipeline(struct kinc_g4_pipeline *pipeline, bool scissoring) {
+	dx_ctx.context->lpVtbl->OMSetDepthStencilState(dx_ctx.context, pipeline->impl.depthStencilState, pipeline->stencil_reference_value);
+	UINT sampleMask = 0xffffffff;
+	dx_ctx.context->lpVtbl->OMSetBlendState(dx_ctx.context, pipeline->impl.blendState, currentBlendFactor, sampleMask);
+	kinc_internal_set_rasterizer_state(pipeline, scissoring);
+
+	dx_ctx.context->lpVtbl->VSSetShader(dx_ctx.context, (ID3D11VertexShader *)pipeline->vertex_shader->impl.shader, NULL, 0);
+	dx_ctx.context->lpVtbl->PSSetShader(dx_ctx.context, (ID3D11PixelShader *)pipeline->fragment_shader->impl.shader, NULL, 0);
+
+	dx_ctx.context->lpVtbl->GSSetShader(dx_ctx.context,
+	                                    pipeline->geometry_shader != NULL ? (ID3D11GeometryShader *)pipeline->geometry_shader->impl.shader : NULL, NULL, 0);
+	dx_ctx.context->lpVtbl->HSSetShader(
+	    dx_ctx.context, pipeline->tessellation_control_shader != NULL ? (ID3D11HullShader *)pipeline->tessellation_control_shader->impl.shader : NULL, NULL, 0);
+	dx_ctx.context->lpVtbl->DSSetShader(
+	    dx_ctx.context, pipeline->tessellation_evaluation_shader != NULL ? (ID3D11DomainShader *)pipeline->tessellation_evaluation_shader->impl.shader : NULL,
+	    NULL, 0);
+
+	dx_ctx.context->lpVtbl->IASetInputLayout(dx_ctx.context, pipeline->impl.d3d11inputLayout);
+}
+
+void kinc_internal_pipeline_rebind() {
+	if (currentPipeline != NULL && needPipelineRebind) {
+		kinc_internal_set_pipeline(currentPipeline, kinc_internal_scissoring);
+		needPipelineRebind = false;
+	}
+}
+
+static kinc_internal_shader_constant_t *findConstant(kinc_internal_shader_constant_t *constants, uint32_t hash) {
+	for (int i = 0; i < 64; ++i) {
+		if (constants[i].hash == hash) {
+			return &constants[i];
+		}
+	}
+	return NULL;
+}
+
+static kinc_internal_hash_index_t *findTextureUnit(kinc_internal_hash_index_t *units, uint32_t hash) {
+	for (int i = 0; i < 64; ++i) {
+		if (units[i].hash == hash) {
+			return &units[i];
+		}
+	}
+	return NULL;
+}
+
+void kinc_g4_pipeline_get_constant_locations(kinc_g4_pipeline_t *state, kinc_g4_constant_location_t *vertex_locations,
+                                             kinc_g4_constant_location_t *fragment_locations, int *vertex_sizes, int *fragment_sizes, int *max_vertex,
+                                             int *max_fragment) {
+
+	// *max_vertex = state->vertex_shader->impl.constantsSize;
+	// *max_fragment = state->fragment_shader->impl.constantsSize;
+	// if (vertex_locations != null && fragment_locations != null) {
+	// 	for (int i = 0; i < state->vertex_shader->impl.constantsSize; i++) {
+	// 		kinc_internal_shader_constant_t *constant = state->vertex_shader->impl.constants[i];
+	// 		vertex_location[i].impl.vertexOffset = constant->offset;
+	// 		vertex_location[i].impl.vertexSize = constant->size;
+	// 		vertex_location[i].impl.vertexColumns = constant->columns;
+	// 		vertex_location[i].impl.vertexRows = constant->rows;
+	// 		vertex_sizes[i] = constant->size;
+	// 	}
+
+	// 	for (int i = 0; i < state->fragment_shader->impl.constantsSize; i++) {
+	// 		kinc_internal_shader_constant_t *constant = state->fragment_shader->impl.constants[i];
+	// 		fragment_location[i].impl.vertexOffset = constant->offset;
+	// 		fragment_location[i].impl.vertexSize = constant->size;
+	// 		fragment_location[i].impl.vertexColumns = constant->columns;
+	// 		fragment_location[i].impl.vertexRows = constant->rows;
+	// 		fragment_sizes[i] = constant->size;
+	// 	}
+	// }
+}
+
+kinc_g4_constant_location_t kinc_g4_pipeline_get_constant_location(struct kinc_g4_pipeline *state, const char *name) {
+	kinc_g4_constant_location_t location = {0};
+
+	uint32_t hash = kinc_internal_hash_name((unsigned char *)name);
+
+	kinc_internal_shader_constant_t *constant = findConstant(state->vertex_shader->impl.constants, hash);
+	if (constant == NULL) {
+		location.impl.vertexOffset = 0;
+		location.impl.vertexSize = 0;
+		location.impl.vertexColumns = 0;
+		location.impl.vertexRows = 0;
+	}
+	else {
+		location.impl.vertexOffset = constant->offset;
+		location.impl.vertexSize = constant->size;
+		location.impl.vertexColumns = constant->columns;
+		location.impl.vertexRows = constant->rows;
+	}
+
+	constant = findConstant(state->fragment_shader->impl.constants, hash);
+	if (constant == NULL) {
+		location.impl.fragmentOffset = 0;
+		location.impl.fragmentSize = 0;
+		location.impl.fragmentColumns = 0;
+		location.impl.fragmentRows = 0;
+	}
+	else {
+		location.impl.fragmentOffset = constant->offset;
+		location.impl.fragmentSize = constant->size;
+		location.impl.fragmentColumns = constant->columns;
+		location.impl.fragmentRows = constant->rows;
+	}
+
+	constant = state->geometry_shader == NULL ? NULL : findConstant(state->geometry_shader->impl.constants, hash);
+	if (constant == NULL) {
+		location.impl.geometryOffset = 0;
+		location.impl.geometrySize = 0;
+		location.impl.geometryColumns = 0;
+		location.impl.geometryRows = 0;
+	}
+	else {
+		location.impl.geometryOffset = constant->offset;
+		location.impl.geometrySize = constant->size;
+		location.impl.geometryColumns = constant->columns;
+		location.impl.geometryRows = constant->rows;
+	}
+
+	constant = state->tessellation_control_shader == NULL ? NULL : findConstant(state->tessellation_control_shader->impl.constants, hash);
+	if (constant == NULL) {
+		location.impl.tessControlOffset = 0;
+		location.impl.tessControlSize = 0;
+		location.impl.tessControlColumns = 0;
+		location.impl.tessControlRows = 0;
+	}
+	else {
+		location.impl.tessControlOffset = constant->offset;
+		location.impl.tessControlSize = constant->size;
+		location.impl.tessControlColumns = constant->columns;
+		location.impl.tessControlRows = constant->rows;
+	}
+
+	constant = state->tessellation_evaluation_shader == NULL ? NULL : findConstant(state->tessellation_evaluation_shader->impl.constants, hash);
+	if (constant == NULL) {
+		location.impl.tessEvalOffset = 0;
+		location.impl.tessEvalSize = 0;
+		location.impl.tessEvalColumns = 0;
+		location.impl.tessEvalRows = 0;
+	}
+	else {
+		location.impl.tessEvalOffset = constant->offset;
+		location.impl.tessEvalSize = constant->size;
+		location.impl.tessEvalColumns = constant->columns;
+		location.impl.tessEvalRows = constant->rows;
+	}
+
+	if (location.impl.vertexSize == 0 && location.impl.fragmentSize == 0 && location.impl.geometrySize == 0 && location.impl.tessControlSize == 0 &&
+	    location.impl.tessEvalSize == 0) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "Uniform %s not found.", name);
+	}
+
+	return location;
+}
+
+kinc_g4_texture_unit_t kinc_g4_pipeline_get_texture_unit(struct kinc_g4_pipeline *state, const char *name) {
+	char unitName[64];
+	int unitOffset = 0;
+	size_t len = strlen(name);
+	if (len > 63)
+		len = 63;
+	strncpy(unitName, name, len + 1);
+	if (unitName[len - 1] == ']') {                  // Check for array - mySampler[2]
+		unitOffset = (int)(unitName[len - 2] - '0'); // Array index is unit offset
+		unitName[len - 3] = 0;                       // Strip array from name
+	}
+
+	uint32_t hash = kinc_internal_hash_name((unsigned char *)unitName);
+
+	kinc_g4_texture_unit_t unit;
+	for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) {
+		unit.stages[i] = -1;
+	}
+
+	kinc_internal_hash_index_t *fragmentUnit = findTextureUnit(state->fragment_shader->impl.textures, hash);
+	if (fragmentUnit != NULL) {
+		unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] = fragmentUnit->index + unitOffset;
+	}
+
+	kinc_internal_hash_index_t *vertexUnit = findTextureUnit(state->vertex_shader->impl.textures, hash);
+	if (vertexUnit != NULL) {
+		unit.stages[KINC_G4_SHADER_TYPE_VERTEX] = vertexUnit->index + unitOffset;
+	}
+
+	return unit;
+}
+
+static char stringCache[1024];
+static int stringCacheIndex = 0;
+
+static void setVertexDesc(D3D11_INPUT_ELEMENT_DESC *vertexDesc, int attributeIndex, int index, int stream, bool instanced, int subindex) {
+	if (subindex < 0) {
+		vertexDesc->SemanticName = "TEXCOORD";
+		vertexDesc->SemanticIndex = attributeIndex;
+	}
+	else {
+		// SPIRV_CROSS uses TEXCOORD_0_0,... for split up matrices
+		int stringStart = stringCacheIndex;
+		strcpy(&stringCache[stringCacheIndex], "TEXCOORD");
+		stringCacheIndex += (int)strlen("TEXCOORD");
+		sprintf(&stringCache[stringCacheIndex], "%i", attributeIndex);
+		stringCacheIndex += (int)strlen(&stringCache[stringCacheIndex]);
+		strcpy(&stringCache[stringCacheIndex], "_");
+		stringCacheIndex += 2;
+		vertexDesc->SemanticName = &stringCache[stringStart];
+		vertexDesc->SemanticIndex = subindex;
+	}
+	vertexDesc->InputSlot = stream;
+	vertexDesc->AlignedByteOffset = (index == 0) ? 0 : D3D11_APPEND_ALIGNED_ELEMENT;
+	vertexDesc->InputSlotClass = instanced ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA;
+	vertexDesc->InstanceDataStepRate = instanced ? 1 : 0;
+}
+
+#define usedCount 32
+
+static int getAttributeLocation(kinc_internal_hash_index_t *attributes, const char *name, bool *used) {
+	uint32_t hash = kinc_internal_hash_name((unsigned char *)name);
+
+	for (int i = 0; i < 64; ++i) {
+		if (attributes[i].hash == hash) {
+			return attributes[i].index;
+		}
+	}
+
+	for (int i = 0; i < usedCount; ++i) {
+		if (!used[i]) {
+			used[i] = true;
+			return i;
+		}
+	}
+
+	return 0;
+}
+
+static void createRenderTargetBlendDesc(struct kinc_g4_pipeline *pipe, D3D11_RENDER_TARGET_BLEND_DESC *rtbd, int targetNum) {
+	rtbd->BlendEnable = pipe->blend_source != KINC_G4_BLEND_ONE || pipe->blend_destination != KINC_G4_BLEND_ZERO ||
+	                    pipe->alpha_blend_source != KINC_G4_BLEND_ONE || pipe->alpha_blend_destination != KINC_G4_BLEND_ZERO;
+	rtbd->SrcBlend = convert_blend_factor(pipe->blend_source);
+	rtbd->DestBlend = convert_blend_factor(pipe->blend_destination);
+	rtbd->BlendOp = convert_blend_operation(pipe->blend_operation);
+	rtbd->SrcBlendAlpha = convert_blend_factor(pipe->alpha_blend_source);
+	rtbd->DestBlendAlpha = convert_blend_factor(pipe->alpha_blend_destination);
+	rtbd->BlendOpAlpha = convert_blend_operation(pipe->alpha_blend_operation);
+	rtbd->RenderTargetWriteMask = (((pipe->color_write_mask_red[targetNum] ? D3D11_COLOR_WRITE_ENABLE_RED : 0) |
+	                                (pipe->color_write_mask_green[targetNum] ? D3D11_COLOR_WRITE_ENABLE_GREEN : 0)) |
+	                               (pipe->color_write_mask_blue[targetNum] ? D3D11_COLOR_WRITE_ENABLE_BLUE : 0)) |
+	                              (pipe->color_write_mask_alpha[targetNum] ? D3D11_COLOR_WRITE_ENABLE_ALPHA : 0);
+}
+
+void kinc_g4_pipeline_compile(struct kinc_g4_pipeline *state) {
+	if (state->vertex_shader->impl.constantsSize > 0) {
+		D3D11_BUFFER_DESC desc;
+		desc.ByteWidth = (UINT)get_multiple_of_16(state->vertex_shader->impl.constantsSize);
+		desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+		desc.Usage = D3D11_USAGE_DEFAULT;
+		desc.CPUAccessFlags = 0;
+		desc.MiscFlags = 0;
+		desc.StructureByteStride = 0;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &state->impl.vertexConstantBuffer));
+	}
+	if (state->fragment_shader->impl.constantsSize > 0) {
+		D3D11_BUFFER_DESC desc;
+		desc.ByteWidth = (UINT)get_multiple_of_16(state->fragment_shader->impl.constantsSize);
+		desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+		desc.Usage = D3D11_USAGE_DEFAULT;
+		desc.CPUAccessFlags = 0;
+		desc.MiscFlags = 0;
+		desc.StructureByteStride = 0;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &state->impl.fragmentConstantBuffer));
+	}
+	if (state->geometry_shader != NULL && state->geometry_shader->impl.constantsSize > 0) {
+		D3D11_BUFFER_DESC desc;
+		desc.ByteWidth = (UINT)get_multiple_of_16(state->geometry_shader->impl.constantsSize);
+		desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+		desc.Usage = D3D11_USAGE_DEFAULT;
+		desc.CPUAccessFlags = 0;
+		desc.MiscFlags = 0;
+		desc.StructureByteStride = 0;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &state->impl.geometryConstantBuffer));
+	}
+	if (state->tessellation_control_shader != NULL && state->tessellation_control_shader->impl.constantsSize > 0) {
+		D3D11_BUFFER_DESC desc;
+		desc.ByteWidth = (UINT)get_multiple_of_16(state->tessellation_control_shader->impl.constantsSize);
+		desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+		desc.Usage = D3D11_USAGE_DEFAULT;
+		desc.CPUAccessFlags = 0;
+		desc.MiscFlags = 0;
+		desc.StructureByteStride = 0;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &state->impl.tessControlConstantBuffer));
+	}
+	if (state->tessellation_evaluation_shader != NULL && state->tessellation_evaluation_shader->impl.constantsSize > 0) {
+		D3D11_BUFFER_DESC desc;
+		desc.ByteWidth = (UINT)get_multiple_of_16(state->tessellation_evaluation_shader->impl.constantsSize);
+		desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+		desc.Usage = D3D11_USAGE_DEFAULT;
+		desc.CPUAccessFlags = 0;
+		desc.MiscFlags = 0;
+		desc.StructureByteStride = 0;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &state->impl.tessEvalConstantBuffer));
+	}
+
+	int all = 0;
+	for (int stream = 0; state->input_layout[stream] != NULL; ++stream) {
+		for (int index = 0; index < state->input_layout[stream]->size; ++index) {
+			if (state->input_layout[stream]->elements[index].data == KINC_G4_VERTEX_DATA_F32_4X4) {
+				all += 4;
+			}
+			else {
+				all += 1;
+			}
+		}
+	}
+
+	bool used[usedCount];
+	for (int i = 0; i < usedCount; ++i)
+		used[i] = false;
+	for (int i = 0; i < 64; ++i) {
+		used[state->vertex_shader->impl.attributes[i].index] = true;
+	}
+	stringCacheIndex = 0;
+	D3D11_INPUT_ELEMENT_DESC *vertexDesc = (D3D11_INPUT_ELEMENT_DESC *)alloca(sizeof(D3D11_INPUT_ELEMENT_DESC) * all);
+
+	int i = 0;
+	for (int stream = 0; state->input_layout[stream] != NULL; ++stream) {
+		for (int index = 0; index < state->input_layout[stream]->size; ++index) {
+			switch (state->input_layout[stream]->elements[index].data) {
+			case KINC_G4_VERTEX_DATA_F32_1X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32_FLOAT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_F32_2X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32G32_FLOAT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_F32_3X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32G32B32_FLOAT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_F32_4X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_1X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8_SINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_1X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8_UINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8_SNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8_UNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_2X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8G8_SINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_2X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8G8_UINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8G8_SNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8G8_UNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_4X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8G8B8A8_SINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_4X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8G8B8A8_UINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8G8B8A8_SNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_1X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16_SINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_1X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16_UINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16_SNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16_UNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_2X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16G16_SINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_2X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16G16_UINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16G16_SNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16G16_UNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_4X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16G16B16A16_SINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_4X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16G16B16A16_UINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16G16B16A16_SNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R16G16B16A16_UNORM;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I32_1X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32_SINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U32_1X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32_UINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I32_2X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32G32_SINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U32_2X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32G32_UINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I32_3X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32G32B32_SINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U32_3X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32G32B32_UINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_I32_4X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32G32B32A32_SINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_U32_4X:
+				setVertexDesc(&vertexDesc[i],
+				              getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index,
+				              stream, state->input_layout[stream]->instanced, -1);
+				vertexDesc[i].Format = DXGI_FORMAT_R32G32B32A32_UINT;
+				++i;
+				break;
+			case KINC_G4_VERTEX_DATA_F32_4X4: {
+				char name[101];
+				strcpy(name, state->input_layout[stream]->elements[index].name);
+				strcat(name, "_");
+				size_t length = strlen(name);
+				sprintf(&name[length], "%i", 0);
+				name[length + 1] = 0;
+				int attributeLocation = getAttributeLocation(state->vertex_shader->impl.attributes, name, used);
+
+				for (int i2 = 0; i2 < 4; ++i2) {
+					setVertexDesc(&vertexDesc[i], attributeLocation, index + i2, stream, state->input_layout[stream]->instanced, i2);
+					vertexDesc[i].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
+					++i;
+				}
+				break;
+			}
+			default:
+				break;
+			}
+		}
+	}
+
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateInputLayout(dx_ctx.device, vertexDesc, all, state->vertex_shader->impl.data,
+	                                                               state->vertex_shader->impl.length, &state->impl.d3d11inputLayout));
+	{
+		D3D11_DEPTH_STENCIL_DESC desc;
+		memset(&desc, 0, sizeof(desc));
+
+		desc.DepthEnable = state->depth_mode != KINC_G4_COMPARE_ALWAYS;
+		desc.DepthWriteMask = state->depth_write ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
+		desc.DepthFunc = get_comparison(state->depth_mode);
+
+		desc.StencilEnable = state->stencil_front_mode != KINC_G4_COMPARE_ALWAYS || state->stencil_back_mode != KINC_G4_COMPARE_ALWAYS ||
+		                     state->stencil_front_both_pass != KINC_G4_STENCIL_KEEP || state->stencil_back_both_pass != KINC_G4_STENCIL_KEEP ||
+		                     state->stencil_front_depth_fail != KINC_G4_STENCIL_KEEP || state->stencil_back_depth_fail != KINC_G4_STENCIL_KEEP ||
+		                     state->stencil_front_fail != KINC_G4_STENCIL_KEEP || state->stencil_back_fail != KINC_G4_STENCIL_KEEP;
+		desc.StencilReadMask = state->stencil_read_mask;
+		desc.StencilWriteMask = state->stencil_write_mask;
+		desc.FrontFace.StencilFunc = desc.BackFace.StencilFunc = get_comparison(state->stencil_front_mode);
+		desc.FrontFace.StencilDepthFailOp = desc.BackFace.StencilDepthFailOp = get_stencil_action(state->stencil_front_depth_fail);
+		desc.FrontFace.StencilPassOp = desc.BackFace.StencilPassOp = get_stencil_action(state->stencil_front_both_pass);
+		desc.FrontFace.StencilFailOp = desc.BackFace.StencilFailOp = get_stencil_action(state->stencil_front_fail);
+		desc.BackFace.StencilFunc = desc.BackFace.StencilFunc = get_comparison(state->stencil_back_mode);
+		desc.BackFace.StencilDepthFailOp = desc.BackFace.StencilDepthFailOp = get_stencil_action(state->stencil_back_depth_fail);
+		desc.BackFace.StencilPassOp = desc.BackFace.StencilPassOp = get_stencil_action(state->stencil_back_both_pass);
+		desc.BackFace.StencilFailOp = desc.BackFace.StencilFailOp = get_stencil_action(state->stencil_back_fail);
+
+		dx_ctx.device->lpVtbl->CreateDepthStencilState(dx_ctx.device, &desc, &state->impl.depthStencilState);
+	}
+
+	{
+		D3D11_RASTERIZER_DESC rasterDesc;
+		rasterDesc.CullMode = convert_cull_mode(state->cull_mode);
+		rasterDesc.FillMode = D3D11_FILL_SOLID;
+		rasterDesc.FrontCounterClockwise = TRUE;
+		rasterDesc.DepthBias = 0;
+		rasterDesc.SlopeScaledDepthBias = 0.0f;
+		rasterDesc.DepthBiasClamp = 0.0f;
+		rasterDesc.DepthClipEnable = TRUE;
+		rasterDesc.ScissorEnable = FALSE;
+		rasterDesc.MultisampleEnable = FALSE;
+		rasterDesc.AntialiasedLineEnable = FALSE;
+
+		dx_ctx.device->lpVtbl->CreateRasterizerState(dx_ctx.device, &rasterDesc, &state->impl.rasterizerState);
+		rasterDesc.ScissorEnable = TRUE;
+		dx_ctx.device->lpVtbl->CreateRasterizerState(dx_ctx.device, &rasterDesc, &state->impl.rasterizerStateScissor);
+
+		// We need d3d11_3 for conservative raster
+		// D3D11_RASTERIZER_DESC2 rasterDesc;
+		// rasterDesc.ConservativeRaster = conservativeRasterization ? D3D11_CONSERVATIVE_RASTERIZATION_MODE_ON : D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF;
+		// dx_ctx.device->CreateRasterizerState2(&rasterDesc, &rasterizerState);
+		// rasterDesc.ScissorEnable = TRUE;
+		// dx_ctx.device->CreateRasterizerState2(&rasterDesc, &rasterizerStateScissor);
+	}
+
+	{
+		bool independentBlend = false;
+		for (int i = 1; i < 8; ++i) {
+			if (state->color_write_mask_red[0] != state->color_write_mask_red[i] || state->color_write_mask_green[0] != state->color_write_mask_green[i] ||
+			    state->color_write_mask_blue[0] != state->color_write_mask_blue[i] || state->color_write_mask_alpha[0] != state->color_write_mask_alpha[i]) {
+				independentBlend = true;
+				break;
+			}
+		}
+
+		D3D11_BLEND_DESC blendDesc;
+		memset(&blendDesc, 0, sizeof(blendDesc));
+		blendDesc.AlphaToCoverageEnable = false;
+		blendDesc.IndependentBlendEnable = independentBlend;
+
+		D3D11_RENDER_TARGET_BLEND_DESC rtbd[8];
+		memset(&rtbd, 0, sizeof(rtbd));
+		createRenderTargetBlendDesc(state, &rtbd[0], 0);
+		blendDesc.RenderTarget[0] = rtbd[0];
+		if (independentBlend) {
+			for (int i = 1; i < 8; ++i) {
+				createRenderTargetBlendDesc(state, &rtbd[i], i);
+				blendDesc.RenderTarget[i] = rtbd[i];
+			}
+		}
+
+		dx_ctx.device->lpVtbl->CreateBlendState(dx_ctx.device, &blendDesc, &state->impl.blendState);
+	}
+}

+ 37 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.h

@@ -0,0 +1,37 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ID3D11InputLayout;
+struct ID3D11PixelShader;
+struct ID3D11VertexShader;
+struct ID3D11Buffer;
+struct ID3D11DepthStencilState;
+struct ID3D11RasterizerState;
+struct ID3D11BlendState;
+
+typedef struct {
+	// PipelineStateImpl();
+	//~PipelineStateImpl();
+	struct ID3D11InputLayout *d3d11inputLayout;
+	struct ID3D11Buffer *fragmentConstantBuffer;
+	struct ID3D11Buffer *vertexConstantBuffer;
+	struct ID3D11Buffer *geometryConstantBuffer;
+	struct ID3D11Buffer *tessEvalConstantBuffer;
+	struct ID3D11Buffer *tessControlConstantBuffer;
+	struct ID3D11DepthStencilState *depthStencilState;
+	struct ID3D11RasterizerState *rasterizerState;
+	struct ID3D11RasterizerState *rasterizerStateScissor;
+	struct ID3D11BlendState *blendState;
+	// void set(Graphics4::PipelineState* pipeline, bool scissoring);
+	// void setRasterizerState(bool scissoring);
+	// static void setConstants();
+} kinc_g4_pipeline_impl_t;
+
+void kinc_internal_set_constants(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 456 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.c.h

@@ -0,0 +1,456 @@
+#include <kinc/error.h>
+#include <kinc/graphics4/rendertarget.h>
+#include <kinc/log.h>
+
+static DXGI_FORMAT convertRenderTargetFormat(kinc_g4_render_target_format_t format) {
+	switch (format) {
+	case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT:
+		return DXGI_FORMAT_R32G32B32A32_FLOAT;
+	case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT:
+		return DXGI_FORMAT_R16G16B16A16_FLOAT;
+	case KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT:
+		return DXGI_FORMAT_R32_FLOAT;
+	case KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT:
+		return DXGI_FORMAT_R16_FLOAT;
+	case KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED:
+		return DXGI_FORMAT_R8_UNORM;
+	case KINC_G4_RENDER_TARGET_FORMAT_32BIT:
+	default:
+		return DXGI_FORMAT_R8G8B8A8_UNORM;
+	}
+}
+
+static int formatRenderTargetByteSize(kinc_g4_render_target_format_t format) {
+	switch (format) {
+	case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT:
+		return 16;
+	case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT:
+		return 8;
+	case KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT:
+		return 4;
+	case KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT:
+		return 2;
+	case KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED:
+		return 1;
+	case KINC_G4_RENDER_TARGET_FORMAT_32BIT:
+	default:
+		return 4;
+	}
+}
+
+void kinc_g4_render_target_init_with_multisampling(kinc_g4_render_target_t *renderTarget, int width, int height, kinc_g4_render_target_format_t format,
+                                                   int depthBufferBits, int stencilBufferBits, int samples_per_pixel) {
+	renderTarget->isCubeMap = false;
+	renderTarget->isDepthAttachment = false;
+	renderTarget->texWidth = renderTarget->width = width;
+	renderTarget->texHeight = renderTarget->height = height;
+	renderTarget->impl.format = format;
+	renderTarget->impl.textureStaging = NULL;
+
+	D3D11_TEXTURE2D_DESC desc;
+	desc.Width = width;
+	desc.Height = height;
+	desc.MipLevels = desc.ArraySize = 1;
+	desc.Format = convertRenderTargetFormat(format);
+	if (format == KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH) {
+		renderTarget->isDepthAttachment = true;
+		depthBufferBits = 16;
+		stencilBufferBits = 0;
+	}
+
+	bool antialiasing = samples_per_pixel > 1;
+	if (antialiasing) {
+		desc.SampleDesc.Count = samples_per_pixel;
+		desc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;
+	}
+	else {
+		desc.SampleDesc.Count = 1;
+		desc.SampleDesc.Quality = 0;
+	}
+	desc.Usage = D3D11_USAGE_DEFAULT;
+	desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+	desc.CPUAccessFlags = 0; // D3D11_CPU_ACCESS_WRITE;
+	desc.MiscFlags = 0;
+
+	renderTarget->impl.textureRender = NULL;
+	renderTarget->impl.textureSample = NULL;
+	renderTarget->impl.renderTargetSRV = NULL;
+	for (int i = 0; i < 6; i++) {
+		renderTarget->impl.renderTargetViewRender[i] = NULL;
+		renderTarget->impl.renderTargetViewSample[i] = NULL;
+	}
+	if (!renderTarget->isDepthAttachment) {
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &renderTarget->impl.textureRender));
+
+		D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
+		renderTargetViewDesc.Format = desc.Format;
+		renderTargetViewDesc.ViewDimension = antialiasing ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
+		renderTargetViewDesc.Texture2D.MipSlice = 0;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureRender,
+		                                                                    &renderTargetViewDesc, &renderTarget->impl.renderTargetViewRender[0]));
+
+		if (antialiasing) {
+			desc.SampleDesc.Count = 1;
+			desc.SampleDesc.Quality = 0;
+			kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &renderTarget->impl.textureSample));
+
+			D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
+			renderTargetViewDesc.Format = desc.Format;
+			renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+			renderTargetViewDesc.Texture2D.MipSlice = 0;
+			kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureSample,
+			                                                                    &renderTargetViewDesc, &renderTarget->impl.renderTargetViewSample[0]));
+		}
+		else {
+			renderTarget->impl.textureSample = renderTarget->impl.textureRender;
+			renderTarget->impl.renderTargetViewSample[0] = renderTarget->impl.renderTargetViewRender[0];
+		}
+	}
+
+	renderTarget->impl.depthStencil = NULL;
+	renderTarget->impl.depthStencilSRV = NULL;
+	for (int i = 0; i < 6; i++) {
+		renderTarget->impl.depthStencilView[i] = NULL;
+	}
+
+	DXGI_FORMAT depthFormat;
+	DXGI_FORMAT depthViewFormat;
+	DXGI_FORMAT depthResourceFormat;
+	if (depthBufferBits == 16 && stencilBufferBits == 0) {
+		depthFormat = DXGI_FORMAT_R16_TYPELESS;
+		depthViewFormat = DXGI_FORMAT_D16_UNORM;
+		depthResourceFormat = DXGI_FORMAT_R16_UNORM;
+	}
+	else {
+		depthFormat = DXGI_FORMAT_R24G8_TYPELESS;
+		depthViewFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
+		depthResourceFormat = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+	}
+
+	if (depthBufferBits > 0) {
+		D3D11_TEXTURE2D_DESC depthStencilDesc;
+		depthStencilDesc.Format = depthFormat;
+		depthStencilDesc.Width = width;
+		depthStencilDesc.Height = height;
+		depthStencilDesc.ArraySize = 1;
+		depthStencilDesc.MipLevels = 1;
+		depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
+		depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
+		depthStencilDesc.CPUAccessFlags = 0;
+		depthStencilDesc.MiscFlags = 0;
+		if (antialiasing) {
+			depthStencilDesc.SampleDesc.Count = 4;
+			depthStencilDesc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;
+		}
+		else {
+			depthStencilDesc.SampleDesc.Count = 1;
+			depthStencilDesc.SampleDesc.Quality = 0;
+		}
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &depthStencilDesc, NULL, &renderTarget->impl.depthStencil));
+		D3D11_DEPTH_STENCIL_VIEW_DESC viewDesc;
+		viewDesc.Format = depthViewFormat;
+		viewDesc.ViewDimension = antialiasing ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D;
+		viewDesc.Flags = 0;
+		if (!antialiasing) {
+			viewDesc.Texture2D.MipSlice = 0;
+		}
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateDepthStencilView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.depthStencil, &viewDesc,
+		                                                                    &renderTarget->impl.depthStencilView[0]));
+	}
+
+	D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
+	if (!renderTarget->isDepthAttachment) {
+		shaderResourceViewDesc.Format = desc.Format;
+		shaderResourceViewDesc.ViewDimension = antialiasing ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D;
+		shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
+		shaderResourceViewDesc.Texture2D.MipLevels = 1;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureSample,
+		                                                                      &shaderResourceViewDesc, &renderTarget->impl.renderTargetSRV));
+	}
+
+	if (depthBufferBits > 0) {
+		shaderResourceViewDesc.Format = depthResourceFormat;
+		shaderResourceViewDesc.ViewDimension = antialiasing ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D;
+		shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
+		shaderResourceViewDesc.Texture2D.MipLevels = 1;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.depthStencil,
+		                                                                      &shaderResourceViewDesc, &renderTarget->impl.depthStencilSRV));
+	}
+
+	if (renderTarget->impl.renderTargetViewRender[0] != NULL) {
+		FLOAT colors[4] = {0, 0, 0, 0};
+		dx_ctx.context->lpVtbl->ClearRenderTargetView(dx_ctx.context, renderTarget->impl.renderTargetViewRender[0], colors);
+	}
+}
+
+void kinc_g4_render_target_init_cube_with_multisampling(kinc_g4_render_target_t *renderTarget, int cubeMapSize, kinc_g4_render_target_format_t format,
+                                                        int depthBufferBits, int stencilBufferBits, int samples_per_pixel) {
+	renderTarget->width = cubeMapSize;
+	renderTarget->height = cubeMapSize;
+	renderTarget->isCubeMap = true;
+	renderTarget->isDepthAttachment = false;
+
+	renderTarget->texWidth = renderTarget->width;
+	renderTarget->texHeight = renderTarget->height;
+	renderTarget->impl.format = format;
+	renderTarget->impl.textureStaging = NULL;
+
+	D3D11_TEXTURE2D_DESC desc;
+	desc.Width = renderTarget->width;
+	desc.Height = renderTarget->height;
+	desc.MipLevels = 1;
+	desc.ArraySize = 6;
+	desc.Format = convertRenderTargetFormat(format);
+	if (format == KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH) {
+		renderTarget->isDepthAttachment = true;
+		depthBufferBits = 16;
+		stencilBufferBits = 0;
+	}
+
+	bool antialiasing = samples_per_pixel > 1;
+	if (antialiasing) {
+		desc.SampleDesc.Count = samples_per_pixel;
+		desc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;
+	}
+	else {
+		desc.SampleDesc.Count = 1;
+		desc.SampleDesc.Quality = 0;
+	}
+	desc.Usage = D3D11_USAGE_DEFAULT;
+	desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+	desc.CPUAccessFlags = 0; // D3D11_CPU_ACCESS_WRITE;
+	desc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
+
+	renderTarget->impl.textureRender = NULL;
+	renderTarget->impl.textureSample = NULL;
+	renderTarget->impl.renderTargetSRV = NULL;
+	for (int i = 0; i < 6; i++) {
+		renderTarget->impl.renderTargetViewRender[i] = NULL;
+		renderTarget->impl.renderTargetViewSample[i] = NULL;
+	}
+	if (!renderTarget->isDepthAttachment) {
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &renderTarget->impl.textureRender));
+
+		D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
+		renderTargetViewDesc.Format = desc.Format;
+		renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+		renderTargetViewDesc.Texture2DArray.MipSlice = 0;
+		renderTargetViewDesc.Texture2DArray.ArraySize = 1;
+
+		for (int i = 0; i < 6; i++) {
+			renderTargetViewDesc.Texture2DArray.FirstArraySlice = i;
+			kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureRender,
+			                                                                    &renderTargetViewDesc, &renderTarget->impl.renderTargetViewRender[i]));
+		}
+
+		if (antialiasing) {
+			desc.SampleDesc.Count = 1;
+			desc.SampleDesc.Quality = 0;
+			kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &renderTarget->impl.textureSample));
+
+			D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
+			renderTargetViewDesc.Format = desc.Format;
+			renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+			renderTargetViewDesc.Texture2D.MipSlice = 0;
+			renderTargetViewDesc.Texture2DArray.ArraySize = 1;
+			for (int i = 0; i < 6; i++) {
+				renderTargetViewDesc.Texture2DArray.FirstArraySlice = i;
+				kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureSample,
+				                                                                    &renderTargetViewDesc, &renderTarget->impl.renderTargetViewSample[i]));
+			}
+		}
+		else {
+			renderTarget->impl.textureSample = renderTarget->impl.textureRender;
+			for (int i = 0; i < 6; i++) {
+				renderTarget->impl.renderTargetViewSample[i] = renderTarget->impl.renderTargetViewRender[i];
+			}
+		}
+	}
+
+	renderTarget->impl.depthStencil = NULL;
+	renderTarget->impl.depthStencilSRV = NULL;
+	for (int i = 0; i < 6; i++) {
+		renderTarget->impl.depthStencilView[i] = NULL;
+	}
+
+	DXGI_FORMAT depthFormat;
+	DXGI_FORMAT depthViewFormat;
+	DXGI_FORMAT depthResourceFormat;
+	if (depthBufferBits == 16 && stencilBufferBits == 0) {
+		depthFormat = DXGI_FORMAT_R16_TYPELESS;
+		depthViewFormat = DXGI_FORMAT_D16_UNORM;
+		depthResourceFormat = DXGI_FORMAT_R16_UNORM;
+	}
+	else {
+		depthFormat = DXGI_FORMAT_R24G8_TYPELESS;
+		depthViewFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
+		depthResourceFormat = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+	}
+
+	if (depthBufferBits > 0) {
+		D3D11_TEXTURE2D_DESC depthStencilDesc;
+		depthStencilDesc.Format = depthFormat;
+		depthStencilDesc.Width = renderTarget->width;
+		depthStencilDesc.Height = renderTarget->height;
+		depthStencilDesc.ArraySize = 1;
+		depthStencilDesc.MipLevels = 1;
+		depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
+		depthStencilDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
+		depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
+		depthStencilDesc.CPUAccessFlags = 0;
+		depthStencilDesc.ArraySize = 6;
+		if (antialiasing) {
+			depthStencilDesc.SampleDesc.Count = 4;
+			depthStencilDesc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;
+		}
+		else {
+			depthStencilDesc.SampleDesc.Count = 1;
+			depthStencilDesc.SampleDesc.Quality = 0;
+		}
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &depthStencilDesc, NULL, &renderTarget->impl.depthStencil));
+
+		D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
+		depthStencilViewDesc.Format = depthViewFormat;
+		depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
+		depthStencilViewDesc.Texture2DArray.MipSlice = 0;
+		depthStencilViewDesc.Texture2DArray.ArraySize = 1;
+		depthStencilViewDesc.Flags = 0;
+		for (int i = 0; i < 6; i++) {
+			depthStencilViewDesc.Texture2DArray.FirstArraySlice = i;
+			kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateDepthStencilView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.depthStencil,
+			                                                                    &depthStencilViewDesc, &renderTarget->impl.depthStencilView[i]));
+		}
+	}
+
+	D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
+	if (!renderTarget->isDepthAttachment) {
+		shaderResourceViewDesc.Format = desc.Format;
+		shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
+		shaderResourceViewDesc.TextureCube.MostDetailedMip = 0;
+		shaderResourceViewDesc.TextureCube.MipLevels = 1;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureSample,
+		                                                                      &shaderResourceViewDesc, &renderTarget->impl.renderTargetSRV));
+	}
+
+	if (depthBufferBits > 0) {
+		shaderResourceViewDesc.Format = depthResourceFormat;
+		shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
+		shaderResourceViewDesc.TextureCube.MostDetailedMip = 0;
+		shaderResourceViewDesc.TextureCube.MipLevels = 1;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.depthStencil,
+		                                                                      &shaderResourceViewDesc, &renderTarget->impl.depthStencilSRV));
+	}
+
+	if (!renderTarget->isDepthAttachment) {
+		FLOAT colors[4] = {0, 0, 0, 0};
+		for (int i = 0; i < 6; i++) {
+			dx_ctx.context->lpVtbl->ClearRenderTargetView(dx_ctx.context, renderTarget->impl.renderTargetViewRender[i], colors);
+		}
+	}
+}
+
+void kinc_g4_render_target_destroy(kinc_g4_render_target_t *renderTarget) {
+	for (int i = 0; i < 6; i++) {
+		if (renderTarget->impl.renderTargetViewRender[i] != NULL)
+			renderTarget->impl.renderTargetViewRender[i]->lpVtbl->Release(renderTarget->impl.renderTargetViewRender[i]);
+		if (renderTarget->impl.renderTargetViewSample[i] != NULL &&
+		    renderTarget->impl.renderTargetViewSample[i] != renderTarget->impl.renderTargetViewRender[i])
+			renderTarget->impl.renderTargetViewSample[i]->lpVtbl->Release(renderTarget->impl.renderTargetViewSample[i]);
+		if (renderTarget->impl.depthStencilView[i] != NULL)
+			renderTarget->impl.depthStencilView[i]->lpVtbl->Release(renderTarget->impl.depthStencilView[i]);
+	}
+	if (renderTarget->impl.renderTargetSRV != NULL)
+		renderTarget->impl.renderTargetSRV->lpVtbl->Release(renderTarget->impl.renderTargetSRV);
+	if (renderTarget->impl.depthStencilSRV != NULL)
+		renderTarget->impl.depthStencilSRV->lpVtbl->Release(renderTarget->impl.depthStencilSRV);
+	if (renderTarget->impl.depthStencil != NULL)
+		renderTarget->impl.depthStencil->lpVtbl->Release(renderTarget->impl.depthStencil);
+	if (renderTarget->impl.textureRender != NULL)
+		renderTarget->impl.textureRender->lpVtbl->Release(renderTarget->impl.textureRender);
+	if (renderTarget->impl.textureStaging != NULL)
+		renderTarget->impl.textureStaging->lpVtbl->Release(renderTarget->impl.textureStaging);
+	if (renderTarget->impl.textureSample != NULL && renderTarget->impl.textureSample != renderTarget->impl.textureRender)
+		renderTarget->impl.textureSample->lpVtbl->Release(renderTarget->impl.textureSample);
+}
+
+void kinc_g4_render_target_use_color_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0 && unit.stages[KINC_G4_SHADER_TYPE_VERTEX] < 0)
+		return;
+
+	if (renderTarget->impl.textureSample != renderTarget->impl.textureRender) {
+		dx_ctx.context->lpVtbl->ResolveSubresource(dx_ctx.context, (ID3D11Resource *)renderTarget->impl.textureSample, 0,
+		                                           (ID3D11Resource *)renderTarget->impl.textureRender, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
+	}
+
+	if (unit.stages[KINC_G4_SHADER_TYPE_VERTEX] >= 0) {
+		dx_ctx.context->lpVtbl->VSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_VERTEX], 1,
+		                                             renderTarget->isDepthAttachment ? &renderTarget->impl.depthStencilSRV
+		                                                                             : &renderTarget->impl.renderTargetSRV);
+	}
+
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0) {
+		dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1,
+		                                             renderTarget->isDepthAttachment ? &renderTarget->impl.depthStencilSRV
+		                                                                             : &renderTarget->impl.renderTargetSRV);
+	}
+}
+
+void kinc_g4_render_target_use_depth_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_VERTEX] >= 0) {
+		dx_ctx.context->lpVtbl->VSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_VERTEX], 1, &renderTarget->impl.depthStencilSRV);
+	}
+
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0) {
+		dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &renderTarget->impl.depthStencilSRV);
+	}
+}
+
+void kinc_g4_render_target_set_depth_stencil_from(kinc_g4_render_target_t *renderTarget, kinc_g4_render_target_t *source) {
+	renderTarget->impl.depthStencil = source->impl.depthStencil;
+	for (int i = 0; i < 6; i++) {
+		renderTarget->impl.depthStencilView[i] = source->impl.depthStencilView[i];
+	}
+	renderTarget->impl.depthStencilSRV = source->impl.depthStencilSRV;
+}
+
+void kinc_g4_render_target_get_pixels(kinc_g4_render_target_t *renderTarget, uint8_t *data) {
+	if (renderTarget->impl.textureStaging == NULL) {
+		D3D11_TEXTURE2D_DESC desc;
+		desc.Width = renderTarget->texWidth;
+		desc.Height = renderTarget->texHeight;
+		desc.MipLevels = 1;
+		desc.ArraySize = 1;
+		desc.Format = convertRenderTargetFormat((kinc_g4_render_target_format_t)renderTarget->impl.format);
+		desc.SampleDesc.Count = 1;
+		desc.SampleDesc.Quality = 0;
+		desc.Usage = D3D11_USAGE_STAGING;
+		desc.BindFlags = 0;
+		desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+		desc.MiscFlags = 0;
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &renderTarget->impl.textureStaging));
+	}
+
+	D3D11_BOX sourceRegion;
+	sourceRegion.left = 0;
+	sourceRegion.right = renderTarget->texWidth;
+	sourceRegion.top = 0;
+	sourceRegion.bottom = renderTarget->texHeight;
+	sourceRegion.front = 0;
+	sourceRegion.back = 1;
+	dx_ctx.context->lpVtbl->CopySubresourceRegion(dx_ctx.context, (ID3D11Resource *)renderTarget->impl.textureStaging, 0, 0, 0, 0,
+	                                              (ID3D11Resource *)renderTarget->impl.textureRender, 0, &sourceRegion);
+
+	D3D11_MAPPED_SUBRESOURCE mappedResource;
+	dx_ctx.context->lpVtbl->Map(dx_ctx.context, (ID3D11Resource *)renderTarget->impl.textureStaging, 0, D3D11_MAP_READ, 0, &mappedResource);
+	int size;
+	if (mappedResource.RowPitch != 0) {
+		size = mappedResource.RowPitch * renderTarget->texHeight;
+	}
+	else {
+		size = renderTarget->texWidth * renderTarget->texHeight * formatRenderTargetByteSize((kinc_g4_render_target_format_t)renderTarget->impl.format);
+	}
+	memcpy(data, mappedResource.pData, size);
+	dx_ctx.context->lpVtbl->Unmap(dx_ctx.context, (ID3D11Resource *)renderTarget->impl.textureStaging, 0);
+}
+
+void kinc_g4_render_target_generate_mipmaps(kinc_g4_render_target_t *renderTarget, int levels) {}

+ 27 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ID3D11Texture2D;
+struct ID3D11RenderTargetView;
+struct ID3D11DepthStencilView;
+struct ID3D11ShaderResourceView;
+
+typedef struct {
+	struct ID3D11Texture2D *textureRender;
+	struct ID3D11Texture2D *textureSample;
+	struct ID3D11Texture2D *textureStaging;
+	struct ID3D11RenderTargetView *renderTargetViewRender[6];
+	struct ID3D11RenderTargetView *renderTargetViewSample[6];
+	struct ID3D11Texture2D *depthStencil;
+	struct ID3D11DepthStencilView *depthStencilView[6];
+	struct ID3D11ShaderResourceView *renderTargetSRV;
+	struct ID3D11ShaderResourceView *depthStencilSRV;
+	int format;
+} kinc_g4_render_target_impl_t;
+
+#ifdef __cplusplus
+}
+#endif

+ 112 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.c.h

@@ -0,0 +1,112 @@
+#include <kinc/graphics4/shader.h>
+
+void kinc_g4_shader_destroy(kinc_g4_shader_t *shader) {
+	if (shader->impl.shader != NULL) {
+		((IUnknown *)shader->impl.shader)->lpVtbl->Release(shader->impl.shader);
+		free(shader->impl.data);
+	}
+}
+
+void kinc_g4_shader_init(kinc_g4_shader_t *shader, const void *_data, size_t length, kinc_g4_shader_type_t type) {
+	unsigned index = 0;
+	uint8_t *data = (uint8_t *)_data;
+	shader->impl.type = (int)type;
+
+	memset(&shader->impl.attributes, 0, sizeof(shader->impl.attributes));
+	int attributesCount = data[index++];
+	for (int i = 0; i < attributesCount; ++i) {
+		unsigned char name[256];
+		for (unsigned i2 = 0; i2 < 255; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		shader->impl.attributes[i].hash = kinc_internal_hash_name(name);
+		shader->impl.attributes[i].index = data[index++];
+	}
+
+	memset(&shader->impl.textures, 0, sizeof(shader->impl.textures));
+	uint8_t texCount = data[index++];
+	for (unsigned i = 0; i < texCount; ++i) {
+		unsigned char name[256];
+		for (unsigned i2 = 0; i2 < 255; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		shader->impl.textures[i].hash = kinc_internal_hash_name(name);
+		shader->impl.textures[i].index = data[index++];
+	}
+
+	memset(&shader->impl.constants, 0, sizeof(shader->impl.constants));
+	uint8_t constantCount = data[index++];
+	shader->impl.constantsSize = 0;
+	for (unsigned i = 0; i < constantCount; ++i) {
+		unsigned char name[256];
+		for (unsigned i2 = 0; i2 < 255; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		kinc_internal_shader_constant_t constant;
+		constant.hash = kinc_internal_hash_name(name);
+		constant.offset = *(uint32_t *)&data[index];
+		index += 4;
+		constant.size = *(uint32_t *)&data[index];
+		index += 4;
+		constant.columns = data[index];
+		index += 1;
+		constant.rows = data[index];
+		index += 1;
+
+		shader->impl.constants[i] = constant;
+		shader->impl.constantsSize = constant.offset + constant.size;
+	}
+
+	shader->impl.length = (int)(length - index);
+	shader->impl.data = (uint8_t *)malloc(shader->impl.length);
+	assert(shader->impl.data != NULL);
+	memcpy(shader->impl.data, &data[index], shader->impl.length);
+
+	switch (type) {
+	case KINC_G4_SHADER_TYPE_VERTEX:
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateVertexShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL,
+		                                                                (ID3D11VertexShader **)&shader->impl.shader));
+		break;
+	case KINC_G4_SHADER_TYPE_FRAGMENT:
+		kinc_microsoft_affirm(
+		    dx_ctx.device->lpVtbl->CreatePixelShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL, (ID3D11PixelShader **)&shader->impl.shader));
+		break;
+	case KINC_G4_SHADER_TYPE_GEOMETRY:
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateGeometryShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL,
+		                                                                  (ID3D11GeometryShader **)&shader->impl.shader));
+		break;
+	case KINC_G4_SHADER_TYPE_TESSELLATION_CONTROL:
+		kinc_microsoft_affirm(
+		    dx_ctx.device->lpVtbl->CreateHullShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL, (ID3D11HullShader **)&shader->impl.shader));
+		break;
+	case KINC_G4_SHADER_TYPE_TESSELLATION_EVALUATION:
+		kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateDomainShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL,
+		                                                                (ID3D11DomainShader **)&shader->impl.shader));
+		break;
+	}
+}
+
+#ifdef KRAFIX_LIBRARY
+extern int krafix_compile(const char *source, char *output, int *length, const char *targetlang, const char *system, const char *shadertype, int version);
+#endif
+
+int kinc_g4_shader_init_from_source(kinc_g4_shader_t *shader, const char *source, kinc_g4_shader_type_t type) {
+#ifdef KRAFIX_LIBRARY
+	char *output = malloc(1024 * 1024);
+	int length;
+	int errors = krafix_compile(source, output, &length, "d3d11", "windows", type == KINC_G4_SHADER_TYPE_FRAGMENT ? "frag" : "vert", -1);
+	if (errors > 0) {
+		return errors;
+	}
+	kinc_g4_shader_init(shader, output, length, type);
+	return 0;
+#else
+	return 0;
+#endif
+}

+ 60 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.h

@@ -0,0 +1,60 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <kinc/backend/graphics4/ShaderHash.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	uint32_t hash;
+	uint32_t offset;
+	uint32_t size;
+	uint8_t columns;
+	uint8_t rows;
+} kinc_internal_shader_constant_t;
+
+typedef struct {
+	kinc_internal_shader_constant_t constants[64];
+	int constantsSize;
+	kinc_internal_hash_index_t attributes[64];
+	kinc_internal_hash_index_t textures[64];
+	void *shader;
+	uint8_t *data;
+	int length;
+	int type;
+} kinc_g4_shader_impl_t;
+
+typedef struct {
+	uint32_t vertexOffset;
+	uint32_t vertexSize;
+	uint32_t fragmentOffset;
+	uint32_t fragmentSize;
+	uint32_t geometryOffset;
+	uint32_t geometrySize;
+	uint32_t tessEvalOffset;
+	uint32_t tessEvalSize;
+	uint32_t tessControlOffset;
+	uint32_t tessControlSize;
+	uint32_t computeOffset;
+	uint32_t computeSize;
+	uint8_t vertexColumns;
+	uint8_t vertexRows;
+	uint8_t fragmentColumns;
+	uint8_t fragmentRows;
+	uint8_t geometryColumns;
+	uint8_t geometryRows;
+	uint8_t tessEvalColumns;
+	uint8_t tessEvalRows;
+	uint8_t tessControlColumns;
+	uint8_t tessControlRows;
+	uint8_t computeColumns;
+	uint8_t computeRows;
+} kinc_g4_constant_location_impl_t;
+
+#ifdef __cplusplus
+}
+#endif

+ 371 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.c.h

@@ -0,0 +1,371 @@
+#include <kinc/graphics4/texture.h>
+#include <kinc/graphics4/textureunit.h>
+
+#include <assert.h>
+
+static kinc_g4_texture_t *setTextures[16] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+static DXGI_FORMAT convertFormat(kinc_image_format_t format) {
+	switch (format) {
+	case KINC_IMAGE_FORMAT_RGBA128:
+		return DXGI_FORMAT_R32G32B32A32_FLOAT;
+	case KINC_IMAGE_FORMAT_RGBA64:
+		return DXGI_FORMAT_R16G16B16A16_FLOAT;
+	case KINC_IMAGE_FORMAT_RGB24:
+		return DXGI_FORMAT_R8G8B8A8_UNORM;
+	case KINC_IMAGE_FORMAT_A32:
+		return DXGI_FORMAT_R32_FLOAT;
+	case KINC_IMAGE_FORMAT_A16:
+		return DXGI_FORMAT_R16_FLOAT;
+	case KINC_IMAGE_FORMAT_GREY8:
+		return DXGI_FORMAT_R8_UNORM;
+	case KINC_IMAGE_FORMAT_BGRA32:
+		return DXGI_FORMAT_B8G8R8A8_UNORM;
+	case KINC_IMAGE_FORMAT_RGBA32:
+		return DXGI_FORMAT_R8G8B8A8_UNORM;
+	default:
+		assert(false);
+		return DXGI_FORMAT_R8G8B8A8_UNORM;
+	}
+}
+
+static int formatByteSize(kinc_image_format_t format) {
+	switch (format) {
+	case KINC_IMAGE_FORMAT_RGBA128:
+		return 16;
+	case KINC_IMAGE_FORMAT_RGBA64:
+		return 8;
+	case KINC_IMAGE_FORMAT_RGB24:
+		return 4;
+	case KINC_IMAGE_FORMAT_A32:
+		return 4;
+	case KINC_IMAGE_FORMAT_A16:
+		return 2;
+	case KINC_IMAGE_FORMAT_GREY8:
+		return 1;
+	case KINC_IMAGE_FORMAT_BGRA32:
+	case KINC_IMAGE_FORMAT_RGBA32:
+		return 4;
+	default:
+		assert(false);
+		return 4;
+	}
+}
+
+static bool isHdr(kinc_image_format_t format) {
+	return format == KINC_IMAGE_FORMAT_RGBA128 || format == KINC_IMAGE_FORMAT_RGBA64 || format == KINC_IMAGE_FORMAT_A32 || format == KINC_IMAGE_FORMAT_A16;
+}
+
+void kinc_g4_texture_init_from_image(kinc_g4_texture_t *texture, kinc_image_t *image) {
+	memset(&texture->impl, 0, sizeof(texture->impl));
+	texture->impl.stage = 0;
+	texture->tex_width = image->width;
+	texture->tex_height = image->height;
+	texture->tex_depth = 1;
+	texture->format = image->format;
+	texture->impl.rowPitch = 0;
+
+	D3D11_TEXTURE2D_DESC desc;
+	desc.Width = image->width;
+	desc.Height = image->height;
+	desc.MipLevels = desc.ArraySize = 1;
+	desc.Format = convertFormat(image->format);
+	desc.SampleDesc.Count = 1;
+	desc.SampleDesc.Quality = 0;
+	desc.Usage = D3D11_USAGE_DEFAULT;
+	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+	desc.CPUAccessFlags = 0; // D3D11_CPU_ACCESS_WRITE;
+	desc.MiscFlags = 0;
+
+	D3D11_SUBRESOURCE_DATA data;
+	data.pSysMem = image->data;
+	data.SysMemPitch = image->width * formatByteSize(image->format);
+	data.SysMemSlicePitch = 0;
+
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, &data, &texture->impl.texture));
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture, NULL, &texture->impl.view));
+}
+
+void kinc_g4_texture_init_from_image3d(kinc_g4_texture_t *texture, kinc_image_t *image) {
+	memset(&texture->impl, 0, sizeof(texture->impl));
+	texture->impl.stage = 0;
+	texture->tex_width = image->width;
+	texture->tex_height = image->height;
+	texture->tex_depth = image->depth;
+	texture->format = image->format;
+	texture->impl.rowPitch = 0;
+
+	D3D11_TEXTURE3D_DESC desc;
+	desc.Width = image->width;
+	desc.Height = image->height;
+	desc.Depth = image->depth;
+	desc.MipLevels = 1;
+	desc.Usage = D3D11_USAGE_DEFAULT;
+	desc.MiscFlags = 0;
+	desc.Format = convertFormat(image->format);
+	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+	desc.Usage = D3D11_USAGE_DEFAULT;
+	desc.CPUAccessFlags = 0;
+
+	D3D11_SUBRESOURCE_DATA data;
+	data.pSysMem = image->data;
+	data.SysMemPitch = image->width * formatByteSize(image->format);
+	data.SysMemSlicePitch = image->width * image->height * formatByteSize(image->format);
+
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture3D(dx_ctx.device, &desc, &data, &texture->impl.texture3D));
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture3D, NULL, &texture->impl.view));
+}
+
+void kinc_g4_texture_init(kinc_g4_texture_t *texture, int width, int height, kinc_image_format_t format) {
+	memset(&texture->impl, 0, sizeof(texture->impl));
+	texture->impl.stage = 0;
+	texture->tex_width = width;
+	texture->tex_height = height;
+	texture->tex_depth = 1;
+	texture->format = format;
+
+	D3D11_TEXTURE2D_DESC desc;
+	desc.Width = width;
+	desc.Height = height;
+	desc.MipLevels = desc.ArraySize = 1;
+	desc.SampleDesc.Count = 1;
+	desc.SampleDesc.Quality = 0;
+	desc.Usage = D3D11_USAGE_DEFAULT;
+	desc.MiscFlags = 0;
+
+	if (format == KINC_IMAGE_FORMAT_RGBA128) { // for compute
+		desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
+		desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
+		desc.CPUAccessFlags = 0;
+	}
+	else {
+		desc.Format = convertFormat(format);
+		desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+		desc.Usage = D3D11_USAGE_DYNAMIC;
+		desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+	}
+
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &texture->impl.texture));
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture, NULL, &texture->impl.view));
+
+	if (format == KINC_IMAGE_FORMAT_RGBA128) {
+		D3D11_UNORDERED_ACCESS_VIEW_DESC du;
+		du.Format = desc.Format;
+		du.Texture2D.MipSlice = 0;
+		du.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
+		kinc_microsoft_affirm(
+		    dx_ctx.device->lpVtbl->CreateUnorderedAccessView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture, &du, &texture->impl.computeView));
+	}
+}
+
+void kinc_g4_texture_init3d(kinc_g4_texture_t *texture, int width, int height, int depth, kinc_image_format_t format) {
+	memset(&texture->impl, 0, sizeof(texture->impl));
+	texture->impl.stage = 0;
+	texture->tex_width = width;
+	texture->tex_height = height;
+	texture->tex_depth = depth;
+	texture->format = format;
+	texture->impl.hasMipmaps = true;
+
+	D3D11_TEXTURE3D_DESC desc;
+	desc.Width = width;
+	desc.Height = height;
+	desc.Depth = depth;
+	desc.MipLevels = 0;
+	desc.Usage = D3D11_USAGE_DEFAULT;
+	desc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS;
+	desc.Format = format == KINC_IMAGE_FORMAT_RGBA32 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R8_UNORM;
+	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS;
+	desc.Usage = D3D11_USAGE_DEFAULT;
+	desc.CPUAccessFlags = 0;
+
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture3D(dx_ctx.device, &desc, NULL, &texture->impl.texture3D));
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture3D, NULL, &texture->impl.view));
+}
+
+// TextureImpl::TextureImpl() : hasMipmaps(false), renderView(nullptr), computeView(nullptr) {}
+
+void kinc_internal_texture_unset(kinc_g4_texture_t *texture);
+
+void kinc_g4_texture_destroy(kinc_g4_texture_t *texture) {
+	kinc_internal_texture_unset(texture);
+	if (texture->impl.view != NULL) {
+		texture->impl.view->lpVtbl->Release(texture->impl.view);
+	}
+	if (texture->impl.texture != NULL) {
+		texture->impl.texture->lpVtbl->Release(texture->impl.texture);
+	}
+	if (texture->impl.texture3D != NULL) {
+		texture->impl.texture3D->lpVtbl->Release(texture->impl.texture3D);
+	}
+	if (texture->impl.computeView != NULL) {
+		texture->impl.computeView->lpVtbl->Release(texture->impl.computeView);
+	}
+}
+
+void kinc_internal_texture_unmipmap(kinc_g4_texture_t *texture) {
+	texture->impl.hasMipmaps = false;
+}
+
+void kinc_internal_texture_set(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0 && unit.stages[KINC_G4_SHADER_TYPE_VERTEX] < 0 && unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] < 0)
+		return;
+
+	if (unit.stages[KINC_G4_SHADER_TYPE_VERTEX] >= 0) {
+		dx_ctx.context->lpVtbl->VSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_VERTEX], 1, &texture->impl.view);
+	}
+
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0) {
+		dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &texture->impl.view);
+	}
+
+	if (unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] >= 0) {
+		dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_COMPUTE], 1, &texture->impl.view);
+	}
+
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0 || unit.stages[KINC_G4_SHADER_TYPE_VERTEX] >= 0) {
+		texture->impl.stage =
+		    unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0 ? unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] : unit.stages[KINC_G4_SHADER_TYPE_VERTEX];
+		setTextures[texture->impl.stage] = texture;
+	}
+}
+
+void kinc_internal_texture_set_image(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit) {
+	if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0 && unit.stages[KINC_G4_SHADER_TYPE_VERTEX] < 0 && unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] < 0)
+		return;
+
+	if (texture->impl.computeView == NULL) {
+		D3D11_UNORDERED_ACCESS_VIEW_DESC du;
+		du.Format = texture->format == KINC_IMAGE_FORMAT_RGBA32 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R8_UNORM;
+		du.Texture3D.MipSlice = 0;
+		du.Texture3D.FirstWSlice = 0;
+		du.Texture3D.WSize = -1;
+		du.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
+		kinc_microsoft_affirm(
+		    dx_ctx.device->lpVtbl->CreateUnorderedAccessView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture3D, &du, &texture->impl.computeView));
+	}
+
+	dx_ctx.context->lpVtbl->OMSetRenderTargetsAndUnorderedAccessViews(dx_ctx.context, 0, NULL, NULL, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1,
+	                                                                  &texture->impl.computeView, NULL);
+
+	/*if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0) {
+		ID3D11ShaderResourceView *nullView = NULL;
+		dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, 0, 1, &nullView);
+
+		dx_ctx.context->lpVtbl->CSSetUnorderedAccessViews(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &texture->impl.computeView, NULL);
+	}
+	if (unit.stages[KINC_G4_SHADER_TYPE_VERTEX] >= 0) {
+		ID3D11ShaderResourceView *nullView = NULL;
+		dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, 0, 1, &nullView);
+
+		dx_ctx.context->lpVtbl->CSSetUnorderedAccessViews(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_VERTEX], 1, &texture->impl.computeView, NULL);
+	}
+	if (unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] >= 0) {
+		ID3D11ShaderResourceView *nullView = NULL;
+		dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, 0, 1, &nullView);
+
+		dx_ctx.context->lpVtbl->CSSetUnorderedAccessViews(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_COMPUTE], 1, &texture->impl.computeView, NULL);
+	}*/
+}
+
+void kinc_internal_texture_unset(kinc_g4_texture_t *texture) {
+	if (setTextures[texture->impl.stage] == texture) {
+
+		setTextures[texture->impl.stage] = NULL;
+	}
+}
+
+uint8_t *kinc_g4_texture_lock(kinc_g4_texture_t *texture) {
+	D3D11_MAPPED_SUBRESOURCE mappedResource;
+	kinc_microsoft_affirm(dx_ctx.context->lpVtbl->Map(dx_ctx.context, (ID3D11Resource *)texture->impl.texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource));
+	texture->impl.rowPitch = mappedResource.RowPitch;
+	return (uint8_t *)mappedResource.pData;
+}
+
+void kinc_g4_texture_unlock(kinc_g4_texture_t *texture) {
+	dx_ctx.context->lpVtbl->Unmap(dx_ctx.context, (ID3D11Resource *)texture->impl.texture, 0);
+}
+
+void kinc_g4_texture_clear(kinc_g4_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color) {
+	if (texture->impl.renderView == NULL) {
+		texture->tex_depth > 1 ? kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture3D,
+		                                                                                             0, &texture->impl.renderView))
+		                       : kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture, 0,
+		                                                                                             &texture->impl.renderView));
+	}
+	static float clearColor[4];
+	clearColor[0] = ((color & 0x00ff0000) >> 16) / 255.0f;
+	clearColor[1] = ((color & 0x0000ff00) >> 8) / 255.0f;
+	clearColor[2] = (color & 0x000000ff) / 255.0f;
+	clearColor[3] = ((color & 0xff000000) >> 24) / 255.0f;
+	dx_ctx.context->lpVtbl->ClearRenderTargetView(dx_ctx.context, texture->impl.renderView, clearColor);
+}
+
+int kinc_g4_texture_stride(kinc_g4_texture_t *texture) {
+	assert(texture->impl.rowPitch != 0); // stride is not yet set, lock and unlock the texture first (or find a good fix for this and send a PR)
+	return texture->impl.rowPitch;
+}
+
+static void enableMipmaps(kinc_g4_texture_t *texture, int texWidth, int texHeight, int format) {
+	D3D11_TEXTURE2D_DESC desc;
+	desc.Width = texWidth;
+	desc.Height = texHeight;
+	desc.MipLevels = 0;
+	desc.ArraySize = 1;
+	desc.Format = convertFormat((kinc_image_format_t)format);
+	desc.SampleDesc.Count = 1;
+	desc.SampleDesc.Quality = 0;
+	desc.Usage = D3D11_USAGE_DEFAULT;
+	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
+	desc.CPUAccessFlags = 0;
+	desc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS;
+
+	ID3D11Texture2D *mipMappedTexture;
+	ID3D11ShaderResourceView *mipMappedView;
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &mipMappedTexture));
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)mipMappedTexture, NULL, &mipMappedView));
+
+	D3D11_BOX sourceRegion;
+	sourceRegion.left = 0;
+	sourceRegion.right = texWidth;
+	sourceRegion.top = 0;
+	sourceRegion.bottom = texHeight;
+	sourceRegion.front = 0;
+	sourceRegion.back = 1;
+	dx_ctx.context->lpVtbl->CopySubresourceRegion(dx_ctx.context, (ID3D11Resource *)mipMappedTexture, 0, 0, 0, 0, (ID3D11Resource *)texture->impl.texture, 0,
+	                                              &sourceRegion);
+
+	if (texture->impl.texture != NULL) {
+		texture->impl.texture->lpVtbl->Release(texture->impl.texture);
+	}
+	texture->impl.texture = mipMappedTexture;
+
+	if (texture->impl.view != NULL) {
+		texture->impl.view->lpVtbl->Release(texture->impl.view);
+	}
+	texture->impl.view = mipMappedView;
+
+	texture->impl.hasMipmaps = true;
+}
+
+void kinc_g4_texture_generate_mipmaps(kinc_g4_texture_t *texture, int levels) {
+	if (!texture->impl.hasMipmaps) {
+		enableMipmaps(texture, texture->tex_width, texture->tex_height, texture->format);
+	}
+	dx_ctx.context->lpVtbl->GenerateMips(dx_ctx.context, texture->impl.view);
+}
+
+void kinc_g4_texture_set_mipmap(kinc_g4_texture_t *texture, kinc_image_t *mipmap, int level) {
+	if (!texture->impl.hasMipmaps) {
+		enableMipmaps(texture, texture->tex_width, texture->tex_height, texture->format);
+	}
+	D3D11_BOX dstRegion;
+	dstRegion.left = 0;
+	dstRegion.right = mipmap->width;
+	dstRegion.top = 0;
+	dstRegion.bottom = mipmap->height;
+	dstRegion.front = 0;
+	dstRegion.back = 1;
+	dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)texture->impl.texture, level, &dstRegion, mipmap->data,
+	                                          mipmap->width * formatByteSize(mipmap->format), 0);
+}

+ 24 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.h

@@ -0,0 +1,24 @@
+#pragma once
+
+struct ID3D11Texture2D;
+struct ID3D11Texture3D;
+struct ID3D11ShaderResourceView;
+struct ID3D11UnorderedAccessView;
+struct ID3D11RenderTargetView;
+
+// TextureImpl();
+//~TextureImpl();
+// void enableMipmaps(int texWidth, int texHeight, int format);
+// void unmipmap();
+// void unset();
+
+typedef struct {
+	bool hasMipmaps;
+	int stage;
+	struct ID3D11Texture2D *texture;
+	struct ID3D11Texture3D *texture3D;
+	struct ID3D11ShaderResourceView *view;
+	struct ID3D11UnorderedAccessView *computeView;
+	struct ID3D11RenderTargetView *renderView;
+	int rowPitch;
+} kinc_g4_texture_impl_t;

+ 95 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.c.h

@@ -0,0 +1,95 @@
+#include <kinc/graphics4/vertexbuffer.h>
+
+void kinc_g4_vertex_buffer_init(kinc_g4_vertex_buffer_t *buffer, int count, kinc_g4_vertex_structure_t *structure, kinc_g4_usage_t usage,
+                                int instance_data_step_rate) {
+	buffer->impl.count = count;
+	buffer->impl.stride = 0;
+	for (int i = 0; i < structure->size; ++i) {
+		buffer->impl.stride += kinc_g4_vertex_data_size(structure->elements[i].data);
+	}
+
+	if (usage == KINC_G4_USAGE_DYNAMIC) {
+		buffer->impl.vertices = NULL;
+	}
+	else {
+		buffer->impl.vertices = (float *)malloc(buffer->impl.stride * count);
+	}
+
+	D3D11_BUFFER_DESC bufferDesc;
+	bufferDesc.CPUAccessFlags = 0;
+
+	buffer->impl.usage = usage;
+	switch (usage) {
+	case KINC_G4_USAGE_STATIC:
+		bufferDesc.Usage = D3D11_USAGE_DEFAULT;
+		break;
+	case KINC_G4_USAGE_DYNAMIC:
+		bufferDesc.Usage = D3D11_USAGE_DYNAMIC;
+		bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+		break;
+	case KINC_G4_USAGE_READABLE:
+		bufferDesc.Usage = D3D11_USAGE_DEFAULT;
+		break;
+	}
+
+	bufferDesc.ByteWidth = buffer->impl.stride * count;
+	bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+	bufferDesc.MiscFlags = 0;
+	bufferDesc.StructureByteStride = 0;
+
+	kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &bufferDesc, NULL, &buffer->impl.vb));
+}
+
+void kinc_g4_vertex_buffer_destroy(kinc_g4_vertex_buffer_t *buffer) {
+	buffer->impl.vb->lpVtbl->Release(buffer->impl.vb);
+	free(buffer->impl.vertices);
+	buffer->impl.vertices = NULL;
+}
+
+float *kinc_g4_vertex_buffer_lock_all(kinc_g4_vertex_buffer_t *buffer) {
+	return kinc_g4_vertex_buffer_lock(buffer, 0, buffer->impl.count);
+}
+
+float *kinc_g4_vertex_buffer_lock(kinc_g4_vertex_buffer_t *buffer, int start, int count) {
+	buffer->impl.lockStart = start;
+	buffer->impl.lockCount = count;
+
+	if (buffer->impl.usage == KINC_G4_USAGE_DYNAMIC) {
+		D3D11_MAPPED_SUBRESOURCE mappedResource;
+		memset(&mappedResource, 0, sizeof(D3D11_MAPPED_SUBRESOURCE));
+		dx_ctx.context->lpVtbl->Map(dx_ctx.context, (ID3D11Resource *)buffer->impl.vb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
+		float *data = (float *)mappedResource.pData;
+		return &data[start * buffer->impl.stride / 4];
+	}
+	else {
+		return &buffer->impl.vertices[start * buffer->impl.stride / 4];
+	}
+}
+
+void kinc_g4_vertex_buffer_unlock_all(kinc_g4_vertex_buffer_t *buffer) {
+	kinc_g4_vertex_buffer_unlock(buffer, buffer->impl.lockCount);
+}
+
+void kinc_g4_vertex_buffer_unlock(kinc_g4_vertex_buffer_t *buffer, int count) {
+	if (buffer->impl.usage == KINC_G4_USAGE_DYNAMIC) {
+		dx_ctx.context->lpVtbl->Unmap(dx_ctx.context, (ID3D11Resource *)buffer->impl.vb, 0);
+	}
+	else {
+		dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)buffer->impl.vb, 0, NULL, buffer->impl.vertices, 0, 0);
+	}
+}
+
+int kinc_internal_g4_vertex_buffer_set(kinc_g4_vertex_buffer_t *buffer, int offset) {
+	// UINT stride = myStride;
+	// UINT internaloffset = 0;
+	// dx_ctx.context->IASetVertexBuffers(0, 1, &vb, &stride, &internaloffset);
+	return 0;
+}
+
+int kinc_g4_vertex_buffer_count(kinc_g4_vertex_buffer_t *buffer) {
+	return buffer->impl.count;
+}
+
+int kinc_g4_vertex_buffer_stride(kinc_g4_vertex_buffer_t *buffer) {
+	return buffer->impl.stride;
+}

+ 13 - 0
armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.h

@@ -0,0 +1,13 @@
+#pragma once
+
+struct ID3D11Buffer;
+
+typedef struct {
+	struct ID3D11Buffer *vb;
+	int stride;
+	int count;
+	int lockStart;
+	int lockCount;
+	float *vertices;
+	int usage;
+} kinc_g4_vertex_buffer_impl_t;

+ 2654 - 0
armorcore/sources/backends/direct3d12/d3dx12.h

@@ -0,0 +1,2654 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+#ifndef __D3DX12_H__
+#define __D3DX12_H__
+
+#include "d3d12.h"
+
+#if defined( __cplusplus )
+
+struct CD3DX12_DEFAULT {};
+extern const DECLSPEC_SELECTANY CD3DX12_DEFAULT D3D12_DEFAULT;
+
+//------------------------------------------------------------------------------------------------
+inline bool operator==( const D3D12_VIEWPORT& l, const D3D12_VIEWPORT& r )
+{
+    return l.TopLeftX == r.TopLeftX && l.TopLeftY == r.TopLeftY && l.Width == r.Width &&
+        l.Height == r.Height && l.MinDepth == r.MinDepth && l.MaxDepth == r.MaxDepth;
+}
+
+//------------------------------------------------------------------------------------------------
+inline bool operator!=( const D3D12_VIEWPORT& l, const D3D12_VIEWPORT& r )
+{ return !( l == r ); }
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_RECT : public D3D12_RECT
+{
+    CD3DX12_RECT() = default;
+    explicit CD3DX12_RECT( const D3D12_RECT& o ) :
+        D3D12_RECT( o )
+    {}
+    explicit CD3DX12_RECT(
+        LONG Left,
+        LONG Top,
+        LONG Right,
+        LONG Bottom )
+    {
+        left = Left;
+        top = Top;
+        right = Right;
+        bottom = Bottom;
+    }
+    ~CD3DX12_RECT() {}
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_VIEWPORT : public D3D12_VIEWPORT
+{
+    CD3DX12_VIEWPORT() = default;
+    explicit CD3DX12_VIEWPORT( const D3D12_VIEWPORT& o ) :
+        D3D12_VIEWPORT( o )
+    {}
+    explicit CD3DX12_VIEWPORT(
+        FLOAT topLeftX,
+        FLOAT topLeftY,
+        FLOAT width,
+        FLOAT height,
+        FLOAT minDepth = D3D12_MIN_DEPTH,
+        FLOAT maxDepth = D3D12_MAX_DEPTH )
+    {
+        TopLeftX = topLeftX;
+        TopLeftY = topLeftY;
+        Width = width;
+        Height = height;
+        MinDepth = minDepth;
+        MaxDepth = maxDepth;
+    }
+    explicit CD3DX12_VIEWPORT(
+        _In_ ID3D12Resource* pResource,
+        UINT mipSlice = 0,
+        FLOAT topLeftX = 0.0f,
+        FLOAT topLeftY = 0.0f,
+        FLOAT minDepth = D3D12_MIN_DEPTH,
+        FLOAT maxDepth = D3D12_MAX_DEPTH )
+    {
+        D3D12_RESOURCE_DESC Desc = pResource->GetDesc();
+        const UINT64 SubresourceWidth = Desc.Width >> mipSlice;
+        const UINT64 SubresourceHeight = Desc.Height >> mipSlice;
+        switch (Desc.Dimension)
+        {
+        case D3D12_RESOURCE_DIMENSION_BUFFER:
+            TopLeftX = topLeftX;
+            TopLeftY = 0.0f;
+            Width = Desc.Width - topLeftX;
+            Height = 1.0f;
+            break;
+        case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
+            TopLeftX = topLeftX;
+            TopLeftY = 0.0f;
+            Width = (SubresourceWidth ? SubresourceWidth : 1.0f) - topLeftX;
+            Height = 1.0f;
+            break;
+        case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
+        case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
+            TopLeftX = topLeftX;
+            TopLeftY = topLeftY;
+            Width = (SubresourceWidth ? SubresourceWidth : 1.0f) - topLeftX;
+            Height = (SubresourceHeight ? SubresourceHeight: 1.0f) - topLeftY;
+            break;
+        default: break;
+        }
+
+        MinDepth = minDepth;
+        MaxDepth = maxDepth;
+    }
+    ~CD3DX12_VIEWPORT() {}
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_BOX : public D3D12_BOX
+{
+    CD3DX12_BOX() = default;
+    explicit CD3DX12_BOX( const D3D12_BOX& o ) :
+        D3D12_BOX( o )
+    {}
+    explicit CD3DX12_BOX(
+        LONG Left,
+        LONG Right )
+    {
+        left = Left;
+        top = 0;
+        front = 0;
+        right = Right;
+        bottom = 1;
+        back = 1;
+    }
+    explicit CD3DX12_BOX(
+        LONG Left,
+        LONG Top,
+        LONG Right,
+        LONG Bottom )
+    {
+        left = Left;
+        top = Top;
+        front = 0;
+        right = Right;
+        bottom = Bottom;
+        back = 1;
+    }
+    explicit CD3DX12_BOX(
+        LONG Left,
+        LONG Top,
+        LONG Front,
+        LONG Right,
+        LONG Bottom,
+        LONG Back )
+    {
+        left = Left;
+        top = Top;
+        front = Front;
+        right = Right;
+        bottom = Bottom;
+        back = Back;
+    }
+    ~CD3DX12_BOX() {}
+};
+inline bool operator==( const D3D12_BOX& l, const D3D12_BOX& r )
+{
+    return l.left == r.left && l.top == r.top && l.front == r.front &&
+        l.right == r.right && l.bottom == r.bottom && l.back == r.back;
+}
+inline bool operator!=( const D3D12_BOX& l, const D3D12_BOX& r )
+{ return !( l == r ); }
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_DEPTH_STENCIL_DESC : public D3D12_DEPTH_STENCIL_DESC
+{
+    CD3DX12_DEPTH_STENCIL_DESC() = default;
+    explicit CD3DX12_DEPTH_STENCIL_DESC( const D3D12_DEPTH_STENCIL_DESC& o ) :
+        D3D12_DEPTH_STENCIL_DESC( o )
+    {}
+    explicit CD3DX12_DEPTH_STENCIL_DESC( CD3DX12_DEFAULT )
+    {
+        DepthEnable = TRUE;
+        DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
+        DepthFunc = D3D12_COMPARISON_FUNC_LESS;
+        StencilEnable = FALSE;
+        StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
+        StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
+        const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp =
+        { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS };
+        FrontFace = defaultStencilOp;
+        BackFace = defaultStencilOp;
+    }
+    explicit CD3DX12_DEPTH_STENCIL_DESC(
+        BOOL depthEnable,
+        D3D12_DEPTH_WRITE_MASK depthWriteMask,
+        D3D12_COMPARISON_FUNC depthFunc,
+        BOOL stencilEnable,
+        UINT8 stencilReadMask,
+        UINT8 stencilWriteMask,
+        D3D12_STENCIL_OP frontStencilFailOp,
+        D3D12_STENCIL_OP frontStencilDepthFailOp,
+        D3D12_STENCIL_OP frontStencilPassOp,
+        D3D12_COMPARISON_FUNC frontStencilFunc,
+        D3D12_STENCIL_OP backStencilFailOp,
+        D3D12_STENCIL_OP backStencilDepthFailOp,
+        D3D12_STENCIL_OP backStencilPassOp,
+        D3D12_COMPARISON_FUNC backStencilFunc )
+    {
+        DepthEnable = depthEnable;
+        DepthWriteMask = depthWriteMask;
+        DepthFunc = depthFunc;
+        StencilEnable = stencilEnable;
+        StencilReadMask = stencilReadMask;
+        StencilWriteMask = stencilWriteMask;
+        FrontFace.StencilFailOp = frontStencilFailOp;
+        FrontFace.StencilDepthFailOp = frontStencilDepthFailOp;
+        FrontFace.StencilPassOp = frontStencilPassOp;
+        FrontFace.StencilFunc = frontStencilFunc;
+        BackFace.StencilFailOp = backStencilFailOp;
+        BackFace.StencilDepthFailOp = backStencilDepthFailOp;
+        BackFace.StencilPassOp = backStencilPassOp;
+        BackFace.StencilFunc = backStencilFunc;
+    }
+    ~CD3DX12_DEPTH_STENCIL_DESC() {}
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_DEPTH_STENCIL_DESC1 : public D3D12_DEPTH_STENCIL_DESC1
+{
+    CD3DX12_DEPTH_STENCIL_DESC1() = default;
+    explicit CD3DX12_DEPTH_STENCIL_DESC1( const D3D12_DEPTH_STENCIL_DESC1& o ) :
+        D3D12_DEPTH_STENCIL_DESC1( o )
+    {}
+    explicit CD3DX12_DEPTH_STENCIL_DESC1( const D3D12_DEPTH_STENCIL_DESC& o )
+    {
+        DepthEnable                  = o.DepthEnable;
+        DepthWriteMask               = o.DepthWriteMask;
+        DepthFunc                    = o.DepthFunc;
+        StencilEnable                = o.StencilEnable;
+        StencilReadMask              = o.StencilReadMask;
+        StencilWriteMask             = o.StencilWriteMask;
+        FrontFace.StencilFailOp      = o.FrontFace.StencilFailOp;
+        FrontFace.StencilDepthFailOp = o.FrontFace.StencilDepthFailOp;
+        FrontFace.StencilPassOp      = o.FrontFace.StencilPassOp;
+        FrontFace.StencilFunc        = o.FrontFace.StencilFunc;
+        BackFace.StencilFailOp       = o.BackFace.StencilFailOp;
+        BackFace.StencilDepthFailOp  = o.BackFace.StencilDepthFailOp;
+        BackFace.StencilPassOp       = o.BackFace.StencilPassOp;
+        BackFace.StencilFunc         = o.BackFace.StencilFunc;
+        DepthBoundsTestEnable        = FALSE;
+    }
+    explicit CD3DX12_DEPTH_STENCIL_DESC1( CD3DX12_DEFAULT )
+    {
+        DepthEnable = TRUE;
+        DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
+        DepthFunc = D3D12_COMPARISON_FUNC_LESS;
+        StencilEnable = FALSE;
+        StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
+        StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
+        const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp =
+        { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS };
+        FrontFace = defaultStencilOp;
+        BackFace = defaultStencilOp;
+        DepthBoundsTestEnable = FALSE;
+    }
+    explicit CD3DX12_DEPTH_STENCIL_DESC1(
+        BOOL depthEnable,
+        D3D12_DEPTH_WRITE_MASK depthWriteMask,
+        D3D12_COMPARISON_FUNC depthFunc,
+        BOOL stencilEnable,
+        UINT8 stencilReadMask,
+        UINT8 stencilWriteMask,
+        D3D12_STENCIL_OP frontStencilFailOp,
+        D3D12_STENCIL_OP frontStencilDepthFailOp,
+        D3D12_STENCIL_OP frontStencilPassOp,
+        D3D12_COMPARISON_FUNC frontStencilFunc,
+        D3D12_STENCIL_OP backStencilFailOp,
+        D3D12_STENCIL_OP backStencilDepthFailOp,
+        D3D12_STENCIL_OP backStencilPassOp,
+        D3D12_COMPARISON_FUNC backStencilFunc,
+        BOOL depthBoundsTestEnable )
+    {
+        DepthEnable = depthEnable;
+        DepthWriteMask = depthWriteMask;
+        DepthFunc = depthFunc;
+        StencilEnable = stencilEnable;
+        StencilReadMask = stencilReadMask;
+        StencilWriteMask = stencilWriteMask;
+        FrontFace.StencilFailOp = frontStencilFailOp;
+        FrontFace.StencilDepthFailOp = frontStencilDepthFailOp;
+        FrontFace.StencilPassOp = frontStencilPassOp;
+        FrontFace.StencilFunc = frontStencilFunc;
+        BackFace.StencilFailOp = backStencilFailOp;
+        BackFace.StencilDepthFailOp = backStencilDepthFailOp;
+        BackFace.StencilPassOp = backStencilPassOp;
+        BackFace.StencilFunc = backStencilFunc;
+        DepthBoundsTestEnable = depthBoundsTestEnable;
+    }
+    ~CD3DX12_DEPTH_STENCIL_DESC1() {}
+    operator D3D12_DEPTH_STENCIL_DESC() const
+    {
+        D3D12_DEPTH_STENCIL_DESC D;
+        D.DepthEnable                  = DepthEnable;
+        D.DepthWriteMask               = DepthWriteMask;
+        D.DepthFunc                    = DepthFunc;
+        D.StencilEnable                = StencilEnable;
+        D.StencilReadMask              = StencilReadMask;
+        D.StencilWriteMask             = StencilWriteMask;
+        D.FrontFace.StencilFailOp      = FrontFace.StencilFailOp;
+        D.FrontFace.StencilDepthFailOp = FrontFace.StencilDepthFailOp;
+        D.FrontFace.StencilPassOp      = FrontFace.StencilPassOp;
+        D.FrontFace.StencilFunc        = FrontFace.StencilFunc;
+        D.BackFace.StencilFailOp       = BackFace.StencilFailOp;
+        D.BackFace.StencilDepthFailOp  = BackFace.StencilDepthFailOp;
+        D.BackFace.StencilPassOp       = BackFace.StencilPassOp;
+        D.BackFace.StencilFunc         = BackFace.StencilFunc;
+        return D;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_BLEND_DESC : public D3D12_BLEND_DESC
+{
+    CD3DX12_BLEND_DESC() = default;
+    explicit CD3DX12_BLEND_DESC( const D3D12_BLEND_DESC& o ) :
+        D3D12_BLEND_DESC( o )
+    {}
+    explicit CD3DX12_BLEND_DESC( CD3DX12_DEFAULT )
+    {
+        AlphaToCoverageEnable = FALSE;
+        IndependentBlendEnable = FALSE;
+        const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc =
+        {
+            FALSE,FALSE,
+            D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
+            D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
+            D3D12_LOGIC_OP_NOOP,
+            D3D12_COLOR_WRITE_ENABLE_ALL,
+        };
+        for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
+            RenderTarget[ i ] = defaultRenderTargetBlendDesc;
+    }
+    ~CD3DX12_BLEND_DESC() {}
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_RASTERIZER_DESC : public D3D12_RASTERIZER_DESC
+{
+    CD3DX12_RASTERIZER_DESC() = default;
+    explicit CD3DX12_RASTERIZER_DESC( const D3D12_RASTERIZER_DESC& o ) :
+        D3D12_RASTERIZER_DESC( o )
+    {}
+    explicit CD3DX12_RASTERIZER_DESC( CD3DX12_DEFAULT )
+    {
+        FillMode = D3D12_FILL_MODE_SOLID;
+        CullMode = D3D12_CULL_MODE_BACK;
+        FrontCounterClockwise = FALSE;
+        DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
+        DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
+        SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
+        DepthClipEnable = TRUE;
+        MultisampleEnable = FALSE;
+        AntialiasedLineEnable = FALSE;
+        ForcedSampleCount = 0;
+        ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
+    }
+    explicit CD3DX12_RASTERIZER_DESC(
+        D3D12_FILL_MODE fillMode,
+        D3D12_CULL_MODE cullMode,
+        BOOL frontCounterClockwise,
+        INT depthBias,
+        FLOAT depthBiasClamp,
+        FLOAT slopeScaledDepthBias,
+        BOOL depthClipEnable,
+        BOOL multisampleEnable,
+        BOOL antialiasedLineEnable, 
+        UINT forcedSampleCount, 
+        D3D12_CONSERVATIVE_RASTERIZATION_MODE conservativeRaster)
+    {
+        FillMode = fillMode;
+        CullMode = cullMode;
+        FrontCounterClockwise = frontCounterClockwise;
+        DepthBias = depthBias;
+        DepthBiasClamp = depthBiasClamp;
+        SlopeScaledDepthBias = slopeScaledDepthBias;
+        DepthClipEnable = depthClipEnable;
+        MultisampleEnable = multisampleEnable;
+        AntialiasedLineEnable = antialiasedLineEnable;
+        ForcedSampleCount = forcedSampleCount;
+        ConservativeRaster = conservativeRaster;
+    }
+    ~CD3DX12_RASTERIZER_DESC() {}
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_RESOURCE_ALLOCATION_INFO : public D3D12_RESOURCE_ALLOCATION_INFO
+{
+    CD3DX12_RESOURCE_ALLOCATION_INFO() = default;
+    explicit CD3DX12_RESOURCE_ALLOCATION_INFO( const D3D12_RESOURCE_ALLOCATION_INFO& o ) :
+        D3D12_RESOURCE_ALLOCATION_INFO( o )
+    {}
+    CD3DX12_RESOURCE_ALLOCATION_INFO(
+        UINT64 size,
+        UINT64 alignment )
+    {
+        SizeInBytes = size;
+        Alignment = alignment;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_HEAP_PROPERTIES : public D3D12_HEAP_PROPERTIES
+{
+    CD3DX12_HEAP_PROPERTIES() = default;
+    explicit CD3DX12_HEAP_PROPERTIES(const D3D12_HEAP_PROPERTIES &o) :
+        D3D12_HEAP_PROPERTIES(o)
+    {}
+    CD3DX12_HEAP_PROPERTIES( 
+        D3D12_CPU_PAGE_PROPERTY cpuPageProperty, 
+        D3D12_MEMORY_POOL memoryPoolPreference,
+        UINT creationNodeMask = 1, 
+        UINT nodeMask = 1 )
+    {
+        Type = D3D12_HEAP_TYPE_CUSTOM;
+        CPUPageProperty = cpuPageProperty;
+        MemoryPoolPreference = memoryPoolPreference;
+        CreationNodeMask = creationNodeMask;
+        VisibleNodeMask = nodeMask;
+    }
+    explicit CD3DX12_HEAP_PROPERTIES( 
+        D3D12_HEAP_TYPE type, 
+        UINT creationNodeMask = 1, 
+        UINT nodeMask = 1 )
+    {
+        Type = type;
+        CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+        MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+        CreationNodeMask = creationNodeMask;
+        VisibleNodeMask = nodeMask;
+    }
+    bool IsCPUAccessible() const
+    {
+        return Type == D3D12_HEAP_TYPE_UPLOAD || Type == D3D12_HEAP_TYPE_READBACK || (Type == D3D12_HEAP_TYPE_CUSTOM &&
+            (CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE || CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_BACK));
+    }
+};
+inline bool operator==( const D3D12_HEAP_PROPERTIES& l, const D3D12_HEAP_PROPERTIES& r )
+{
+    return l.Type == r.Type && l.CPUPageProperty == r.CPUPageProperty && 
+        l.MemoryPoolPreference == r.MemoryPoolPreference &&
+        l.CreationNodeMask == r.CreationNodeMask &&
+        l.VisibleNodeMask == r.VisibleNodeMask;
+}
+inline bool operator!=( const D3D12_HEAP_PROPERTIES& l, const D3D12_HEAP_PROPERTIES& r )
+{ return !( l == r ); }
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_HEAP_DESC : public D3D12_HEAP_DESC
+{
+    CD3DX12_HEAP_DESC() = default;
+    explicit CD3DX12_HEAP_DESC(const D3D12_HEAP_DESC &o) :
+        D3D12_HEAP_DESC(o)
+    {}
+    CD3DX12_HEAP_DESC( 
+        UINT64 size, 
+        D3D12_HEAP_PROPERTIES properties, 
+        UINT64 alignment = 0, 
+        D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE )
+    {
+        SizeInBytes = size;
+        Properties = properties;
+        Alignment = alignment;
+        Flags = flags;
+    }
+    CD3DX12_HEAP_DESC( 
+        UINT64 size, 
+        D3D12_HEAP_TYPE type, 
+        UINT64 alignment = 0, 
+        D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE )
+    {
+        SizeInBytes = size;
+        Properties = CD3DX12_HEAP_PROPERTIES( type );
+        Alignment = alignment;
+        Flags = flags;
+    }
+    CD3DX12_HEAP_DESC( 
+        UINT64 size, 
+        D3D12_CPU_PAGE_PROPERTY cpuPageProperty, 
+        D3D12_MEMORY_POOL memoryPoolPreference, 
+        UINT64 alignment = 0, 
+        D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE )
+    {
+        SizeInBytes = size;
+        Properties = CD3DX12_HEAP_PROPERTIES( cpuPageProperty, memoryPoolPreference );
+        Alignment = alignment;
+        Flags = flags;
+    }
+    CD3DX12_HEAP_DESC( 
+        const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo,
+        D3D12_HEAP_PROPERTIES properties, 
+        D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE )
+    {
+        SizeInBytes = resAllocInfo.SizeInBytes;
+        Properties = properties;
+        Alignment = resAllocInfo.Alignment;
+        Flags = flags;
+    }
+    CD3DX12_HEAP_DESC( 
+        const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo,
+        D3D12_HEAP_TYPE type, 
+        D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE )
+    {
+        SizeInBytes = resAllocInfo.SizeInBytes;
+        Properties = CD3DX12_HEAP_PROPERTIES( type );
+        Alignment = resAllocInfo.Alignment;
+        Flags = flags;
+    }
+    CD3DX12_HEAP_DESC( 
+        const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo,
+        D3D12_CPU_PAGE_PROPERTY cpuPageProperty, 
+        D3D12_MEMORY_POOL memoryPoolPreference, 
+        D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE )
+    {
+        SizeInBytes = resAllocInfo.SizeInBytes;
+        Properties = CD3DX12_HEAP_PROPERTIES( cpuPageProperty, memoryPoolPreference );
+        Alignment = resAllocInfo.Alignment;
+        Flags = flags;
+    }
+    bool IsCPUAccessible() const
+    { return static_cast< const CD3DX12_HEAP_PROPERTIES* >( &Properties )->IsCPUAccessible(); }
+};
+inline bool operator==( const D3D12_HEAP_DESC& l, const D3D12_HEAP_DESC& r )
+{
+    return l.SizeInBytes == r.SizeInBytes &&
+        l.Properties == r.Properties && 
+        l.Alignment == r.Alignment &&
+        l.Flags == r.Flags;
+}
+inline bool operator!=( const D3D12_HEAP_DESC& l, const D3D12_HEAP_DESC& r )
+{ return !( l == r ); }
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_CLEAR_VALUE : public D3D12_CLEAR_VALUE
+{
+    CD3DX12_CLEAR_VALUE() = default;
+    explicit CD3DX12_CLEAR_VALUE(const D3D12_CLEAR_VALUE &o) :
+        D3D12_CLEAR_VALUE(o)
+    {}
+    CD3DX12_CLEAR_VALUE( 
+        DXGI_FORMAT format, 
+        const FLOAT color[4] )
+    {
+        Format = format;
+        memcpy( Color, color, sizeof( Color ) );
+    }
+    CD3DX12_CLEAR_VALUE( 
+        DXGI_FORMAT format, 
+        FLOAT depth,
+        UINT8 stencil )
+    {
+        Format = format;
+        /* Use memcpy to preserve NAN values */
+        memcpy( &DepthStencil.Depth, &depth, sizeof( depth ) );
+        DepthStencil.Stencil = stencil;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_RANGE : public D3D12_RANGE
+{
+    CD3DX12_RANGE() = default;
+    explicit CD3DX12_RANGE(const D3D12_RANGE &o) :
+        D3D12_RANGE(o)
+    {}
+    CD3DX12_RANGE( 
+        SIZE_T begin, 
+        SIZE_T end )
+    {
+        Begin = begin;
+        End = end;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_RANGE_UINT64 : public D3D12_RANGE_UINT64
+{
+    CD3DX12_RANGE_UINT64() = default;
+    explicit CD3DX12_RANGE_UINT64(const D3D12_RANGE_UINT64 &o) :
+        D3D12_RANGE_UINT64(o)
+    {}
+    CD3DX12_RANGE_UINT64( 
+        UINT64 begin, 
+        UINT64 end )
+    {
+        Begin = begin;
+        End = end;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_SUBRESOURCE_RANGE_UINT64 : public D3D12_SUBRESOURCE_RANGE_UINT64
+{
+    CD3DX12_SUBRESOURCE_RANGE_UINT64() = default;
+    explicit CD3DX12_SUBRESOURCE_RANGE_UINT64(const D3D12_SUBRESOURCE_RANGE_UINT64 &o) :
+        D3D12_SUBRESOURCE_RANGE_UINT64(o)
+    {}
+    CD3DX12_SUBRESOURCE_RANGE_UINT64( 
+        UINT subresource,
+        const D3D12_RANGE_UINT64& range )
+    {
+        Subresource = subresource;
+        Range = range;
+    }
+    CD3DX12_SUBRESOURCE_RANGE_UINT64( 
+        UINT subresource,
+        UINT64 begin, 
+        UINT64 end )
+    {
+        Subresource = subresource;
+        Range.Begin = begin;
+        Range.End = end;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_SHADER_BYTECODE : public D3D12_SHADER_BYTECODE
+{
+    CD3DX12_SHADER_BYTECODE() = default;
+    explicit CD3DX12_SHADER_BYTECODE(const D3D12_SHADER_BYTECODE &o) :
+        D3D12_SHADER_BYTECODE(o)
+    {}
+    CD3DX12_SHADER_BYTECODE(
+        _In_ ID3DBlob* pShaderBlob )
+    {
+        pShaderBytecode = pShaderBlob->GetBufferPointer();
+        BytecodeLength = pShaderBlob->GetBufferSize();
+    }
+    CD3DX12_SHADER_BYTECODE(
+        const void* _pShaderBytecode,
+        SIZE_T bytecodeLength )
+    {
+        pShaderBytecode = _pShaderBytecode;
+        BytecodeLength = bytecodeLength;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_TILED_RESOURCE_COORDINATE : public D3D12_TILED_RESOURCE_COORDINATE
+{
+    CD3DX12_TILED_RESOURCE_COORDINATE() = default;
+    explicit CD3DX12_TILED_RESOURCE_COORDINATE(const D3D12_TILED_RESOURCE_COORDINATE &o) :
+        D3D12_TILED_RESOURCE_COORDINATE(o)
+    {}
+    CD3DX12_TILED_RESOURCE_COORDINATE( 
+        UINT x, 
+        UINT y, 
+        UINT z, 
+        UINT subresource ) 
+    {
+        X = x;
+        Y = y;
+        Z = z;
+        Subresource = subresource;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_TILE_REGION_SIZE : public D3D12_TILE_REGION_SIZE
+{
+    CD3DX12_TILE_REGION_SIZE() = default;
+    explicit CD3DX12_TILE_REGION_SIZE(const D3D12_TILE_REGION_SIZE &o) :
+        D3D12_TILE_REGION_SIZE(o)
+    {}
+    CD3DX12_TILE_REGION_SIZE( 
+        UINT numTiles, 
+        BOOL useBox, 
+        UINT width, 
+        UINT16 height, 
+        UINT16 depth ) 
+    {
+        NumTiles = numTiles;
+        UseBox = useBox;
+        Width = width;
+        Height = height;
+        Depth = depth;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_SUBRESOURCE_TILING : public D3D12_SUBRESOURCE_TILING
+{
+    CD3DX12_SUBRESOURCE_TILING() = default;
+    explicit CD3DX12_SUBRESOURCE_TILING(const D3D12_SUBRESOURCE_TILING &o) :
+        D3D12_SUBRESOURCE_TILING(o)
+    {}
+    CD3DX12_SUBRESOURCE_TILING( 
+        UINT widthInTiles, 
+        UINT16 heightInTiles, 
+        UINT16 depthInTiles, 
+        UINT startTileIndexInOverallResource ) 
+    {
+        WidthInTiles = widthInTiles;
+        HeightInTiles = heightInTiles;
+        DepthInTiles = depthInTiles;
+        StartTileIndexInOverallResource = startTileIndexInOverallResource;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_TILE_SHAPE : public D3D12_TILE_SHAPE
+{
+    CD3DX12_TILE_SHAPE() = default;
+    explicit CD3DX12_TILE_SHAPE(const D3D12_TILE_SHAPE &o) :
+        D3D12_TILE_SHAPE(o)
+    {}
+    CD3DX12_TILE_SHAPE( 
+        UINT widthInTexels, 
+        UINT heightInTexels, 
+        UINT depthInTexels ) 
+    {
+        WidthInTexels = widthInTexels;
+        HeightInTexels = heightInTexels;
+        DepthInTexels = depthInTexels;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_RESOURCE_BARRIER : public D3D12_RESOURCE_BARRIER
+{
+    CD3DX12_RESOURCE_BARRIER() = default;
+    explicit CD3DX12_RESOURCE_BARRIER(const D3D12_RESOURCE_BARRIER &o) :
+        D3D12_RESOURCE_BARRIER(o)
+    {}
+    static inline CD3DX12_RESOURCE_BARRIER Transition(
+        _In_ ID3D12Resource* pResource,
+        D3D12_RESOURCE_STATES stateBefore,
+        D3D12_RESOURCE_STATES stateAfter,
+        UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
+        D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
+    {
+        CD3DX12_RESOURCE_BARRIER result;
+        ZeroMemory(&result, sizeof(result));
+        D3D12_RESOURCE_BARRIER &barrier = result;
+        result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+        result.Flags = flags;
+        barrier.Transition.pResource = pResource;
+        barrier.Transition.StateBefore = stateBefore;
+        barrier.Transition.StateAfter = stateAfter;
+        barrier.Transition.Subresource = subresource;
+        return result;
+    }
+    static inline CD3DX12_RESOURCE_BARRIER Aliasing(
+        _In_ ID3D12Resource* pResourceBefore,
+        _In_ ID3D12Resource* pResourceAfter)
+    {
+        CD3DX12_RESOURCE_BARRIER result;
+        ZeroMemory(&result, sizeof(result));
+        D3D12_RESOURCE_BARRIER &barrier = result;
+        result.Type = D3D12_RESOURCE_BARRIER_TYPE_ALIASING;
+        barrier.Aliasing.pResourceBefore = pResourceBefore;
+        barrier.Aliasing.pResourceAfter = pResourceAfter;
+        return result;
+    }
+    static inline CD3DX12_RESOURCE_BARRIER UAV(
+        _In_ ID3D12Resource* pResource)
+    {
+        CD3DX12_RESOURCE_BARRIER result;
+        ZeroMemory(&result, sizeof(result));
+        D3D12_RESOURCE_BARRIER &barrier = result;
+        result.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
+        barrier.UAV.pResource = pResource;
+        return result;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_PACKED_MIP_INFO : public D3D12_PACKED_MIP_INFO
+{
+    CD3DX12_PACKED_MIP_INFO() = default;
+    explicit CD3DX12_PACKED_MIP_INFO(const D3D12_PACKED_MIP_INFO &o) :
+        D3D12_PACKED_MIP_INFO(o)
+    {}
+    CD3DX12_PACKED_MIP_INFO( 
+        UINT8 numStandardMips, 
+        UINT8 numPackedMips, 
+        UINT numTilesForPackedMips, 
+        UINT startTileIndexInOverallResource ) 
+    {
+        NumStandardMips = numStandardMips;
+        NumPackedMips = numPackedMips;
+        NumTilesForPackedMips = numTilesForPackedMips;
+        StartTileIndexInOverallResource = startTileIndexInOverallResource;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_SUBRESOURCE_FOOTPRINT : public D3D12_SUBRESOURCE_FOOTPRINT
+{
+    CD3DX12_SUBRESOURCE_FOOTPRINT() = default;
+    explicit CD3DX12_SUBRESOURCE_FOOTPRINT(const D3D12_SUBRESOURCE_FOOTPRINT &o) :
+        D3D12_SUBRESOURCE_FOOTPRINT(o)
+    {}
+    CD3DX12_SUBRESOURCE_FOOTPRINT( 
+        DXGI_FORMAT format, 
+        UINT width, 
+        UINT height, 
+        UINT depth, 
+        UINT rowPitch ) 
+    {
+        Format = format;
+        Width = width;
+        Height = height;
+        Depth = depth;
+        RowPitch = rowPitch;
+    }
+    explicit CD3DX12_SUBRESOURCE_FOOTPRINT( 
+        const D3D12_RESOURCE_DESC& resDesc, 
+        UINT rowPitch ) 
+    {
+        Format = resDesc.Format;
+        Width = UINT( resDesc.Width );
+        Height = resDesc.Height;
+        Depth = (resDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? resDesc.DepthOrArraySize : 1);
+        RowPitch = rowPitch;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_TEXTURE_COPY_LOCATION : public D3D12_TEXTURE_COPY_LOCATION
+{ 
+    CD3DX12_TEXTURE_COPY_LOCATION() = default;
+    explicit CD3DX12_TEXTURE_COPY_LOCATION(const D3D12_TEXTURE_COPY_LOCATION &o) :
+        D3D12_TEXTURE_COPY_LOCATION(o)
+    {}
+    CD3DX12_TEXTURE_COPY_LOCATION(ID3D12Resource* pRes) { pResource = pRes; }
+    CD3DX12_TEXTURE_COPY_LOCATION(ID3D12Resource* pRes, D3D12_PLACED_SUBRESOURCE_FOOTPRINT const& Footprint)
+    {
+        pResource = pRes;
+        Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+        PlacedFootprint = Footprint;
+    }
+    CD3DX12_TEXTURE_COPY_LOCATION(ID3D12Resource* pRes, UINT Sub)
+    {
+        pResource = pRes;
+        Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+        SubresourceIndex = Sub;
+    }
+}; 
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_DESCRIPTOR_RANGE : public D3D12_DESCRIPTOR_RANGE
+{
+    CD3DX12_DESCRIPTOR_RANGE() = default;
+    explicit CD3DX12_DESCRIPTOR_RANGE(const D3D12_DESCRIPTOR_RANGE &o) :
+        D3D12_DESCRIPTOR_RANGE(o)
+    {}
+    CD3DX12_DESCRIPTOR_RANGE(
+        D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
+        UINT numDescriptors,
+        UINT baseShaderRegister,
+        UINT registerSpace = 0,
+        UINT offsetInDescriptorsFromTableStart =
+        D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
+    {
+        Init(rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart);
+    }
+    
+    inline void Init(
+        D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
+        UINT numDescriptors,
+        UINT baseShaderRegister,
+        UINT registerSpace = 0,
+        UINT offsetInDescriptorsFromTableStart =
+        D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
+    {
+        Init(*this, rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart);
+    }
+    
+    static inline void Init(
+        _Out_ D3D12_DESCRIPTOR_RANGE &range,
+        D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
+        UINT numDescriptors,
+        UINT baseShaderRegister,
+        UINT registerSpace = 0,
+        UINT offsetInDescriptorsFromTableStart =
+        D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
+    {
+        range.RangeType = rangeType;
+        range.NumDescriptors = numDescriptors;
+        range.BaseShaderRegister = baseShaderRegister;
+        range.RegisterSpace = registerSpace;
+        range.OffsetInDescriptorsFromTableStart = offsetInDescriptorsFromTableStart;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_ROOT_DESCRIPTOR_TABLE : public D3D12_ROOT_DESCRIPTOR_TABLE
+{
+    CD3DX12_ROOT_DESCRIPTOR_TABLE() = default;
+    explicit CD3DX12_ROOT_DESCRIPTOR_TABLE(const D3D12_ROOT_DESCRIPTOR_TABLE &o) :
+        D3D12_ROOT_DESCRIPTOR_TABLE(o)
+    {}
+    CD3DX12_ROOT_DESCRIPTOR_TABLE(
+        UINT numDescriptorRanges,
+        _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges)
+    {
+        Init(numDescriptorRanges, _pDescriptorRanges);
+    }
+    
+    inline void Init(
+        UINT numDescriptorRanges,
+        _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges)
+    {
+        Init(*this, numDescriptorRanges, _pDescriptorRanges);
+    }
+    
+    static inline void Init(
+        _Out_ D3D12_ROOT_DESCRIPTOR_TABLE &rootDescriptorTable,
+        UINT numDescriptorRanges,
+        _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges)
+    {
+        rootDescriptorTable.NumDescriptorRanges = numDescriptorRanges;
+        rootDescriptorTable.pDescriptorRanges = _pDescriptorRanges;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_ROOT_CONSTANTS : public D3D12_ROOT_CONSTANTS
+{
+    CD3DX12_ROOT_CONSTANTS() = default;
+    explicit CD3DX12_ROOT_CONSTANTS(const D3D12_ROOT_CONSTANTS &o) :
+        D3D12_ROOT_CONSTANTS(o)
+    {}
+    CD3DX12_ROOT_CONSTANTS(
+        UINT num32BitValues,
+        UINT shaderRegister,
+        UINT registerSpace = 0)
+    {
+        Init(num32BitValues, shaderRegister, registerSpace);
+    }
+    
+    inline void Init(
+        UINT num32BitValues,
+        UINT shaderRegister,
+        UINT registerSpace = 0)
+    {
+        Init(*this, num32BitValues, shaderRegister, registerSpace);
+    }
+    
+    static inline void Init(
+        _Out_ D3D12_ROOT_CONSTANTS &rootConstants,
+        UINT num32BitValues,
+        UINT shaderRegister,
+        UINT registerSpace = 0)
+    {
+        rootConstants.Num32BitValues = num32BitValues;
+        rootConstants.ShaderRegister = shaderRegister;
+        rootConstants.RegisterSpace = registerSpace;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_ROOT_DESCRIPTOR : public D3D12_ROOT_DESCRIPTOR
+{
+    CD3DX12_ROOT_DESCRIPTOR() = default;
+    explicit CD3DX12_ROOT_DESCRIPTOR(const D3D12_ROOT_DESCRIPTOR &o) :
+        D3D12_ROOT_DESCRIPTOR(o)
+    {}
+    CD3DX12_ROOT_DESCRIPTOR(
+        UINT shaderRegister,
+        UINT registerSpace = 0)
+    {
+        Init(shaderRegister, registerSpace);
+    }
+    
+    inline void Init(
+        UINT shaderRegister,
+        UINT registerSpace = 0)
+    {
+        Init(*this, shaderRegister, registerSpace);
+    }
+    
+    static inline void Init(_Out_ D3D12_ROOT_DESCRIPTOR &table, UINT shaderRegister, UINT registerSpace = 0)
+    {
+        table.ShaderRegister = shaderRegister;
+        table.RegisterSpace = registerSpace;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_ROOT_PARAMETER : public D3D12_ROOT_PARAMETER
+{
+    CD3DX12_ROOT_PARAMETER() = default;
+    explicit CD3DX12_ROOT_PARAMETER(const D3D12_ROOT_PARAMETER &o) :
+        D3D12_ROOT_PARAMETER(o)
+    {}
+    
+    static inline void InitAsDescriptorTable(
+        _Out_ D3D12_ROOT_PARAMETER &rootParam,
+        UINT numDescriptorRanges,
+        _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* pDescriptorRanges,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+        rootParam.ShaderVisibility = visibility;
+        CD3DX12_ROOT_DESCRIPTOR_TABLE::Init(rootParam.DescriptorTable, numDescriptorRanges, pDescriptorRanges);
+    }
+
+    static inline void InitAsConstants(
+        _Out_ D3D12_ROOT_PARAMETER &rootParam,
+        UINT num32BitValues,
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
+        rootParam.ShaderVisibility = visibility;
+        CD3DX12_ROOT_CONSTANTS::Init(rootParam.Constants, num32BitValues, shaderRegister, registerSpace);
+    }
+
+    static inline void InitAsConstantBufferView(
+        _Out_ D3D12_ROOT_PARAMETER &rootParam,
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+        rootParam.ShaderVisibility = visibility;
+        CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace);
+    }
+
+    static inline void InitAsShaderResourceView(
+        _Out_ D3D12_ROOT_PARAMETER &rootParam,
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
+        rootParam.ShaderVisibility = visibility;
+        CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace);
+    }
+
+    static inline void InitAsUnorderedAccessView(
+        _Out_ D3D12_ROOT_PARAMETER &rootParam,
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
+        rootParam.ShaderVisibility = visibility;
+        CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace);
+    }
+    
+    inline void InitAsDescriptorTable(
+        UINT numDescriptorRanges,
+        _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* pDescriptorRanges,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        InitAsDescriptorTable(*this, numDescriptorRanges, pDescriptorRanges, visibility);
+    }
+    
+    inline void InitAsConstants(
+        UINT num32BitValues,
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        InitAsConstants(*this, num32BitValues, shaderRegister, registerSpace, visibility);
+    }
+
+    inline void InitAsConstantBufferView(
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        InitAsConstantBufferView(*this, shaderRegister, registerSpace, visibility);
+    }
+
+    inline void InitAsShaderResourceView(
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        InitAsShaderResourceView(*this, shaderRegister, registerSpace, visibility);
+    }
+
+    inline void InitAsUnorderedAccessView(
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        InitAsUnorderedAccessView(*this, shaderRegister, registerSpace, visibility);
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_STATIC_SAMPLER_DESC : public D3D12_STATIC_SAMPLER_DESC
+{
+    CD3DX12_STATIC_SAMPLER_DESC() = default;
+    explicit CD3DX12_STATIC_SAMPLER_DESC(const D3D12_STATIC_SAMPLER_DESC &o) :
+        D3D12_STATIC_SAMPLER_DESC(o)
+    {}
+    CD3DX12_STATIC_SAMPLER_DESC(
+         UINT shaderRegister,
+         D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC,
+         D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
+         D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
+         D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
+         FLOAT mipLODBias = 0,
+         UINT maxAnisotropy = 16,
+         D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL,
+         D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE,
+         FLOAT minLOD = 0.f,
+         FLOAT maxLOD = D3D12_FLOAT32_MAX,
+         D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, 
+         UINT registerSpace = 0)
+    {
+        Init(
+            shaderRegister,
+            filter,
+            addressU,
+            addressV,
+            addressW,
+            mipLODBias,
+            maxAnisotropy,
+            comparisonFunc,
+            borderColor,
+            minLOD,
+            maxLOD,
+            shaderVisibility,
+            registerSpace);
+    }
+    
+    static inline void Init(
+        _Out_ D3D12_STATIC_SAMPLER_DESC &samplerDesc,
+         UINT shaderRegister,
+         D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC,
+         D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
+         D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
+         D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
+         FLOAT mipLODBias = 0,
+         UINT maxAnisotropy = 16,
+         D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL,
+         D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE,
+         FLOAT minLOD = 0.f,
+         FLOAT maxLOD = D3D12_FLOAT32_MAX,
+         D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, 
+         UINT registerSpace = 0)
+    {
+        samplerDesc.ShaderRegister = shaderRegister;
+        samplerDesc.Filter = filter;
+        samplerDesc.AddressU = addressU;
+        samplerDesc.AddressV = addressV;
+        samplerDesc.AddressW = addressW;
+        samplerDesc.MipLODBias = mipLODBias;
+        samplerDesc.MaxAnisotropy = maxAnisotropy;
+        samplerDesc.ComparisonFunc = comparisonFunc;
+        samplerDesc.BorderColor = borderColor;
+        samplerDesc.MinLOD = minLOD;
+        samplerDesc.MaxLOD = maxLOD;
+        samplerDesc.ShaderVisibility = shaderVisibility;
+        samplerDesc.RegisterSpace = registerSpace;
+    }
+    inline void Init(
+         UINT shaderRegister,
+         D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC,
+         D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
+         D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
+         D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
+         FLOAT mipLODBias = 0,
+         UINT maxAnisotropy = 16,
+         D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL,
+         D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE,
+         FLOAT minLOD = 0.f,
+         FLOAT maxLOD = D3D12_FLOAT32_MAX,
+         D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, 
+         UINT registerSpace = 0)
+    {
+        Init(
+            *this,
+            shaderRegister,
+            filter,
+            addressU,
+            addressV,
+            addressW,
+            mipLODBias,
+            maxAnisotropy,
+            comparisonFunc,
+            borderColor,
+            minLOD,
+            maxLOD,
+            shaderVisibility,
+            registerSpace);
+    }
+    
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_ROOT_SIGNATURE_DESC : public D3D12_ROOT_SIGNATURE_DESC
+{
+    CD3DX12_ROOT_SIGNATURE_DESC() = default;
+    explicit CD3DX12_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC &o) :
+        D3D12_ROOT_SIGNATURE_DESC(o)
+    {}
+    CD3DX12_ROOT_SIGNATURE_DESC(
+        UINT numParameters,
+        _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters,
+        UINT numStaticSamplers = 0,
+        _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL,
+        D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE)
+    {
+        Init(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags);
+    }
+    CD3DX12_ROOT_SIGNATURE_DESC(CD3DX12_DEFAULT)
+    {
+        Init(0, NULL, 0, NULL, D3D12_ROOT_SIGNATURE_FLAG_NONE);
+    }
+    
+    inline void Init(
+        UINT numParameters,
+        _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters,
+        UINT numStaticSamplers = 0,
+        _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL,
+        D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE)
+    {
+        Init(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags);
+    }
+
+    static inline void Init(
+        _Out_ D3D12_ROOT_SIGNATURE_DESC &desc,
+        UINT numParameters,
+        _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters,
+        UINT numStaticSamplers = 0,
+        _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL,
+        D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE)
+    {
+        desc.NumParameters = numParameters;
+        desc.pParameters = _pParameters;
+        desc.NumStaticSamplers = numStaticSamplers;
+        desc.pStaticSamplers = _pStaticSamplers;
+        desc.Flags = flags;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_DESCRIPTOR_RANGE1 : public D3D12_DESCRIPTOR_RANGE1
+{
+    CD3DX12_DESCRIPTOR_RANGE1() = default;
+    explicit CD3DX12_DESCRIPTOR_RANGE1(const D3D12_DESCRIPTOR_RANGE1 &o) :
+        D3D12_DESCRIPTOR_RANGE1(o)
+    {}
+    CD3DX12_DESCRIPTOR_RANGE1(
+        D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
+        UINT numDescriptors,
+        UINT baseShaderRegister,
+        UINT registerSpace = 0,
+        D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE,
+        UINT offsetInDescriptorsFromTableStart =
+        D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
+    {
+        Init(rangeType, numDescriptors, baseShaderRegister, registerSpace, flags, offsetInDescriptorsFromTableStart);
+    }
+    
+    inline void Init(
+        D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
+        UINT numDescriptors,
+        UINT baseShaderRegister,
+        UINT registerSpace = 0,
+        D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE,
+        UINT offsetInDescriptorsFromTableStart =
+        D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
+    {
+        Init(*this, rangeType, numDescriptors, baseShaderRegister, registerSpace, flags, offsetInDescriptorsFromTableStart);
+    }
+    
+    static inline void Init(
+        _Out_ D3D12_DESCRIPTOR_RANGE1 &range,
+        D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
+        UINT numDescriptors,
+        UINT baseShaderRegister,
+        UINT registerSpace = 0,
+        D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE,
+        UINT offsetInDescriptorsFromTableStart =
+        D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
+    {
+        range.RangeType = rangeType;
+        range.NumDescriptors = numDescriptors;
+        range.BaseShaderRegister = baseShaderRegister;
+        range.RegisterSpace = registerSpace;
+        range.Flags = flags;
+        range.OffsetInDescriptorsFromTableStart = offsetInDescriptorsFromTableStart;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_ROOT_DESCRIPTOR_TABLE1 : public D3D12_ROOT_DESCRIPTOR_TABLE1
+{
+    CD3DX12_ROOT_DESCRIPTOR_TABLE1() = default;
+    explicit CD3DX12_ROOT_DESCRIPTOR_TABLE1(const D3D12_ROOT_DESCRIPTOR_TABLE1 &o) :
+        D3D12_ROOT_DESCRIPTOR_TABLE1(o)
+    {}
+    CD3DX12_ROOT_DESCRIPTOR_TABLE1(
+        UINT numDescriptorRanges,
+        _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges)
+    {
+        Init(numDescriptorRanges, _pDescriptorRanges);
+    }
+    
+    inline void Init(
+        UINT numDescriptorRanges,
+        _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges)
+    {
+        Init(*this, numDescriptorRanges, _pDescriptorRanges);
+    }
+    
+    static inline void Init(
+        _Out_ D3D12_ROOT_DESCRIPTOR_TABLE1 &rootDescriptorTable,
+        UINT numDescriptorRanges,
+        _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges)
+    {
+        rootDescriptorTable.NumDescriptorRanges = numDescriptorRanges;
+        rootDescriptorTable.pDescriptorRanges = _pDescriptorRanges;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_ROOT_DESCRIPTOR1 : public D3D12_ROOT_DESCRIPTOR1
+{
+    CD3DX12_ROOT_DESCRIPTOR1() = default;
+    explicit CD3DX12_ROOT_DESCRIPTOR1(const D3D12_ROOT_DESCRIPTOR1 &o) :
+        D3D12_ROOT_DESCRIPTOR1(o)
+    {}
+    CD3DX12_ROOT_DESCRIPTOR1(
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE)
+    {
+        Init(shaderRegister, registerSpace, flags);
+    }
+    
+    inline void Init(
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE)
+    {
+        Init(*this, shaderRegister, registerSpace, flags);
+    }
+    
+    static inline void Init(
+        _Out_ D3D12_ROOT_DESCRIPTOR1 &table, 
+        UINT shaderRegister, 
+        UINT registerSpace = 0, 
+        D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE)
+    {
+        table.ShaderRegister = shaderRegister;
+        table.RegisterSpace = registerSpace;
+        table.Flags = flags;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_ROOT_PARAMETER1 : public D3D12_ROOT_PARAMETER1
+{
+    CD3DX12_ROOT_PARAMETER1() = default;
+    explicit CD3DX12_ROOT_PARAMETER1(const D3D12_ROOT_PARAMETER1 &o) :
+        D3D12_ROOT_PARAMETER1(o)
+    {}
+    
+    static inline void InitAsDescriptorTable(
+        _Out_ D3D12_ROOT_PARAMETER1 &rootParam,
+        UINT numDescriptorRanges,
+        _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* pDescriptorRanges,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+        rootParam.ShaderVisibility = visibility;
+        CD3DX12_ROOT_DESCRIPTOR_TABLE1::Init(rootParam.DescriptorTable, numDescriptorRanges, pDescriptorRanges);
+    }
+
+    static inline void InitAsConstants(
+        _Out_ D3D12_ROOT_PARAMETER1 &rootParam,
+        UINT num32BitValues,
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
+        rootParam.ShaderVisibility = visibility;
+        CD3DX12_ROOT_CONSTANTS::Init(rootParam.Constants, num32BitValues, shaderRegister, registerSpace);
+    }
+
+    static inline void InitAsConstantBufferView(
+        _Out_ D3D12_ROOT_PARAMETER1 &rootParam,
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+        rootParam.ShaderVisibility = visibility;
+        CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags);
+    }
+
+    static inline void InitAsShaderResourceView(
+        _Out_ D3D12_ROOT_PARAMETER1 &rootParam,
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
+        rootParam.ShaderVisibility = visibility;
+        CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags);
+    }
+
+    static inline void InitAsUnorderedAccessView(
+        _Out_ D3D12_ROOT_PARAMETER1 &rootParam,
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
+        rootParam.ShaderVisibility = visibility;
+        CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags);
+    }
+    
+    inline void InitAsDescriptorTable(
+        UINT numDescriptorRanges,
+        _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* pDescriptorRanges,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        InitAsDescriptorTable(*this, numDescriptorRanges, pDescriptorRanges, visibility);
+    }
+    
+    inline void InitAsConstants(
+        UINT num32BitValues,
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        InitAsConstants(*this, num32BitValues, shaderRegister, registerSpace, visibility);
+    }
+
+    inline void InitAsConstantBufferView(
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        InitAsConstantBufferView(*this, shaderRegister, registerSpace, flags, visibility);
+    }
+
+    inline void InitAsShaderResourceView(
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        InitAsShaderResourceView(*this, shaderRegister, registerSpace, flags, visibility);
+    }
+
+    inline void InitAsUnorderedAccessView(
+        UINT shaderRegister,
+        UINT registerSpace = 0,
+        D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE,
+        D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
+    {
+        InitAsUnorderedAccessView(*this, shaderRegister, registerSpace, flags, visibility);
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC : public D3D12_VERSIONED_ROOT_SIGNATURE_DESC
+{
+    CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC() = default;
+    explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_VERSIONED_ROOT_SIGNATURE_DESC &o) :
+        D3D12_VERSIONED_ROOT_SIGNATURE_DESC(o)
+    {}
+    explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC &o)
+    {
+        Version = D3D_ROOT_SIGNATURE_VERSION_1_0;
+        Desc_1_0 = o;
+    }
+    explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC1 &o)
+    {
+        Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
+        Desc_1_1 = o;
+    }
+    CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(
+        UINT numParameters,
+        _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters,
+        UINT numStaticSamplers = 0,
+        _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL,
+        D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE)
+    {
+        Init_1_0(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags);
+    }
+    CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(
+        UINT numParameters,
+        _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters,
+        UINT numStaticSamplers = 0,
+        _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL,
+        D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE)
+    {
+        Init_1_1(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags);
+    }
+    CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(CD3DX12_DEFAULT)
+    {
+        Init_1_1(0, NULL, 0, NULL, D3D12_ROOT_SIGNATURE_FLAG_NONE);
+    }
+    
+    inline void Init_1_0(
+        UINT numParameters,
+        _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters,
+        UINT numStaticSamplers = 0,
+        _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL,
+        D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE)
+    {
+        Init_1_0(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags);
+    }
+
+    static inline void Init_1_0(
+        _Out_ D3D12_VERSIONED_ROOT_SIGNATURE_DESC &desc,
+        UINT numParameters,
+        _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters,
+        UINT numStaticSamplers = 0,
+        _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL,
+        D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE)
+    {
+        desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_0;
+        desc.Desc_1_0.NumParameters = numParameters;
+        desc.Desc_1_0.pParameters = _pParameters;
+        desc.Desc_1_0.NumStaticSamplers = numStaticSamplers;
+        desc.Desc_1_0.pStaticSamplers = _pStaticSamplers;
+        desc.Desc_1_0.Flags = flags;
+    }
+
+    inline void Init_1_1(
+        UINT numParameters,
+        _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters,
+        UINT numStaticSamplers = 0,
+        _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL,
+        D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE)
+    {
+        Init_1_1(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags);
+    }
+
+    static inline void Init_1_1(
+        _Out_ D3D12_VERSIONED_ROOT_SIGNATURE_DESC &desc,
+        UINT numParameters,
+        _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters,
+        UINT numStaticSamplers = 0,
+        _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL,
+        D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE)
+    {
+        desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
+        desc.Desc_1_1.NumParameters = numParameters;
+        desc.Desc_1_1.pParameters = _pParameters;
+        desc.Desc_1_1.NumStaticSamplers = numStaticSamplers;
+        desc.Desc_1_1.pStaticSamplers = _pStaticSamplers;
+        desc.Desc_1_1.Flags = flags;
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_CPU_DESCRIPTOR_HANDLE : public D3D12_CPU_DESCRIPTOR_HANDLE
+{
+    CD3DX12_CPU_DESCRIPTOR_HANDLE() = default;
+    explicit CD3DX12_CPU_DESCRIPTOR_HANDLE(const D3D12_CPU_DESCRIPTOR_HANDLE &o) :
+        D3D12_CPU_DESCRIPTOR_HANDLE(o)
+    {}
+    CD3DX12_CPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT) { ptr = 0; }
+    CD3DX12_CPU_DESCRIPTOR_HANDLE(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &other, INT offsetScaledByIncrementSize)
+    {
+        InitOffsetted(other, offsetScaledByIncrementSize);
+    }
+    CD3DX12_CPU_DESCRIPTOR_HANDLE(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &other, INT offsetInDescriptors, UINT descriptorIncrementSize)
+    {
+        InitOffsetted(other, offsetInDescriptors, descriptorIncrementSize);
+    }
+    CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetInDescriptors, UINT descriptorIncrementSize)
+    { 
+        ptr += INT64(offsetInDescriptors) * UINT64(descriptorIncrementSize);
+        return *this;
+    }
+    CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetScaledByIncrementSize) 
+    { 
+        ptr += offsetScaledByIncrementSize;
+        return *this;
+    }
+    bool operator==(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other) const
+    {
+        return (ptr == other.ptr);
+    }
+    bool operator!=(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other) const
+    {
+        return (ptr != other.ptr);
+    }
+    CD3DX12_CPU_DESCRIPTOR_HANDLE &operator=(const D3D12_CPU_DESCRIPTOR_HANDLE &other)
+    {
+        ptr = other.ptr;
+        return *this;
+    }
+
+    inline void InitOffsetted(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize)
+    {
+        InitOffsetted(*this, base, offsetScaledByIncrementSize);
+    }
+    
+    inline void InitOffsetted(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize)
+    {
+        InitOffsetted(*this, base, offsetInDescriptors, descriptorIncrementSize);
+    }
+    
+    static inline void InitOffsetted(_Out_ D3D12_CPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize)
+    {
+        handle.ptr = base.ptr + offsetScaledByIncrementSize;
+    }
+    
+    static inline void InitOffsetted(_Out_ D3D12_CPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize)
+    {
+        handle.ptr = base.ptr + INT64(offsetInDescriptors) * UINT64(descriptorIncrementSize);
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_GPU_DESCRIPTOR_HANDLE : public D3D12_GPU_DESCRIPTOR_HANDLE
+{
+    CD3DX12_GPU_DESCRIPTOR_HANDLE() = default;
+    explicit CD3DX12_GPU_DESCRIPTOR_HANDLE(const D3D12_GPU_DESCRIPTOR_HANDLE &o) :
+        D3D12_GPU_DESCRIPTOR_HANDLE(o)
+    {}
+    CD3DX12_GPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT) { ptr = 0; }
+    CD3DX12_GPU_DESCRIPTOR_HANDLE(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &other, INT offsetScaledByIncrementSize)
+    {
+        InitOffsetted(other, offsetScaledByIncrementSize);
+    }
+    CD3DX12_GPU_DESCRIPTOR_HANDLE(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &other, INT offsetInDescriptors, UINT descriptorIncrementSize)
+    {
+        InitOffsetted(other, offsetInDescriptors, descriptorIncrementSize);
+    }
+    CD3DX12_GPU_DESCRIPTOR_HANDLE& Offset(INT offsetInDescriptors, UINT descriptorIncrementSize)
+    { 
+        ptr += INT64(offsetInDescriptors) * UINT64(descriptorIncrementSize);
+        return *this;
+    }
+    CD3DX12_GPU_DESCRIPTOR_HANDLE& Offset(INT offsetScaledByIncrementSize) 
+    { 
+        ptr += offsetScaledByIncrementSize;
+        return *this;
+    }
+    inline bool operator==(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE& other) const
+    {
+        return (ptr == other.ptr);
+    }
+    inline bool operator!=(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE& other) const
+    {
+        return (ptr != other.ptr);
+    }
+    CD3DX12_GPU_DESCRIPTOR_HANDLE &operator=(const D3D12_GPU_DESCRIPTOR_HANDLE &other)
+    {
+        ptr = other.ptr;
+        return *this;
+    }
+
+    inline void InitOffsetted(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize)
+    {
+        InitOffsetted(*this, base, offsetScaledByIncrementSize);
+    }
+    
+    inline void InitOffsetted(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize)
+    {
+        InitOffsetted(*this, base, offsetInDescriptors, descriptorIncrementSize);
+    }
+    
+    static inline void InitOffsetted(_Out_ D3D12_GPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize)
+    {
+        handle.ptr = base.ptr + offsetScaledByIncrementSize;
+    }
+    
+    static inline void InitOffsetted(_Out_ D3D12_GPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize)
+    {
+        handle.ptr = base.ptr + INT64(offsetInDescriptors) * UINT64(descriptorIncrementSize);
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+inline UINT D3D12CalcSubresource( UINT MipSlice, UINT ArraySlice, UINT PlaneSlice, UINT MipLevels, UINT ArraySize )
+{ 
+    return MipSlice + ArraySlice * MipLevels + PlaneSlice * MipLevels * ArraySize; 
+}
+
+//------------------------------------------------------------------------------------------------
+template <typename T, typename U, typename V>
+inline void D3D12DecomposeSubresource( UINT Subresource, UINT MipLevels, UINT ArraySize, _Out_ T& MipSlice, _Out_ U& ArraySlice, _Out_ V& PlaneSlice )
+{
+    MipSlice = static_cast<T>(Subresource % MipLevels);
+    ArraySlice = static_cast<U>((Subresource / MipLevels) % ArraySize);
+    PlaneSlice = static_cast<V>(Subresource / (MipLevels * ArraySize));
+}
+
+//------------------------------------------------------------------------------------------------
+inline UINT8 D3D12GetFormatPlaneCount(
+    _In_ ID3D12Device* pDevice,
+    DXGI_FORMAT Format
+    )
+{
+    D3D12_FEATURE_DATA_FORMAT_INFO formatInfo = {Format};
+    if (FAILED(pDevice->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &formatInfo, sizeof(formatInfo))))
+    {
+        return 0;
+    }
+    return formatInfo.PlaneCount;
+}
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_RESOURCE_DESC : public D3D12_RESOURCE_DESC
+{
+    CD3DX12_RESOURCE_DESC() = default;
+    explicit CD3DX12_RESOURCE_DESC( const D3D12_RESOURCE_DESC& o ) :
+        D3D12_RESOURCE_DESC( o )
+    {}
+    CD3DX12_RESOURCE_DESC( 
+        D3D12_RESOURCE_DIMENSION dimension,
+        UINT64 alignment,
+        UINT64 width,
+        UINT height,
+        UINT16 depthOrArraySize,
+        UINT16 mipLevels,
+        DXGI_FORMAT format,
+        UINT sampleCount,
+        UINT sampleQuality,
+        D3D12_TEXTURE_LAYOUT layout,
+        D3D12_RESOURCE_FLAGS flags )
+    {
+        Dimension = dimension;
+        Alignment = alignment;
+        Width = width;
+        Height = height;
+        DepthOrArraySize = depthOrArraySize;
+        MipLevels = mipLevels;
+        Format = format;
+        SampleDesc.Count = sampleCount;
+        SampleDesc.Quality = sampleQuality;
+        Layout = layout;
+        Flags = flags;
+    }
+    static inline CD3DX12_RESOURCE_DESC Buffer( 
+        const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo,
+        D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE )
+    {
+        return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_BUFFER, resAllocInfo.Alignment, resAllocInfo.SizeInBytes, 
+            1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags );
+    }
+    static inline CD3DX12_RESOURCE_DESC Buffer( 
+        UINT64 width,
+        D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE,
+        UINT64 alignment = 0 )
+    {
+        return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_BUFFER, alignment, width, 1, 1, 1, 
+            DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags );
+    }
+    static inline CD3DX12_RESOURCE_DESC Tex1D( 
+        DXGI_FORMAT format,
+        UINT64 width,
+        UINT16 arraySize = 1,
+        UINT16 mipLevels = 0,
+        D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE,
+        D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN,
+        UINT64 alignment = 0 )
+    {
+        return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE1D, alignment, width, 1, arraySize, 
+            mipLevels, format, 1, 0, layout, flags );
+    }
+    static inline CD3DX12_RESOURCE_DESC Tex2D( 
+        DXGI_FORMAT format,
+        UINT64 width,
+        UINT height,
+        UINT16 arraySize = 1,
+        UINT16 mipLevels = 0,
+        UINT sampleCount = 1,
+        UINT sampleQuality = 0,
+        D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE,
+        D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN,
+        UINT64 alignment = 0 )
+    {
+        return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE2D, alignment, width, height, arraySize, 
+            mipLevels, format, sampleCount, sampleQuality, layout, flags );
+    }
+    static inline CD3DX12_RESOURCE_DESC Tex3D( 
+        DXGI_FORMAT format,
+        UINT64 width,
+        UINT height,
+        UINT16 depth,
+        UINT16 mipLevels = 0,
+        D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE,
+        D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN,
+        UINT64 alignment = 0 )
+    {
+        return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE3D, alignment, width, height, depth, 
+            mipLevels, format, 1, 0, layout, flags );
+    }
+    inline UINT16 Depth() const
+    { return (Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1); }
+    inline UINT16 ArraySize() const
+    { return (Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1); }
+    inline UINT8 PlaneCount(_In_ ID3D12Device* pDevice) const
+    { return D3D12GetFormatPlaneCount(pDevice, Format); }
+    inline UINT Subresources(_In_ ID3D12Device* pDevice) const
+    { return MipLevels * ArraySize() * PlaneCount(pDevice); }
+    inline UINT CalcSubresource(UINT MipSlice, UINT ArraySlice, UINT PlaneSlice)
+    { return D3D12CalcSubresource(MipSlice, ArraySlice, PlaneSlice, MipLevels, ArraySize()); }
+};
+inline bool operator==( const D3D12_RESOURCE_DESC& l, const D3D12_RESOURCE_DESC& r )
+{
+    return l.Dimension == r.Dimension &&
+        l.Alignment == r.Alignment &&
+        l.Width == r.Width &&
+        l.Height == r.Height &&
+        l.DepthOrArraySize == r.DepthOrArraySize &&
+        l.MipLevels == r.MipLevels &&
+        l.Format == r.Format &&
+        l.SampleDesc.Count == r.SampleDesc.Count &&
+        l.SampleDesc.Quality == r.SampleDesc.Quality &&
+        l.Layout == r.Layout &&
+        l.Flags == r.Flags;
+}
+inline bool operator!=( const D3D12_RESOURCE_DESC& l, const D3D12_RESOURCE_DESC& r )
+{ return !( l == r ); }
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_VIEW_INSTANCING_DESC : public D3D12_VIEW_INSTANCING_DESC
+{
+    CD3DX12_VIEW_INSTANCING_DESC() = default;
+    explicit CD3DX12_VIEW_INSTANCING_DESC( const D3D12_VIEW_INSTANCING_DESC& o ) :
+        D3D12_VIEW_INSTANCING_DESC( o )
+    {}
+    explicit CD3DX12_VIEW_INSTANCING_DESC( CD3DX12_DEFAULT )
+    {
+        ViewInstanceCount = 0;
+        pViewInstanceLocations = nullptr;
+        Flags = D3D12_VIEW_INSTANCING_FLAG_NONE;
+    }
+    explicit CD3DX12_VIEW_INSTANCING_DESC( 
+        UINT InViewInstanceCount,
+        const D3D12_VIEW_INSTANCE_LOCATION* InViewInstanceLocations,
+        D3D12_VIEW_INSTANCING_FLAGS InFlags)
+    {
+        ViewInstanceCount = InViewInstanceCount;
+        pViewInstanceLocations = InViewInstanceLocations;
+        Flags = InFlags;
+    }
+    ~CD3DX12_VIEW_INSTANCING_DESC() {}
+};
+
+//------------------------------------------------------------------------------------------------
+// Row-by-row memcpy
+inline void MemcpySubresource(
+    _In_ const D3D12_MEMCPY_DEST* pDest,
+    _In_ const D3D12_SUBRESOURCE_DATA* pSrc,
+    SIZE_T RowSizeInBytes,
+    UINT NumRows,
+    UINT NumSlices)
+{
+    for (UINT z = 0; z < NumSlices; ++z)
+    {
+        BYTE* pDestSlice = reinterpret_cast<BYTE*>(pDest->pData) + pDest->SlicePitch * z;
+        const BYTE* pSrcSlice = reinterpret_cast<const BYTE*>(pSrc->pData) + pSrc->SlicePitch * z;
+        for (UINT y = 0; y < NumRows; ++y)
+        {
+            memcpy(pDestSlice + pDest->RowPitch * y,
+                   pSrcSlice + pSrc->RowPitch * y,
+                   RowSizeInBytes);
+        }
+    }
+}
+
+//------------------------------------------------------------------------------------------------
+// Returns required size of a buffer to be used for data upload
+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;
+    pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast<void**>(&pDevice));
+    pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, 0, nullptr, nullptr, nullptr, &RequiredSize);
+    pDevice->Release();
+    
+    return RequiredSize;
+}
+
+//------------------------------------------------------------------------------------------------
+// All arrays must be populated (e.g. by calling GetCopyableFootprints)
+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<void**>(&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, SIZE_T(pLayouts[i].Footprint.RowPitch) * SIZE_T(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)
+    {
+        pCmdList->CopyBufferRegion(
+            pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width);
+    }
+    else
+    {
+        for (UINT i = 0; i < NumSubresources; ++i)
+        {
+            CD3DX12_TEXTURE_COPY_LOCATION Dst(pDestinationResource, i + FirstSubresource);
+            CD3DX12_TEXTURE_COPY_LOCATION Src(pIntermediate, pLayouts[i]);
+            pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr);
+        }
+    }
+    return RequiredSize;
+}
+
+//------------------------------------------------------------------------------------------------
+// Heap-allocating UpdateSubresources implementation
+inline UINT64 UpdateSubresources( 
+    _In_ ID3D12GraphicsCommandList* pCmdList,
+    _In_ ID3D12Resource* pDestinationResource,
+    _In_ ID3D12Resource* pIntermediate,
+    UINT64 IntermediateOffset,
+    _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource,
+    _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources,
+    _In_reads_(NumSubresources) D3D12_SUBRESOURCE_DATA* pSrcData)
+{
+    UINT64 RequiredSize = 0;
+    UINT64 MemToAlloc = static_cast<UINT64>(sizeof(D3D12_PLACED_SUBRESOURCE_FOOTPRINT) + sizeof(UINT) + sizeof(UINT64)) * NumSubresources;
+    if (MemToAlloc > SIZE_MAX)
+    {
+       return 0;
+    }
+    void* pMem = HeapAlloc(GetProcessHeap(), 0, static_cast<SIZE_T>(MemToAlloc));
+    if (pMem == NULL)
+    {
+       return 0;
+    }
+    D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts = reinterpret_cast<D3D12_PLACED_SUBRESOURCE_FOOTPRINT*>(pMem);
+    UINT64* pRowSizesInBytes = reinterpret_cast<UINT64*>(pLayouts + NumSubresources);
+    UINT* pNumRows = reinterpret_cast<UINT*>(pRowSizesInBytes + NumSubresources);
+    
+    D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc();
+    ID3D12Device* pDevice;
+    pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast<void**>(&pDevice));
+    pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, pLayouts, pNumRows, pRowSizesInBytes, &RequiredSize);
+    pDevice->Release();
+    
+    UINT64 Result = UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, pLayouts, pNumRows, pRowSizesInBytes, pSrcData);
+    HeapFree(GetProcessHeap(), 0, pMem);
+    return Result;
+}
+
+//------------------------------------------------------------------------------------------------
+// Stack-allocating UpdateSubresources implementation
+template <UINT MaxSubresources>
+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;
+    pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast<void**>(&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);
+}
+
+//------------------------------------------------------------------------------------------------
+inline bool D3D12IsLayoutOpaque( D3D12_TEXTURE_LAYOUT Layout )
+{ return Layout == D3D12_TEXTURE_LAYOUT_UNKNOWN || Layout == D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE; }
+
+//------------------------------------------------------------------------------------------------
+template <typename t_CommandListType>
+inline ID3D12CommandList * const * CommandListCast(t_CommandListType * const * pp)
+{
+    // This cast is useful for passing strongly typed command list pointers into
+    // ExecuteCommandLists.
+    // This cast is valid as long as the const-ness is respected. D3D12 APIs do
+    // respect the const-ness of their arguments.
+    return reinterpret_cast<ID3D12CommandList * const *>(pp);
+}
+
+//------------------------------------------------------------------------------------------------
+// D3D12 exports a new method for serializing root signatures in the Windows 10 Anniversary Update.
+// To help enable root signature 1.1 features when they are available and not require maintaining
+// two code paths for building root signatures, this helper method reconstructs a 1.0 signature when
+// 1.1 is not supported.
+inline HRESULT D3DX12SerializeVersionedRootSignature(
+    _In_ const D3D12_VERSIONED_ROOT_SIGNATURE_DESC* pRootSignatureDesc,
+    D3D_ROOT_SIGNATURE_VERSION MaxVersion,
+    _Outptr_ ID3DBlob** ppBlob,
+    _Always_(_Outptr_opt_result_maybenull_) ID3DBlob** ppErrorBlob)
+{
+    if (ppErrorBlob != NULL)
+    {
+        *ppErrorBlob = NULL;
+    }
+
+    switch (MaxVersion)
+    {
+        case D3D_ROOT_SIGNATURE_VERSION_1_0:
+            switch (pRootSignatureDesc->Version)
+            {
+                case D3D_ROOT_SIGNATURE_VERSION_1_0:
+                    return D3D12SerializeRootSignature(&pRootSignatureDesc->Desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob);
+
+                case D3D_ROOT_SIGNATURE_VERSION_1_1:
+                {
+                    HRESULT hr = S_OK;
+                    const D3D12_ROOT_SIGNATURE_DESC1& desc_1_1 = pRootSignatureDesc->Desc_1_1;
+
+                    const SIZE_T ParametersSize = sizeof(D3D12_ROOT_PARAMETER) * desc_1_1.NumParameters;
+                    void* pParameters = (ParametersSize > 0) ? HeapAlloc(GetProcessHeap(), 0, ParametersSize) : NULL;
+                    if (ParametersSize > 0 && pParameters == NULL)
+                    {
+                        hr = E_OUTOFMEMORY;
+                    }
+                    D3D12_ROOT_PARAMETER* pParameters_1_0 = reinterpret_cast<D3D12_ROOT_PARAMETER*>(pParameters);
+
+                    if (SUCCEEDED(hr))
+                    {
+                        for (UINT n = 0; n < desc_1_1.NumParameters; n++)
+                        {
+                            __analysis_assume(ParametersSize == sizeof(D3D12_ROOT_PARAMETER) * desc_1_1.NumParameters);
+                            pParameters_1_0[n].ParameterType = desc_1_1.pParameters[n].ParameterType;
+                            pParameters_1_0[n].ShaderVisibility = desc_1_1.pParameters[n].ShaderVisibility;
+
+                            switch (desc_1_1.pParameters[n].ParameterType)
+                            {
+                            case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS:
+                                pParameters_1_0[n].Constants.Num32BitValues = desc_1_1.pParameters[n].Constants.Num32BitValues;
+                                pParameters_1_0[n].Constants.RegisterSpace = desc_1_1.pParameters[n].Constants.RegisterSpace;
+                                pParameters_1_0[n].Constants.ShaderRegister = desc_1_1.pParameters[n].Constants.ShaderRegister;
+                                break;
+
+                            case D3D12_ROOT_PARAMETER_TYPE_CBV:
+                            case D3D12_ROOT_PARAMETER_TYPE_SRV:
+                            case D3D12_ROOT_PARAMETER_TYPE_UAV:
+                                pParameters_1_0[n].Descriptor.RegisterSpace = desc_1_1.pParameters[n].Descriptor.RegisterSpace;
+                                pParameters_1_0[n].Descriptor.ShaderRegister = desc_1_1.pParameters[n].Descriptor.ShaderRegister;
+                                break;
+
+                            case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE:
+                                const D3D12_ROOT_DESCRIPTOR_TABLE1& table_1_1 = desc_1_1.pParameters[n].DescriptorTable;
+
+                                const SIZE_T DescriptorRangesSize = sizeof(D3D12_DESCRIPTOR_RANGE) * table_1_1.NumDescriptorRanges;
+                                void* pDescriptorRanges = (DescriptorRangesSize > 0 && SUCCEEDED(hr)) ? HeapAlloc(GetProcessHeap(), 0, DescriptorRangesSize) : NULL;
+                                if (DescriptorRangesSize > 0 && pDescriptorRanges == NULL)
+                                {
+                                    hr = E_OUTOFMEMORY;
+                                }
+                                D3D12_DESCRIPTOR_RANGE* pDescriptorRanges_1_0 = reinterpret_cast<D3D12_DESCRIPTOR_RANGE*>(pDescriptorRanges);
+
+                                if (SUCCEEDED(hr))
+                                {
+                                    for (UINT x = 0; x < table_1_1.NumDescriptorRanges; x++)
+                                    {
+                                        __analysis_assume(DescriptorRangesSize == sizeof(D3D12_DESCRIPTOR_RANGE) * table_1_1.NumDescriptorRanges);
+                                        pDescriptorRanges_1_0[x].BaseShaderRegister = table_1_1.pDescriptorRanges[x].BaseShaderRegister;
+                                        pDescriptorRanges_1_0[x].NumDescriptors = table_1_1.pDescriptorRanges[x].NumDescriptors;
+                                        pDescriptorRanges_1_0[x].OffsetInDescriptorsFromTableStart = table_1_1.pDescriptorRanges[x].OffsetInDescriptorsFromTableStart;
+                                        pDescriptorRanges_1_0[x].RangeType = table_1_1.pDescriptorRanges[x].RangeType;
+                                        pDescriptorRanges_1_0[x].RegisterSpace = table_1_1.pDescriptorRanges[x].RegisterSpace;
+                                    }
+                                }
+
+                                D3D12_ROOT_DESCRIPTOR_TABLE& table_1_0 = pParameters_1_0[n].DescriptorTable;
+                                table_1_0.NumDescriptorRanges = table_1_1.NumDescriptorRanges;
+                                table_1_0.pDescriptorRanges = pDescriptorRanges_1_0;
+                            }
+                        }
+                    }
+
+                    if (SUCCEEDED(hr))
+                    {
+                        CD3DX12_ROOT_SIGNATURE_DESC desc_1_0(desc_1_1.NumParameters, pParameters_1_0, desc_1_1.NumStaticSamplers, desc_1_1.pStaticSamplers, desc_1_1.Flags);
+                        hr = D3D12SerializeRootSignature(&desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob);
+                    }
+
+                    if (pParameters)
+                    {
+                        for (UINT n = 0; n < desc_1_1.NumParameters; n++)
+                        {
+                            if (desc_1_1.pParameters[n].ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
+                            {
+                                HeapFree(GetProcessHeap(), 0, reinterpret_cast<void*>(const_cast<D3D12_DESCRIPTOR_RANGE*>(pParameters_1_0[n].DescriptorTable.pDescriptorRanges)));
+                            }
+                        }
+                        HeapFree(GetProcessHeap(), 0, pParameters);
+                    }
+                    return hr;
+                }
+            }
+            break;
+
+        case D3D_ROOT_SIGNATURE_VERSION_1_1:
+            return D3D12SerializeVersionedRootSignature(pRootSignatureDesc, ppBlob, ppErrorBlob);
+    }
+
+    return E_INVALIDARG;
+}
+
+//------------------------------------------------------------------------------------------------
+struct CD3DX12_RT_FORMAT_ARRAY : public D3D12_RT_FORMAT_ARRAY
+{
+    CD3DX12_RT_FORMAT_ARRAY() = default;
+    explicit CD3DX12_RT_FORMAT_ARRAY(const D3D12_RT_FORMAT_ARRAY& o)
+        : D3D12_RT_FORMAT_ARRAY(o)
+    {}
+    explicit CD3DX12_RT_FORMAT_ARRAY(const DXGI_FORMAT* pFormats, UINT NumFormats)
+    {
+        NumRenderTargets = NumFormats;
+        memcpy(RTFormats, pFormats, sizeof(RTFormats));
+        // assumes ARRAY_SIZE(pFormats) == ARRAY_SIZE(RTFormats)
+    }
+};
+
+//------------------------------------------------------------------------------------------------
+// Pipeline State Stream Helpers
+//------------------------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------------------------
+// Stream Subobjects, i.e. elements of a stream
+
+struct DefaultSampleMask { operator UINT() { return UINT_MAX; } };
+struct DefaultSampleDesc { operator DXGI_SAMPLE_DESC() { return DXGI_SAMPLE_DESC{1, 0}; } };
+
+template <typename InnerStructType, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type, typename DefaultArg = InnerStructType>
+class alignas(void*) CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT
+{
+private:
+    D3D12_PIPELINE_STATE_SUBOBJECT_TYPE _Type;
+    InnerStructType _Inner;
+public:
+    CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT() noexcept : _Type(Type), _Inner(DefaultArg()) {}
+    CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT(InnerStructType const& i) : _Type(Type), _Inner(i) {}
+    CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT& operator=(InnerStructType const& i) { _Inner = i; return *this; }
+    operator InnerStructType() const { return _Inner; }
+    operator InnerStructType&() { return _Inner; }
+};
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_PIPELINE_STATE_FLAGS,         D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS>                             CD3DX12_PIPELINE_STATE_STREAM_FLAGS;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< UINT,                               D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK>                         CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< ID3D12RootSignature*,               D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE>                    CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_INPUT_LAYOUT_DESC,            D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT>                      CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_INDEX_BUFFER_STRIP_CUT_VALUE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE>                CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_PRIMITIVE_TOPOLOGY_TYPE,      D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY>                CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE,              D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS>                                CD3DX12_PIPELINE_STATE_STREAM_VS;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE,              D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS>                                CD3DX12_PIPELINE_STATE_STREAM_GS;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_STREAM_OUTPUT_DESC,           D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT>                     CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE,              D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS>                                CD3DX12_PIPELINE_STATE_STREAM_HS;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE,              D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS>                                CD3DX12_PIPELINE_STATE_STREAM_DS;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE,              D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS>                                CD3DX12_PIPELINE_STATE_STREAM_PS;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE,              D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS>                                CD3DX12_PIPELINE_STATE_STREAM_CS;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_BLEND_DESC,                 D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND,          CD3DX12_DEFAULT>   CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_DEPTH_STENCIL_DESC,         D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL,  CD3DX12_DEFAULT>   CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_DEPTH_STENCIL_DESC1,        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1, CD3DX12_DEFAULT>   CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< DXGI_FORMAT,                        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT>              CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_RASTERIZER_DESC,            D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER,     CD3DX12_DEFAULT>   CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_RT_FORMAT_ARRAY,              D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS>             CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< DXGI_SAMPLE_DESC,                   D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC,    DefaultSampleDesc> CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< UINT,                               D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK,    DefaultSampleMask> CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_CACHED_PIPELINE_STATE,        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO>                        CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO;
+typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_VIEW_INSTANCING_DESC,       D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING, CD3DX12_DEFAULT>  CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING;
+
+//------------------------------------------------------------------------------------------------
+// Stream Parser Helpers
+
+struct ID3DX12PipelineParserCallbacks
+{
+    // Subobject Callbacks
+    virtual void FlagsCb(D3D12_PIPELINE_STATE_FLAGS) {}
+    virtual void NodeMaskCb(UINT) {}
+    virtual void RootSignatureCb(ID3D12RootSignature*) {}
+    virtual void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC&) {}
+    virtual void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE) {}
+    virtual void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE) {}
+    virtual void VSCb(const D3D12_SHADER_BYTECODE&) {}
+    virtual void GSCb(const D3D12_SHADER_BYTECODE&) {}
+    virtual void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC&) {}
+    virtual void HSCb(const D3D12_SHADER_BYTECODE&) {}
+    virtual void DSCb(const D3D12_SHADER_BYTECODE&) {}
+    virtual void PSCb(const D3D12_SHADER_BYTECODE&) {}
+    virtual void CSCb(const D3D12_SHADER_BYTECODE&) {}
+    virtual void BlendStateCb(const D3D12_BLEND_DESC&) {}
+    virtual void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC&) {}
+    virtual void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1&) {}
+    virtual void DSVFormatCb(DXGI_FORMAT) {}
+    virtual void RasterizerStateCb(const D3D12_RASTERIZER_DESC&) {}
+    virtual void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY&) {}
+    virtual void SampleDescCb(const DXGI_SAMPLE_DESC&) {}
+    virtual void SampleMaskCb(UINT) {}
+    virtual void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC&) {}
+    virtual void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE&) {}
+
+    // Error Callbacks
+    virtual void ErrorBadInputParameter(UINT /*ParameterIndex*/) {}
+    virtual void ErrorDuplicateSubobject(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE /*DuplicateType*/) {}
+    virtual void ErrorUnknownSubobject(UINT /*UnknownTypeValue*/) {}
+
+};
+
+// CD3DX12_PIPELINE_STATE_STREAM1 Works on RS3+ (where there is a new view instancing subobject).  
+// Use CD3DX12_PIPELINE_STATE_STREAM for RS2+ support.
+struct CD3DX12_PIPELINE_STATE_STREAM1
+{
+    CD3DX12_PIPELINE_STATE_STREAM1() = default;
+    CD3DX12_PIPELINE_STATE_STREAM1(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc)
+        : Flags(Desc.Flags)
+        , NodeMask(Desc.NodeMask)
+        , pRootSignature(Desc.pRootSignature)
+        , InputLayout(Desc.InputLayout)
+        , IBStripCutValue(Desc.IBStripCutValue)
+        , PrimitiveTopologyType(Desc.PrimitiveTopologyType)
+        , VS(Desc.VS)
+        , GS(Desc.GS)
+        , StreamOutput(Desc.StreamOutput)
+        , HS(Desc.HS)
+        , DS(Desc.DS)
+        , PS(Desc.PS)
+        , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState))
+        , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState))
+        , DSVFormat(Desc.DSVFormat)
+        , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState))
+        , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets))
+        , SampleDesc(Desc.SampleDesc)
+        , SampleMask(Desc.SampleMask)
+        , CachedPSO(Desc.CachedPSO)
+        , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT()))
+    {}
+    CD3DX12_PIPELINE_STATE_STREAM1(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc)
+        : Flags(Desc.Flags)
+        , NodeMask(Desc.NodeMask)
+        , pRootSignature(Desc.pRootSignature)
+        , CS(CD3DX12_SHADER_BYTECODE(Desc.CS))
+        , CachedPSO(Desc.CachedPSO)
+    {
+        static_cast<D3D12_DEPTH_STENCIL_DESC1&>(DepthStencilState).DepthEnable = false;
+    }
+    CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags;
+    CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask;
+    CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature;
+    CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout;
+    CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue;
+    CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType;
+    CD3DX12_PIPELINE_STATE_STREAM_VS VS;
+    CD3DX12_PIPELINE_STATE_STREAM_GS GS;
+    CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput;
+    CD3DX12_PIPELINE_STATE_STREAM_HS HS;
+    CD3DX12_PIPELINE_STATE_STREAM_DS DS;
+    CD3DX12_PIPELINE_STATE_STREAM_PS PS;
+    CD3DX12_PIPELINE_STATE_STREAM_CS CS;
+    CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState;
+    CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState;
+    CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat;
+    CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState;
+    CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats;
+    CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc;
+    CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask;
+    CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO;
+    CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc;
+    D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const
+    {
+        D3D12_GRAPHICS_PIPELINE_STATE_DESC D;
+        D.Flags                 = this->Flags;
+        D.NodeMask              = this->NodeMask;
+        D.pRootSignature        = this->pRootSignature;
+        D.InputLayout           = this->InputLayout;
+        D.IBStripCutValue       = this->IBStripCutValue;
+        D.PrimitiveTopologyType = this->PrimitiveTopologyType;
+        D.VS                    = this->VS;
+        D.GS                    = this->GS;
+        D.StreamOutput          = this->StreamOutput;
+        D.HS                    = this->HS;
+        D.DS                    = this->DS;
+        D.PS                    = this->PS;
+        D.BlendState            = this->BlendState;
+        D.DepthStencilState     = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState));
+        D.DSVFormat             = this->DSVFormat;
+        D.RasterizerState       = this->RasterizerState;
+        D.NumRenderTargets      = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets;
+        memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats));
+        D.SampleDesc            = this->SampleDesc;
+        D.SampleMask            = this->SampleMask;
+        D.CachedPSO             = this->CachedPSO;
+        return D;
+    }
+    D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const
+    {
+        D3D12_COMPUTE_PIPELINE_STATE_DESC D;
+        D.Flags                 = this->Flags;
+        D.NodeMask              = this->NodeMask;
+        D.pRootSignature        = this->pRootSignature;
+        D.CS                    = this->CS;
+        D.CachedPSO             = this->CachedPSO;
+        return D;
+    }
+};
+
+// CD3DX12_PIPELINE_STATE_STREAM works on RS2+ but does not support new subobject(s) added in RS3+.
+// See CD3DX12_PIPELINE_STATE_STREAM1 for instance.
+struct CD3DX12_PIPELINE_STATE_STREAM
+{
+    CD3DX12_PIPELINE_STATE_STREAM() = default;
+    CD3DX12_PIPELINE_STATE_STREAM(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc)
+        : Flags(Desc.Flags)
+        , NodeMask(Desc.NodeMask)
+        , pRootSignature(Desc.pRootSignature)
+        , InputLayout(Desc.InputLayout)
+        , IBStripCutValue(Desc.IBStripCutValue)
+        , PrimitiveTopologyType(Desc.PrimitiveTopologyType)
+        , VS(Desc.VS)
+        , GS(Desc.GS)
+        , StreamOutput(Desc.StreamOutput)
+        , HS(Desc.HS)
+        , DS(Desc.DS)
+        , PS(Desc.PS)
+        , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState))
+        , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState))
+        , DSVFormat(Desc.DSVFormat)
+        , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState))
+        , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets))
+        , SampleDesc(Desc.SampleDesc)
+        , SampleMask(Desc.SampleMask)
+        , CachedPSO(Desc.CachedPSO)
+    {}
+    CD3DX12_PIPELINE_STATE_STREAM(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc)
+        : Flags(Desc.Flags)
+        , NodeMask(Desc.NodeMask)
+        , pRootSignature(Desc.pRootSignature)
+        , CS(CD3DX12_SHADER_BYTECODE(Desc.CS))
+        , CachedPSO(Desc.CachedPSO)
+    {}
+    CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags;
+    CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask;
+    CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature;
+    CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout;
+    CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue;
+    CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType;
+    CD3DX12_PIPELINE_STATE_STREAM_VS VS;
+    CD3DX12_PIPELINE_STATE_STREAM_GS GS;
+    CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput;
+    CD3DX12_PIPELINE_STATE_STREAM_HS HS;
+    CD3DX12_PIPELINE_STATE_STREAM_DS DS;
+    CD3DX12_PIPELINE_STATE_STREAM_PS PS;
+    CD3DX12_PIPELINE_STATE_STREAM_CS CS;
+    CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState;
+    CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState;
+    CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat;
+    CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState;
+    CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats;
+    CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc;
+    CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask;
+    CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO;
+    D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const
+    {
+        D3D12_GRAPHICS_PIPELINE_STATE_DESC D;
+        D.Flags                 = this->Flags;
+        D.NodeMask              = this->NodeMask;
+        D.pRootSignature        = this->pRootSignature;
+        D.InputLayout           = this->InputLayout;
+        D.IBStripCutValue       = this->IBStripCutValue;
+        D.PrimitiveTopologyType = this->PrimitiveTopologyType;
+        D.VS                    = this->VS;
+        D.GS                    = this->GS;
+        D.StreamOutput          = this->StreamOutput;
+        D.HS                    = this->HS;
+        D.DS                    = this->DS;
+        D.PS                    = this->PS;
+        D.BlendState            = this->BlendState;
+        D.DepthStencilState     = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState));
+        D.DSVFormat             = this->DSVFormat;
+        D.RasterizerState       = this->RasterizerState;
+        D.NumRenderTargets      = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets;
+        memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats));
+        D.SampleDesc            = this->SampleDesc;
+        D.SampleMask            = this->SampleMask;
+        D.CachedPSO             = this->CachedPSO;
+        return D;
+    }
+    D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const
+    {
+        D3D12_COMPUTE_PIPELINE_STATE_DESC D;
+        D.Flags                 = this->Flags;
+        D.NodeMask              = this->NodeMask;
+        D.pRootSignature        = this->pRootSignature;
+        D.CS                    = this->CS;
+        D.CachedPSO             = this->CachedPSO;
+        return D;
+    }
+};
+
+struct CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER : public ID3DX12PipelineParserCallbacks
+{
+    CD3DX12_PIPELINE_STATE_STREAM1 PipelineStream;
+    CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER() noexcept
+        : SeenDSS(false)
+    {
+        // Adjust defaults to account for absent members.
+        PipelineStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+
+        // Depth disabled if no DSV format specified.
+        static_cast<D3D12_DEPTH_STENCIL_DESC1&>(PipelineStream.DepthStencilState).DepthEnable = false;
+    }
+    virtual ~CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER() {}
+
+    // ID3DX12PipelineParserCallbacks
+    void FlagsCb(D3D12_PIPELINE_STATE_FLAGS Flags) {PipelineStream.Flags = Flags;}
+    void NodeMaskCb(UINT NodeMask) {PipelineStream.NodeMask = NodeMask;}
+    void RootSignatureCb(ID3D12RootSignature* pRootSignature) {PipelineStream.pRootSignature = pRootSignature;}
+    void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC& InputLayout) {PipelineStream.InputLayout = InputLayout;}
+    void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue) {PipelineStream.IBStripCutValue = IBStripCutValue;}
+    void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType) {PipelineStream.PrimitiveTopologyType = PrimitiveTopologyType;}
+    void VSCb(const D3D12_SHADER_BYTECODE& VS) {PipelineStream.VS = VS;}
+    void GSCb(const D3D12_SHADER_BYTECODE& GS) {PipelineStream.GS = GS;}
+    void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC& StreamOutput) {PipelineStream.StreamOutput = StreamOutput;}
+    void HSCb(const D3D12_SHADER_BYTECODE& HS) {PipelineStream.HS = HS;}
+    void DSCb(const D3D12_SHADER_BYTECODE& DS) {PipelineStream.DS = DS;}
+    void PSCb(const D3D12_SHADER_BYTECODE& PS) {PipelineStream.PS = PS;}
+    void CSCb(const D3D12_SHADER_BYTECODE& CS) {PipelineStream.CS = CS;}
+    void BlendStateCb(const D3D12_BLEND_DESC& BlendState) {PipelineStream.BlendState = CD3DX12_BLEND_DESC(BlendState);}
+    void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC& DepthStencilState)
+    {
+        PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState);
+        SeenDSS = true;
+    }
+    void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1& DepthStencilState)
+    {
+        PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState);
+        SeenDSS = true;
+    }
+    void DSVFormatCb(DXGI_FORMAT DSVFormat)
+    {
+        PipelineStream.DSVFormat = DSVFormat;
+        if (!SeenDSS && DSVFormat != DXGI_FORMAT_UNKNOWN)
+        {
+            // Re-enable depth for the default state.
+            static_cast<D3D12_DEPTH_STENCIL_DESC1&>(PipelineStream.DepthStencilState).DepthEnable = true;
+        }
+    }
+    void RasterizerStateCb(const D3D12_RASTERIZER_DESC& RasterizerState) {PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC(RasterizerState);}
+    void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY& RTVFormats) {PipelineStream.RTVFormats = RTVFormats;}
+    void SampleDescCb(const DXGI_SAMPLE_DESC& SampleDesc) {PipelineStream.SampleDesc = SampleDesc;}
+    void SampleMaskCb(UINT SampleMask) {PipelineStream.SampleMask = SampleMask;}
+    void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC& ViewInstancingDesc) {PipelineStream.ViewInstancingDesc = CD3DX12_VIEW_INSTANCING_DESC(ViewInstancingDesc);}
+    void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE& CachedPSO) {PipelineStream.CachedPSO = CachedPSO;}
+    void ErrorBadInputParameter(UINT) {}
+    void ErrorDuplicateSubobject(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE) {}
+    void ErrorUnknownSubobject(UINT) {}
+
+private:
+    bool SeenDSS;
+};
+
+inline D3D12_PIPELINE_STATE_SUBOBJECT_TYPE D3DX12GetBaseSubobjectType(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE SubobjectType)
+{
+    switch (SubobjectType)
+    {
+    case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1: 
+        return D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL;
+    default:
+        return SubobjectType;
+    }
+}
+
+inline HRESULT D3DX12ParsePipelineStream(const D3D12_PIPELINE_STATE_STREAM_DESC& Desc, ID3DX12PipelineParserCallbacks* pCallbacks)
+{
+    if (pCallbacks == nullptr)
+    {
+        return E_INVALIDARG;
+    }
+
+    if (Desc.SizeInBytes == 0 || Desc.pPipelineStateSubobjectStream == nullptr)
+    {
+        pCallbacks->ErrorBadInputParameter(1); // first parameter issue
+        return E_INVALIDARG;
+    }
+
+    bool SubobjectSeen[D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MAX_VALID] = {};
+    for (SIZE_T CurOffset = 0, SizeOfSubobject = 0; CurOffset < Desc.SizeInBytes; CurOffset += SizeOfSubobject)
+    {
+        BYTE* pStream = static_cast<BYTE*>(Desc.pPipelineStateSubobjectStream)+CurOffset;
+        auto SubobjectType = *reinterpret_cast<D3D12_PIPELINE_STATE_SUBOBJECT_TYPE*>(pStream);
+        if (SubobjectType >= D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MAX_VALID)
+        {
+            pCallbacks->ErrorUnknownSubobject(SubobjectType);
+            return E_INVALIDARG;
+        }
+        if (SubobjectSeen[D3DX12GetBaseSubobjectType(SubobjectType)])
+        {
+            pCallbacks->ErrorDuplicateSubobject(SubobjectType);
+            return E_INVALIDARG; // disallow subobject duplicates in a stream
+        }
+        SubobjectSeen[SubobjectType] = true;
+        switch (SubobjectType)
+        {
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE: 
+            pCallbacks->RootSignatureCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::pRootSignature)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::pRootSignature);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS:
+            pCallbacks->VSCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::VS)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::VS);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS: 
+            pCallbacks->PSCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::PS)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::PS);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS: 
+            pCallbacks->DSCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::DS)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DS);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS: 
+            pCallbacks->HSCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::HS)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::HS);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS: 
+            pCallbacks->GSCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::GS)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::GS);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS:
+            pCallbacks->CSCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::CS)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::CS);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT: 
+            pCallbacks->StreamOutputCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::StreamOutput)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::StreamOutput);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND: 
+            pCallbacks->BlendStateCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::BlendState)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::BlendState);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK: 
+            pCallbacks->SampleMaskCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::SampleMask)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::SampleMask);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER: 
+            pCallbacks->RasterizerStateCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::RasterizerState)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::RasterizerState);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL: 
+            pCallbacks->DepthStencilStateCb(*reinterpret_cast<CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1: 
+            pCallbacks->DepthStencilState1Cb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::DepthStencilState)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DepthStencilState);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT: 
+            pCallbacks->InputLayoutCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::InputLayout)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::InputLayout);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE: 
+            pCallbacks->IBStripCutValueCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::IBStripCutValue)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::IBStripCutValue);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY: 
+            pCallbacks->PrimitiveTopologyTypeCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::PrimitiveTopologyType)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::PrimitiveTopologyType);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS: 
+            pCallbacks->RTVFormatsCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::RTVFormats)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::RTVFormats);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT: 
+            pCallbacks->DSVFormatCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::DSVFormat)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DSVFormat);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC: 
+            pCallbacks->SampleDescCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::SampleDesc)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::SampleDesc);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK: 
+            pCallbacks->NodeMaskCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::NodeMask)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::NodeMask);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO: 
+            pCallbacks->CachedPSOCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::CachedPSO)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::CachedPSO);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS:
+            pCallbacks->FlagsCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM::Flags)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::Flags);
+            break;
+        case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING:
+            pCallbacks->ViewInstancingCb(*reinterpret_cast<decltype(CD3DX12_PIPELINE_STATE_STREAM1::ViewInstancingDesc)*>(pStream));
+            SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM1::ViewInstancingDesc);
+            break;
+        default:
+            pCallbacks->ErrorUnknownSubobject(SubobjectType);
+            return E_INVALIDARG;
+            break;
+        }
+    }
+
+    return S_OK;
+}
+
+
+#endif // defined( __cplusplus )
+
+#endif //__D3DX12_H__
+
+
+

+ 622 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/Direct3D12.c.h

@@ -0,0 +1,622 @@
+#include "commandlist.h"
+#include "indexbuffer.h"
+#include "pipeline.h"
+#include "vertexbuffer.h"
+
+#include <kinc/graphics5/graphics.h>
+#include <kinc/graphics5/pipeline.h>
+#include <kinc/math/core.h>
+#include <kinc/window.h>
+#ifdef KINC_WINDOWS
+#include <dxgi1_4.h>
+#undef CreateWindow
+#endif
+#include <kinc/system.h>
+#ifdef KINC_WINDOWS
+#include <kinc/backend/Windows.h>
+#endif
+#include <kinc/backend/SystemMicrosoft.h>
+
+/*IDXGIFactory4* dxgiFactory;
+ID3D12Device* device;
+ID3D12GraphicsCommandList* commandList;
+ID3D12CommandQueue* commandQueue;
+ID3D12CommandAllocator* commandAllocators[frameCount];
+unsigned currentFrame = 0;
+ID3D12Fence* fence;
+UINT64 window->current_fence_value[frameCount];
+HANDLE fenceEvent;
+ID3D12Resource* renderTargets[frameCount];
+ID3D12DescriptorHeap* rtvHeap;
+unsigned rtvDescriptorSize;
+ID3D12CommandAllocator* bundleAllocator;
+ID3D12RootSignature* rootSignature;
+D3D12_VIEWPORT screenViewport;
+D3D12_RECT scissorRect;
+ID3D12DescriptorHeap* cbvHeap;*/
+// ID3D12DeviceContext* context;
+// ID3D12RenderTargetView* renderTargetView;
+// ID3D12DepthStencilView* depthStencilView;
+
+// ID3D12GraphicsCommandList* commandList;
+
+extern "C" {
+ID3D12CommandQueue *commandQueue;
+#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN
+ID3D12Resource *swapChainRenderTargets[QUEUE_SLOT_COUNT];
+#else
+// IDXGISwapChain *swapChain;
+#endif
+}
+
+// int window->width;
+// int window->height;
+// int window->new_width;
+// int window->new_height;
+
+#ifndef KINC_WINDOWS
+#define DXGI_SWAP_CHAIN_DESC DXGI_SWAP_CHAIN_DESC1
+#define IDXGISwapChain IDXGISwapChain1
+#endif
+
+struct RenderEnvironment {
+	ID3D12Device *device;
+	ID3D12CommandQueue *queue;
+#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN
+	ID3D12Resource *renderTargets[QUEUE_SLOT_COUNT];
+#else
+	IDXGISwapChain *swapChain;
+#endif
+};
+
+#ifndef KINC_WINDOWS
+#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN
+extern "C" void createSwapChain(struct RenderEnvironment *env, int bufferCount);
+#else
+extern "C" void createSwapChain(struct RenderEnvironment *env, const DXGI_SWAP_CHAIN_DESC1 *desc);
+#endif
+#endif
+
+extern bool bilinearFiltering;
+
+// ID3D12Resource* renderTarget;
+// ID3D12DescriptorHeap* renderTargetDescriptorHeap;
+
+// static UINT64 window->current_fence_value;
+// static UINT64 window->current_fence_value[QUEUE_SLOT_COUNT];
+// static HANDLE window->frame_fence_events[QUEUE_SLOT_COUNT];
+// static ID3D12Fence *window->frame_fences[QUEUE_SLOT_COUNT];
+static ID3D12Fence *uploadFence;
+static ID3D12GraphicsCommandList *initCommandList;
+static ID3D12CommandAllocator *initCommandAllocator;
+
+extern "C" struct RenderEnvironment createDeviceAndSwapChainHelper(D3D_FEATURE_LEVEL minimumFeatureLevel, const struct DXGI_SWAP_CHAIN_DESC *swapChainDesc) {
+	struct RenderEnvironment result = {0};
+#ifdef KINC_WINDOWS
+	kinc_microsoft_affirm(D3D12CreateDevice(NULL, minimumFeatureLevel, IID_PPV_ARGS(&result.device)));
+
+	D3D12_COMMAND_QUEUE_DESC queueDesc = {};
+	queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
+	queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+
+	kinc_microsoft_affirm(result.device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&result.queue)));
+
+	IDXGIFactory4 *dxgiFactory;
+	kinc_microsoft_affirm(CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)));
+
+	DXGI_SWAP_CHAIN_DESC swapChainDescCopy = *swapChainDesc;
+	kinc_microsoft_affirm(dxgiFactory->CreateSwapChain((IUnknown *)result.queue, &swapChainDescCopy, &result.swapChain));
+#else
+#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN
+	createSwapChain(&result, QUEUE_SLOT_COUNT);
+#else
+	createSwapChain(&result, swapChainDesc);
+#endif
+#endif
+	return result;
+}
+
+static void waitForFence(ID3D12Fence *fence, UINT64 completionValue, HANDLE waitEvent) {
+	if (fence->GetCompletedValue() < completionValue) {
+		kinc_microsoft_affirm(fence->SetEventOnCompletion(completionValue, waitEvent));
+		WaitForSingleObject(waitEvent, INFINITE);
+	}
+}
+
+extern "C" void setupSwapChain(struct dx_window *window) {
+	/*D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
+	heapDesc.NumDescriptors = 1;
+	heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+	heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+	device->CreateDescriptorHeap(&heapDesc, IID_GRAPHICS_PPV_ARGS(&renderTargetDescriptorHeap));*/
+
+	D3D12_RESOURCE_DESC depthTexture = {};
+	depthTexture.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+	depthTexture.Alignment = 0;
+	depthTexture.Width = window->width;
+	depthTexture.Height = window->height;
+	depthTexture.DepthOrArraySize = 1;
+	depthTexture.MipLevels = 1;
+	depthTexture.Format = DXGI_FORMAT_D32_FLOAT;
+	depthTexture.SampleDesc.Count = 1;
+	depthTexture.SampleDesc.Quality = 0;
+	depthTexture.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+	depthTexture.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
+
+	D3D12_CLEAR_VALUE clearValue = {};
+	clearValue.Format = DXGI_FORMAT_D32_FLOAT;
+	clearValue.DepthStencil.Depth = 1.0f;
+	clearValue.DepthStencil.Stencil = 0;
+
+	D3D12_HEAP_PROPERTIES heapProperties = {};
+	heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
+	heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+	heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+	heapProperties.CreationNodeMask = 1;
+	heapProperties.VisibleNodeMask = 1;
+
+	window->current_fence_value = 0;
+
+	for (int i = 0; i < QUEUE_SLOT_COUNT; ++i) {
+		window->frame_fence_events[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
+		window->fence_values[i] = 0;
+		device->CreateFence(window->current_fence_value, D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(&window->frame_fences[i]));
+	}
+
+	//**swapChain->GetBuffer(window->current_backbuffer, IID_GRAPHICS_PPV_ARGS(&renderTarget));
+	//**createRenderTargetView();
+}
+
+#ifdef KINC_CONSOLE
+extern "C" void createDeviceAndSwapChain(struct dx_window *window);
+#else
+static void createDeviceAndSwapChain(struct dx_window *window) {
+#ifdef _DEBUG
+	ID3D12Debug *debugController = NULL;
+	D3D12GetDebugInterface(IID_PPV_ARGS(&debugController));
+	debugController->EnableDebugLayer();
+#endif
+
+	struct DXGI_SWAP_CHAIN_DESC swapChainDesc;
+	ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
+
+	swapChainDesc.BufferCount = QUEUE_SLOT_COUNT;
+	swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+	swapChainDesc.BufferDesc.Width = window->width;
+	swapChainDesc.BufferDesc.Height = window->height;
+	swapChainDesc.OutputWindow = kinc_windows_window_handle(window->window_index);
+	swapChainDesc.SampleDesc.Count = 1;
+	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+	swapChainDesc.Windowed = true;
+
+	struct RenderEnvironment renderEnv = createDeviceAndSwapChainHelper(D3D_FEATURE_LEVEL_11_0, &swapChainDesc);
+
+	device = renderEnv.device;
+	commandQueue = renderEnv.queue;
+
+	// swapChain = renderEnv.swapChain;
+
+	setupSwapChain(NULL);
+}
+#endif
+
+static void createRootSignature() {
+	ID3DBlob *rootBlob;
+	ID3DBlob *errorBlob;
+
+	D3D12_ROOT_PARAMETER parameters[4] = {};
+
+	D3D12_DESCRIPTOR_RANGE range;
+	range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	range.NumDescriptors = (UINT)KINC_INTERNAL_G5_TEXTURE_COUNT;
+	range.BaseShaderRegister = 0;
+	range.RegisterSpace = 0;
+	range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+	parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	parameters[0].DescriptorTable.NumDescriptorRanges = 1;
+	parameters[0].DescriptorTable.pDescriptorRanges = &range;
+
+	D3D12_DESCRIPTOR_RANGE range2;
+	range2.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
+	range2.NumDescriptors = (UINT)KINC_INTERNAL_G5_TEXTURE_COUNT;
+	range2.BaseShaderRegister = 0;
+	range2.RegisterSpace = 0;
+	range2.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+	parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	parameters[1].DescriptorTable.NumDescriptorRanges = 1;
+	parameters[1].DescriptorTable.pDescriptorRanges = &range2;
+
+	parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+	parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
+	parameters[2].Descriptor.ShaderRegister = 0;
+	parameters[2].Descriptor.RegisterSpace = 0;
+
+	parameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+	parameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+	parameters[3].Descriptor.ShaderRegister = 0;
+	parameters[3].Descriptor.RegisterSpace = 0;
+
+	D3D12_STATIC_SAMPLER_DESC samplers[KINC_INTERNAL_G5_TEXTURE_COUNT * 2];
+	for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) {
+		samplers[i].ShaderRegister = i;
+		samplers[i].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
+		samplers[i].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].MipLODBias = 0;
+		samplers[i].MaxAnisotropy = 16;
+		samplers[i].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
+		samplers[i].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
+		samplers[i].MinLOD = 0.0f;
+		samplers[i].MaxLOD = D3D12_FLOAT32_MAX;
+		samplers[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+		samplers[i].RegisterSpace = 0;
+	}
+	for (int i = KINC_INTERNAL_G5_TEXTURE_COUNT; i < KINC_INTERNAL_G5_TEXTURE_COUNT * 2; ++i) {
+		samplers[i].ShaderRegister = i;
+		samplers[i].Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+		samplers[i].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].MipLODBias = 0;
+		samplers[i].MaxAnisotropy = 16;
+		samplers[i].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
+		samplers[i].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
+		samplers[i].MinLOD = 0.0f;
+		samplers[i].MaxLOD = D3D12_FLOAT32_MAX;
+		samplers[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+		samplers[i].RegisterSpace = 0;
+	}
+
+	D3D12_ROOT_SIGNATURE_DESC descRootSignature;
+	descRootSignature.NumParameters = 4;
+	descRootSignature.pParameters = parameters;
+	descRootSignature.NumStaticSamplers = 0;
+	descRootSignature.pStaticSamplers = NULL;
+	descRootSignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
+
+	kinc_microsoft_affirm(D3D12SerializeRootSignature(&descRootSignature, D3D_ROOT_SIGNATURE_VERSION_1, &rootBlob, &errorBlob));
+	device->CreateRootSignature(0, rootBlob->GetBufferPointer(), rootBlob->GetBufferSize(), IID_GRAPHICS_PPV_ARGS(&globalRootSignature));
+}
+
+static void createComputeRootSignature() {
+	ID3DBlob *rootBlob;
+	ID3DBlob *errorBlob;
+
+	D3D12_ROOT_PARAMETER parameters[4] = {};
+
+	D3D12_DESCRIPTOR_RANGE range;
+	range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	range.NumDescriptors = (UINT)KINC_INTERNAL_G5_TEXTURE_COUNT;
+	range.BaseShaderRegister = 0;
+	range.RegisterSpace = 0;
+	range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+	parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	parameters[0].DescriptorTable.NumDescriptorRanges = 1;
+	parameters[0].DescriptorTable.pDescriptorRanges = &range;
+
+	D3D12_DESCRIPTOR_RANGE uav_range;
+	uav_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
+	uav_range.NumDescriptors = (UINT)KINC_INTERNAL_G5_TEXTURE_COUNT;
+	uav_range.BaseShaderRegister = 0;
+	uav_range.RegisterSpace = 0;
+	uav_range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+	parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	parameters[1].DescriptorTable.NumDescriptorRanges = 1;
+	parameters[1].DescriptorTable.pDescriptorRanges = &uav_range;
+
+	D3D12_DESCRIPTOR_RANGE range2;
+	range2.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
+	range2.NumDescriptors = (UINT)KINC_INTERNAL_G5_TEXTURE_COUNT;
+	range2.BaseShaderRegister = 0;
+	range2.RegisterSpace = 0;
+	range2.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+	parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	parameters[2].DescriptorTable.NumDescriptorRanges = 1;
+	parameters[2].DescriptorTable.pDescriptorRanges = &range2;
+
+	parameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+	parameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	parameters[3].Descriptor.ShaderRegister = 0;
+	parameters[3].Descriptor.RegisterSpace = 0;
+
+	D3D12_STATIC_SAMPLER_DESC samplers[KINC_INTERNAL_G5_TEXTURE_COUNT * 2];
+	for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) {
+		samplers[i].ShaderRegister = i;
+		samplers[i].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
+		samplers[i].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].MipLODBias = 0;
+		samplers[i].MaxAnisotropy = 16;
+		samplers[i].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
+		samplers[i].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
+		samplers[i].MinLOD = 0.0f;
+		samplers[i].MaxLOD = D3D12_FLOAT32_MAX;
+		samplers[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+		samplers[i].RegisterSpace = 0;
+	}
+	for (int i = KINC_INTERNAL_G5_TEXTURE_COUNT; i < KINC_INTERNAL_G5_TEXTURE_COUNT * 2; ++i) {
+		samplers[i].ShaderRegister = i;
+		samplers[i].Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+		samplers[i].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+		samplers[i].MipLODBias = 0;
+		samplers[i].MaxAnisotropy = 16;
+		samplers[i].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
+		samplers[i].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
+		samplers[i].MinLOD = 0.0f;
+		samplers[i].MaxLOD = D3D12_FLOAT32_MAX;
+		samplers[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+		samplers[i].RegisterSpace = 0;
+	}
+
+	D3D12_ROOT_SIGNATURE_DESC descRootSignature;
+	descRootSignature.NumParameters = 4;
+	descRootSignature.pParameters = parameters;
+	descRootSignature.NumStaticSamplers = 0;
+	descRootSignature.pStaticSamplers = NULL;
+	descRootSignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
+
+	kinc_microsoft_affirm(D3D12SerializeRootSignature(&descRootSignature, D3D_ROOT_SIGNATURE_VERSION_1, &rootBlob, &errorBlob));
+	device->CreateRootSignature(0, rootBlob->GetBufferPointer(), rootBlob->GetBufferSize(), IID_GRAPHICS_PPV_ARGS(&globalComputeRootSignature));
+
+	// createSamplersAndHeaps();
+}
+
+static void initialize(struct dx_window *window) {
+	createDeviceAndSwapChain(window);
+	createRootSignature();
+	createComputeRootSignature();
+
+	device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(&uploadFence));
+
+	device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_GRAPHICS_PPV_ARGS(&initCommandAllocator));
+	device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, initCommandAllocator, NULL, IID_GRAPHICS_PPV_ARGS(&initCommandList));
+
+	initCommandList->Close();
+
+	ID3D12CommandList *commandLists[] = {(ID3D12CommandList *)initCommandList};
+	commandQueue->ExecuteCommandLists(1, commandLists);
+	commandQueue->Signal(uploadFence, 1);
+
+	HANDLE waitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+	waitForFence(uploadFence, 1, waitEvent);
+
+	initCommandAllocator->Reset();
+	initCommandList->Release();      // check me
+	initCommandAllocator->Release(); // check me
+
+	CloseHandle(waitEvent);
+}
+
+static void shutdown() {
+	for (int i = 0; i < QUEUE_SLOT_COUNT; ++i) {
+		// waitForFence(window->frame_fences[i], window->current_fence_value[i], window->frame_fence_events[i]);
+	}
+
+	for (int i = 0; i < QUEUE_SLOT_COUNT; ++i) {
+		// CloseHandle(window->frame_fence_events[i]);
+	}
+}
+
+#ifdef KINC_WINDOWS
+static void initWindow(struct dx_window *window, int windowIndex) {
+	HWND hwnd = kinc_windows_window_handle(windowIndex);
+
+	DXGI_SWAP_CHAIN_DESC swapChainDesc;
+	ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
+
+	swapChainDesc.BufferCount = QUEUE_SLOT_COUNT;
+	swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+	swapChainDesc.BufferDesc.Width = kinc_window_width(windowIndex);
+	swapChainDesc.BufferDesc.Height = kinc_window_height(windowIndex);
+	swapChainDesc.OutputWindow = hwnd;
+	swapChainDesc.SampleDesc.Count = 1;
+	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+	swapChainDesc.Windowed = true;
+
+	IDXGIFactory4 *dxgiFactory = NULL;
+	kinc_microsoft_affirm(CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)));
+
+	kinc_microsoft_affirm(dxgiFactory->CreateSwapChain((IUnknown *)commandQueue, &swapChainDesc, &window->swapChain));
+
+	setupSwapChain(window);
+}
+#endif
+
+void kinc_g5_internal_destroy_window(int window) {}
+
+void kinc_g5_internal_destroy() {
+#ifdef KINC_WINDOWS
+	if (device) {
+		device->Release();
+		device = NULL;
+	}
+#endif
+}
+
+void kinc_g5_internal_init() {
+#ifdef KINC_WINDOWS
+#ifdef _DEBUG
+	ID3D12Debug *debugController = NULL;
+	if (D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)) == S_OK) {
+		debugController->EnableDebugLayer();
+	}
+#endif
+	kinc_microsoft_affirm(D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device)));
+
+	createRootSignature();
+	createComputeRootSignature();
+
+	D3D12_COMMAND_QUEUE_DESC queueDesc = {};
+	queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
+	queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+
+	kinc_microsoft_affirm(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)));
+
+	device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&uploadFence));
+
+	device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&initCommandAllocator));
+	device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, initCommandAllocator, NULL, IID_PPV_ARGS(&initCommandList));
+
+	initCommandList->Close();
+
+	ID3D12CommandList *commandLists[] = {(ID3D12CommandList *)initCommandList};
+	commandQueue->ExecuteCommandLists(1, commandLists);
+	commandQueue->Signal(uploadFence, 1);
+
+	HANDLE waitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+	waitForFence(uploadFence, 1, waitEvent);
+
+	initCommandAllocator->Reset();
+	initCommandList->Release();      // check me
+	initCommandAllocator->Release(); // check me
+
+	CloseHandle(waitEvent);
+#endif
+}
+
+void kinc_g5_internal_init_window(int windowIndex, int depthBufferBits, int stencilBufferBits, bool verticalSync) {
+	struct dx_window *window = &dx_ctx.windows[windowIndex];
+	window->window_index = windowIndex;
+	window->vsync = verticalSync;
+	window->width = window->new_width = kinc_window_width(windowIndex);
+	window->height = window->new_height = kinc_window_height(windowIndex);
+#ifdef KINC_WINDOWS
+	initWindow(window, windowIndex);
+#else
+	HWND hwnd = NULL;
+	window->vsync = verticalSync;
+	window->new_width = window->width = kinc_width();
+	window->new_height = window->height = kinc_height();
+	initialize(window);
+#endif
+}
+
+int kinc_g5_max_bound_textures(void) {
+	return D3D12_COMMONSHADER_SAMPLER_SLOT_COUNT;
+}
+
+#ifndef KINC_WINDOWS
+extern "C" void kinc_internal_wait_for_frame();
+#endif
+
+static bool began = false;
+
+void kinc_g5_begin(kinc_g5_render_target_t *renderTarget, int windowId) {
+	if (began)
+		return;
+	began = true;
+
+#ifndef KINC_WINDOWS
+	kinc_internal_wait_for_frame();
+#endif
+
+	struct dx_window *window = &dx_ctx.windows[windowId];
+	dx_ctx.current_window = windowId;
+
+	window->current_backbuffer = (window->current_backbuffer + 1) % QUEUE_SLOT_COUNT;
+
+	if (window->new_width != window->width || window->new_height != window->height) {
+#ifndef KINC_DIRECT3D_HAS_NO_SWAPCHAIN
+		kinc_microsoft_affirm(window->swapChain->ResizeBuffers(QUEUE_SLOT_COUNT, window->new_width, window->new_height, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
+#endif
+		setupSwapChain(window);
+		window->width = window->new_width;
+		window->height = window->new_height;
+		window->current_backbuffer = 0;
+	}
+
+	const UINT64 fenceValue = window->current_fence_value;
+	commandQueue->Signal(window->frame_fences[window->current_backbuffer], fenceValue);
+	window->fence_values[window->current_backbuffer] = fenceValue;
+	++window->current_fence_value;
+
+	waitForFence(window->frame_fences[window->current_backbuffer], window->fence_values[window->current_backbuffer],
+	             window->frame_fence_events[window->current_backbuffer]);
+
+	// static const float clearColor[] = {0.042f, 0.042f, 0.042f, 1};
+
+	// commandList->ClearRenderTargetView(GetCPUDescriptorHandle(renderTargetDescriptorHeap), clearColor, 0, nullptr);
+
+	// commandList->ClearDepthStencilView(GetCPUDescriptorHandle(depthStencilDescriptorHeap), D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
+
+	static int frameNumber = 0;
+	frameNumber++;
+}
+
+void kinc_g5_end(int window) {
+	began = false;
+}
+
+bool kinc_g5_vsynced() {
+	return true;
+}
+
+bool kinc_window_vsynced(int window) {
+	return true;
+}
+
+extern "C" void kinc_g4_on_g5_internal_resize(int, int, int);
+
+extern "C" void kinc_internal_resize(int windowId, int width, int height) {
+	if (width == 0 || height == 0)
+		return;
+	struct dx_window *window = &dx_ctx.windows[windowId];
+	window->new_width = width;
+	window->new_height = height;
+	kinc_g4_on_g5_internal_resize(windowId, width, height);
+}
+
+extern "C" void kinc_internal_change_framebuffer(int window, kinc_framebuffer_options_t *frame) {}
+
+#ifndef KINC_DIRECT3D_HAS_NO_SWAPCHAIN
+bool kinc_g5_swap_buffers() {
+	for (int i = 0; i < MAXIMUM_WINDOWS; i++) {
+		struct dx_window *window = &dx_ctx.windows[i];
+		if (window->swapChain) {
+			kinc_microsoft_affirm(window->swapChain->Present(window->vsync, 0));
+		}
+	}
+	return true;
+}
+#endif
+
+void kinc_g5_flush() {}
+
+bool kinc_g5_render_targets_inverted_y() {
+	return false;
+}
+
+bool kinc_g5_supports_raytracing() {
+	D3D12_FEATURE_DATA_D3D12_OPTIONS5 options;
+	if (device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options, sizeof(options)) == S_OK) {
+		return options.RaytracingTier >= D3D12_RAYTRACING_TIER_1_0;
+	}
+	return false;
+}
+
+bool kinc_g5_supports_instanced_rendering() {
+	return true;
+}
+
+bool kinc_g5_supports_compute_shaders() {
+	return true;
+}
+
+bool kinc_g5_supports_blend_constants() {
+	return true;
+}
+
+bool kinc_g5_supports_non_pow2_textures() {
+	return true;
+}

+ 11 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.c.h

@@ -0,0 +1,11 @@
+#include "ShaderHash.h"
+
+// djb2
+uint32_t kinc_internal_hash_name(unsigned char *str) {
+	unsigned long hash = 5381;
+	int c;
+	while ((c = *str++)) {
+		hash = hash * 33 ^ c;
+	}
+	return hash;
+}

+ 19 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	uint32_t hash;
+	uint32_t index;
+} kinc_internal_hash_index_t;
+
+uint32_t kinc_internal_hash_name(unsigned char *str);
+
+#ifdef __cplusplus
+}
+#endif

+ 611 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.c.h

@@ -0,0 +1,611 @@
+#include <kinc/graphics5/commandlist.h>
+#include <kinc/graphics5/compute.h>
+#include <kinc/graphics5/indexbuffer.h>
+#include <kinc/graphics5/pipeline.h>
+#include <kinc/graphics5/vertexbuffer.h>
+#include <kinc/graphics5/constantbuffer.h>
+#include <kinc/window.h>
+
+void createHeaps(kinc_g5_command_list_t *list);
+
+static int formatSize(DXGI_FORMAT format) {
+	switch (format) {
+	case DXGI_FORMAT_R32G32B32A32_FLOAT:
+		return 16;
+	case DXGI_FORMAT_R16G16B16A16_FLOAT:
+		return 8;
+	case DXGI_FORMAT_R16_FLOAT:
+		return 2;
+	case DXGI_FORMAT_R8_UNORM:
+		return 1;
+	default:
+		return 4;
+	}
+}
+
+void kinc_g5_command_list_init(struct kinc_g5_command_list *list) {
+#ifndef NDEBUG
+	list->impl.open = false;
+#endif
+
+	device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_GRAPHICS_PPV_ARGS(&list->impl._commandAllocator));
+	device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, list->impl._commandAllocator, NULL, IID_GRAPHICS_PPV_ARGS(&list->impl._commandList));
+
+	list->impl.fence_value = 0;
+	list->impl.fence_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+	device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(&list->impl.fence));
+
+	list->impl._indexCount = 0;
+
+	list->impl.current_full_scissor.left = -1;
+
+	for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) {
+		list->impl.currentRenderTargets[i] = NULL;
+		list->impl.currentTextures[i] = NULL;
+		list->impl.current_samplers[i] = NULL;
+	}
+	list->impl.heapIndex = 0;
+	createHeaps(list);
+}
+
+void kinc_g5_command_list_destroy(struct kinc_g5_command_list *list) {}
+
+void kinc_g5_internal_reset_textures(struct kinc_g5_command_list *list);
+
+void kinc_g5_command_list_begin(struct kinc_g5_command_list *list) {
+	assert(!list->impl.open);
+
+	compute_pipeline_set = false;
+
+	if (list->impl.fence_value > 0) {
+		waitForFence(list->impl.fence, list->impl.fence_value, list->impl.fence_event);
+		list->impl._commandAllocator->Reset();
+		list->impl._commandList->Reset(list->impl._commandAllocator, NULL);
+	}
+
+	kinc_g5_internal_reset_textures(list);
+
+#ifndef NDEBUG
+	list->impl.open = true;
+#endif
+}
+
+void kinc_g5_command_list_end(struct kinc_g5_command_list *list) {
+	assert(list->impl.open);
+
+	list->impl._commandList->Close();
+
+#ifndef NDEBUG
+	list->impl.open = false;
+#endif
+}
+
+void kinc_g5_command_list_clear(struct kinc_g5_command_list *list, kinc_g5_render_target_t *renderTarget, unsigned flags, unsigned color, float depth,
+                                int stencil) {
+	assert(list->impl.open);
+
+	if (flags & KINC_G5_CLEAR_COLOR) {
+		float clearColor[] = {((color & 0x00ff0000) >> 16) / 255.0f, ((color & 0x0000ff00) >> 8) / 255.0f, (color & 0x000000ff) / 255.0f,
+		                      ((color & 0xff000000) >> 24) / 255.0f};
+		list->impl._commandList->ClearRenderTargetView(renderTarget->impl.renderTargetDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), clearColor, 0,
+		                                               NULL);
+	}
+	if ((flags & KINC_G5_CLEAR_DEPTH) || (flags & KINC_G5_CLEAR_STENCIL)) {
+		D3D12_CLEAR_FLAGS d3dflags = (flags & KINC_G5_CLEAR_DEPTH) && (flags & KINC_G5_CLEAR_STENCIL) ? D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL
+		                             : (flags & KINC_G5_CLEAR_DEPTH)                                  ? D3D12_CLEAR_FLAG_DEPTH
+		                                                                                              : D3D12_CLEAR_FLAG_STENCIL;
+
+		if (renderTarget->impl.depthStencilDescriptorHeap != NULL) {
+			list->impl._commandList->ClearDepthStencilView(renderTarget->impl.depthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), d3dflags, depth,
+			                                               stencil, 0, NULL);
+		}
+	}
+}
+
+void kinc_g5_command_list_render_target_to_framebuffer_barrier(struct kinc_g5_command_list *list, kinc_g5_render_target_t *renderTarget) {
+	assert(list->impl.open);
+
+	D3D12_RESOURCE_BARRIER barrier;
+	barrier.Transition.pResource = renderTarget->impl.renderTarget;
+	barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+	barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+	barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
+	barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
+	barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+
+	list->impl._commandList->ResourceBarrier(1, &barrier);
+}
+
+void kinc_g5_command_list_framebuffer_to_render_target_barrier(struct kinc_g5_command_list *list, kinc_g5_render_target_t *renderTarget) {
+	assert(list->impl.open);
+
+	D3D12_RESOURCE_BARRIER barrier;
+	barrier.Transition.pResource = renderTarget->impl.renderTarget;
+	barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+	barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+	barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
+	barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
+	barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+
+	list->impl._commandList->ResourceBarrier(1, &barrier);
+}
+
+void kinc_g5_command_list_texture_to_render_target_barrier(struct kinc_g5_command_list *list, kinc_g5_render_target_t *renderTarget) {
+	assert(list->impl.open);
+
+	D3D12_RESOURCE_BARRIER barrier;
+	barrier.Transition.pResource = renderTarget->impl.renderTarget;
+	barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+	barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+	barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
+	barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
+	barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+
+	list->impl._commandList->ResourceBarrier(1, &barrier);
+}
+
+void kinc_g5_command_list_render_target_to_texture_barrier(struct kinc_g5_command_list *list, kinc_g5_render_target_t *renderTarget) {
+	assert(list->impl.open);
+
+	D3D12_RESOURCE_BARRIER barrier;
+	barrier.Transition.pResource = renderTarget->impl.renderTarget;
+	barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+	barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+	barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
+	barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
+	barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+
+	list->impl._commandList->ResourceBarrier(1, &barrier);
+}
+
+void kinc_g5_command_list_set_vertex_constant_buffer(struct kinc_g5_command_list *list, kinc_g5_constant_buffer_t *buffer, int offset, size_t size) {
+	assert(list->impl.open);
+
+#ifdef KINC_DXC
+	if (list->impl._currentPipeline->impl.vertexConstantsSize > 0) {
+		if (list->impl._currentPipeline->impl.textures > 0) {
+			list->impl._commandList->SetGraphicsRootConstantBufferView(2, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset);
+		}
+		else {
+			list->impl._commandList->SetGraphicsRootConstantBufferView(0, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset);
+		}
+	}
+#else
+	list->impl._commandList->SetGraphicsRootConstantBufferView(2, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset);
+#endif
+}
+
+void kinc_g5_command_list_set_fragment_constant_buffer(struct kinc_g5_command_list *list, kinc_g5_constant_buffer_t *buffer, int offset, size_t size) {
+	assert(list->impl.open);
+
+#ifdef KINC_DXC
+	if (list->impl._currentPipeline->impl.fragmentConstantsSize > 0) {
+		list->impl._commandList->SetGraphicsRootConstantBufferView(3, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset);
+	}
+#else
+	list->impl._commandList->SetGraphicsRootConstantBufferView(3, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset);
+#endif
+}
+
+void kinc_g5_command_list_set_compute_constant_buffer(struct kinc_g5_command_list *list, kinc_g5_constant_buffer_t *buffer, int offset, size_t size) {
+	assert(list->impl.open);
+
+#ifdef KINC_DXC
+	if (list->impl._currentPipeline->impl.fragmentConstantsSize > 0) {
+		list->impl._commandList->SetGraphicsRootConstantBufferView(3, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset);
+	}
+#else
+	list->impl._commandList->SetComputeRootConstantBufferView(3, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset);
+#endif
+}
+
+void kinc_g5_command_list_draw_indexed_vertices(struct kinc_g5_command_list *list) {
+	kinc_g5_command_list_draw_indexed_vertices_from_to(list, 0, list->impl._indexCount);
+}
+
+void kinc_g5_command_list_draw_indexed_vertices_from_to(struct kinc_g5_command_list *list, int start, int count) {
+	assert(list->impl.open);
+
+	list->impl._commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+
+	/*u8* data;
+	D3D12_RANGE range;
+	range.Begin = currentConstantBuffer * sizeof(vertexConstants);
+	range.End = range.Begin + sizeof(vertexConstants);
+	vertexConstantBuffer->Map(0, &range, (void**)&data);
+	memcpy(data + currentConstantBuffer * sizeof(vertexConstants), vertexConstants, sizeof(vertexConstants));
+	vertexConstantBuffer->Unmap(0, &range);
+
+	range.Begin = currentConstantBuffer * sizeof(fragmentConstants);
+	range.End = range.Begin + sizeof(fragmentConstants);
+	fragmentConstantBuffer->Map(0, &range, (void**)&data);
+	memcpy(data + currentConstantBuffer * sizeof(fragmentConstants), fragmentConstants, sizeof(fragmentConstants));
+	fragmentConstantBuffer->Unmap(0, &range);
+
+	_commandList->SetGraphicsRootConstantBufferView(1, vertexConstantBuffer->GetGPUVirtualAddress() + currentConstantBuffer * sizeof(vertexConstants));
+	_commandList->SetGraphicsRootConstantBufferView(2, fragmentConstantBuffer->GetGPUVirtualAddress() + currentConstantBuffer * sizeof(fragmentConstants));
+
+	++currentConstantBuffer;
+	if (currentConstantBuffer >= constantBufferMultiply) {
+	    currentConstantBuffer = 0;
+	}*/
+
+	list->impl._commandList->DrawIndexedInstanced(count, 1, start, 0, 0);
+}
+
+void kinc_g5_command_list_draw_indexed_vertices_from_to_from(struct kinc_g5_command_list *list, int start, int count, int vertex_offset) {
+	assert(list->impl.open);
+
+	list->impl._commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+	list->impl._commandList->DrawIndexedInstanced(count, 1, start, vertex_offset, 0);
+}
+
+void kinc_g5_command_list_draw_indexed_vertices_instanced(kinc_g5_command_list_t *list, int instanceCount) {
+	kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(list, instanceCount, 0, list->impl._indexCount);
+}
+void kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(kinc_g5_command_list_t *list, int instanceCount, int start, int count) {
+	assert(list->impl.open);
+
+	list->impl._commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+	list->impl._commandList->DrawIndexedInstanced(count, instanceCount, start, 0, 0);
+}
+
+void kinc_g5_command_list_execute(kinc_g5_command_list_t *list) {
+	assert(!list->impl.open);
+
+	ID3D12CommandList *commandLists[] = {(ID3D12CommandList *)list->impl._commandList};
+	commandQueue->ExecuteCommandLists(1, commandLists);
+
+	commandQueue->Signal(list->impl.fence, ++list->impl.fence_value);
+}
+
+void kinc_g5_command_list_wait_for_execution_to_finish(kinc_g5_command_list_t *list) {
+	waitForFence(list->impl.fence, list->impl.fence_value, list->impl.fence_event);
+}
+
+bool kinc_g5_non_pow2_textures_supported(void) {
+	return true;
+}
+
+void kinc_g5_command_list_viewport(struct kinc_g5_command_list *list, int x, int y, int width, int height) {
+	assert(list->impl.open);
+
+	D3D12_VIEWPORT viewport;
+	viewport.TopLeftX = (float)x;
+	viewport.TopLeftY = (float)y;
+	viewport.Width = (float)width;
+	viewport.Height = (float)height;
+	viewport.MinDepth = 0.0f;
+	viewport.MaxDepth = 1.0f;
+	list->impl._commandList->RSSetViewports(1, &viewport);
+}
+
+void kinc_g5_command_list_scissor(struct kinc_g5_command_list *list, int x, int y, int width, int height) {
+	assert(list->impl.open);
+
+	D3D12_RECT scissor;
+	scissor.left = x;
+	scissor.top = y;
+	scissor.right = x + width;
+	scissor.bottom = y + height;
+	list->impl._commandList->RSSetScissorRects(1, &scissor);
+}
+
+void kinc_g5_command_list_disable_scissor(struct kinc_g5_command_list *list) {
+	assert(list->impl.open);
+
+	if (list->impl.current_full_scissor.left >= 0) {
+		list->impl._commandList->RSSetScissorRects(1, (D3D12_RECT *)&list->impl.current_full_scissor);
+	}
+	else {
+		D3D12_RECT scissor;
+		scissor.left = 0;
+		scissor.top = 0;
+		scissor.right = kinc_window_width(0);
+		scissor.bottom = kinc_window_height(0);
+		list->impl._commandList->RSSetScissorRects(1, &scissor);
+	}
+}
+
+void kinc_g5_command_list_set_pipeline(struct kinc_g5_command_list *list, kinc_g5_pipeline_t *pipeline) {
+	assert(list->impl.open);
+
+	list->impl._currentPipeline = pipeline;
+	list->impl._commandList->SetPipelineState(pipeline->impl.pso);
+	compute_pipeline_set = false;
+
+	for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) {
+		list->impl.currentRenderTargets[i] = NULL;
+		list->impl.currentTextures[i] = NULL;
+	}
+	kinc_g5_internal_setConstants(list, list->impl._currentPipeline);
+}
+
+void kinc_g5_command_list_set_blend_constant(kinc_g5_command_list_t *list, float r, float g, float b, float a) {
+	const FLOAT BlendFactor[4] = {r, g, b, a};
+	list->impl._commandList->OMSetBlendFactor(BlendFactor);
+}
+
+void kinc_g5_command_list_set_vertex_buffers(struct kinc_g5_command_list *list, kinc_g5_vertex_buffer_t **buffers, int *offsets, int count) {
+	assert(list->impl.open);
+
+	D3D12_VERTEX_BUFFER_VIEW *views = (D3D12_VERTEX_BUFFER_VIEW *)alloca(sizeof(D3D12_VERTEX_BUFFER_VIEW) * count);
+	ZeroMemory(views, sizeof(D3D12_VERTEX_BUFFER_VIEW) * count);
+	for (int i = 0; i < count; ++i) {
+		views[i].BufferLocation = buffers[i]->impl.uploadBuffer->GetGPUVirtualAddress() + offsets[i] * kinc_g5_vertex_buffer_stride(buffers[i]);
+		views[i].SizeInBytes = (kinc_g5_vertex_buffer_count(buffers[i]) - offsets[i]) * kinc_g5_vertex_buffer_stride(buffers[i]);
+		views[i].StrideInBytes = kinc_g5_vertex_buffer_stride(buffers[i]); // * kinc_g5_vertex_buffer_count(buffers[i]);
+	}
+	list->impl._commandList->IASetVertexBuffers(0, count, views);
+}
+
+void kinc_g5_command_list_set_index_buffer(struct kinc_g5_command_list *list, kinc_g5_index_buffer_t *buffer) {
+	assert(list->impl.open);
+
+	list->impl._indexCount = kinc_g5_index_buffer_count(buffer);
+	list->impl._commandList->IASetIndexBuffer((D3D12_INDEX_BUFFER_VIEW *)&buffer->impl.index_buffer_view);
+}
+
+void kinc_g5_command_list_set_render_targets(struct kinc_g5_command_list *list, kinc_g5_render_target_t **targets, int count) {
+	assert(list->impl.open);
+
+	kinc_g5_render_target_t *render_target = targets[0];
+
+	D3D12_CPU_DESCRIPTOR_HANDLE target_descriptors[16];
+	for (int i = 0; i < count; ++i) {
+		target_descriptors[i] = targets[i]->impl.renderTargetDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+	}
+
+	assert(render_target != NULL);
+
+	if (render_target->impl.depthStencilDescriptorHeap != NULL) {
+		D3D12_CPU_DESCRIPTOR_HANDLE heapStart = render_target->impl.depthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+		list->impl._commandList->OMSetRenderTargets(count, &target_descriptors[0], false, &heapStart);
+	}
+	else {
+		list->impl._commandList->OMSetRenderTargets(count, &target_descriptors[0], false, NULL);
+	}
+
+	list->impl._commandList->RSSetViewports(1, (D3D12_VIEWPORT *)&render_target->impl.viewport);
+	list->impl._commandList->RSSetScissorRects(1, (D3D12_RECT *)&render_target->impl.scissor);
+
+	list->impl.current_full_scissor = render_target->impl.scissor;
+}
+
+void kinc_g5_command_list_upload_vertex_buffer(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer *buffer) {}
+
+void kinc_g5_command_list_upload_index_buffer(kinc_g5_command_list_t *list, kinc_g5_index_buffer_t *buffer) {
+	assert(list->impl.open);
+	kinc_g5_internal_index_buffer_upload(buffer, list->impl._commandList);
+}
+
+void kinc_g5_command_list_upload_texture(kinc_g5_command_list_t *list, kinc_g5_texture_t *texture) {
+	assert(list->impl.open);
+
+	{
+		D3D12_RESOURCE_BARRIER transition = {};
+		transition.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+		transition.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+		transition.Transition.pResource = texture->impl.image;
+		transition.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
+		transition.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
+		transition.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+
+		list->impl._commandList->ResourceBarrier(1, &transition);
+	}
+
+	D3D12_RESOURCE_DESC Desc = texture->impl.image->GetDesc();
+	ID3D12Device *device = NULL;
+	texture->impl.image->GetDevice(IID_GRAPHICS_PPV_ARGS(&device));
+	D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
+	device->GetCopyableFootprints(&Desc, 0, 1, 0, &footprint, NULL, NULL, NULL);
+	device->Release();
+
+	D3D12_TEXTURE_COPY_LOCATION source = {0};
+	source.pResource = texture->impl.uploadImage;
+	source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+	source.PlacedFootprint = footprint;
+
+	D3D12_TEXTURE_COPY_LOCATION destination = {0};
+	destination.pResource = texture->impl.image;
+	destination.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+	destination.SubresourceIndex = 0;
+
+	list->impl._commandList->CopyTextureRegion(&destination, 0, 0, 0, &source, NULL);
+
+	{
+		D3D12_RESOURCE_BARRIER transition = {};
+		transition.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+		transition.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+		transition.Transition.pResource = texture->impl.image;
+		transition.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
+		transition.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
+		transition.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+
+		list->impl._commandList->ResourceBarrier(1, &transition);
+	}
+}
+
+#if defined(KINC_WINDOWS)
+static int d3d12_textureAlignment() {
+	return D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
+}
+#else
+extern "C" int d3d12_textureAlignment();
+#endif
+
+void kinc_g5_command_list_get_render_target_pixels(kinc_g5_command_list_t *list, kinc_g5_render_target_t *render_target, uint8_t *data) {
+	assert(list->impl.open);
+
+	DXGI_FORMAT dxgiFormat = render_target->impl.renderTarget->GetDesc().Format;
+	int formatByteSize = formatSize(dxgiFormat);
+	int rowPitch = render_target->texWidth * formatByteSize;
+	int align = rowPitch % d3d12_textureAlignment();
+	if (align != 0)
+		rowPitch = rowPitch + (d3d12_textureAlignment() - align);
+
+	// Create readback buffer
+	if (render_target->impl.renderTargetReadback == NULL) {
+		D3D12_HEAP_PROPERTIES heapProperties;
+		heapProperties.Type = D3D12_HEAP_TYPE_READBACK;
+		heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+		heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+		heapProperties.CreationNodeMask = 1;
+		heapProperties.VisibleNodeMask = 1;
+
+		D3D12_RESOURCE_DESC resourceDesc;
+		resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+		resourceDesc.Alignment = 0;
+		resourceDesc.Width = rowPitch * render_target->texHeight;
+		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;
+
+		device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, NULL,
+		                                IID_GRAPHICS_PPV_ARGS(&render_target->impl.renderTargetReadback));
+	}
+
+	// Copy render target to readback buffer
+	D3D12_RESOURCE_STATES sourceState = D3D12_RESOURCE_STATE_RENDER_TARGET;
+
+	{
+		D3D12_RESOURCE_BARRIER barrier;
+		barrier.Transition.pResource = render_target->impl.renderTarget;
+		barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+		barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+		barrier.Transition.StateBefore = sourceState;
+		barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
+		barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+		list->impl._commandList->ResourceBarrier(1, &barrier);
+	}
+
+	D3D12_TEXTURE_COPY_LOCATION source;
+	source.pResource = render_target->impl.renderTarget;
+	source.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+	source.SubresourceIndex = 0;
+
+	D3D12_TEXTURE_COPY_LOCATION dest;
+	dest.pResource = render_target->impl.renderTargetReadback;
+	dest.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+	dest.PlacedFootprint.Offset = 0;
+	dest.PlacedFootprint.Footprint.Format = dxgiFormat;
+	dest.PlacedFootprint.Footprint.Width = render_target->texWidth;
+	dest.PlacedFootprint.Footprint.Height = render_target->texHeight;
+	dest.PlacedFootprint.Footprint.Depth = 1;
+	dest.PlacedFootprint.Footprint.RowPitch = rowPitch;
+
+	list->impl._commandList->CopyTextureRegion(&dest, 0, 0, 0, &source, NULL);
+
+	{
+		D3D12_RESOURCE_BARRIER barrier;
+		barrier.Transition.pResource = render_target->impl.renderTarget;
+		barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+		barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+		barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
+		barrier.Transition.StateAfter = sourceState;
+		barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+		list->impl._commandList->ResourceBarrier(1, &barrier);
+	}
+
+	kinc_g5_command_list_end(list);
+	kinc_g5_command_list_execute(list);
+	kinc_g5_command_list_wait_for_execution_to_finish(list);
+	kinc_g5_command_list_begin(list);
+
+	// Read buffer
+	void *p;
+	render_target->impl.renderTargetReadback->Map(0, NULL, &p);
+	memcpy(data, p, render_target->texWidth * render_target->texHeight * formatByteSize);
+	render_target->impl.renderTargetReadback->Unmap(0, NULL);
+}
+
+void kinc_g5_internal_set_compute_constants(kinc_g5_command_list_t *commandList);
+
+void kinc_g5_command_list_set_compute_shader(kinc_g5_command_list_t *list, kinc_g5_compute_shader *shader) {
+	list->impl._commandList->SetPipelineState(shader->impl.pso);
+	compute_pipeline_set = true;
+
+	for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) {
+		list->impl.currentRenderTargets[i] = NULL;
+		list->impl.currentTextures[i] = NULL;
+	}
+	kinc_g5_internal_set_compute_constants(list);
+}
+
+void kinc_g5_command_list_compute(kinc_g5_command_list_t *list, int x, int y, int z) {
+	assert(list->impl.open);
+	list->impl._commandList->Dispatch(x, y, z);
+}
+
+void kinc_g5_command_list_set_render_target_face(kinc_g5_command_list_t *list, kinc_g5_render_target_t *texture, int face) {}
+
+void kinc_g5_command_list_set_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) {
+	if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) {
+		kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]);
+	}
+	else if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) {
+		kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_VERTEX]);
+	}
+	else if (unit.stages[KINC_G5_SHADER_TYPE_COMPUTE] >= 0) {
+		kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_COMPUTE]);
+	}
+	kinc_g5_internal_set_textures(list);
+}
+
+void kinc_g5_internal_sampler_set(kinc_g5_command_list_t *list, kinc_g5_sampler_t *sampler, int unit);
+
+void kinc_g5_command_list_set_sampler(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_sampler_t *sampler) {
+	if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) {
+		kinc_g5_internal_sampler_set(list, sampler, unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]);
+	}
+	else if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) {
+		kinc_g5_internal_sampler_set(list, sampler, unit.stages[KINC_G5_SHADER_TYPE_VERTEX]);
+	}
+	kinc_g5_internal_set_textures(list);
+}
+
+void kinc_g5_command_list_set_image_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) {
+	if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) {
+		kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]);
+	}
+	else if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) {
+		kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_VERTEX]);
+	}
+	else if (unit.stages[KINC_G5_SHADER_TYPE_COMPUTE] >= 0) {
+		kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_COMPUTE]);
+	}
+	kinc_g5_internal_set_textures(list);
+}
+
+void kinc_g5_command_list_set_texture_from_render_target(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *target) {
+	if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) {
+		target->impl.stage = unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT];
+		list->impl.currentRenderTargets[target->impl.stage] = target;
+	}
+	else if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) {
+		target->impl.stage = unit.stages[KINC_G5_SHADER_TYPE_VERTEX];
+		list->impl.currentRenderTargets[target->impl.stage] = target;
+	}
+	list->impl.currentTextures[target->impl.stage] = NULL;
+
+	kinc_g5_internal_set_textures(list);
+}
+
+void kinc_g5_command_list_set_texture_from_render_target_depth(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *target) {
+	if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) {
+		target->impl.stage_depth = unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT];
+		list->impl.currentRenderTargets[target->impl.stage_depth] = target;
+	}
+	else if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) {
+		target->impl.stage_depth = unit.stages[KINC_G5_SHADER_TYPE_VERTEX];
+		list->impl.currentRenderTargets[target->impl.stage_depth] = target;
+	}
+	list->impl.currentTextures[target->impl.stage_depth] = NULL;
+
+	kinc_g5_internal_set_textures(list);
+}

+ 46 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.h

@@ -0,0 +1,46 @@
+#pragma once
+
+#include <stdint.h>
+
+#include "d3d12mini.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct kinc_g5_pipeline;
+struct kinc_g5_render_target;
+struct kinc_g5_texture;
+struct kinc_g5_sampler;
+
+#define KINC_INTERNAL_G5_TEXTURE_COUNT 16
+
+typedef struct {
+	struct ID3D12CommandAllocator *_commandAllocator;
+	struct ID3D12GraphicsCommandList *_commandList;
+	struct kinc_g5_pipeline *_currentPipeline;
+	int _indexCount;
+
+#ifndef NDEBUG
+	bool open;
+#endif
+
+	struct D3D12Rect current_full_scissor;
+
+	// keep track of when a command-list is done
+	uint64_t fence_value;
+	struct ID3D12Fence *fence;
+	HANDLE fence_event;
+
+	struct kinc_g5_render_target *currentRenderTargets[KINC_INTERNAL_G5_TEXTURE_COUNT];
+	struct kinc_g5_texture *currentTextures[KINC_INTERNAL_G5_TEXTURE_COUNT];
+	struct kinc_g5_sampler *current_samplers[KINC_INTERNAL_G5_TEXTURE_COUNT];
+
+	int heapIndex;
+	struct ID3D12DescriptorHeap *srvHeap;
+	struct ID3D12DescriptorHeap *samplerHeap;
+} CommandList5Impl;
+
+#ifdef __cplusplus
+}
+#endif

+ 167 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.c.h

@@ -0,0 +1,167 @@
+#include <kinc/graphics5/compute.h>
+
+#include <kinc/graphics4/texture.h>
+#include <kinc/log.h>
+#include <kinc/math/core.h>
+
+#include <kinc/backend/SystemMicrosoft.h>
+
+void kinc_g5_compute_shader_init(kinc_g5_compute_shader *shader, void *_data, int length) {
+	unsigned index = 0;
+	uint8_t *data = (uint8_t *)_data;
+
+	memset(&shader->impl.attributes, 0, sizeof(shader->impl.attributes));
+	int attributesCount = data[index++];
+	for (int i = 0; i < attributesCount; ++i) {
+		unsigned char name[256];
+		for (unsigned i2 = 0; i2 < 255; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		shader->impl.attributes[i].hash = kinc_internal_hash_name(name);
+		shader->impl.attributes[i].index = data[index++];
+	}
+
+	memset(&shader->impl.textures, 0, sizeof(shader->impl.textures));
+	uint8_t texCount = data[index++];
+	for (unsigned i = 0; i < texCount; ++i) {
+		unsigned char name[256];
+		for (unsigned i2 = 0; i2 < 255; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		shader->impl.textures[i].hash = kinc_internal_hash_name(name);
+		shader->impl.textures[i].index = data[index++];
+	}
+
+	memset(&shader->impl.constants, 0, sizeof(shader->impl.constants));
+	uint8_t constantCount = data[index++];
+	shader->impl.constantsSize = 0;
+	for (unsigned i = 0; i < constantCount; ++i) {
+		unsigned char name[256];
+		for (unsigned i2 = 0; i2 < 255; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		kinc_compute_internal_shader_constant_t constant;
+		constant.hash = kinc_internal_hash_name(name);
+		memcpy(&constant.offset, &data[index], sizeof(constant.offset));
+		index += 4;
+		memcpy(&constant.size, &data[index], sizeof(constant.size));
+		index += 4;
+		constant.columns = data[index];
+		index += 1;
+		constant.rows = data[index];
+		index += 1;
+
+		shader->impl.constants[i] = constant;
+		shader->impl.constantsSize = constant.offset + constant.size;
+	}
+
+	shader->impl.length = (int)(length - index);
+	shader->impl.data = (uint8_t *)malloc(shader->impl.length);
+	assert(shader->impl.data != NULL);
+	memcpy(shader->impl.data, &data[index], shader->impl.length);
+
+	D3D12_COMPUTE_PIPELINE_STATE_DESC desc = {0};
+	desc.CS.BytecodeLength = shader->impl.length;
+	desc.CS.pShaderBytecode = shader->impl.data;
+	desc.pRootSignature = globalComputeRootSignature;
+	HRESULT hr = device->CreateComputePipelineState(&desc, IID_GRAPHICS_PPV_ARGS(&shader->impl.pso));
+
+	if (hr != S_OK) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize compute shader.");
+		return;
+	}
+
+	// kinc_microsoft_affirm(device->CreateBuffer(&CD3D11_BUFFER_DESC(getMultipleOf16(shader->impl.constantsSize), D3D11_BIND_CONSTANT_BUFFER), nullptr,
+	// &shader->impl.constantBuffer));
+}
+
+void kinc_g5_compute_shader_destroy(kinc_g5_compute_shader *shader) {
+	if (shader->impl.pso != NULL) {
+		shader->impl.pso->Release();
+		shader->impl.pso = NULL;
+	}
+}
+
+static kinc_compute_internal_shader_constant_t *findComputeConstant(kinc_compute_internal_shader_constant_t *constants, uint32_t hash) {
+	for (int i = 0; i < 64; ++i) {
+		if (constants[i].hash == hash) {
+			return &constants[i];
+		}
+	}
+	return NULL;
+}
+
+static kinc_internal_hash_index_t *findComputeTextureUnit(kinc_internal_hash_index_t *units, uint32_t hash) {
+	for (int i = 0; i < 64; ++i) {
+		if (units[i].hash == hash) {
+			return &units[i];
+		}
+	}
+	return NULL;
+}
+
+kinc_g5_constant_location_t kinc_g5_compute_shader_get_constant_location(kinc_g5_compute_shader *shader, const char *name) {
+	kinc_g5_constant_location_t location = {0};
+
+	uint32_t hash = kinc_internal_hash_name((unsigned char *)name);
+
+	kinc_compute_internal_shader_constant_t *constant = findComputeConstant(shader->impl.constants, hash);
+	if (constant == NULL) {
+		location.impl.computeOffset = 0;
+		location.impl.computeSize = 0;
+	}
+	else {
+		location.impl.computeOffset = constant->offset;
+		location.impl.computeSize = constant->size;
+	}
+
+	if (location.impl.computeSize == 0) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "Uniform %s not found.", name);
+	}
+
+	return location;
+}
+
+kinc_g5_texture_unit_t kinc_g5_compute_shader_get_texture_unit(kinc_g5_compute_shader *shader, const char *name) {
+	char unitName[64];
+	int unitOffset = 0;
+	size_t len = strlen(name);
+	if (len > 63)
+		len = 63;
+	strncpy(unitName, name, len + 1);
+	if (unitName[len - 1] == ']') {                  // Check for array - mySampler[2]
+		unitOffset = (int)(unitName[len - 2] - '0'); // Array index is unit offset
+		unitName[len - 3] = 0;                       // Strip array from name
+	}
+
+	uint32_t hash = kinc_internal_hash_name((unsigned char *)unitName);
+
+	kinc_g5_texture_unit_t unit;
+	for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) {
+		unit.stages[i] = -1;
+	}
+	kinc_internal_hash_index_t *computeUnit = findComputeTextureUnit(shader->impl.textures, hash);
+	if (computeUnit == NULL) {
+#ifndef NDEBUG
+		static int notFoundCount = 0;
+		if (notFoundCount < 10) {
+			kinc_log(KINC_LOG_LEVEL_WARNING, "Sampler %s not found.", unitName);
+			++notFoundCount;
+		}
+		else if (notFoundCount == 10) {
+			kinc_log(KINC_LOG_LEVEL_WARNING, "Giving up on sampler not found messages.", unitName);
+			++notFoundCount;
+		}
+#endif
+	}
+	else {
+		unit.stages[KINC_G5_SHADER_TYPE_COMPUTE] = computeUnit->index + unitOffset;
+	}
+	return unit;
+}

+ 30 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.h

@@ -0,0 +1,30 @@
+#pragma once
+
+#include <kinc/backend/graphics5/ShaderHash.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	uint32_t hash;
+	uint32_t offset;
+	uint32_t size;
+	uint8_t columns;
+	uint8_t rows;
+} kinc_compute_internal_shader_constant_t;
+
+typedef struct kinc_g5_compute_shader_impl {
+	kinc_compute_internal_shader_constant_t constants[64];
+	int constantsSize;
+	kinc_internal_hash_index_t attributes[64];
+	kinc_internal_hash_index_t textures[64];
+	uint8_t *data;
+	int length;
+	struct ID3D12Buffer *constantBuffer;
+	struct ID3D12PipelineState *pso;
+} kinc_g5_compute_shader_impl;
+
+#ifdef __cplusplus
+}
+#endif

+ 69 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.c.h

@@ -0,0 +1,69 @@
+
+#include <kinc/backend/SystemMicrosoft.h>
+
+bool kinc_g5_transposeMat3 = false;
+bool kinc_g5_transposeMat4 = false;
+
+void kinc_g5_constant_buffer_init(kinc_g5_constant_buffer_t *buffer, int size) {
+	buffer->impl.mySize = size;
+	buffer->data = NULL;
+
+	D3D12_HEAP_PROPERTIES heapProperties;
+	heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
+	heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+	heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+	heapProperties.CreationNodeMask = 1;
+	heapProperties.VisibleNodeMask = 1;
+
+	D3D12_RESOURCE_DESC resourceDesc;
+	resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+	resourceDesc.Alignment = 0;
+	resourceDesc.Width = size;
+	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;
+
+	kinc_microsoft_affirm(device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+	                                                      IID_GRAPHICS_PPV_ARGS(&buffer->impl.constant_buffer)));
+
+	void *p;
+	buffer->impl.constant_buffer->Map(0, NULL, &p);
+	ZeroMemory(p, size);
+	buffer->impl.constant_buffer->Unmap(0, NULL);
+}
+
+void kinc_g5_constant_buffer_destroy(kinc_g5_constant_buffer_t *buffer) {
+	buffer->impl.constant_buffer->Release();
+}
+
+void kinc_g5_constant_buffer_lock_all(kinc_g5_constant_buffer_t *buffer) {
+	kinc_g5_constant_buffer_lock(buffer, 0, kinc_g5_constant_buffer_size(buffer));
+}
+
+void kinc_g5_constant_buffer_lock(kinc_g5_constant_buffer_t *buffer, int start, int count) {
+	buffer->impl.lastStart = start;
+	buffer->impl.lastCount = count;
+	D3D12_RANGE range;
+	range.Begin = start;
+	range.End = range.Begin + count;
+	uint8_t *p;
+	buffer->impl.constant_buffer->Map(0, &range, (void **)&p);
+	buffer->data = &p[start];
+}
+
+void kinc_g5_constant_buffer_unlock(kinc_g5_constant_buffer_t *buffer) {
+	D3D12_RANGE range;
+	range.Begin = buffer->impl.lastStart;
+	range.End = range.Begin + buffer->impl.lastCount;
+	buffer->impl.constant_buffer->Unmap(0, &range);
+	buffer->data = NULL;
+}
+
+int kinc_g5_constant_buffer_size(kinc_g5_constant_buffer_t *buffer) {
+	return buffer->impl.mySize;
+}

+ 21 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.h

@@ -0,0 +1,21 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ID3D12Resource;
+
+typedef struct {
+	struct ID3D12Resource *constant_buffer;
+	int lastStart;
+	int lastCount;
+	int mySize;
+} ConstantBuffer5Impl;
+
+extern bool kinc_g5_transposeMat3;
+extern bool kinc_g5_transposeMat4;
+
+#ifdef __cplusplus
+}
+#endif

+ 56 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12mini.h

@@ -0,0 +1,56 @@
+#pragma once
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ID3D12CommandAllocator;
+struct ID3D12GraphicsCommandList;
+struct ID3D12Fence;
+struct ID3D12Resource;
+struct ID3D12DescriptorHeap;
+struct IDXGISwapChain;
+
+typedef void *HANDLE;
+typedef unsigned __int64 UINT64;
+
+struct D3D12Viewport {
+	float TopLeftX;
+	float TopLeftY;
+	float Width;
+	float Height;
+	float MinDepth;
+	float MaxDepth;
+};
+
+struct D3D12Rect {
+	long left;
+	long top;
+	long right;
+	long bottom;
+};
+
+#define QUEUE_SLOT_COUNT 2
+
+struct dx_window {
+#ifndef KINC_DIRECT3D_HAS_NO_SWAPCHAIN
+	struct IDXGISwapChain *swapChain;
+#endif
+	UINT64 current_fence_value;
+	UINT64 fence_values[QUEUE_SLOT_COUNT];
+	HANDLE frame_fence_events[QUEUE_SLOT_COUNT];
+	struct ID3D12Fence *frame_fences[QUEUE_SLOT_COUNT];
+	int width;
+	int height;
+	int new_width;
+	int new_height;
+	int current_backbuffer;
+	bool vsync;
+	int window_index;
+};
+
+#ifdef __cplusplus
+}
+#endif

+ 106 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12unit.cpp

@@ -0,0 +1,106 @@
+#include <kinc/global.h>
+
+// Windows 7
+#define WINVER 0x0601
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0601
+
+#define NOATOM
+#define NOCLIPBOARD
+#define NOCOLOR
+#define NOCOMM
+#define NOCTLMGR
+#define NODEFERWINDOWPOS
+#define NODRAWTEXT
+#define NOGDI
+#define NOGDICAPMASKS
+#define NOHELP
+#define NOICONS
+#define NOKANJI
+#define NOKEYSTATES
+#define NOMB
+#define NOMCX
+#define NOMEMMGR
+#define NOMENUS
+#define NOMETAFILE
+#define NOMINMAX
+// #define NOMSG
+#define NONLS
+#define NOOPENFILE
+#define NOPROFILER
+#define NORASTEROPS
+#define NOSCROLL
+#define NOSERVICE
+#define NOSHOWWINDOW
+#define NOSOUND
+#define NOSYSCOMMANDS
+#define NOSYSMETRICS
+#define NOTEXTMETRIC
+// #define NOUSER
+#define NOVIRTUALKEYCODES
+#define NOWH
+#define NOWINMESSAGES
+#define NOWINOFFSETS
+#define NOWINSTYLES
+#define WIN32_LEAN_AND_MEAN
+
+#ifdef KINC_WINDOWS
+#include <d3d12.h>
+#else
+#include <kinc/backend/d3d12_special_edition.h>
+#endif
+#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN
+struct DXGI_SWAP_CHAIN_DESC1;
+#else
+#include <dxgi.h>
+#endif
+
+#include "d3d12mini.h"
+
+#ifndef IID_GRAPHICS_PPV_ARGS
+#define IID_GRAPHICS_PPV_ARGS(x) IID_PPV_ARGS(x)
+#endif
+
+extern "C" {
+ID3D12Device *device = NULL;
+}
+static ID3D12RootSignature *globalRootSignature = NULL;
+static ID3D12RootSignature *globalComputeRootSignature = NULL;
+// extern ID3D12GraphicsCommandList* commandList;
+
+#include <stdbool.h>
+
+#define MAXIMUM_WINDOWS 16
+
+struct dx_ctx {
+	int current_window;
+	struct dx_window windows[MAXIMUM_WINDOWS];
+};
+
+static struct dx_ctx dx_ctx = {0};
+
+inline struct dx_window *kinc_dx_current_window() {
+	return &dx_ctx.windows[dx_ctx.current_window];
+}
+
+static bool compute_pipeline_set = false;
+
+#include <assert.h>
+#include <malloc.h>
+#include <stdbool.h>
+
+#include "Direct3D12.c.h"
+#include "ShaderHash.c.h"
+#include "commandlist.c.h"
+#include "compute.c.h"
+#include "constantbuffer.c.h"
+#include "indexbuffer.c.h"
+#include "pipeline.c.h"
+#include "raytrace.c.h"
+#include "rendertarget.c.h"
+#include "sampler.c.h"
+#include "shader.c.h"
+#include "texture.c.h"
+#include "vertexbuffer.c.h"

+ 6 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/graphics.h

@@ -0,0 +1,6 @@
+#pragma once
+
+#include <kinc/backend/graphics5/indexbuffer.h>
+#include <kinc/backend/graphics5/rendertarget.h>
+#include <kinc/backend/graphics5/texture.h>
+#include <kinc/backend/graphics5/vertexbuffer.h>

+ 130 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.c.h

@@ -0,0 +1,130 @@
+#include "indexbuffer.h"
+
+#include <kinc/backend/SystemMicrosoft.h>
+#include <kinc/graphics5/indexbuffer.h>
+
+void kinc_g5_index_buffer_init(kinc_g5_index_buffer_t *buffer, int count, kinc_g5_index_buffer_format_t format, bool gpuMemory) {
+	buffer->impl.count = count;
+	buffer->impl.gpu_memory = gpuMemory;
+	buffer->impl.format = format;
+
+	// static_assert(sizeof(D3D12IindexBufferView) == sizeof(D3D12_INDEX_BUFFER_VIEW), "Something is wrong with D3D12IindexBufferView");
+	int uploadBufferSize = format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? sizeof(uint16_t) * count : sizeof(uint32_t) * count;
+
+	D3D12_HEAP_PROPERTIES heapProperties;
+	heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
+	heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+	heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+	heapProperties.CreationNodeMask = 1;
+	heapProperties.VisibleNodeMask = 1;
+
+	D3D12_RESOURCE_DESC resourceDesc;
+	resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+	resourceDesc.Alignment = 0;
+	resourceDesc.Width = uploadBufferSize;
+	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;
+
+	kinc_microsoft_affirm(device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+	                                                      IID_GRAPHICS_PPV_ARGS(&buffer->impl.upload_buffer)));
+
+	if (gpuMemory) {
+		D3D12_HEAP_PROPERTIES heapProperties;
+		heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
+		heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+		heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+		heapProperties.CreationNodeMask = 1;
+		heapProperties.VisibleNodeMask = 1;
+
+		kinc_microsoft_affirm(device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, NULL,
+		                                                      IID_GRAPHICS_PPV_ARGS(&buffer->impl.index_buffer)));
+
+		buffer->impl.index_buffer_view.BufferLocation = buffer->impl.index_buffer->GetGPUVirtualAddress();
+	}
+	else {
+		buffer->impl.index_buffer_view.BufferLocation = buffer->impl.upload_buffer->GetGPUVirtualAddress();
+	}
+	buffer->impl.index_buffer_view.SizeInBytes = uploadBufferSize;
+	buffer->impl.index_buffer_view.Format = format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
+
+	buffer->impl.last_start = 0;
+	buffer->impl.last_count = kinc_g5_index_buffer_count(buffer);
+}
+
+void kinc_g5_index_buffer_destroy(kinc_g5_index_buffer_t *buffer) {
+	if (buffer->impl.index_buffer != NULL) {
+		buffer->impl.index_buffer->Release();
+		buffer->impl.index_buffer = NULL;
+	}
+
+	buffer->impl.upload_buffer->Release();
+	buffer->impl.upload_buffer = NULL;
+}
+
+static int kinc_g5_internal_index_buffer_stride(kinc_g5_index_buffer_t *buffer) {
+	return buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_32BIT ? 4 : 2;
+}
+
+void *kinc_g5_index_buffer_lock_all(kinc_g5_index_buffer_t *buffer) {
+	return kinc_g5_index_buffer_lock(buffer, 0, kinc_g5_index_buffer_count(buffer));
+}
+
+void *kinc_g5_index_buffer_lock(kinc_g5_index_buffer_t *buffer, int start, int count) {
+	buffer->impl.last_start = start;
+	buffer->impl.last_count = count;
+
+	D3D12_RANGE range;
+	range.Begin = start * kinc_g5_internal_index_buffer_stride(buffer);
+	range.End = (start + count) * kinc_g5_internal_index_buffer_stride(buffer);
+
+	void *p;
+	buffer->impl.upload_buffer->Map(0, &range, &p);
+	byte *bytes = (byte *)p;
+	bytes += start * kinc_g5_internal_index_buffer_stride(buffer);
+	return bytes;
+}
+
+void kinc_g5_index_buffer_unlock_all(kinc_g5_index_buffer_t *buffer) {
+	D3D12_RANGE range;
+	range.Begin = buffer->impl.last_start * kinc_g5_internal_index_buffer_stride(buffer);
+	range.End = (buffer->impl.last_start + buffer->impl.last_count) * kinc_g5_internal_index_buffer_stride(buffer);
+
+	buffer->impl.upload_buffer->Unmap(0, &range);
+}
+
+void kinc_g5_index_buffer_unlock(kinc_g5_index_buffer_t *buffer, int count) {
+	D3D12_RANGE range;
+	range.Begin = buffer->impl.last_start * kinc_g5_internal_index_buffer_stride(buffer);
+	range.End = (buffer->impl.last_start + count) * kinc_g5_internal_index_buffer_stride(buffer);
+
+	buffer->impl.upload_buffer->Unmap(0, &range);
+}
+
+void kinc_g5_internal_index_buffer_upload(kinc_g5_index_buffer_t *buffer, ID3D12GraphicsCommandList *commandList) {
+	if (!buffer->impl.gpu_memory)
+		return;
+
+	commandList->CopyBufferRegion(buffer->impl.index_buffer, 0, buffer->impl.upload_buffer, 0,
+	                              buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? sizeof(uint16_t) * buffer->impl.count
+	                                                                                       : sizeof(uint32_t) * buffer->impl.count);
+
+	D3D12_RESOURCE_BARRIER barriers[1] = {};
+	barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+	barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+	barriers[0].Transition.pResource = buffer->impl.index_buffer;
+	barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
+	barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_INDEX_BUFFER;
+	barriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+
+	commandList->ResourceBarrier(1, barriers);
+}
+
+int kinc_g5_index_buffer_count(kinc_g5_index_buffer_t *buffer) {
+	return buffer->impl.count;
+}

+ 32 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.h

@@ -0,0 +1,32 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ID3D12Resource;
+
+struct D3D12IindexBufferView {
+	__int64 BufferLocation;
+	unsigned int SizeInBytes;
+	int Format;
+};
+
+typedef struct {
+	struct ID3D12Resource *index_buffer;
+	struct D3D12IindexBufferView index_buffer_view;
+	struct ID3D12Resource *upload_buffer;
+	int count;
+	bool gpu_memory;
+	int format;
+	int last_start;
+	int last_count;
+} IndexBuffer5Impl;
+
+struct kinc_g5_index_buffer;
+
+void kinc_g5_internal_index_buffer_upload(struct kinc_g5_index_buffer *buffer, struct ID3D12GraphicsCommandList *commandList);
+
+#ifdef __cplusplus
+}
+#endif

+ 559 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.c.h

@@ -0,0 +1,559 @@
+#include "pipeline.h"
+
+#include <kinc/graphics5/graphics.h>
+#include <kinc/graphics5/pipeline.h>
+#include <kinc/graphics5/shader.h>
+#include <kinc/log.h>
+
+#include <kinc/backend/SystemMicrosoft.h>
+
+void kinc_g5_internal_setConstants(kinc_g5_command_list_t *commandList, kinc_g5_pipeline_t *pipeline) {
+	/*if (currentProgram->vertexShader->constantsSize > 0) {
+	    context->UpdateSubresource(currentProgram->vertexConstantBuffer, 0, nullptr, vertexConstants, 0, 0);
+	    context->VSSetConstantBuffers(0, 1, &currentProgram->vertexConstantBuffer);
+	}
+	if (currentProgram->fragmentShader->constantsSize > 0) {
+	    context->UpdateSubresource(currentProgram->fragmentConstantBuffer, 0, nullptr, fragmentConstants, 0, 0);
+	    context->PSSetConstantBuffers(0, 1, &currentProgram->fragmentConstantBuffer);
+	}
+	if (currentProgram->geometryShader != nullptr && currentProgram->geometryShader->constantsSize > 0) {
+	    context->UpdateSubresource(currentProgram->geometryConstantBuffer, 0, nullptr, geometryConstants, 0, 0);
+	    context->GSSetConstantBuffers(0, 1, &currentProgram->geometryConstantBuffer);
+	}
+	if (currentProgram->tessControlShader != nullptr && currentProgram->tessControlShader->constantsSize > 0) {
+	    context->UpdateSubresource(currentProgram->tessControlConstantBuffer, 0, nullptr, tessControlConstants, 0, 0);
+	    context->HSSetConstantBuffers(0, 1, &currentProgram->tessControlConstantBuffer);
+	}
+	if (currentProgram->tessEvalShader != nullptr && currentProgram->tessEvalShader->constantsSize > 0) {
+	    context->UpdateSubresource(currentProgram->tessEvalConstantBuffer, 0, nullptr, tessEvalConstants, 0, 0);
+	    context->DSSetConstantBuffers(0, 1, &currentProgram->tessEvalConstantBuffer);
+	}
+	*/
+
+#ifdef KINC_DXC
+	// commandList->SetGraphicsRootSignature(pipeline->impl.rootSignature);
+	commandList->impl._commandList->SetGraphicsRootSignature(globalRootSignature);
+#else
+	commandList->impl._commandList->SetGraphicsRootSignature(globalRootSignature);
+#endif
+
+	if (pipeline->impl.textures > 0) {
+		kinc_g5_internal_set_textures(commandList);
+	}
+}
+
+void kinc_g5_internal_set_compute_constants(kinc_g5_command_list_t *commandList) {
+	/*if (currentProgram->vertexShader->constantsSize > 0) {
+	    context->UpdateSubresource(currentProgram->vertexConstantBuffer, 0, nullptr, vertexConstants, 0, 0);
+	    context->VSSetConstantBuffers(0, 1, &currentProgram->vertexConstantBuffer);
+	}
+	if (currentProgram->fragmentShader->constantsSize > 0) {
+	    context->UpdateSubresource(currentProgram->fragmentConstantBuffer, 0, nullptr, fragmentConstants, 0, 0);
+	    context->PSSetConstantBuffers(0, 1, &currentProgram->fragmentConstantBuffer);
+	}
+	if (currentProgram->geometryShader != nullptr && currentProgram->geometryShader->constantsSize > 0) {
+	    context->UpdateSubresource(currentProgram->geometryConstantBuffer, 0, nullptr, geometryConstants, 0, 0);
+	    context->GSSetConstantBuffers(0, 1, &currentProgram->geometryConstantBuffer);
+	}
+	if (currentProgram->tessControlShader != nullptr && currentProgram->tessControlShader->constantsSize > 0) {
+	    context->UpdateSubresource(currentProgram->tessControlConstantBuffer, 0, nullptr, tessControlConstants, 0, 0);
+	    context->HSSetConstantBuffers(0, 1, &currentProgram->tessControlConstantBuffer);
+	}
+	if (currentProgram->tessEvalShader != nullptr && currentProgram->tessEvalShader->constantsSize > 0) {
+	    context->UpdateSubresource(currentProgram->tessEvalConstantBuffer, 0, nullptr, tessEvalConstants, 0, 0);
+	    context->DSSetConstantBuffers(0, 1, &currentProgram->tessEvalConstantBuffer);
+	}
+	*/
+
+	commandList->impl._commandList->SetComputeRootSignature(globalComputeRootSignature);
+
+	//if (pipeline->impl.textures > 0) {
+		kinc_g5_internal_set_textures(commandList);
+	//}
+}
+
+void kinc_g5_pipeline_init(kinc_g5_pipeline_t *pipe) {
+	kinc_g5_internal_pipeline_init(pipe);
+}
+
+void kinc_g5_pipeline_destroy(kinc_g5_pipeline_t *pipe) {
+	if (pipe->impl.pso != NULL) {
+		pipe->impl.pso->Release();
+		pipe->impl.pso = NULL;
+	}
+}
+
+// void PipelineState5Impl::set(Graphics5::PipelineState* pipeline) {
+//_current = this;
+// context->VSSetShader((ID3D11VertexShader*)vertexShader->shader, nullptr, 0);
+// context->PSSetShader((ID3D11PixelShader*)fragmentShader->shader, nullptr, 0);
+
+// if (geometryShader != nullptr) context->GSSetShader((ID3D11GeometryShader*)geometryShader->shader, nullptr, 0);
+// if (tessControlShader != nullptr) context->HSSetShader((ID3D11HullShader*)tessControlShader->shader, nullptr, 0);
+// if (tessEvalShader != nullptr) context->DSSetShader((ID3D11DomainShader*)tessEvalShader->shader, nullptr, 0);
+
+// context->IASetInputLayout(inputLayout);
+//}
+
+#define MAX_SHADER_THING 32
+
+static ShaderConstant findConstant(kinc_g5_shader_t *shader, const char *name) {
+	if (shader != NULL) {
+		for (int i = 0; i < MAX_SHADER_THING; ++i) {
+			if (strcmp(shader->impl.constants[i].name, name) == 0) {
+				return shader->impl.constants[i];
+			}
+		}
+	}
+
+	ShaderConstant constant;
+	constant.name[0] = 0;
+	constant.offset = -1;
+	constant.size = 0;
+	return constant;
+}
+
+static ShaderTexture findTexture(kinc_g5_shader_t *shader, const char *name) {
+	for (int i = 0; i < MAX_SHADER_THING; ++i) {
+		if (strcmp(shader->impl.textures[i].name, name) == 0) {
+			return shader->impl.textures[i];
+		}
+	}
+
+	ShaderTexture texture;
+	texture.name[0] = 0;
+	texture.texture = -1;
+	return texture;
+}
+
+static ShaderAttribute findAttribute(kinc_g5_shader_t *shader, const char *name) {
+	for (int i = 0; i < MAX_SHADER_THING; ++i) {
+		if (strcmp(shader->impl.attributes[i].name, name) == 0) {
+			return shader->impl.attributes[i];
+		}
+	}
+
+	ShaderAttribute attribute;
+	attribute.name[0] = 0;
+	attribute.attribute = -1;
+	return attribute;
+}
+
+kinc_g5_constant_location_t kinc_g5_pipeline_get_constant_location(struct kinc_g5_pipeline *pipe, const char *name) {
+	kinc_g5_constant_location_t location;
+
+	{
+		ShaderConstant constant = findConstant(pipe->vertexShader, name);
+		location.impl.vertexOffset = constant.offset;
+		location.impl.vertexSize = constant.size;
+	}
+
+	{
+		ShaderConstant constant = findConstant(pipe->fragmentShader, name);
+		location.impl.fragmentOffset = constant.offset;
+		location.impl.fragmentSize = constant.size;
+	}
+
+	location.impl.computeOffset = 0;
+	location.impl.computeSize = 0;
+
+	{
+		ShaderConstant constant = findConstant(pipe->geometryShader, name);
+		location.impl.geometryOffset = constant.offset;
+		location.impl.geometrySize = constant.size;
+	}
+
+	{
+		ShaderConstant constant = findConstant(pipe->tessellationControlShader, name);
+		location.impl.tessControlOffset = constant.offset;
+		location.impl.tessControlSize = constant.size;
+	}
+
+	{
+		ShaderConstant constant = findConstant(pipe->tessellationEvaluationShader, name);
+		location.impl.tessEvalOffset = constant.offset;
+		location.impl.tessEvalSize = constant.size;
+	}
+
+	return location;
+}
+
+kinc_g5_texture_unit_t kinc_g5_pipeline_get_texture_unit(kinc_g5_pipeline_t *pipe, const char *name) {
+	kinc_g5_texture_unit_t unit;
+	for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) {
+		unit.stages[i] = -1;
+	}
+
+	ShaderTexture vertexTexture = findTexture(pipe->vertexShader, name);
+	if (vertexTexture.texture != -1) {
+		unit.stages[KINC_G5_SHADER_TYPE_VERTEX] = vertexTexture.texture;
+	}
+	else {
+		ShaderTexture fragmentTexture = findTexture(pipe->fragmentShader, name);
+		unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] = fragmentTexture.texture;
+	}
+	return unit;
+}
+
+static D3D12_BLEND convert_blend_factor(kinc_g5_blending_factor_t factor) {
+	switch (factor) {
+	case KINC_G5_BLEND_ONE:
+		return D3D12_BLEND_ONE;
+	case KINC_G5_BLEND_ZERO:
+		return D3D12_BLEND_ZERO;
+	case KINC_G5_BLEND_SOURCE_ALPHA:
+		return D3D12_BLEND_SRC_ALPHA;
+	case KINC_G5_BLEND_DEST_ALPHA:
+		return D3D12_BLEND_DEST_ALPHA;
+	case KINC_G5_BLEND_INV_SOURCE_ALPHA:
+		return D3D12_BLEND_INV_SRC_ALPHA;
+	case KINC_G5_BLEND_INV_DEST_ALPHA:
+		return D3D12_BLEND_INV_DEST_ALPHA;
+	case KINC_G5_BLEND_SOURCE_COLOR:
+		return D3D12_BLEND_SRC_COLOR;
+	case KINC_G5_BLEND_DEST_COLOR:
+		return D3D12_BLEND_DEST_COLOR;
+	case KINC_G5_BLEND_INV_SOURCE_COLOR:
+		return D3D12_BLEND_INV_SRC_COLOR;
+	case KINC_G5_BLEND_INV_DEST_COLOR:
+		return D3D12_BLEND_INV_DEST_COLOR;
+	case KINC_G5_BLEND_CONSTANT:
+		return D3D12_BLEND_BLEND_FACTOR;
+	case KINC_G5_BLEND_INV_CONSTANT:
+		return D3D12_BLEND_INV_BLEND_FACTOR;
+	default:
+		assert(false);
+		return D3D12_BLEND_ONE;
+	}
+}
+
+static D3D12_BLEND_OP convert_blend_operation(kinc_g5_blending_operation_t op) {
+	switch (op) {
+	case KINC_G5_BLENDOP_ADD:
+		return D3D12_BLEND_OP_ADD;
+	case KINC_G5_BLENDOP_SUBTRACT:
+		return D3D12_BLEND_OP_SUBTRACT;
+	case KINC_G5_BLENDOP_REVERSE_SUBTRACT:
+		return D3D12_BLEND_OP_REV_SUBTRACT;
+	case KINC_G5_BLENDOP_MIN:
+		return D3D12_BLEND_OP_MIN;
+	case KINC_G5_BLENDOP_MAX:
+		return D3D12_BLEND_OP_MAX;
+	default:
+		assert(false);
+		return D3D12_BLEND_OP_ADD;
+	}
+}
+
+static D3D12_CULL_MODE convert_cull_mode(kinc_g5_cull_mode_t cullMode) {
+	switch (cullMode) {
+	case KINC_G5_CULL_MODE_CLOCKWISE:
+		return D3D12_CULL_MODE_FRONT;
+	case KINC_G5_CULL_MODE_COUNTERCLOCKWISE:
+		return D3D12_CULL_MODE_BACK;
+	case KINC_G5_CULL_MODE_NEVER:
+	default:
+		return D3D12_CULL_MODE_NONE;
+	}
+}
+
+static D3D12_COMPARISON_FUNC convert_compare_mode(kinc_g5_compare_mode_t compare) {
+	switch (compare) {
+	default:
+	case KINC_G5_COMPARE_MODE_ALWAYS:
+		return D3D12_COMPARISON_FUNC_ALWAYS;
+	case KINC_G5_COMPARE_MODE_NEVER:
+		return D3D12_COMPARISON_FUNC_NEVER;
+	case KINC_G5_COMPARE_MODE_EQUAL:
+		return D3D12_COMPARISON_FUNC_EQUAL;
+	case KINC_G5_COMPARE_MODE_NOT_EQUAL:
+		return D3D12_COMPARISON_FUNC_NOT_EQUAL;
+	case KINC_G5_COMPARE_MODE_LESS:
+		return D3D12_COMPARISON_FUNC_LESS;
+	case KINC_G5_COMPARE_MODE_LESS_EQUAL:
+		return D3D12_COMPARISON_FUNC_LESS_EQUAL;
+	case KINC_G5_COMPARE_MODE_GREATER:
+		return D3D12_COMPARISON_FUNC_GREATER;
+	case KINC_G5_COMPARE_MODE_GREATER_EQUAL:
+		return D3D12_COMPARISON_FUNC_GREATER_EQUAL;
+	}
+}
+
+static DXGI_FORMAT convert_format(kinc_g5_render_target_format_t format) {
+	switch (format) {
+	case KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT:
+		return DXGI_FORMAT_R32G32B32A32_FLOAT;
+	case KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT:
+		return DXGI_FORMAT_R16G16B16A16_FLOAT;
+	case KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT:
+		return DXGI_FORMAT_R32_FLOAT;
+	case KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT:
+		return DXGI_FORMAT_R16_FLOAT;
+	case KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED:
+		return DXGI_FORMAT_R8_UNORM;
+	case KINC_G5_RENDER_TARGET_FORMAT_32BIT:
+	default:
+#ifdef KINC_WINDOWS
+		return DXGI_FORMAT_R8G8B8A8_UNORM;
+#else
+		return DXGI_FORMAT_B8G8R8A8_UNORM;
+#endif
+	}
+}
+
+static void set_blend_state(D3D12_BLEND_DESC *blend_desc, kinc_g5_pipeline_t *pipe, int target) {
+	blend_desc->RenderTarget[target].BlendEnable = pipe->blend_source != KINC_G5_BLEND_ONE || pipe->blend_destination != KINC_G5_BLEND_ZERO ||
+	                                               pipe->alpha_blend_source != KINC_G5_BLEND_ONE || pipe->alpha_blend_destination != KINC_G5_BLEND_ZERO;
+	blend_desc->RenderTarget[target].SrcBlend = convert_blend_factor(pipe->blend_source);
+	blend_desc->RenderTarget[target].DestBlend = convert_blend_factor(pipe->blend_destination);
+	blend_desc->RenderTarget[target].BlendOp = convert_blend_operation(pipe->blend_operation);
+	blend_desc->RenderTarget[target].SrcBlendAlpha = convert_blend_factor(pipe->alpha_blend_source);
+	blend_desc->RenderTarget[target].DestBlendAlpha = convert_blend_factor(pipe->alpha_blend_destination);
+	blend_desc->RenderTarget[target].BlendOpAlpha = convert_blend_operation(pipe->alpha_blend_operation);
+	blend_desc->RenderTarget[target].RenderTargetWriteMask =
+	    (((pipe->colorWriteMaskRed[target] ? D3D12_COLOR_WRITE_ENABLE_RED : 0) | (pipe->colorWriteMaskGreen[target] ? D3D12_COLOR_WRITE_ENABLE_GREEN : 0)) |
+	     (pipe->colorWriteMaskBlue[target] ? D3D12_COLOR_WRITE_ENABLE_BLUE : 0)) |
+	    (pipe->colorWriteMaskAlpha[target] ? D3D12_COLOR_WRITE_ENABLE_ALPHA : 0);
+}
+
+void kinc_g5_pipeline_compile(kinc_g5_pipeline_t *pipe) {
+	// TODO FLOAT4x4
+	int vertexAttributeCount = 0;
+	for (int i = 0; i < 16; ++i) {
+		if (pipe->inputLayout[i] == NULL) {
+			break;
+		}
+		vertexAttributeCount += pipe->inputLayout[i]->size;
+	}
+	D3D12_INPUT_ELEMENT_DESC *vertexDesc = (D3D12_INPUT_ELEMENT_DESC *)alloca(sizeof(D3D12_INPUT_ELEMENT_DESC) * vertexAttributeCount);
+	ZeroMemory(vertexDesc, sizeof(D3D12_INPUT_ELEMENT_DESC) * vertexAttributeCount);
+	int curAttr = 0;
+	for (int stream = 0; pipe->inputLayout[stream] != NULL; ++stream) {
+		for (int i = 0; i < pipe->inputLayout[stream]->size; ++i) {
+			vertexDesc[curAttr].SemanticName = "TEXCOORD";
+			vertexDesc[curAttr].SemanticIndex = findAttribute(pipe->vertexShader, pipe->inputLayout[stream]->elements[i].name).attribute;
+			vertexDesc[curAttr].InputSlot = stream;
+			vertexDesc[curAttr].AlignedByteOffset = (i == 0) ? 0 : D3D12_APPEND_ALIGNED_ELEMENT;
+			vertexDesc[curAttr].InputSlotClass =
+			    pipe->inputLayout[stream]->instanced ? D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA : D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
+			vertexDesc[curAttr].InstanceDataStepRate = pipe->inputLayout[stream]->instanced ? 1 : 0;
+
+			switch (pipe->inputLayout[stream]->elements[i].data) {
+			case KINC_G4_VERTEX_DATA_F32_1X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32_FLOAT;
+				break;
+			case KINC_G4_VERTEX_DATA_F32_2X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32_FLOAT;
+				break;
+			case KINC_G4_VERTEX_DATA_F32_3X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32_FLOAT;
+				break;
+			case KINC_G4_VERTEX_DATA_F32_4X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_1X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8_SINT;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_1X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8_UINT;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8_SNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8_UNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_2X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8_SINT;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_2X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8_UINT;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8_SNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8_UNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_4X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8B8A8_SINT;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_4X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8B8A8_UINT;
+				break;
+			case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8B8A8_SNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_1X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16_SINT;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_1X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16_UINT;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16_SNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16_UNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_2X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16_SINT;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_2X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16_UINT;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16_SNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16_UNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_4X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16B16A16_SINT;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_4X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16B16A16_UINT;
+				break;
+			case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16B16A16_SNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16B16A16_UNORM;
+				break;
+			case KINC_G4_VERTEX_DATA_I32_1X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32_SINT;
+				break;
+			case KINC_G4_VERTEX_DATA_U32_1X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32_UINT;
+				break;
+			case KINC_G4_VERTEX_DATA_I32_2X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32_SINT;
+				break;
+			case KINC_G4_VERTEX_DATA_U32_2X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32_UINT;
+				break;
+			case KINC_G4_VERTEX_DATA_I32_3X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32_SINT;
+				break;
+			case KINC_G4_VERTEX_DATA_U32_3X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32_UINT;
+				break;
+			case KINC_G4_VERTEX_DATA_I32_4X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32A32_SINT;
+				break;
+			case KINC_G4_VERTEX_DATA_U32_4X:
+				vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32A32_UINT;
+				break;
+			default:
+				break;
+			}
+			curAttr++;
+		}
+	}
+
+	HRESULT hr = S_OK;
+#ifdef KINC_DXC
+	// hr = device->CreateRootSignature(0, pipe->vertexShader->impl.data, pipe->vertexShader->impl.length, IID_GRAPHICS_PPV_ARGS(&pipe->impl.rootSignature));
+	if (hr != S_OK) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "Could not create root signature.");
+	}
+	pipe->impl.vertexConstantsSize = pipe->vertexShader->impl.constantsSize;
+	pipe->impl.fragmentConstantsSize = pipe->fragmentShader->impl.constantsSize;
+#endif
+
+	pipe->impl.textures = pipe->fragmentShader->impl.texturesCount;
+
+	D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {0};
+	psoDesc.VS.BytecodeLength = pipe->vertexShader->impl.length;
+	psoDesc.VS.pShaderBytecode = pipe->vertexShader->impl.data;
+	psoDesc.PS.BytecodeLength = pipe->fragmentShader->impl.length;
+	psoDesc.PS.pShaderBytecode = pipe->fragmentShader->impl.data;
+#ifdef KINC_DXC
+	// psoDesc.pRootSignature = pipe->impl.rootSignature;
+	psoDesc.pRootSignature = globalRootSignature;
+#else
+	psoDesc.pRootSignature = globalRootSignature;
+#endif
+	psoDesc.NumRenderTargets = pipe->colorAttachmentCount;
+	for (int i = 0; i < pipe->colorAttachmentCount; ++i) {
+		psoDesc.RTVFormats[i] = convert_format(pipe->colorAttachment[i]);
+	}
+	psoDesc.DSVFormat = DXGI_FORMAT_UNKNOWN;
+
+	psoDesc.InputLayout.NumElements = vertexAttributeCount;
+	psoDesc.InputLayout.pInputElementDescs = vertexDesc;
+
+	psoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
+	psoDesc.RasterizerState.CullMode = convert_cull_mode(pipe->cullMode);
+	psoDesc.RasterizerState.FrontCounterClockwise = FALSE;
+	psoDesc.RasterizerState.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
+	psoDesc.RasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
+	psoDesc.RasterizerState.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
+	psoDesc.RasterizerState.DepthClipEnable = TRUE;
+	psoDesc.RasterizerState.MultisampleEnable = FALSE;
+	psoDesc.RasterizerState.AntialiasedLineEnable = FALSE;
+	psoDesc.RasterizerState.ForcedSampleCount = 0;
+	psoDesc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
+
+	psoDesc.BlendState.AlphaToCoverageEnable = FALSE;
+	psoDesc.BlendState.IndependentBlendEnable = FALSE;
+	const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
+	    FALSE,
+	    FALSE,
+	    D3D12_BLEND_ONE,
+	    D3D12_BLEND_ZERO,
+	    D3D12_BLEND_OP_ADD,
+	    D3D12_BLEND_ONE,
+	    D3D12_BLEND_ZERO,
+	    D3D12_BLEND_OP_ADD,
+	    D3D12_LOGIC_OP_NOOP,
+	    D3D12_COLOR_WRITE_ENABLE_ALL,
+	};
+	for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) {
+		psoDesc.BlendState.RenderTarget[i] = defaultRenderTargetBlendDesc;
+	}
+
+	bool independentBlend = false;
+	for (int i = 1; i < 8; ++i) {
+		if (pipe->colorWriteMaskRed[0] != pipe->colorWriteMaskRed[i] || pipe->colorWriteMaskGreen[0] != pipe->colorWriteMaskGreen[i] ||
+		    pipe->colorWriteMaskBlue[0] != pipe->colorWriteMaskBlue[i] || pipe->colorWriteMaskAlpha[0] != pipe->colorWriteMaskAlpha[i]) {
+			independentBlend = true;
+			break;
+		}
+	}
+
+	set_blend_state(&psoDesc.BlendState, pipe, 0);
+	if (independentBlend) {
+		psoDesc.BlendState.IndependentBlendEnable = true;
+		for (int i = 1; i < 8; ++i) {
+			set_blend_state(&psoDesc.BlendState, pipe, i);
+		}
+	}
+
+	psoDesc.DepthStencilState.DepthEnable = TRUE;
+	psoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
+	psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
+	psoDesc.DepthStencilState.StencilEnable = FALSE;
+	psoDesc.DepthStencilState.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
+	psoDesc.DepthStencilState.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
+	const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = {D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS};
+	psoDesc.DepthStencilState.FrontFace = defaultStencilOp;
+	psoDesc.DepthStencilState.BackFace = defaultStencilOp;
+
+	psoDesc.DepthStencilState.DepthEnable = pipe->depthMode != KINC_G5_COMPARE_MODE_ALWAYS;
+	psoDesc.DepthStencilState.DepthWriteMask = pipe->depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
+	psoDesc.DepthStencilState.DepthFunc = convert_compare_mode(pipe->depthMode);
+	psoDesc.DepthStencilState.StencilEnable = false;
+	psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
+	psoDesc.SampleDesc.Count = 1;
+	psoDesc.SampleMask = 0xFFFFFFFF;
+	psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+
+	hr = device->CreateGraphicsPipelineState(&psoDesc, IID_GRAPHICS_PPV_ARGS(&pipe->impl.pso));
+	if (hr != S_OK) {
+		kinc_log(KINC_LOG_LEVEL_WARNING, "Could not create pipeline.");
+	}
+}

+ 77 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.h

@@ -0,0 +1,77 @@
+#pragma once
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct kinc_g5_shader;
+
+struct ID3D12PipelineState;
+struct ID3D12GraphicsCommandList;
+struct ID3D12RootSignature;
+
+typedef struct {
+	struct ID3D12PipelineState *pso;
+#ifdef KINC_DXC
+	// struct ID3D12RootSignature *rootSignature;
+	int vertexConstantsSize;
+	int fragmentConstantsSize;
+#endif
+	int textures;
+	// ID3D11InputLayout* inputLayout;
+	// ID3D11Buffer* fragmentConstantBuffer;
+	// ID3D11Buffer* vertexConstantBuffer;
+	// ID3D11Buffer* geometryConstantBuffer;
+	// ID3D11Buffer* tessEvalConstantBuffer;
+	// ID3D11Buffer* tessControlConstantBuffer;
+
+	// static void setConstants(ID3D12GraphicsCommandList *commandList, Graphics5::PipelineState *pipeline);
+} PipelineState5Impl;
+
+typedef struct {
+	struct ID3D12PipelineState *pso;
+#ifdef KINC_DXC
+	struct ID3D12RootSignature *rootSignature;
+	int vertexConstantsSize;
+	int fragmentConstantsSize;
+#endif
+	int textures;
+	// ID3D11InputLayout* inputLayout;
+	// ID3D11Buffer* fragmentConstantBuffer;
+	// ID3D11Buffer* vertexConstantBuffer;
+	// ID3D11Buffer* geometryConstantBuffer;
+	// ID3D11Buffer* tessEvalConstantBuffer;
+	// ID3D11Buffer* tessControlConstantBuffer;
+
+	// static void setConstants(ID3D12GraphicsCommandList *commandList, Graphics5::PipelineState *pipeline);
+} ComputePipelineState5Impl;
+
+typedef struct {
+	int vertexOffset;
+	uint32_t vertexSize;
+	int fragmentOffset;
+	uint32_t fragmentSize;
+	int computeOffset;
+	uint32_t computeSize;
+	int geometryOffset;
+	uint32_t geometrySize;
+	int tessEvalOffset;
+	uint32_t tessEvalSize;
+	int tessControlOffset;
+	uint32_t tessControlSize;
+} ConstantLocation5Impl;
+
+typedef struct {
+	int nothing;
+} AttributeLocation5Impl;
+
+struct kinc_g5_pipeline;
+struct kinc_g5_command_list;
+
+void kinc_g5_internal_setConstants(struct kinc_g5_command_list *commandList, struct kinc_g5_pipeline *pipeline);
+
+#ifdef __cplusplus
+}
+#endif

+ 752 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.c.h

@@ -0,0 +1,752 @@
+#ifndef KINC_XBOX_ONE
+
+#include <kinc/backend/graphics5/raytrace.h>
+
+#include <kinc/graphics5/commandlist.h>
+#include <kinc/graphics5/graphics.h>
+#include <kinc/graphics5/indexbuffer.h>
+#include <kinc/graphics5/raytrace.h>
+#include <kinc/graphics5/vertexbuffer.h>
+
+static const wchar_t *hit_group_name = L"hitgroup";
+static const wchar_t *raygen_shader_name = L"raygeneration";
+static const wchar_t *closesthit_shader_name = L"closesthit";
+static const wchar_t *miss_shader_name = L"miss";
+
+static ID3D12Device5 *dxrDevice = NULL;
+static ID3D12GraphicsCommandList4 *dxrCommandList = NULL;
+static ID3D12RootSignature *dxrRootSignature = NULL;
+static ID3D12DescriptorHeap *descriptorHeap = NULL;
+static kinc_raytrace_acceleration_structure_t *accel;
+static kinc_raytrace_pipeline_t *pipeline;
+static kinc_g5_render_target_t *output = NULL;
+static D3D12_CPU_DESCRIPTOR_HANDLE outputCpuDescriptor;
+static D3D12_GPU_DESCRIPTOR_HANDLE outputDescriptorHandle;
+static D3D12_GPU_DESCRIPTOR_HANDLE vbgpuDescriptorHandle;
+static D3D12_GPU_DESCRIPTOR_HANDLE ibgpuDescriptorHandle;
+static D3D12_GPU_DESCRIPTOR_HANDLE tex0gpuDescriptorHandle;
+static D3D12_GPU_DESCRIPTOR_HANDLE tex1gpuDescriptorHandle;
+static D3D12_GPU_DESCRIPTOR_HANDLE tex2gpuDescriptorHandle;
+static D3D12_GPU_DESCRIPTOR_HANDLE texenvgpuDescriptorHandle;
+static D3D12_GPU_DESCRIPTOR_HANDLE texsobolgpuDescriptorHandle;
+static D3D12_GPU_DESCRIPTOR_HANDLE texscramblegpuDescriptorHandle;
+static D3D12_GPU_DESCRIPTOR_HANDLE texrankgpuDescriptorHandle;
+static int descriptorsAllocated = 0;
+static UINT descriptorSize;
+
+void kinc_raytrace_pipeline_init(kinc_raytrace_pipeline_t *pipeline, kinc_g5_command_list_t *command_list, void *ray_shader, int ray_shader_size,
+                                 kinc_g5_constant_buffer_t *constant_buffer) {
+	output = NULL;
+	descriptorsAllocated = 0;
+	pipeline->_constant_buffer = constant_buffer;
+	// Descriptor heap
+	D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {};
+	// Allocate a heap for 3 descriptors:
+	// 2 - bottom and top level acceleration structure
+	// 1 - raytracing output texture SRV
+	descriptorHeapDesc.NumDescriptors = 12;
+	descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+	descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+	descriptorHeapDesc.NodeMask = 0;
+	if (descriptorHeap != NULL) descriptorHeap->Release();
+	device->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(&descriptorHeap));
+	descriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+
+	// Device
+	if (dxrDevice != NULL) dxrDevice->Release();
+	device->QueryInterface(IID_GRAPHICS_PPV_ARGS(&dxrDevice));
+	if (dxrCommandList != NULL) dxrCommandList->Release();
+	command_list->impl._commandList->QueryInterface(IID_GRAPHICS_PPV_ARGS(&dxrCommandList));
+
+	// Root signatures
+	// This is a root signature that is shared across all raytracing shaders invoked during a DispatchRays() call.
+	D3D12_DESCRIPTOR_RANGE UAVDescriptor = {};
+	UAVDescriptor.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
+	UAVDescriptor.NumDescriptors = 1;
+	UAVDescriptor.BaseShaderRegister = 0;
+	UAVDescriptor.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+	D3D12_DESCRIPTOR_RANGE SRVDescriptorA = {};
+	SRVDescriptorA.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	SRVDescriptorA.NumDescriptors = 1;
+	SRVDescriptorA.BaseShaderRegister = 1;
+	SRVDescriptorA.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+	D3D12_DESCRIPTOR_RANGE SRVDescriptorB = {};
+	SRVDescriptorB.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	SRVDescriptorB.NumDescriptors = 1;
+	SRVDescriptorB.BaseShaderRegister = 2;
+	SRVDescriptorB.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+	D3D12_DESCRIPTOR_RANGE SRVDescriptor0 = {};
+	SRVDescriptor0.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	SRVDescriptor0.NumDescriptors = 1;
+	SRVDescriptor0.BaseShaderRegister = 3;
+	SRVDescriptor0.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+	D3D12_DESCRIPTOR_RANGE SRVDescriptor1 = {};
+	SRVDescriptor1.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	SRVDescriptor1.NumDescriptors = 1;
+	SRVDescriptor1.BaseShaderRegister = 4;
+	SRVDescriptor1.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+	D3D12_DESCRIPTOR_RANGE SRVDescriptor2 = {};
+	SRVDescriptor2.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	SRVDescriptor2.NumDescriptors = 1;
+	SRVDescriptor2.BaseShaderRegister = 5;
+	SRVDescriptor2.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+	D3D12_DESCRIPTOR_RANGE SRVDescriptorEnv = {};
+	SRVDescriptorEnv.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	SRVDescriptorEnv.NumDescriptors = 1;
+	SRVDescriptorEnv.BaseShaderRegister = 6;
+	SRVDescriptorEnv.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+	D3D12_DESCRIPTOR_RANGE SRVDescriptorSobol = {};
+	SRVDescriptorSobol.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	SRVDescriptorSobol.NumDescriptors = 1;
+	SRVDescriptorSobol.BaseShaderRegister = 7;
+	SRVDescriptorSobol.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+	D3D12_DESCRIPTOR_RANGE SRVDescriptorScramble = {};
+	SRVDescriptorScramble.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	SRVDescriptorScramble.NumDescriptors = 1;
+	SRVDescriptorScramble.BaseShaderRegister = 8;
+	SRVDescriptorScramble.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+	D3D12_DESCRIPTOR_RANGE SRVDescriptorRank = {};
+	SRVDescriptorRank.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+	SRVDescriptorRank.NumDescriptors = 1;
+	SRVDescriptorRank.BaseShaderRegister = 9;
+	SRVDescriptorRank.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+	D3D12_ROOT_PARAMETER rootParameters[12] = {};
+	// Output view
+	rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[0].DescriptorTable.NumDescriptorRanges = 1;
+	rootParameters[0].DescriptorTable.pDescriptorRanges = &UAVDescriptor;
+	// Acceleration structure
+	rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
+	rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[1].Descriptor.ShaderRegister = 0;
+	// Constant buffer
+	rootParameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	rootParameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[2].DescriptorTable.NumDescriptorRanges = 1;
+	rootParameters[2].DescriptorTable.pDescriptorRanges = &SRVDescriptorA;
+	rootParameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	rootParameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[3].DescriptorTable.NumDescriptorRanges = 1;
+	rootParameters[3].DescriptorTable.pDescriptorRanges = &SRVDescriptorB;
+	rootParameters[4].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+	rootParameters[4].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[4].Descriptor.ShaderRegister = 0;
+	rootParameters[5].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	rootParameters[5].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[5].DescriptorTable.NumDescriptorRanges = 1;
+	rootParameters[5].DescriptorTable.pDescriptorRanges = &SRVDescriptor0;
+	rootParameters[6].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	rootParameters[6].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[6].DescriptorTable.NumDescriptorRanges = 1;
+	rootParameters[6].DescriptorTable.pDescriptorRanges = &SRVDescriptor1;
+	rootParameters[7].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	rootParameters[7].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[7].DescriptorTable.NumDescriptorRanges = 1;
+	rootParameters[7].DescriptorTable.pDescriptorRanges = &SRVDescriptor2;
+	rootParameters[8].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	rootParameters[8].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[8].DescriptorTable.NumDescriptorRanges = 1;
+	rootParameters[8].DescriptorTable.pDescriptorRanges = &SRVDescriptorEnv;
+	rootParameters[9].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	rootParameters[9].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[9].DescriptorTable.NumDescriptorRanges = 1;
+	rootParameters[9].DescriptorTable.pDescriptorRanges = &SRVDescriptorSobol;
+	rootParameters[10].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	rootParameters[10].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[10].DescriptorTable.NumDescriptorRanges = 1;
+	rootParameters[10].DescriptorTable.pDescriptorRanges = &SRVDescriptorScramble;
+	rootParameters[11].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+	rootParameters[11].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+	rootParameters[11].DescriptorTable.NumDescriptorRanges = 1;
+	rootParameters[11].DescriptorTable.pDescriptorRanges = &SRVDescriptorRank;
+
+	D3D12_ROOT_SIGNATURE_DESC dxrRootSignatureDesc = {0};
+	dxrRootSignatureDesc.NumParameters = ARRAYSIZE(rootParameters);
+	dxrRootSignatureDesc.pParameters = rootParameters;
+	ID3DBlob *blob = NULL;
+	ID3DBlob *error = NULL;
+	D3D12SerializeRootSignature(&dxrRootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, &error);
+	if (dxrRootSignature != NULL) dxrRootSignature->Release();
+	device->CreateRootSignature(1, blob->GetBufferPointer(), blob->GetBufferSize(), IID_GRAPHICS_PPV_ARGS(&dxrRootSignature));
+
+	// Pipeline
+	D3D12_STATE_OBJECT_DESC raytracingPipeline = {};
+	raytracingPipeline.Type = D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE;
+
+	D3D12_SHADER_BYTECODE shaderBytecode = {};
+	shaderBytecode.pShaderBytecode = ray_shader;
+	shaderBytecode.BytecodeLength = ray_shader_size;
+
+	D3D12_DXIL_LIBRARY_DESC dxilLibrary = {};
+	dxilLibrary.DXILLibrary = shaderBytecode;
+	D3D12_EXPORT_DESC exports[3] = {};
+	exports[0].Name = raygen_shader_name;
+	exports[1].Name = closesthit_shader_name;
+	exports[2].Name = miss_shader_name;
+	dxilLibrary.pExports = exports;
+	dxilLibrary.NumExports = 3;
+
+	D3D12_HIT_GROUP_DESC hitGroup = {};
+	hitGroup.ClosestHitShaderImport = closesthit_shader_name;
+	hitGroup.HitGroupExport = hit_group_name;
+	hitGroup.Type = D3D12_HIT_GROUP_TYPE_TRIANGLES;
+
+	D3D12_RAYTRACING_SHADER_CONFIG shaderConfig = {};
+	shaderConfig.MaxPayloadSizeInBytes = 10 * sizeof(float); // float4 color
+	shaderConfig.MaxAttributeSizeInBytes = 8 * sizeof(float); // float2 barycentrics
+
+	D3D12_RAYTRACING_PIPELINE_CONFIG pipelineConfig = {};
+	pipelineConfig.MaxTraceRecursionDepth = 1; // ~ primary rays only
+
+	D3D12_STATE_SUBOBJECT subobjects[5] = {};
+	subobjects[0].Type = D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY;
+	subobjects[0].pDesc = &dxilLibrary;
+	subobjects[1].Type = D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP;
+	subobjects[1].pDesc = &hitGroup;
+	subobjects[2].Type = D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG;
+	subobjects[2].pDesc = &shaderConfig;
+	subobjects[3].Type = D3D12_STATE_SUBOBJECT_TYPE_GLOBAL_ROOT_SIGNATURE;
+	subobjects[3].pDesc = &dxrRootSignature;
+	subobjects[4].Type = D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG;
+	subobjects[4].pDesc = &pipelineConfig;
+	raytracingPipeline.NumSubobjects = 5;
+	raytracingPipeline.pSubobjects = subobjects;
+
+	dxrDevice->CreateStateObject(&raytracingPipeline, IID_GRAPHICS_PPV_ARGS(&pipeline->impl.dxr_state));
+
+	// Shader tables
+	// Get shader identifiers
+	ID3D12StateObjectProperties *stateObjectProps = NULL;
+	pipeline->impl.dxr_state->QueryInterface(IID_GRAPHICS_PPV_ARGS(&stateObjectProps));
+	const void *rayGenShaderId = stateObjectProps->GetShaderIdentifier(raygen_shader_name);
+	const void *missShaderId = stateObjectProps->GetShaderIdentifier(miss_shader_name);
+	const void *hitGroupShaderId = stateObjectProps->GetShaderIdentifier(hit_group_name);
+	UINT shaderIdSize = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
+	int align = D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT;
+
+	// Ray gen shader table
+	{
+		UINT size = shaderIdSize + constant_buffer->impl.mySize;
+		UINT shaderRecordSize = (size + (align - 1)) & ~(align - 1);
+		D3D12_RESOURCE_DESC bufferDesc = {};
+		bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+		bufferDesc.Width = shaderRecordSize;
+		bufferDesc.Height = 1;
+		bufferDesc.DepthOrArraySize = 1;
+		bufferDesc.MipLevels = 1;
+		bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
+		bufferDesc.SampleDesc.Count = 1;
+		bufferDesc.SampleDesc.Quality = 0;
+		bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+		D3D12_HEAP_PROPERTIES uploadHeapProperties = {};
+		uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
+		uploadHeapProperties.CreationNodeMask = 1;
+		uploadHeapProperties.VisibleNodeMask = 1;
+
+		device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+		                                IID_GRAPHICS_PPV_ARGS(&pipeline->impl.raygen_shader_table));
+
+		D3D12_RANGE rstRange = {};
+		rstRange.Begin = 0;
+		rstRange.End = 0;
+		uint8_t *byteDest;
+		pipeline->impl.raygen_shader_table->Map(0, &rstRange, (void **)(&byteDest));
+
+		D3D12_RANGE cbRange = {};
+		cbRange.Begin = 0;
+		cbRange.End = constant_buffer->impl.mySize;
+		void *constantBufferData;
+		constant_buffer->impl.constant_buffer->Map(0, &cbRange, (void **)&constantBufferData);
+		memcpy(byteDest, rayGenShaderId, size);
+		memcpy(byteDest + size, constantBufferData, constant_buffer->impl.mySize);
+		pipeline->impl.raygen_shader_table->Unmap(0, NULL);
+	}
+
+	// Miss shader table
+	{
+		UINT size = shaderIdSize;
+		UINT shaderRecordSize = (size + (align - 1)) & ~(align - 1);
+		D3D12_RESOURCE_DESC bufferDesc = {};
+		bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+		bufferDesc.Width = shaderRecordSize;
+		bufferDesc.Height = 1;
+		bufferDesc.DepthOrArraySize = 1;
+		bufferDesc.MipLevels = 1;
+		bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
+		bufferDesc.SampleDesc.Count = 1;
+		bufferDesc.SampleDesc.Quality = 0;
+		bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+		D3D12_HEAP_PROPERTIES uploadHeapProperties = {};
+		uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
+		uploadHeapProperties.CreationNodeMask = 1;
+		uploadHeapProperties.VisibleNodeMask = 1;
+
+		device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+		                                IID_GRAPHICS_PPV_ARGS(&pipeline->impl.miss_shader_table));
+
+		D3D12_RANGE mstRange = {};
+		mstRange.Begin = 0;
+		mstRange.End = 0;
+		uint8_t *byteDest;
+		pipeline->impl.miss_shader_table->Map(0, &mstRange, (void **)(&byteDest));
+		memcpy(byteDest, missShaderId, size);
+		pipeline->impl.miss_shader_table->Unmap(0, NULL);
+	}
+
+	// Hit group shader table
+	{
+		UINT size = shaderIdSize;
+		UINT shaderRecordSize = (size + (align - 1)) & ~(align - 1);
+		D3D12_RESOURCE_DESC bufferDesc = {};
+		bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+		bufferDesc.Width = shaderRecordSize;
+		bufferDesc.Height = 1;
+		bufferDesc.DepthOrArraySize = 1;
+		bufferDesc.MipLevels = 1;
+		bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
+		bufferDesc.SampleDesc.Count = 1;
+		bufferDesc.SampleDesc.Quality = 0;
+		bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+		D3D12_HEAP_PROPERTIES uploadHeapProperties = {};
+		uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
+		uploadHeapProperties.CreationNodeMask = 1;
+		uploadHeapProperties.VisibleNodeMask = 1;
+
+		device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+		                                IID_GRAPHICS_PPV_ARGS(&pipeline->impl.hitgroup_shader_table));
+
+		D3D12_RANGE hstRange = {};
+		hstRange.Begin = 0;
+		hstRange.End = 0;
+		uint8_t *byteDest;
+		pipeline->impl.hitgroup_shader_table->Map(0, &hstRange, (void **)(&byteDest));
+		memcpy(byteDest, hitGroupShaderId, size);
+		pipeline->impl.hitgroup_shader_table->Unmap(0, NULL);
+	}
+
+	// Output descriptor
+	outputCpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorsAllocated) * (UINT64)(descriptorSize);
+
+	int descriptorHeapIndex = descriptorsAllocated++;
+	outputDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorHeapIndex) * (UINT64)(descriptorSize);
+}
+
+void kinc_raytrace_pipeline_destroy(kinc_raytrace_pipeline_t *pipeline) {
+	pipeline->impl.dxr_state->Release();
+	pipeline->impl.raygen_shader_table->Release();
+	pipeline->impl.miss_shader_table->Release();
+	pipeline->impl.hitgroup_shader_table->Release();
+}
+
+UINT create_srv_vb(kinc_g5_vertex_buffer_t *vb, UINT numElements, UINT elementSize) {
+	D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+	srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
+	srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+	srvDesc.Buffer.NumElements = numElements;
+	if (elementSize == 0) {
+		srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
+		srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
+		srvDesc.Buffer.StructureByteStride = 0;
+	}
+	else {
+		srvDesc.Format = DXGI_FORMAT_UNKNOWN;
+		srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
+		srvDesc.Buffer.StructureByteStride = elementSize;
+	}
+
+	D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor = {0};
+	cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorsAllocated) * (UINT64)(descriptorSize);
+	UINT descriptorIndex = descriptorsAllocated++;
+
+	device->CreateShaderResourceView(vb->impl.uploadBuffer, &srvDesc, cpuDescriptor);
+	vbgpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorIndex) * (UINT64)(descriptorSize);
+
+	return descriptorIndex;
+}
+
+UINT create_srv_ib(kinc_g5_index_buffer_t *ib, UINT numElements, UINT elementSize) {
+	D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+	srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
+	srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+	srvDesc.Buffer.NumElements = numElements;
+	if (elementSize == 0) {
+		srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
+		srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
+		srvDesc.Buffer.StructureByteStride = 0;
+	}
+	else {
+		srvDesc.Format = DXGI_FORMAT_UNKNOWN;
+		srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
+		srvDesc.Buffer.StructureByteStride = elementSize;
+	}
+
+	D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor = {0};
+	cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorsAllocated) * (UINT64)(descriptorSize);
+	UINT descriptorIndex = descriptorsAllocated++;
+
+	device->CreateShaderResourceView(ib->impl.upload_buffer, &srvDesc, cpuDescriptor);
+	ibgpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorIndex) * (UINT64)(descriptorSize);
+
+	return descriptorIndex;
+}
+
+void kinc_raytrace_acceleration_structure_init(kinc_raytrace_acceleration_structure_t *accel, kinc_g5_command_list_t *command_list, kinc_g5_vertex_buffer_t *vb,
+                                               kinc_g5_index_buffer_t *ib, float scale) {
+	create_srv_ib(ib, ib->impl.count, 0);
+	create_srv_vb(vb, vb->impl.myCount, 8 * 2);
+
+	// Reset the command list for the acceleration structure construction
+	command_list->impl._commandList->Reset(command_list->impl._commandAllocator, NULL);
+
+	D3D12_RAYTRACING_GEOMETRY_DESC geometryDesc = {};
+	geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES;
+	geometryDesc.Triangles.IndexBuffer = ib->impl.upload_buffer->GetGPUVirtualAddress();
+	geometryDesc.Triangles.IndexCount = ib->impl.count;
+	geometryDesc.Triangles.IndexFormat = ib->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
+	geometryDesc.Triangles.Transform3x4 = 0;
+	geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R16G16B16A16_SNORM;
+	geometryDesc.Triangles.VertexCount = vb->impl.myCount;
+	geometryDesc.Triangles.VertexBuffer.StartAddress = vb->impl.uploadBuffer->GetGPUVirtualAddress();
+	geometryDesc.Triangles.VertexBuffer.StrideInBytes = vb->impl.uploadBuffer->GetDesc().Width / vb->impl.myCount;
+	geometryDesc.Flags = D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE;
+
+	// Get required sizes for an acceleration structure
+	D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS topLevelInputs = {};
+	topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
+	topLevelInputs.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE;
+	topLevelInputs.NumDescs = 1;
+	topLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL;
+
+	D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {0};
+	dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&topLevelInputs, &topLevelPrebuildInfo);
+
+	D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {0};
+	D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS bottomLevelInputs = topLevelInputs;
+	bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;
+	bottomLevelInputs.pGeometryDescs = &geometryDesc;
+	bottomLevelInputs.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE;
+	dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&bottomLevelInputs, &bottomLevelPrebuildInfo);
+
+	ID3D12Resource *scratchResource;
+	{
+		UINT64 tlSize = topLevelPrebuildInfo.ScratchDataSizeInBytes;
+		UINT64 blSize = bottomLevelPrebuildInfo.ScratchDataSizeInBytes;
+		D3D12_RESOURCE_DESC bufferDesc = {};
+		bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+		bufferDesc.Width = tlSize > blSize ? tlSize : blSize;
+		bufferDesc.Height = 1;
+		bufferDesc.DepthOrArraySize = 1;
+		bufferDesc.MipLevels = 1;
+		bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
+		bufferDesc.SampleDesc.Count = 1;
+		bufferDesc.SampleDesc.Quality = 0;
+		bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+		bufferDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+		D3D12_HEAP_PROPERTIES uploadHeapProperties = {};
+		uploadHeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
+		uploadHeapProperties.CreationNodeMask = 1;
+		uploadHeapProperties.VisibleNodeMask = 1;
+
+		device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL,
+		                                IID_GRAPHICS_PPV_ARGS(&scratchResource));
+	}
+
+	// Allocate resources for acceleration structures
+	// The resources that will contain acceleration structures must be created in the state D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE,
+	// and must have resource flag D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS.
+	{
+		D3D12_RESOURCE_DESC bufferDesc = {};
+		bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+		bufferDesc.Width = bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes;
+		bufferDesc.Height = 1;
+		bufferDesc.DepthOrArraySize = 1;
+		bufferDesc.MipLevels = 1;
+		bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
+		bufferDesc.SampleDesc.Count = 1;
+		bufferDesc.SampleDesc.Quality = 0;
+		bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+		bufferDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+		D3D12_HEAP_PROPERTIES uploadHeapProperties = {};
+		uploadHeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
+		uploadHeapProperties.CreationNodeMask = 1;
+		uploadHeapProperties.VisibleNodeMask = 1;
+
+		device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, NULL,
+		                                IID_GRAPHICS_PPV_ARGS(&accel->impl.bottom_level_accel));
+	}
+	{
+		D3D12_RESOURCE_DESC bufferDesc = {};
+		bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+		bufferDesc.Width = topLevelPrebuildInfo.ResultDataMaxSizeInBytes;
+		bufferDesc.Height = 1;
+		bufferDesc.DepthOrArraySize = 1;
+		bufferDesc.MipLevels = 1;
+		bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
+		bufferDesc.SampleDesc.Count = 1;
+		bufferDesc.SampleDesc.Quality = 0;
+		bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+		bufferDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+		D3D12_HEAP_PROPERTIES uploadHeapProperties = {};
+		uploadHeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
+		uploadHeapProperties.CreationNodeMask = 1;
+		uploadHeapProperties.VisibleNodeMask = 1;
+
+		device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, NULL,
+		                                IID_GRAPHICS_PPV_ARGS(&accel->impl.top_level_accel));
+	}
+
+	// Create an instance desc for the bottom-level acceleration structure
+	ID3D12Resource *instanceDescs;
+	D3D12_RAYTRACING_INSTANCE_DESC instanceDesc = {};
+	instanceDesc.Transform[0][0] = instanceDesc.Transform[1][1] = instanceDesc.Transform[2][2] = scale;
+	instanceDesc.InstanceMask = 1;
+	instanceDesc.AccelerationStructure = accel->impl.bottom_level_accel->GetGPUVirtualAddress();
+
+	D3D12_RESOURCE_DESC bufferDesc = {};
+	bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+	bufferDesc.Width = sizeof(instanceDesc);
+	bufferDesc.Height = 1;
+	bufferDesc.DepthOrArraySize = 1;
+	bufferDesc.MipLevels = 1;
+	bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
+	bufferDesc.SampleDesc.Count = 1;
+	bufferDesc.SampleDesc.Quality = 0;
+	bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+	D3D12_HEAP_PROPERTIES uploadHeapProperties = {};
+	uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
+	uploadHeapProperties.CreationNodeMask = 1;
+	uploadHeapProperties.VisibleNodeMask = 1;
+
+	device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+	                                IID_GRAPHICS_PPV_ARGS(&instanceDescs));
+	void *mappedData;
+	instanceDescs->Map(0, NULL, &mappedData);
+	memcpy(mappedData, &instanceDesc, sizeof(instanceDesc));
+	instanceDescs->Unmap(0, NULL);
+
+	// Bottom Level Acceleration Structure desc
+	D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bottomLevelBuildDesc = {0};
+	bottomLevelBuildDesc.Inputs = bottomLevelInputs;
+	bottomLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress();
+	bottomLevelBuildDesc.DestAccelerationStructureData = accel->impl.bottom_level_accel->GetGPUVirtualAddress();
+
+	// Top Level Acceleration Structure desc
+	D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC topLevelBuildDesc = bottomLevelBuildDesc;
+	topLevelInputs.InstanceDescs = instanceDescs->GetGPUVirtualAddress();
+	topLevelBuildDesc.Inputs = topLevelInputs;
+	topLevelBuildDesc.DestAccelerationStructureData = accel->impl.top_level_accel->GetGPUVirtualAddress();
+	topLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress();
+
+	// Build acceleration structure
+	dxrCommandList->BuildRaytracingAccelerationStructure(&bottomLevelBuildDesc, 0, NULL);
+	D3D12_RESOURCE_BARRIER barrier = {};
+	barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
+	barrier.UAV.pResource = accel->impl.bottom_level_accel;
+	command_list->impl._commandList->ResourceBarrier(1, &barrier);
+	dxrCommandList->BuildRaytracingAccelerationStructure(&topLevelBuildDesc, 0, NULL);
+
+	kinc_g5_command_list_end(command_list);
+	kinc_g5_command_list_execute(command_list);
+	kinc_g5_command_list_wait_for_execution_to_finish(command_list);
+	kinc_g5_command_list_begin(command_list);
+
+	scratchResource->Release();
+	instanceDescs->Release();
+}
+
+void kinc_raytrace_acceleration_structure_destroy(kinc_raytrace_acceleration_structure_t *accel) {
+	accel->impl.bottom_level_accel->Release();
+	accel->impl.top_level_accel->Release();
+}
+
+void kinc_raytrace_set_textures(kinc_g5_render_target_t *texpaint0, kinc_g5_render_target_t *texpaint1, kinc_g5_render_target_t *texpaint2, kinc_g5_texture_t *texenv, kinc_g5_texture_t *texsobol, kinc_g5_texture_t *texscramble, kinc_g5_texture_t *texrank) {
+	D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor = {0};
+	cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 5 * (UINT64)(descriptorSize);
+	D3D12_CPU_DESCRIPTOR_HANDLE sourceCpu = texpaint0->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+	device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+	tex0gpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 5 * (UINT64)(descriptorSize);
+
+	cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 6 * (UINT64)(descriptorSize);
+	sourceCpu = texpaint1->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+	device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+	tex1gpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 6 * (UINT64)(descriptorSize);
+
+	cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 7 * (UINT64)(descriptorSize);
+	sourceCpu = texpaint2->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+	device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+	tex2gpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 7 * (UINT64)(descriptorSize);
+
+	cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 8 * (UINT64)(descriptorSize);
+	sourceCpu = texenv->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+	device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+	texenvgpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 8 * (UINT64)(descriptorSize);
+
+	cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 9 * (UINT64)(descriptorSize);
+	sourceCpu = texsobol->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+	device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+	texsobolgpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 9 * (UINT64)(descriptorSize);
+
+	cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 10 * (UINT64)(descriptorSize);
+	sourceCpu = texscramble->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+	device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+	texscramblegpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 10 * (UINT64)(descriptorSize);
+
+	cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 11 * (UINT64)(descriptorSize);
+	sourceCpu = texrank->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+	device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+	texrankgpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 11 * (UINT64)(descriptorSize);
+}
+
+void kinc_raytrace_set_acceleration_structure(kinc_raytrace_acceleration_structure_t *_accel) {
+	accel = _accel;
+}
+
+void kinc_raytrace_set_pipeline(kinc_raytrace_pipeline_t *_pipeline) {
+	pipeline = _pipeline;
+}
+
+void kinc_raytrace_set_target(kinc_g5_render_target_t *_output) {
+	if (_output != output) {
+		_output->impl.renderTarget->Release();
+		_output->impl.renderTargetDescriptorHeap->Release();
+		_output->impl.srvDescriptorHeap->Release();
+
+		D3D12_HEAP_PROPERTIES heapProperties = {};
+		heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
+		heapProperties.CreationNodeMask = 1;
+		heapProperties.VisibleNodeMask = 1;
+		D3D12_RESOURCE_DESC desc = {};
+		desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+		desc.Width = _output->texWidth;
+		desc.Height = _output->texHeight;
+		desc.DepthOrArraySize = 1;
+		desc.MipLevels = 1;
+		desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
+		desc.SampleDesc.Count = 1;
+		desc.SampleDesc.Quality = 0;
+		desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+		desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+		D3D12_CLEAR_VALUE clearValue;
+		clearValue.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
+		clearValue.Color[0] = 0.0f;
+		clearValue.Color[1] = 0.0f;
+		clearValue.Color[2] = 0.0f;
+		clearValue.Color[3] = 1.0f;
+
+		device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &desc,
+		                                D3D12_RESOURCE_STATE_COMMON, &clearValue, IID_GRAPHICS_PPV_ARGS(&_output->impl.renderTarget));
+
+		D3D12_RENDER_TARGET_VIEW_DESC view;
+		view.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
+		view.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
+		view.Texture2D.MipSlice = 0;
+		view.Texture2D.PlaneSlice = 0;
+		D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
+		heapDesc.NumDescriptors = 1;
+		heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+		heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+		device->CreateDescriptorHeap(&heapDesc, IID_GRAPHICS_PPV_ARGS(&_output->impl.renderTargetDescriptorHeap));
+		device->CreateRenderTargetView(_output->impl.renderTarget, &view,
+		                               _output->impl.renderTargetDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
+
+		D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {};
+		descriptorHeapDesc.NumDescriptors = 1;
+		descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+		descriptorHeapDesc.NodeMask = 0;
+		descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+		device->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(&_output->impl.srvDescriptorHeap));
+
+		D3D12_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc = {};
+		shaderResourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+		shaderResourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+		shaderResourceViewDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
+		shaderResourceViewDesc.Texture2D.MipLevels = 1;
+		shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
+		shaderResourceViewDesc.Texture2D.ResourceMinLODClamp = 0.0f;
+		device->CreateShaderResourceView(_output->impl.renderTarget, &shaderResourceViewDesc,
+		                                 _output->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
+
+		D3D12_UNORDERED_ACCESS_VIEW_DESC UAVDesc = {};
+		UAVDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
+		device->CreateUnorderedAccessView(_output->impl.renderTarget, NULL, &UAVDesc, outputCpuDescriptor);
+	}
+	output = _output;
+}
+
+void kinc_raytrace_dispatch_rays(kinc_g5_command_list_t *command_list) {
+	command_list->impl._commandList->SetComputeRootSignature(dxrRootSignature);
+
+	// Bind the heaps, acceleration structure and dispatch rays
+	command_list->impl._commandList->SetDescriptorHeaps(1, &descriptorHeap);
+	command_list->impl._commandList->SetComputeRootDescriptorTable(0, outputDescriptorHandle);
+	command_list->impl._commandList->SetComputeRootShaderResourceView(1, accel->impl.top_level_accel->GetGPUVirtualAddress());
+	command_list->impl._commandList->SetComputeRootDescriptorTable(2, ibgpuDescriptorHandle);
+	command_list->impl._commandList->SetComputeRootDescriptorTable(3, vbgpuDescriptorHandle);
+	command_list->impl._commandList->SetComputeRootConstantBufferView(4, pipeline->_constant_buffer->impl.constant_buffer->GetGPUVirtualAddress());
+	command_list->impl._commandList->SetComputeRootDescriptorTable(5, tex0gpuDescriptorHandle);
+	command_list->impl._commandList->SetComputeRootDescriptorTable(6, tex1gpuDescriptorHandle);
+	command_list->impl._commandList->SetComputeRootDescriptorTable(7, tex2gpuDescriptorHandle);
+	command_list->impl._commandList->SetComputeRootDescriptorTable(8, texenvgpuDescriptorHandle);
+	command_list->impl._commandList->SetComputeRootDescriptorTable(9, texsobolgpuDescriptorHandle);
+	command_list->impl._commandList->SetComputeRootDescriptorTable(10, texscramblegpuDescriptorHandle);
+	command_list->impl._commandList->SetComputeRootDescriptorTable(11, texrankgpuDescriptorHandle);
+
+	// Since each shader table has only one shader record, the stride is same as the size.
+	D3D12_DISPATCH_RAYS_DESC dispatchDesc = {0};
+	dispatchDesc.HitGroupTable.StartAddress = pipeline->impl.hitgroup_shader_table->GetGPUVirtualAddress();
+	dispatchDesc.HitGroupTable.SizeInBytes = pipeline->impl.hitgroup_shader_table->GetDesc().Width;
+	dispatchDesc.HitGroupTable.StrideInBytes = dispatchDesc.HitGroupTable.SizeInBytes;
+	dispatchDesc.MissShaderTable.StartAddress = pipeline->impl.miss_shader_table->GetGPUVirtualAddress();
+	dispatchDesc.MissShaderTable.SizeInBytes = pipeline->impl.miss_shader_table->GetDesc().Width;
+	dispatchDesc.MissShaderTable.StrideInBytes = dispatchDesc.MissShaderTable.SizeInBytes;
+	dispatchDesc.RayGenerationShaderRecord.StartAddress = pipeline->impl.raygen_shader_table->GetGPUVirtualAddress();
+	dispatchDesc.RayGenerationShaderRecord.SizeInBytes = pipeline->impl.raygen_shader_table->GetDesc().Width;
+	dispatchDesc.Width = output->texWidth;
+	dispatchDesc.Height = output->texHeight;
+	dispatchDesc.Depth = 1;
+	dxrCommandList->SetPipelineState1(pipeline->impl.dxr_state);
+	dxrCommandList->DispatchRays(&dispatchDesc);
+}
+
+void kinc_raytrace_copy(kinc_g5_command_list_t *command_list, kinc_g5_render_target_t *target, kinc_g5_texture_t *source) {
+	D3D12_RESOURCE_BARRIER preCopyBarriers[2] = {};
+	preCopyBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+	preCopyBarriers[0].Transition.pResource = target->impl.renderTarget;
+	preCopyBarriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
+	preCopyBarriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
+	preCopyBarriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+	preCopyBarriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+	preCopyBarriers[1].Transition.pResource = source->impl.image;
+	preCopyBarriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
+	preCopyBarriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
+	preCopyBarriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+	command_list->impl._commandList->ResourceBarrier(ARRAYSIZE(preCopyBarriers), preCopyBarriers);
+
+	command_list->impl._commandList->CopyResource(target->impl.renderTarget, source->impl.image);
+
+	D3D12_RESOURCE_BARRIER postCopyBarriers[2] = {};
+	postCopyBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+	postCopyBarriers[0].Transition.pResource = target->impl.renderTarget;
+	postCopyBarriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
+	postCopyBarriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
+	postCopyBarriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+	postCopyBarriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+	postCopyBarriers[1].Transition.pResource = source->impl.image;
+	postCopyBarriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
+	postCopyBarriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
+	postCopyBarriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+	command_list->impl._commandList->ResourceBarrier(ARRAYSIZE(postCopyBarriers), postCopyBarriers);
+}
+
+#endif

+ 24 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ID3D12StateObject;
+struct ID3D12Resource;
+
+typedef struct {
+	struct ID3D12StateObject *dxr_state;
+	struct ID3D12Resource *raygen_shader_table;
+	struct ID3D12Resource *miss_shader_table;
+	struct ID3D12Resource *hitgroup_shader_table;
+} kinc_raytrace_pipeline_impl_t;
+
+typedef struct {
+	struct ID3D12Resource *bottom_level_accel;
+	struct ID3D12Resource *top_level_accel;
+} kinc_raytrace_acceleration_structure_impl_t;
+
+#ifdef __cplusplus
+}
+#endif

+ 287 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.c.h

@@ -0,0 +1,287 @@
+#include "rendertarget.h"
+
+#include <kinc/graphics5/rendertarget.h>
+#include <kinc/graphics5/texture.h>
+#include <kinc/backend/SystemMicrosoft.h>
+
+#ifdef KINC_WINDOWS
+#include <dxgi1_4.h>
+#endif
+
+#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN
+extern ID3D12Resource *swapChainRenderTargets[QUEUE_SLOT_COUNT];
+#endif
+
+static void WaitForFence(ID3D12Fence *fence, UINT64 completionValue, HANDLE waitEvent) {
+	if (fence->GetCompletedValue() < completionValue) {
+		fence->SetEventOnCompletion(completionValue, waitEvent);
+		WaitForSingleObject(waitEvent, INFINITE);
+	}
+}
+
+static void createRenderTargetView(ID3D12Resource *renderTarget, ID3D12DescriptorHeap *renderTargetDescriptorHeap, DXGI_FORMAT format) {
+	// const D3D12_RESOURCE_DESC resourceDesc = renderTarget->lpVtbl->GetDesc(renderTarget);
+
+	D3D12_RENDER_TARGET_VIEW_DESC viewDesc;
+	viewDesc.Format = format;
+	viewDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
+	viewDesc.Texture2D.MipSlice = 0;
+	viewDesc.Texture2D.PlaneSlice = 0;
+
+	device->CreateRenderTargetView(renderTarget, &viewDesc, renderTargetDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
+}
+
+static DXGI_FORMAT convertFormat(kinc_g5_render_target_format_t format) {
+	switch (format) {
+	case KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT:
+		return DXGI_FORMAT_R32G32B32A32_FLOAT;
+	case KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT:
+		return DXGI_FORMAT_R16G16B16A16_FLOAT;
+	case KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT:
+		return DXGI_FORMAT_R32_FLOAT;
+	case KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT:
+		return DXGI_FORMAT_R16_FLOAT;
+	case KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED:
+		return DXGI_FORMAT_R8_UNORM;
+	case KINC_G5_RENDER_TARGET_FORMAT_32BIT:
+	default:
+#ifdef KINC_WINDOWS
+		return DXGI_FORMAT_R8G8B8A8_UNORM;
+#else
+		return DXGI_FORMAT_B8G8R8A8_UNORM;
+#endif
+	}
+}
+
+extern "C" void kinc_memory_emergency();
+
+static void render_target_init(kinc_g5_render_target_t *render_target, int width, int height, kinc_g5_render_target_format_t format, int depthBufferBits,
+                               int stencilBufferBits, int samples_per_pixel, int framebuffer_index) {
+	render_target->texWidth = render_target->width = width;
+	render_target->texHeight = render_target->height = height;
+	render_target->impl.stage = 0;
+	render_target->impl.stage_depth = -1;
+	render_target->impl.renderTargetReadback = NULL;
+	render_target->impl.framebuffer_index = framebuffer_index;
+
+	DXGI_FORMAT dxgiFormat = convertFormat(format);
+
+	D3D12_CLEAR_VALUE clearValue;
+	clearValue.Format = dxgiFormat;
+	clearValue.Color[0] = 0.0f;
+	clearValue.Color[1] = 0.0f;
+	clearValue.Color[2] = 0.0f;
+	clearValue.Color[3] = 1.0f;
+
+	D3D12_HEAP_PROPERTIES heapProperties;
+	heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
+	heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+	heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+	heapProperties.CreationNodeMask = 1;
+	heapProperties.VisibleNodeMask = 1;
+
+	D3D12_RESOURCE_DESC texResourceDesc;
+	texResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+	texResourceDesc.Alignment = 0;
+	texResourceDesc.Width = render_target->texWidth;
+	texResourceDesc.Height = render_target->texHeight;
+	texResourceDesc.DepthOrArraySize = 1;
+	texResourceDesc.MipLevels = 1;
+	texResourceDesc.Format = dxgiFormat;
+	texResourceDesc.SampleDesc.Count = 1;
+	texResourceDesc.SampleDesc.Quality = 0;
+	texResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+	texResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
+
+	D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
+	heapDesc.NumDescriptors = 1;
+	heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+	heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+	kinc_microsoft_affirm(device->CreateDescriptorHeap(&heapDesc, IID_GRAPHICS_PPV_ARGS(&render_target->impl.renderTargetDescriptorHeap)));
+
+	if (framebuffer_index >= 0) {
+#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN
+		render_target->impl.renderTarget = swapChainRenderTargets[framebuffer_index];
+#else
+		IDXGISwapChain *swapChain = kinc_dx_current_window()->swapChain;
+		kinc_microsoft_affirm(swapChain->GetBuffer(framebuffer_index, IID_PPV_ARGS(&render_target->impl.renderTarget)));
+		wchar_t buffer[128];
+		wsprintf(buffer, L"Backbuffer (index %i)", framebuffer_index);
+		render_target->impl.renderTarget->SetName(buffer);
+#endif
+		createRenderTargetView(render_target->impl.renderTarget, render_target->impl.renderTargetDescriptorHeap, dxgiFormat);
+	}
+	else {
+		HRESULT result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &texResourceDesc, D3D12_RESOURCE_STATE_RENDER_TARGET,
+		                                                 &clearValue, IID_GRAPHICS_PPV_ARGS(&render_target->impl.renderTarget));
+		if (result != S_OK) {
+			for (int i = 0; i < 10; ++i) {
+				kinc_memory_emergency();
+				result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &texResourceDesc, D3D12_RESOURCE_STATE_RENDER_TARGET,
+				                                         &clearValue, IID_GRAPHICS_PPV_ARGS(&render_target->impl.renderTarget));
+				if (result == S_OK) {
+					break;
+				}
+			}
+		}
+
+		D3D12_RENDER_TARGET_VIEW_DESC view;
+		// const D3D12_RESOURCE_DESC resourceDesc = render_target->impl.renderTarget->lpVtbl->GetDesc(render_target->impl.renderTarget);
+		view.Format = dxgiFormat;
+		view.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
+		view.Texture2D.MipSlice = 0;
+		view.Texture2D.PlaneSlice = 0;
+
+		device->CreateRenderTargetView(render_target->impl.renderTarget, &view,
+		                               render_target->impl.renderTargetDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
+	}
+
+	D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {};
+	descriptorHeapDesc.NumDescriptors = 1;
+
+	descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+	descriptorHeapDesc.NodeMask = 0;
+	descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+
+	kinc_microsoft_affirm(device->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(&render_target->impl.srvDescriptorHeap)));
+
+	D3D12_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
+	ZeroMemory(&shaderResourceViewDesc, sizeof(shaderResourceViewDesc));
+	shaderResourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+	shaderResourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+	shaderResourceViewDesc.Format = dxgiFormat;
+	shaderResourceViewDesc.Texture2D.MipLevels = 1;
+	shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
+	shaderResourceViewDesc.Texture2D.ResourceMinLODClamp = 0.0f;
+
+	device->CreateShaderResourceView(render_target->impl.renderTarget, &shaderResourceViewDesc,
+	                                 render_target->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
+
+	if (depthBufferBits > 0) {
+		D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
+		dsvHeapDesc.NumDescriptors = 1;
+		dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
+		dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+		kinc_microsoft_affirm(device->CreateDescriptorHeap(&dsvHeapDesc, IID_GRAPHICS_PPV_ARGS(&render_target->impl.depthStencilDescriptorHeap)));
+
+		D3D12_RESOURCE_DESC depthTexture;
+		depthTexture.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+		depthTexture.Alignment = 0;
+		depthTexture.Width = width;
+		depthTexture.Height = height;
+		depthTexture.DepthOrArraySize = 1;
+		depthTexture.MipLevels = 1;
+		depthTexture.Format = DXGI_FORMAT_D32_FLOAT;
+		depthTexture.SampleDesc.Count = 1;
+		depthTexture.SampleDesc.Quality = 0;
+		depthTexture.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+		depthTexture.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
+
+		D3D12_CLEAR_VALUE clearValue;
+		clearValue.Format = DXGI_FORMAT_D32_FLOAT;
+		clearValue.DepthStencil.Depth = 1.0f;
+		clearValue.DepthStencil.Stencil = 0;
+
+		D3D12_HEAP_PROPERTIES heapProperties;
+		heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
+		heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+		heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+		heapProperties.CreationNodeMask = 1;
+		heapProperties.VisibleNodeMask = 1;
+
+		HRESULT result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &depthTexture, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue,
+		                                                 IID_GRAPHICS_PPV_ARGS(&render_target->impl.depthStencilTexture));
+		if (result != S_OK) {
+			for (int i = 0; i < 10; ++i) {
+				kinc_memory_emergency();
+				result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &depthTexture, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue,
+				                                         IID_GRAPHICS_PPV_ARGS(&render_target->impl.depthStencilTexture));
+				if (result == S_OK) {
+					break;
+				}
+			}
+		}
+
+		device->CreateDepthStencilView(render_target->impl.depthStencilTexture, NULL,
+		                               render_target->impl.depthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
+
+		// Reading depth texture as a shader resource
+		D3D12_DESCRIPTOR_HEAP_DESC srvDepthHeapDesc = {};
+		srvDepthHeapDesc.NumDescriptors = 1;
+		srvDepthHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+		srvDepthHeapDesc.NodeMask = 0;
+		srvDepthHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+		kinc_microsoft_affirm(device->CreateDescriptorHeap(&srvDepthHeapDesc, IID_GRAPHICS_PPV_ARGS(&render_target->impl.srvDepthDescriptorHeap)));
+
+		D3D12_SHADER_RESOURCE_VIEW_DESC srvDepthViewDesc;
+		ZeroMemory(&srvDepthViewDesc, sizeof(srvDepthViewDesc));
+		srvDepthViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+		srvDepthViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+		srvDepthViewDesc.Format = DXGI_FORMAT_R32_FLOAT;
+		srvDepthViewDesc.Texture2D.MipLevels = 1;
+		srvDepthViewDesc.Texture2D.MostDetailedMip = 0;
+		srvDepthViewDesc.Texture2D.ResourceMinLODClamp = 0.0f;
+		device->CreateShaderResourceView(render_target->impl.depthStencilTexture, &srvDepthViewDesc,
+		                                 render_target->impl.srvDepthDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
+	}
+	else {
+		render_target->impl.depthStencilDescriptorHeap = NULL;
+		render_target->impl.depthStencilTexture = NULL;
+		render_target->impl.srvDepthDescriptorHeap = NULL;
+	}
+
+	render_target->impl.scissor.top = 0;
+	render_target->impl.scissor.left = 0;
+	render_target->impl.scissor.right = width;
+	render_target->impl.scissor.bottom = height;
+
+	render_target->impl.viewport.TopLeftX = 0.0f;
+	render_target->impl.viewport.TopLeftY = 0.0f;
+	render_target->impl.viewport.Width = (float)width;
+	render_target->impl.viewport.Height = (float)height;
+	render_target->impl.viewport.MinDepth = 0.0f;
+	render_target->impl.viewport.MaxDepth = 1.0f;
+}
+
+void kinc_g5_render_target_init_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format,
+                                                   int depthBufferBits, int stencilBufferBits, int samples_per_pixel) {
+	render_target_init(target, width, height, format, depthBufferBits, stencilBufferBits, samples_per_pixel, -1);
+}
+
+static int framebuffer_count = 0;
+
+void kinc_g5_render_target_init_framebuffer_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format,
+                                                               int depthBufferBits, int stencilBufferBits, int samples_per_pixel) {
+	render_target_init(target, width, height, format, depthBufferBits, stencilBufferBits, samples_per_pixel, framebuffer_count);
+	framebuffer_count += 1;
+}
+
+void kinc_g5_render_target_init_cube_with_multisampling(kinc_g5_render_target_t *render_target, int cubeMapSize, kinc_g5_render_target_format_t format,
+                                                        int depthBufferBits, int stencilBufferBits, int samples_per_pixel) {
+	render_target->impl.stage = 0;
+	render_target->impl.stage_depth = -1;
+}
+
+void kinc_g5_render_target_destroy(kinc_g5_render_target_t *render_target) {
+	if (render_target->impl.framebuffer_index >= 0) {
+		framebuffer_count -= 1;
+	}
+
+	render_target->impl.renderTarget->Release();
+	render_target->impl.renderTargetDescriptorHeap->Release();
+	render_target->impl.srvDescriptorHeap->Release();
+	if (render_target->impl.depthStencilTexture != NULL) {
+		render_target->impl.depthStencilTexture->Release();
+		render_target->impl.depthStencilDescriptorHeap->Release();
+		render_target->impl.srvDepthDescriptorHeap->Release();
+	}
+	if (render_target->impl.renderTargetReadback != NULL) {
+		render_target->impl.renderTargetReadback->Release();
+	}
+}
+
+void kinc_g5_render_target_set_depth_stencil_from(kinc_g5_render_target_t *render_target, kinc_g5_render_target_t *source) {
+	render_target->impl.depthStencilDescriptorHeap = source->impl.depthStencilDescriptorHeap;
+	render_target->impl.srvDepthDescriptorHeap = source->impl.srvDepthDescriptorHeap;
+	render_target->impl.depthStencilTexture = source->impl.depthStencilTexture;
+}

+ 28 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.h

@@ -0,0 +1,28 @@
+#pragma once
+
+#include "d3d12mini.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum RenderTargetResourceState { RenderTargetResourceStateUndefined, RenderTargetResourceStateRenderTarget, RenderTargetResourceStateTexture };
+
+typedef struct {
+	struct ID3D12Resource *renderTarget;
+	struct ID3D12Resource *renderTargetReadback;
+	struct ID3D12DescriptorHeap *renderTargetDescriptorHeap;
+	struct ID3D12DescriptorHeap *srvDescriptorHeap;
+	struct ID3D12DescriptorHeap *depthStencilDescriptorHeap;
+	struct ID3D12DescriptorHeap *srvDepthDescriptorHeap;
+	struct ID3D12Resource *depthStencilTexture;
+	struct D3D12Viewport viewport;
+	struct D3D12Rect scissor;
+	int stage;
+	int stage_depth;
+	int framebuffer_index;
+} RenderTarget5Impl;
+
+#ifdef __cplusplus
+}
+#endif

+ 145 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.c.h

@@ -0,0 +1,145 @@
+#include <kinc/graphics5/sampler.h>
+
+static D3D12_TEXTURE_ADDRESS_MODE convert_texture_addressing(kinc_g5_texture_addressing_t addressing) {
+	switch (addressing) {
+	case KINC_G5_TEXTURE_ADDRESSING_REPEAT:
+		return D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+	case KINC_G5_TEXTURE_ADDRESSING_MIRROR:
+		return D3D12_TEXTURE_ADDRESS_MODE_MIRROR;
+	case KINC_G5_TEXTURE_ADDRESSING_CLAMP:
+		return D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+	case KINC_G5_TEXTURE_ADDRESSING_BORDER:
+		return D3D12_TEXTURE_ADDRESS_MODE_BORDER;
+	default:
+		assert(false);
+		return D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+	}
+}
+
+static D3D12_FILTER convert_filter(kinc_g5_texture_filter_t minification, kinc_g5_texture_filter_t magnification, kinc_g5_mipmap_filter_t mipmap) {
+	switch (minification) {
+	case KINC_G5_TEXTURE_FILTER_POINT:
+		switch (magnification) {
+		case KINC_G5_TEXTURE_FILTER_POINT:
+			switch (mipmap) {
+			case KINC_G5_MIPMAP_FILTER_NONE:
+			case KINC_G5_MIPMAP_FILTER_POINT:
+				return D3D12_FILTER_MIN_MAG_MIP_POINT;
+			case KINC_G5_MIPMAP_FILTER_LINEAR:
+				return D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR;
+			}
+		case KINC_G5_TEXTURE_FILTER_LINEAR:
+			switch (mipmap) {
+			case KINC_G5_MIPMAP_FILTER_NONE:
+			case KINC_G5_MIPMAP_FILTER_POINT:
+				return D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
+			case KINC_G5_MIPMAP_FILTER_LINEAR:
+				return D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR;
+			}
+		case KINC_G5_TEXTURE_FILTER_ANISOTROPIC:
+			return D3D12_FILTER_ANISOTROPIC;
+		}
+	case KINC_G5_TEXTURE_FILTER_LINEAR:
+		switch (magnification) {
+		case KINC_G5_TEXTURE_FILTER_POINT:
+			switch (mipmap) {
+			case KINC_G5_MIPMAP_FILTER_NONE:
+			case KINC_G5_MIPMAP_FILTER_POINT:
+				return D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT;
+			case KINC_G5_MIPMAP_FILTER_LINEAR:
+				return D3D12_FILTER_MIN_MAG_MIP_LINEAR;
+			}
+		case KINC_G5_TEXTURE_FILTER_LINEAR:
+			switch (mipmap) {
+			case KINC_G5_MIPMAP_FILTER_NONE:
+			case KINC_G5_MIPMAP_FILTER_POINT:
+				return D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+			case KINC_G5_MIPMAP_FILTER_LINEAR:
+				return D3D12_FILTER_MIN_MAG_MIP_LINEAR;
+			}
+		case KINC_G5_TEXTURE_FILTER_ANISOTROPIC:
+			return D3D12_FILTER_ANISOTROPIC;
+		}
+	case KINC_G5_TEXTURE_FILTER_ANISOTROPIC:
+		return D3D12_FILTER_ANISOTROPIC;
+	}
+
+	assert(false);
+	return D3D12_FILTER_MIN_MAG_MIP_POINT;
+}
+
+static D3D12_FILTER convert_comparison_filter(kinc_g5_texture_filter_t minification, kinc_g5_texture_filter_t magnification, kinc_g5_mipmap_filter_t mipmap) {
+	switch (minification) {
+	case KINC_G5_TEXTURE_FILTER_POINT:
+		switch (magnification) {
+		case KINC_G5_TEXTURE_FILTER_POINT:
+			switch (mipmap) {
+			case KINC_G5_MIPMAP_FILTER_NONE:
+			case KINC_G5_MIPMAP_FILTER_POINT:
+				return D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
+			case KINC_G5_MIPMAP_FILTER_LINEAR:
+				return D3D12_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR;
+			}
+		case KINC_G5_TEXTURE_FILTER_LINEAR:
+			switch (mipmap) {
+			case KINC_G5_MIPMAP_FILTER_NONE:
+			case KINC_G5_MIPMAP_FILTER_POINT:
+				return D3D12_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT;
+			case KINC_G5_MIPMAP_FILTER_LINEAR:
+				return D3D12_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR;
+			}
+		case KINC_G5_TEXTURE_FILTER_ANISOTROPIC:
+			return D3D12_FILTER_COMPARISON_ANISOTROPIC;
+		}
+	case KINC_G5_TEXTURE_FILTER_LINEAR:
+		switch (magnification) {
+		case KINC_G5_TEXTURE_FILTER_POINT:
+			switch (mipmap) {
+			case KINC_G5_MIPMAP_FILTER_NONE:
+			case KINC_G5_MIPMAP_FILTER_POINT:
+				return D3D12_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT;
+			case KINC_G5_MIPMAP_FILTER_LINEAR:
+				return D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR;
+			}
+		case KINC_G5_TEXTURE_FILTER_LINEAR:
+			switch (mipmap) {
+			case KINC_G5_MIPMAP_FILTER_NONE:
+			case KINC_G5_MIPMAP_FILTER_POINT:
+				return D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
+			case KINC_G5_MIPMAP_FILTER_LINEAR:
+				return D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR;
+			}
+		case KINC_G5_TEXTURE_FILTER_ANISOTROPIC:
+			return D3D12_FILTER_COMPARISON_ANISOTROPIC;
+		}
+	case KINC_G5_TEXTURE_FILTER_ANISOTROPIC:
+		return D3D12_FILTER_COMPARISON_ANISOTROPIC;
+	}
+
+	assert(false);
+	return D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
+}
+
+void kinc_g5_sampler_init(kinc_g5_sampler_t *sampler, const kinc_g5_sampler_options_t *options) {
+	D3D12_DESCRIPTOR_HEAP_DESC descHeapSampler = {};
+	descHeapSampler.NumDescriptors = 2;
+	descHeapSampler.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
+	descHeapSampler.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+	device->CreateDescriptorHeap(&descHeapSampler, IID_GRAPHICS_PPV_ARGS(&sampler->impl.sampler_heap));
+
+	D3D12_SAMPLER_DESC samplerDesc;
+	ZeroMemory(&samplerDesc, sizeof(D3D12_SAMPLER_DESC));
+	samplerDesc.Filter = options->is_comparison ? convert_comparison_filter(options->minification_filter, options->magnification_filter, options->mipmap_filter)
+	                                            : convert_filter(options->minification_filter, options->magnification_filter, options->mipmap_filter);
+	samplerDesc.AddressU = convert_texture_addressing(options->u_addressing);
+	samplerDesc.AddressV = convert_texture_addressing(options->v_addressing);
+	samplerDesc.AddressW = convert_texture_addressing(options->w_addressing);
+	samplerDesc.MinLOD = options->lod_min_clamp;
+	samplerDesc.MaxLOD = options->lod_max_clamp;
+	samplerDesc.MipLODBias = 0.0f;
+	samplerDesc.MaxAnisotropy = options->max_anisotropy;
+	samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
+	device->CreateSampler(&samplerDesc, sampler->impl.sampler_heap->GetCPUDescriptorHandleForHeapStart());
+}
+
+void kinc_g5_sampler_destroy(kinc_g5_sampler_t *sampler) {}

+ 7 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.h

@@ -0,0 +1,7 @@
+#pragma once
+
+struct ID3D12DescriptorHeap;
+
+typedef struct kinc_g5_sampler_impl {
+	struct ID3D12DescriptorHeap *sampler_heap;
+} kinc_g5_sampler_impl_t;

+ 87 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.c.h

@@ -0,0 +1,87 @@
+#include <kinc/backend/SystemMicrosoft.h>
+#include <kinc/graphics5/shader.h>
+#include <kinc/math/core.h>
+
+void kinc_g5_shader_init(kinc_g5_shader_t *shader, const void *_data, size_t length, kinc_g5_shader_type_t type) {
+	memset(shader->impl.constants, 0, sizeof(shader->impl.constants));
+	memset(shader->impl.attributes, 0, sizeof(shader->impl.attributes));
+	memset(shader->impl.textures, 0, sizeof(shader->impl.textures));
+
+	unsigned index = 0;
+	uint8_t *data = (uint8_t *)_data;
+
+	int attributesCount = data[index++];
+	for (int i = 0; i < attributesCount; ++i) {
+		char name[64];
+		for (unsigned i2 = 0; i2 < 63; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		strcpy(shader->impl.attributes[i].name, name);
+		shader->impl.attributes[i].attribute = data[index++];
+	}
+
+	uint8_t texCount = data[index++];
+	for (unsigned i = 0; i < texCount; ++i) {
+		char name[64];
+		for (unsigned i2 = 0; i2 < 63; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		strcpy(shader->impl.textures[i].name, name);
+		shader->impl.textures[i].texture = data[index++];
+	}
+	shader->impl.texturesCount = texCount;
+
+	uint8_t constantCount = data[index++];
+	shader->impl.constantsSize = 0;
+	for (unsigned i = 0; i < constantCount; ++i) {
+		char name[64];
+		for (unsigned i2 = 0; i2 < 63; ++i2) {
+			name[i2] = data[index++];
+			if (name[i2] == 0)
+				break;
+		}
+		ShaderConstant constant;
+		memcpy(&constant.offset, &data[index], sizeof(constant.offset));
+		index += 4;
+		memcpy(&constant.size, &data[index], sizeof(constant.size));
+		index += 4;
+#ifdef KINC_WINDOWS
+		index += 2; // columns and rows
+#endif
+		strcpy(constant.name, name);
+		shader->impl.constants[i] = constant;
+		shader->impl.constantsSize = constant.offset + constant.size;
+	}
+
+	shader->impl.length = (int)length - index;
+	shader->impl.data = (uint8_t *)malloc(shader->impl.length);
+	memcpy(shader->impl.data, &data[index], shader->impl.length);
+
+	switch (type) {
+	case KINC_G5_SHADER_TYPE_VERTEX:
+		// Microsoft::affirm(device->CreateVertexShader(this->data, this->length, nullptr, (ID3D11VertexShader**)&shader));
+		break;
+	case KINC_G5_SHADER_TYPE_FRAGMENT:
+		// Microsoft::affirm(device->CreatePixelShader(this->data, this->length, nullptr, (ID3D11PixelShader**)&shader));
+		break;
+	case KINC_G5_SHADER_TYPE_GEOMETRY:
+		// Microsoft::affirm(device->CreateGeometryShader(this->data, this->length, nullptr, (ID3D11GeometryShader**)&shader));
+		break;
+	case KINC_G5_SHADER_TYPE_TESSELLATION_CONTROL:
+		// Microsoft::affirm(device->CreateHullShader(this->data, this->length, nullptr, (ID3D11HullShader**)&shader));
+		break;
+	case KINC_G5_SHADER_TYPE_TESSELLATION_EVALUATION:
+		// Microsoft::affirm(device->CreateDomainShader(this->data, this->length, nullptr, (ID3D11DomainShader**)&shader));
+		break;
+	default:
+		break;
+	}
+}
+
+void kinc_g5_shader_destroy(kinc_g5_shader_t *shader) {
+	free(shader->impl.data);
+}

+ 36 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.h

@@ -0,0 +1,36 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	char name[64];
+	uint32_t offset;
+	uint32_t size;
+} ShaderConstant;
+
+typedef struct {
+	char name[64];
+	int attribute;
+} ShaderAttribute;
+
+typedef struct {
+	char name[64];
+	int texture;
+} ShaderTexture;
+
+typedef struct {
+	ShaderConstant constants[32];
+	int constantsSize;
+	ShaderAttribute attributes[32];
+	ShaderTexture textures[32];
+	int texturesCount;
+	void *shader;
+	uint8_t *data;
+	int length;
+} Shader5Impl;
+
+#ifdef __cplusplus
+}
+#endif

+ 425 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.c.h

@@ -0,0 +1,425 @@
+#include "texture.h"
+
+#include <kinc/graphics5/rendertarget.h>
+#include <kinc/graphics5/texture.h>
+#include <kinc/math/core.h>
+
+#include <kinc/backend/SystemMicrosoft.h>
+
+#include <math.h>
+
+static const int heapSize = 1024;
+
+#if defined(KINC_WINDOWS)
+/*static int d3d12_textureAlignment() {
+    return D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
+}*/
+#else
+int d3d12_textureAlignment();
+#endif
+
+void kinc_g5_internal_reset_textures(struct kinc_g5_command_list *list) {
+	for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) {
+		list->impl.currentRenderTargets[i] = NULL;
+		list->impl.currentTextures[i] = NULL;
+		list->impl.current_samplers[i] = NULL;
+	}
+}
+
+static inline UINT64 GetRequiredIntermediateSize(ID3D12Resource *destinationResource, UINT FirstSubresource, UINT NumSubresources) {
+	D3D12_RESOURCE_DESC desc = destinationResource->GetDesc();
+	UINT64 requiredSize = 0;
+	device->GetCopyableFootprints(&desc, FirstSubresource, NumSubresources, 0, NULL, NULL, NULL, &requiredSize);
+	device->Release();
+	return requiredSize;
+}
+
+static DXGI_FORMAT convertImageFormat(kinc_image_format_t format) {
+	switch (format) {
+	case KINC_IMAGE_FORMAT_RGBA128:
+		return DXGI_FORMAT_R32G32B32A32_FLOAT;
+	case KINC_IMAGE_FORMAT_RGBA64:
+		return DXGI_FORMAT_R16G16B16A16_FLOAT;
+	case KINC_IMAGE_FORMAT_RGB24:
+		return DXGI_FORMAT_R8G8B8A8_UNORM;
+	case KINC_IMAGE_FORMAT_A32:
+		return DXGI_FORMAT_R32_FLOAT;
+	case KINC_IMAGE_FORMAT_A16:
+		return DXGI_FORMAT_R16_FLOAT;
+	case KINC_IMAGE_FORMAT_GREY8:
+		return DXGI_FORMAT_R8_UNORM;
+	case KINC_IMAGE_FORMAT_BGRA32:
+		return DXGI_FORMAT_B8G8R8A8_UNORM;
+	case KINC_IMAGE_FORMAT_RGBA32:
+		return DXGI_FORMAT_R8G8B8A8_UNORM;
+	default:
+		return DXGI_FORMAT_R8G8B8A8_UNORM;
+	}
+}
+
+static int formatByteSize(kinc_image_format_t format) {
+	switch (format) {
+	case KINC_IMAGE_FORMAT_RGBA128:
+		return 16;
+	case KINC_IMAGE_FORMAT_RGBA64:
+		return 8;
+	case KINC_IMAGE_FORMAT_RGB24:
+		return 4;
+	case KINC_IMAGE_FORMAT_A32:
+		return 4;
+	case KINC_IMAGE_FORMAT_A16:
+		return 2;
+	case KINC_IMAGE_FORMAT_GREY8:
+		return 1;
+	case KINC_IMAGE_FORMAT_BGRA32:
+	case KINC_IMAGE_FORMAT_RGBA32:
+		return 4;
+	default:
+		return 4;
+	}
+}
+
+void kinc_g5_internal_set_textures(kinc_g5_command_list_t *list) {
+	if (list->impl.currentRenderTargets[0] != NULL || list->impl.currentTextures[0] != NULL) {
+		int srvStep = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+		int samplerStep = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+
+		if (list->impl.heapIndex + KINC_INTERNAL_G5_TEXTURE_COUNT >= heapSize) {
+			list->impl.heapIndex = 0;
+		}
+
+		D3D12_GPU_DESCRIPTOR_HANDLE srvGpu = list->impl.srvHeap->GetGPUDescriptorHandleForHeapStart();
+		D3D12_GPU_DESCRIPTOR_HANDLE samplerGpu = list->impl.samplerHeap->GetGPUDescriptorHandleForHeapStart();
+		srvGpu.ptr += list->impl.heapIndex * srvStep;
+		samplerGpu.ptr += list->impl.heapIndex * samplerStep;
+
+		for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) {
+			if ((list->impl.currentRenderTargets[i] != NULL || list->impl.currentTextures[i] != NULL) && list->impl.current_samplers[i] != NULL) {
+				ID3D12DescriptorHeap *samplerDescriptorHeap = list->impl.current_samplers[i]->impl.sampler_heap;
+
+				D3D12_CPU_DESCRIPTOR_HANDLE srvCpu = list->impl.srvHeap->GetCPUDescriptorHandleForHeapStart();
+				D3D12_CPU_DESCRIPTOR_HANDLE samplerCpu = list->impl.samplerHeap->GetCPUDescriptorHandleForHeapStart();
+				srvCpu.ptr += list->impl.heapIndex * srvStep;
+				samplerCpu.ptr += list->impl.heapIndex * samplerStep;
+				++list->impl.heapIndex;
+
+				if (list->impl.currentRenderTargets[i] != NULL) {
+					bool is_depth = list->impl.currentRenderTargets[i]->impl.stage_depth == i;
+					D3D12_CPU_DESCRIPTOR_HANDLE sourceCpu =
+					    is_depth ? list->impl.currentRenderTargets[i]->impl.srvDepthDescriptorHeap->GetCPUDescriptorHandleForHeapStart()
+					             : list->impl.currentRenderTargets[i]->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+					device->CopyDescriptorsSimple(1, srvCpu, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+					device->CopyDescriptorsSimple(1, samplerCpu, samplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart(),
+					                              D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+				}
+				else {
+					D3D12_CPU_DESCRIPTOR_HANDLE sourceCpu = list->impl.currentTextures[i]->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
+					device->CopyDescriptorsSimple(1, srvCpu, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+					device->CopyDescriptorsSimple(1, samplerCpu, samplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart(),
+					                              D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+				}
+			}
+		}
+
+		ID3D12DescriptorHeap *heaps[2] = {list->impl.srvHeap, list->impl.samplerHeap};
+		list->impl._commandList->SetDescriptorHeaps(2, heaps);
+		if (compute_pipeline_set) {
+			list->impl._commandList->SetComputeRootDescriptorTable(0, srvGpu);
+			list->impl._commandList->SetComputeRootDescriptorTable(1, srvGpu);
+			list->impl._commandList->SetComputeRootDescriptorTable(2, samplerGpu);
+		}
+		else {
+			list->impl._commandList->SetGraphicsRootDescriptorTable(0, srvGpu);
+			list->impl._commandList->SetGraphicsRootDescriptorTable(1, samplerGpu);
+		}
+	}
+}
+
+void createHeaps(kinc_g5_command_list_t *list) {
+	D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
+	heapDesc.NumDescriptors = heapSize;
+	heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+	heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+	device->CreateDescriptorHeap(&heapDesc, IID_GRAPHICS_PPV_ARGS(&list->impl.srvHeap));
+
+	D3D12_DESCRIPTOR_HEAP_DESC samplerHeapDesc = {};
+	samplerHeapDesc.NumDescriptors = heapSize;
+	samplerHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
+	samplerHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+	device->CreateDescriptorHeap(&samplerHeapDesc, IID_GRAPHICS_PPV_ARGS(&list->impl.samplerHeap));
+}
+
+extern "C" void kinc_memory_emergency();
+
+void kinc_g5_texture_init_from_image(kinc_g5_texture_t *texture, kinc_image_t *image) {
+	memset(&texture->impl, 0, sizeof(texture->impl));
+	texture->impl.stage = 0;
+	texture->impl.mipmap = true;
+	texture->texWidth = image->width;
+	texture->texHeight = image->height;
+
+	DXGI_FORMAT d3dformat = convertImageFormat(image->format);
+	int formatSize = formatByteSize(image->format);
+
+	D3D12_HEAP_PROPERTIES heapPropertiesDefault;
+	heapPropertiesDefault.Type = D3D12_HEAP_TYPE_DEFAULT;
+	heapPropertiesDefault.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+	heapPropertiesDefault.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+	heapPropertiesDefault.CreationNodeMask = 1;
+	heapPropertiesDefault.VisibleNodeMask = 1;
+
+	D3D12_RESOURCE_DESC resourceDescTex;
+	resourceDescTex.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+	resourceDescTex.Alignment = 0;
+	resourceDescTex.Width = texture->texWidth;
+	resourceDescTex.Height = texture->texHeight;
+	resourceDescTex.DepthOrArraySize = 1;
+	resourceDescTex.MipLevels = 1;
+	resourceDescTex.Format = d3dformat;
+	resourceDescTex.SampleDesc.Count = 1;
+	resourceDescTex.SampleDesc.Quality = 0;
+	resourceDescTex.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+	resourceDescTex.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+	HRESULT result = device->CreateCommittedResource(&heapPropertiesDefault, D3D12_HEAP_FLAG_NONE, &resourceDescTex, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
+	                                                 NULL, IID_GRAPHICS_PPV_ARGS(&texture->impl.image));
+	if (result != S_OK) {
+		for (int i = 0; i < 10; ++i) {
+			kinc_memory_emergency();
+			result = device->CreateCommittedResource(&heapPropertiesDefault, D3D12_HEAP_FLAG_NONE, &resourceDescTex, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
+			                                         NULL, IID_GRAPHICS_PPV_ARGS(&texture->impl.image));
+			if (result == S_OK) {
+				break;
+			}
+		}
+	}
+
+	D3D12_HEAP_PROPERTIES heapPropertiesUpload;
+	heapPropertiesUpload.Type = D3D12_HEAP_TYPE_UPLOAD;
+	heapPropertiesUpload.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+	heapPropertiesUpload.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+	heapPropertiesUpload.CreationNodeMask = 1;
+	heapPropertiesUpload.VisibleNodeMask = 1;
+
+	const UINT64 uploadBufferSize = GetRequiredIntermediateSize(texture->impl.image, 0, 1);
+	D3D12_RESOURCE_DESC resourceDescBuffer;
+	resourceDescBuffer.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+	resourceDescBuffer.Alignment = 0;
+	resourceDescBuffer.Width = uploadBufferSize;
+	resourceDescBuffer.Height = 1;
+	resourceDescBuffer.DepthOrArraySize = 1;
+	resourceDescBuffer.MipLevels = 1;
+	resourceDescBuffer.Format = DXGI_FORMAT_UNKNOWN;
+	resourceDescBuffer.SampleDesc.Count = 1;
+	resourceDescBuffer.SampleDesc.Quality = 0;
+	resourceDescBuffer.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+	resourceDescBuffer.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+	result = device->CreateCommittedResource(&heapPropertiesUpload, D3D12_HEAP_FLAG_NONE, &resourceDescBuffer, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+	                                         IID_GRAPHICS_PPV_ARGS(&texture->impl.uploadImage));
+	if (result != S_OK) {
+		for (int i = 0; i < 10; ++i) {
+			kinc_memory_emergency();
+			result = device->CreateCommittedResource(&heapPropertiesUpload, D3D12_HEAP_FLAG_NONE, &resourceDescBuffer, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+			                                         IID_GRAPHICS_PPV_ARGS(&texture->impl.uploadImage));
+			if (result == S_OK) {
+				break;
+			}
+		}
+	}
+
+	texture->impl.stride = (int)ceilf(uploadBufferSize / (float)(image->height * d3d12_textureAlignment())) * d3d12_textureAlignment();
+
+	BYTE *pixel;
+	texture->impl.uploadImage->Map(0, NULL, (void **)&pixel);
+	int pitch = kinc_g5_texture_stride(texture);
+	for (int y = 0; y < texture->texHeight; ++y) {
+		memcpy(&pixel[y * pitch], &((uint8_t *)image->data)[y * texture->texWidth * formatSize], texture->texWidth * formatSize);
+	}
+	texture->impl.uploadImage->Unmap(0, NULL);
+
+	D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {};
+	descriptorHeapDesc.NumDescriptors = 1;
+
+	descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+	descriptorHeapDesc.NodeMask = 0;
+	descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+
+	device->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(&texture->impl.srvDescriptorHeap));
+
+	D3D12_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
+	ZeroMemory(&shaderResourceViewDesc, sizeof(shaderResourceViewDesc));
+	shaderResourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+	shaderResourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+	shaderResourceViewDesc.Format = d3dformat;
+	shaderResourceViewDesc.Texture2D.MipLevels = 1;
+	shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
+	shaderResourceViewDesc.Texture2D.ResourceMinLODClamp = 0.0f;
+
+	device->CreateShaderResourceView(texture->impl.image, &shaderResourceViewDesc, texture->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
+}
+
+void create_texture(struct kinc_g5_texture *texture, int width, int height, kinc_image_format_t format, D3D12_RESOURCE_FLAGS flags) {
+	// kinc_image_init(&texture->image, width, height, format, readable);
+	memset(&texture->impl, 0, sizeof(texture->impl));
+	texture->impl.stage = 0;
+	texture->impl.mipmap = true;
+	texture->texWidth = width;
+	texture->texHeight = height;
+
+	DXGI_FORMAT d3dformat = convertImageFormat(format);
+
+	D3D12_HEAP_PROPERTIES heapPropertiesDefault;
+	heapPropertiesDefault.Type = D3D12_HEAP_TYPE_DEFAULT;
+	heapPropertiesDefault.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+	heapPropertiesDefault.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+	heapPropertiesDefault.CreationNodeMask = 1;
+	heapPropertiesDefault.VisibleNodeMask = 1;
+
+	D3D12_RESOURCE_DESC resourceDescTex;
+	resourceDescTex.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+	resourceDescTex.Alignment = 0;
+	resourceDescTex.Width = texture->texWidth;
+	resourceDescTex.Height = texture->texHeight;
+	resourceDescTex.DepthOrArraySize = 1;
+	resourceDescTex.MipLevels = 1;
+	resourceDescTex.Format = d3dformat;
+	resourceDescTex.SampleDesc.Count = 1;
+	resourceDescTex.SampleDesc.Quality = 0;
+	resourceDescTex.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+	resourceDescTex.Flags = flags;
+
+	HRESULT result = device->CreateCommittedResource(&heapPropertiesDefault, D3D12_HEAP_FLAG_NONE, &resourceDescTex, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
+	                                                 NULL, IID_GRAPHICS_PPV_ARGS(&texture->impl.image));
+	if (result != S_OK) {
+		for (int i = 0; i < 10; ++i) {
+			kinc_memory_emergency();
+			result = device->CreateCommittedResource(&heapPropertiesDefault, D3D12_HEAP_FLAG_NONE, &resourceDescTex, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
+			                                         NULL, IID_GRAPHICS_PPV_ARGS(&texture->impl.image));
+			if (result == S_OK) {
+				break;
+			}
+		}
+	}
+
+	D3D12_HEAP_PROPERTIES heapPropertiesUpload;
+	heapPropertiesUpload.Type = D3D12_HEAP_TYPE_UPLOAD;
+	heapPropertiesUpload.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+	heapPropertiesUpload.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+	heapPropertiesUpload.CreationNodeMask = 1;
+	heapPropertiesUpload.VisibleNodeMask = 1;
+
+	const UINT64 uploadBufferSize = GetRequiredIntermediateSize(texture->impl.image, 0, 1);
+	D3D12_RESOURCE_DESC resourceDescBuffer;
+	resourceDescBuffer.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+	resourceDescBuffer.Alignment = 0;
+	resourceDescBuffer.Width = uploadBufferSize;
+	resourceDescBuffer.Height = 1;
+	resourceDescBuffer.DepthOrArraySize = 1;
+	resourceDescBuffer.MipLevels = 1;
+	resourceDescBuffer.Format = DXGI_FORMAT_UNKNOWN;
+	resourceDescBuffer.SampleDesc.Count = 1;
+	resourceDescBuffer.SampleDesc.Quality = 0;
+	resourceDescBuffer.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+	resourceDescBuffer.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+	result = device->CreateCommittedResource(&heapPropertiesUpload, D3D12_HEAP_FLAG_NONE, &resourceDescBuffer, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+	                                         IID_GRAPHICS_PPV_ARGS(&texture->impl.uploadImage));
+	if (result != S_OK) {
+		for (int i = 0; i < 10; ++i) {
+			kinc_memory_emergency();
+			result = device->CreateCommittedResource(&heapPropertiesUpload, D3D12_HEAP_FLAG_NONE, &resourceDescBuffer, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+			                                         IID_GRAPHICS_PPV_ARGS(&texture->impl.uploadImage));
+			if (result == S_OK) {
+				break;
+			}
+		}
+	}
+
+	texture->impl.stride = (int)ceilf(uploadBufferSize / (float)(height * d3d12_textureAlignment())) * d3d12_textureAlignment();
+
+	D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc;
+	ZeroMemory(&descriptorHeapDesc, sizeof(descriptorHeapDesc));
+	descriptorHeapDesc.NumDescriptors = 1;
+
+	descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+	descriptorHeapDesc.NodeMask = 0;
+	descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+
+	device->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(&texture->impl.srvDescriptorHeap));
+
+	D3D12_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc = {};
+	shaderResourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+	shaderResourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+	shaderResourceViewDesc.Format = d3dformat;
+	shaderResourceViewDesc.Texture2D.MipLevels = 1;
+	shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
+	shaderResourceViewDesc.Texture2D.ResourceMinLODClamp = 0.0f;
+
+	device->CreateShaderResourceView(texture->impl.image, &shaderResourceViewDesc, texture->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
+}
+
+void kinc_g5_texture_init(struct kinc_g5_texture *texture, int width, int height, kinc_image_format_t format) {
+	create_texture(texture, width, height, format, D3D12_RESOURCE_FLAG_NONE);
+}
+
+void kinc_g5_texture_init3d(kinc_g5_texture_t *texture, int width, int height, int depth, kinc_image_format_t format) {
+	// kinc_image_init3d(&texture->image, width, height, depth, format, readable);
+}
+
+void kinc_g5_texture_init_non_sampled_access(struct kinc_g5_texture *texture, int width, int height, kinc_image_format_t format) {
+	create_texture(texture, width, height, format, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
+}
+
+void kinc_g5_texture_destroy(struct kinc_g5_texture *texture) {
+	texture->impl.image->Release();
+	texture->impl.uploadImage->Release();
+	texture->impl.srvDescriptorHeap->Release();
+}
+
+void kinc_g5_internal_texture_unmipmap(struct kinc_g5_texture *texture) {
+	texture->impl.mipmap = false;
+}
+
+void kinc_g5_internal_texture_set(kinc_g5_command_list_t *list, struct kinc_g5_texture *texture, int unit) {
+	if (unit < 0)
+		return;
+	// context->PSSetShaderResources(unit.unit, 1, &view);
+	texture->impl.stage = unit;
+	list->impl.currentTextures[texture->impl.stage] = texture;
+	list->impl.currentRenderTargets[texture->impl.stage] = NULL;
+}
+
+void kinc_g5_internal_sampler_set(kinc_g5_command_list_t *list, kinc_g5_sampler_t *sampler, int unit) {
+	if (unit < 0)
+		return;
+
+	list->impl.current_samplers[unit] = sampler;
+}
+
+uint8_t *kinc_g5_texture_lock(struct kinc_g5_texture *texture) {
+	BYTE *pixel;
+	texture->impl.uploadImage->Map(0, NULL, (void **)&pixel);
+	return pixel;
+}
+
+void kinc_g5_texture_unlock(struct kinc_g5_texture *texture) {
+	texture->impl.uploadImage->Unmap(0, NULL);
+}
+
+void kinc_g5_texture_clear(kinc_g5_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color) {}
+
+int kinc_g5_texture_stride(struct kinc_g5_texture *texture) {
+	/*int baseStride = texture->format == KINC_IMAGE_FORMAT_RGBA32 ? (texture->texWidth * 4) : texture->texWidth;
+	if (texture->format == KINC_IMAGE_FORMAT_GREY8) return texture->texWidth; // please investigate further
+	for (int i = 0;; ++i) {
+	    if (d3d12_textureAlignment() * i >= baseStride) {
+	        return d3d12_textureAlignment() * i;
+	    }
+	}*/
+	return texture->impl.stride;
+}
+
+void kinc_g5_texture_generate_mipmaps(struct kinc_g5_texture *texture, int levels) {}
+
+void kinc_g5_texture_set_mipmap(struct kinc_g5_texture *texture, kinc_image_t *mipmap, int level) {}

+ 32 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.h

@@ -0,0 +1,32 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ID3D12Resource;
+struct ID3D12DescriptorHeap;
+struct ID3D12GraphicsCommandList;
+
+typedef struct {
+	int unit;
+} TextureUnit5Impl;
+
+typedef struct {
+	bool mipmap;
+	int stage;
+	int stride;
+	struct ID3D12Resource *image;
+	struct ID3D12Resource *uploadImage;
+	struct ID3D12DescriptorHeap *srvDescriptorHeap;
+} Texture5Impl;
+
+struct kinc_g5_texture;
+struct kinc_g5_command_list;
+
+void kinc_g5_internal_set_textures(struct kinc_g5_command_list *commandList);
+void kinc_g5_internal_texture_set(struct kinc_g5_command_list *commandList, struct kinc_g5_texture *texture, int unit);
+
+#ifdef __cplusplus
+}
+#endif

+ 123 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.c.h

@@ -0,0 +1,123 @@
+#include "vertexbuffer.h"
+
+#include <kinc/graphics5/vertexbuffer.h>
+
+#include <kinc/backend/SystemMicrosoft.h>
+#include <kinc/graphics4/graphics.h>
+
+kinc_g5_vertex_buffer_t *_current_vertex_buffer = NULL;
+
+void kinc_g5_vertex_buffer_init(kinc_g5_vertex_buffer_t *buffer, int count, kinc_g5_vertex_structure_t *structure, bool gpuMemory, int instanceDataStepRate) {
+	buffer->impl.myCount = count;
+
+	// static_assert(sizeof(D3D12VertexBufferView) == sizeof(D3D12_VERTEX_BUFFER_VIEW), "Something is wrong with D3D12IVertexBufferView");
+
+	buffer->impl.myStride = 0;
+	for (int i = 0; i < structure->size; ++i) {
+		buffer->impl.myStride += kinc_g4_vertex_data_size(structure->elements[i].data);
+	}
+
+	int uploadBufferSize = buffer->impl.myStride * buffer->impl.myCount;
+
+	D3D12_HEAP_PROPERTIES heapProperties;
+	heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
+	heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+	heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+	heapProperties.CreationNodeMask = 1;
+	heapProperties.VisibleNodeMask = 1;
+
+	D3D12_RESOURCE_DESC resourceDesc;
+	resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+	resourceDesc.Alignment = 0;
+	resourceDesc.Width = uploadBufferSize;
+	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;
+
+	device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
+	                                IID_GRAPHICS_PPV_ARGS(&buffer->impl.uploadBuffer));
+
+	// device_->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE,
+	// &CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize),
+	//	D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&vertexBuffer));
+
+	buffer->impl.view.BufferLocation = buffer->impl.uploadBuffer->GetGPUVirtualAddress();
+	buffer->impl.view.SizeInBytes = uploadBufferSize;
+	buffer->impl.view.StrideInBytes = buffer->impl.myStride;
+
+	buffer->impl.lastStart = 0;
+	buffer->impl.lastCount = kinc_g5_vertex_buffer_count(buffer);
+}
+
+void kinc_g5_vertex_buffer_destroy(kinc_g5_vertex_buffer_t *buffer) {
+	// buffer->impl.vertexBuffer->Release();
+	buffer->impl.uploadBuffer->Release();
+}
+
+float *kinc_g5_vertex_buffer_lock_all(kinc_g5_vertex_buffer_t *buffer) {
+	return kinc_g5_vertex_buffer_lock(buffer, 0, kinc_g5_vertex_buffer_count(buffer));
+}
+
+float *kinc_g5_vertex_buffer_lock(kinc_g5_vertex_buffer_t *buffer, int start, int count) {
+	buffer->impl.lastStart = start;
+	buffer->impl.lastCount = count;
+
+	D3D12_RANGE range;
+	range.Begin = start * buffer->impl.myStride;
+	range.End = (start + count) * buffer->impl.myStride;
+
+	void *p;
+	buffer->impl.uploadBuffer->Map(0, &range, &p);
+	byte *bytes = (byte *)p;
+	bytes += start * buffer->impl.myStride;
+	return (float *)bytes;
+}
+
+void kinc_g5_vertex_buffer_unlock_all(kinc_g5_vertex_buffer_t *buffer) {
+	D3D12_RANGE range;
+	range.Begin = buffer->impl.lastStart * buffer->impl.myStride;
+	range.End = (buffer->impl.lastStart + buffer->impl.lastCount) * buffer->impl.myStride;
+	buffer->impl.uploadBuffer->Unmap(0, &range);
+
+	// view.BufferLocation = uploadBuffer->GetGPUVirtualAddress() + myStart * myStride;
+
+	// commandList->CopyBufferRegion(vertexBuffer, 0, uploadBuffer, 0, count() * stride());
+	// CD3DX12_RESOURCE_BARRIER barriers[1] = { CD3DX12_RESOURCE_BARRIER::Transition(vertexBuffer, D3D12_RESOURCE_STATE_COPY_DEST,
+	// D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER) };
+	// commandList->ResourceBarrier(1, barriers);
+}
+
+void kinc_g5_vertex_buffer_unlock(kinc_g5_vertex_buffer_t *buffer, int count) {
+	D3D12_RANGE range;
+	range.Begin = buffer->impl.lastStart * buffer->impl.myStride;
+	range.End = (buffer->impl.lastStart + count) * buffer->impl.myStride;
+	buffer->impl.uploadBuffer->Unmap(0, &range);
+
+	// view.BufferLocation = uploadBuffer->GetGPUVirtualAddress() + myStart * myStride;
+
+	// commandList->CopyBufferRegion(vertexBuffer, 0, uploadBuffer, 0, count() * stride());
+	// CD3DX12_RESOURCE_BARRIER barriers[1] = { CD3DX12_RESOURCE_BARRIER::Transition(vertexBuffer, D3D12_RESOURCE_STATE_COPY_DEST,
+	// D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER) };
+	// commandList->ResourceBarrier(1, barriers);
+}
+
+int kinc_g5_internal_vertex_buffer_set(kinc_g5_vertex_buffer_t *buffer, int offset) {
+	// UINT stride = myStride;
+	// UINT offset = 0;
+	// context->IASetVertexBuffers(0, 1, &vb, &stride, &offset);
+	_current_vertex_buffer = buffer;
+	return 0;
+}
+
+int kinc_g5_vertex_buffer_count(kinc_g5_vertex_buffer_t *buffer) {
+	return buffer->impl.myCount;
+}
+
+int kinc_g5_vertex_buffer_stride(kinc_g5_vertex_buffer_t *buffer) {
+	return buffer->impl.myStride;
+}

+ 30 - 0
armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.h

@@ -0,0 +1,30 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ID3D12Resource;
+
+struct D3D12VertexBufferView {
+	__int64 BufferLocation;
+	unsigned int SizeInBytes;
+	unsigned int StrideInBytes;
+};
+
+typedef struct {
+	// ID3D12Resource* vertexBuffer;
+	struct ID3D12Resource *uploadBuffer;
+	struct D3D12VertexBufferView view;
+
+	int myCount;
+	int myStride;
+	int lastStart;
+	int lastCount;
+	// float* vertices;
+	// static VertexBuffer5Impl* _current;
+} VertexBuffer5Impl;
+
+#ifdef __cplusplus
+}
+#endif

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff