initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

184
drivers/d3d12/SCsub Normal file
View File

@@ -0,0 +1,184 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
import os
from pathlib import Path
import methods
Import("env")
env_d3d12_rdd = env.Clone()
thirdparty_obj = []
# DirectX Headers (must take precedence over Windows SDK's).
env.Prepend(CPPEXTPATH=["#thirdparty/directx_headers/include/directx"])
env_d3d12_rdd.Prepend(CPPEXTPATH=["#thirdparty/directx_headers/include/directx"])
env_d3d12_rdd.Prepend(CPPEXTPATH=["#thirdparty/directx_headers/include/dxguids"])
# Direct3D 12 Memory Allocator.
env.Append(CPPEXTPATH=["#thirdparty/d3d12ma"])
env_d3d12_rdd.Append(CPPEXTPATH=["#thirdparty/d3d12ma"])
# Agility SDK.
if env["agility_sdk_path"] != "" and os.path.exists(env["agility_sdk_path"]):
env_d3d12_rdd.Append(CPPDEFINES=["AGILITY_SDK_ENABLED"])
if env["agility_sdk_multiarch"]:
env_d3d12_rdd.Append(CPPDEFINES=["AGILITY_SDK_MULTIARCH_ENABLED"])
# PIX.
if env["use_pix"]:
env_d3d12_rdd.Append(CPPDEFINES=["PIX_ENABLED"])
env_d3d12_rdd.Append(CPPEXTPATH=[env["pix_path"] + "/Include"])
# Direct composition.
if "dcomp" in env.get("supported", []):
env_d3d12_rdd.Append(CPPDEFINES=["DCOMP_ENABLED"])
env.Append(CPPDEFINES=["DCOMP_ENABLED"]) # Used in header included in platform.
# Mesa (SPIR-V to DXIL functionality).
mesa_libs = env["mesa_libs"]
if env.msvc and os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-msvc"):
mesa_libs = env["mesa_libs"] + "-" + env["arch"] + "-msvc"
elif env["use_llvm"] and os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-llvm"):
mesa_libs = env["mesa_libs"] + "-" + env["arch"] + "-llvm"
elif not env["use_llvm"] and os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-gcc"):
mesa_libs = env["mesa_libs"] + "-" + env["arch"] + "-gcc"
mesa_dir = (mesa_libs + "/godot-mesa").replace("\\", "/")
mesa_gen_dir = (mesa_libs + "/godot-mesa/generated").replace("\\", "/")
mesa_absdir = Dir(mesa_dir).abspath
mesa_gen_absdir = Dir(mesa_dir + "/generated").abspath
custom_build_steps = [
[
"src/compiler",
"glsl/ir_expression_operation.py enum > %s/ir_expression_operation.h",
"ir_expression_operation.h",
],
["src/compiler/nir", "nir_builder_opcodes_h.py > %s/nir_builder_opcodes.h", "nir_builder_opcodes.h"],
["src/compiler/nir", "nir_constant_expressions.py > %s/nir_constant_expressions.c", "nir_constant_expressions.c"],
["src/compiler/nir", "nir_intrinsics_h.py --outdir %s", "nir_intrinsics.h"],
["src/compiler/nir", "nir_intrinsics_c.py --outdir %s", "nir_intrinsics.c"],
["src/compiler/nir", "nir_intrinsics_indices_h.py --outdir %s", "nir_intrinsics_indices.h"],
["src/compiler/nir", "nir_opcodes_h.py > %s/nir_opcodes.h", "nir_opcodes.h"],
["src/compiler/nir", "nir_opcodes_c.py > %s/nir_opcodes.c", "nir_opcodes.c"],
["src/compiler/nir", "nir_opt_algebraic.py > %s/nir_opt_algebraic.c", "nir_opt_algebraic.c"],
["src/compiler/spirv", "vtn_generator_ids_h.py spir-v.xml %s/vtn_generator_ids.h", "vtn_generator_ids.h"],
[
"src/microsoft/compiler",
"dxil_nir_algebraic.py -p ../../../src/compiler/nir > %s/dxil_nir_algebraic.c",
"dxil_nir_algebraic.c",
],
["src/util", "format_srgb.py > %s/format_srgb.c", "format_srgb.c"],
["src/util/format", "u_format_table.py u_format.csv --header > %s/u_format_pack.h", "u_format_pack.h"],
["src/util/format", "u_format_table.py u_format.csv > %s/u_format_table.c", "u_format_table.c"],
]
mesa_gen_include_paths = [mesa_gen_dir + "/src"]
# See update_mesa.sh for explanation.
for v in custom_build_steps:
subdir = v[0]
cmd = v[1]
gen_filename = v[2]
in_dir = str(Path(mesa_absdir + "/" + subdir))
out_full_path = mesa_dir + "/generated/" + subdir
out_file_full_path = out_full_path + "/" + gen_filename
if gen_filename.endswith(".h"):
mesa_gen_include_paths += [out_full_path.replace("\\", "/")]
mesa_private_inc_paths = [v[0] for v in os.walk(mesa_absdir)]
mesa_private_inc_paths = [v.replace(mesa_absdir, mesa_dir) for v in mesa_private_inc_paths]
mesa_private_inc_paths = [v.replace("\\", "/") for v in mesa_private_inc_paths]
# Avoid build results depending on if generated files already exist.
mesa_private_inc_paths = [v for v in mesa_private_inc_paths if not v.startswith(mesa_gen_dir)]
mesa_private_inc_paths.sort()
# Include the list of the generated ones now, so out-of-the-box sources can include generated headers.
mesa_private_inc_paths += mesa_gen_include_paths
# We have to blacklist some because we are bindly adding every Mesa directory
# to the include path and in some cases that causes the wrong header to be included.
mesa_blacklist_inc_paths = [
"src/c11",
]
mesa_blacklist_inc_paths = [mesa_dir + "/" + v for v in mesa_blacklist_inc_paths]
mesa_private_inc_paths = [v for v in mesa_private_inc_paths if v not in mesa_blacklist_inc_paths]
# Added by ourselves.
extra_defines = [
"WINDOWS_NO_FUTEX",
]
mesa_ver = Path(mesa_absdir + "/VERSION.info")
if not mesa_ver.is_file():
mesa_ver = Path(mesa_absdir + "/VERSION")
# These defines are inspired by the Meson build scripts in the original repo.
extra_defines += [
"__STDC_CONSTANT_MACROS",
"__STDC_FORMAT_MACROS",
"__STDC_LIMIT_MACROS",
("PACKAGE_VERSION", '\\"' + mesa_ver.read_text().strip() + '\\"'),
("PACKAGE_BUGREPORT", '\\"https://gitlab.freedesktop.org/mesa/mesa/-/issues\\"'),
"PIPE_SUBSYSTEM_WINDOWS_USER",
("_Static_assert", "static_assert"),
]
if env.msvc:
extra_defines += [
"_USE_MATH_DEFINES",
"VC_EXTRALEAN",
"_CRT_SECURE_NO_WARNINGS",
"_CRT_SECURE_NO_DEPRECATE",
"_SCL_SECURE_NO_WARNINGS",
"_SCL_SECURE_NO_DEPRECATE",
"_ALLOW_KEYWORD_MACROS",
("_HAS_EXCEPTIONS", 0),
"NOMINMAX",
"HAVE_STRUCT_TIMESPEC",
]
else:
extra_defines += [
"HAVE_STRUCT_TIMESPEC",
]
if methods.using_gcc(env) and methods.get_compiler_version(env)["major"] < 13:
# `region` & `endregion` not recognized as valid pragmas.
env_d3d12_rdd.Append(CCFLAGS=["-Wno-unknown-pragmas"])
env.Append(CCFLAGS=["-Wno-unknown-pragmas"])
# This is needed since rendering_device_d3d12.cpp needs to include some Mesa internals.
# FIXME: Should be CPPEXTPATH, but doing so introduces an include-order bug when combined with
# godot-nir-static; this necessitates warning macro wrappers. See #106376.
env_d3d12_rdd.Prepend(CPPPATH=mesa_private_inc_paths)
# For the same reason as above, the defines must be the same as in the 3rd-party code itself.
env_d3d12_rdd.Append(CPPDEFINES=extra_defines)
# Add all.
env.drivers_sources += thirdparty_obj
# Godot source files.
driver_obj = []
env_d3d12_rdd.add_source_files(driver_obj, "*.cpp")
env.drivers_sources += driver_obj
# Needed to force rebuilding the driver files when the thirdparty code is updated.
env.Depends(driver_obj, thirdparty_obj)

View File

@@ -0,0 +1,57 @@
/**************************************************************************/
/* d3d12_godot_nir_bridge.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include <cstdint>
#ifdef __cplusplus
extern "C" {
#endif
// This one leaves room for potentially extremely copious bindings in a set.
static const uint32_t GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER = 100000000;
// This one leaves room for potentially big sized arrays.
static const uint32_t GODOT_NIR_BINDING_MULTIPLIER = 100000;
static const uint64_t GODOT_NIR_SC_SENTINEL_MAGIC = 0x45678900; // This must be as big as to be VBR-ed as a 32 bits number.
static const uint64_t GODOT_NIR_SC_SENTINEL_MAGIC_MASK = 0xffffffffffffff00;
static const uint64_t GODOT_NIR_SC_SENTINEL_ID_MASK = 0x00000000000000ff;
typedef struct GodotNirCallbacks {
void *data;
void (*report_resource)(uint32_t p_register, uint32_t p_space, uint32_t p_dxil_type, void *p_data);
void (*report_sc_bit_offset_fn)(uint32_t p_sc_id, uint64_t p_bit_offset, void *p_data);
void (*report_bitcode_bit_offset_fn)(uint64_t p_bit_offset, void *p_data);
} GodotNirCallbacks;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* d3d12_hooks.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "d3d12_hooks.h"
D3D12Hooks *D3D12Hooks::singleton = nullptr;
D3D12Hooks::D3D12Hooks() {
if (singleton == nullptr) {
singleton = this;
}
}
D3D12Hooks::~D3D12Hooks() {
if (singleton == this) {
singleton = nullptr;
}
}

View File

@@ -0,0 +1,48 @@
/**************************************************************************/
/* d3d12_hooks.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "rendering_device_driver_d3d12.h"
class D3D12Hooks {
private:
static D3D12Hooks *singleton;
public:
D3D12Hooks();
virtual ~D3D12Hooks();
virtual D3D_FEATURE_LEVEL get_feature_level() const = 0;
virtual LUID get_adapter_luid() const = 0;
virtual void set_device(ID3D12Device *p_device) = 0;
virtual void set_command_queue(ID3D12CommandQueue *p_queue) = 0;
virtual void cleanup_device() = 0;
static D3D12Hooks *get_singleton() { return singleton; }
};

41
drivers/d3d12/d3d12ma.cpp Normal file
View File

@@ -0,0 +1,41 @@
/**************************************************************************/
/* d3d12ma.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
// Wrapper needed to set the required rpcndr version for MinGW compatibility.
// Since we're compiling thirdparty code in a Godot SCons environment with
// warnings enabled, we also need to silence them manually.
#include "rendering_device_driver_d3d12.h" // For __REQUIRED_RPCNDR_H_VERSION__.
GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Wmaybe-uninitialized")
#include <D3D12MemAlloc.cpp>
GODOT_GCC_WARNING_POP

209
drivers/d3d12/dxil_hash.cpp Normal file
View File

@@ -0,0 +1,209 @@
/**************************************************************************/
/* dxil_hash.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
// Based on the patched public domain implementation released by Microsoft here:
// https://github.com/microsoft/hlsl-specs/blob/main/proposals/infra/INF-0004-validator-hashing.md
#include "dxil_hash.h"
#include <memory.h>
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
static const BYTE padding[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static void FF(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
a += ((b & c) | (~b & d)) + x + ac;
a = ((a << s) | (a >> (32 - s))) + b;
}
static void GG(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
a += ((b & d) | (c & ~d)) + x + ac;
a = ((a << s) | (a >> (32 - s))) + b;
}
static void HH(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
a += (b ^ c ^ d) + x + ac;
a = ((a << s) | (a >> (32 - s))) + b;
}
static void II(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
a += (c ^ (b | ~d)) + x + ac;
a = ((a << s) | (a >> (32 - s))) + b;
}
void compute_dxil_hash(const BYTE *pData, UINT byteCount, BYTE *pOutHash) {
UINT leftOver = byteCount & 0x3f;
UINT padAmount;
bool bTwoRowsPadding = false;
if (leftOver < 56) {
padAmount = 56 - leftOver;
} else {
padAmount = 120 - leftOver;
bTwoRowsPadding = true;
}
UINT padAmountPlusSize = padAmount + 8;
UINT state[4] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 };
UINT N = (byteCount + padAmountPlusSize) >> 6;
UINT offset = 0;
UINT NextEndState = bTwoRowsPadding ? N - 2 : N - 1;
const BYTE *pCurrData = pData;
for (UINT i = 0; i < N; i++, offset += 64, pCurrData += 64) {
UINT x[16] = {};
const UINT *pX;
if (i == NextEndState) {
if (!bTwoRowsPadding && i == N - 1) {
UINT remainder = byteCount - offset;
x[0] = byteCount << 3;
memcpy((BYTE *)x + 4, pCurrData, remainder);
memcpy((BYTE *)x + 4 + remainder, padding, padAmount);
x[15] = 1 | (byteCount << 1);
} else if (bTwoRowsPadding) {
if (i == N - 2) {
UINT remainder = byteCount - offset;
memcpy(x, pCurrData, remainder);
memcpy((BYTE *)x + remainder, padding, padAmount - 56);
NextEndState = N - 1;
} else if (i == N - 1) {
x[0] = byteCount << 3;
memcpy((BYTE *)x + 4, padding + padAmount - 56, 56);
x[15] = 1 | (byteCount << 1);
}
}
pX = x;
} else {
pX = (const UINT *)pCurrData;
}
UINT a = state[0];
UINT b = state[1];
UINT c = state[2];
UINT d = state[3];
/* Round 1 */
FF(a, b, c, d, pX[0], S11, 0xd76aa478); /* 1 */
FF(d, a, b, c, pX[1], S12, 0xe8c7b756); /* 2 */
FF(c, d, a, b, pX[2], S13, 0x242070db); /* 3 */
FF(b, c, d, a, pX[3], S14, 0xc1bdceee); /* 4 */
FF(a, b, c, d, pX[4], S11, 0xf57c0faf); /* 5 */
FF(d, a, b, c, pX[5], S12, 0x4787c62a); /* 6 */
FF(c, d, a, b, pX[6], S13, 0xa8304613); /* 7 */
FF(b, c, d, a, pX[7], S14, 0xfd469501); /* 8 */
FF(a, b, c, d, pX[8], S11, 0x698098d8); /* 9 */
FF(d, a, b, c, pX[9], S12, 0x8b44f7af); /* 10 */
FF(c, d, a, b, pX[10], S13, 0xffff5bb1); /* 11 */
FF(b, c, d, a, pX[11], S14, 0x895cd7be); /* 12 */
FF(a, b, c, d, pX[12], S11, 0x6b901122); /* 13 */
FF(d, a, b, c, pX[13], S12, 0xfd987193); /* 14 */
FF(c, d, a, b, pX[14], S13, 0xa679438e); /* 15 */
FF(b, c, d, a, pX[15], S14, 0x49b40821); /* 16 */
/* Round 2 */
GG(a, b, c, d, pX[1], S21, 0xf61e2562); /* 17 */
GG(d, a, b, c, pX[6], S22, 0xc040b340); /* 18 */
GG(c, d, a, b, pX[11], S23, 0x265e5a51); /* 19 */
GG(b, c, d, a, pX[0], S24, 0xe9b6c7aa); /* 20 */
GG(a, b, c, d, pX[5], S21, 0xd62f105d); /* 21 */
GG(d, a, b, c, pX[10], S22, 0x2441453); /* 22 */
GG(c, d, a, b, pX[15], S23, 0xd8a1e681); /* 23 */
GG(b, c, d, a, pX[4], S24, 0xe7d3fbc8); /* 24 */
GG(a, b, c, d, pX[9], S21, 0x21e1cde6); /* 25 */
GG(d, a, b, c, pX[14], S22, 0xc33707d6); /* 26 */
GG(c, d, a, b, pX[3], S23, 0xf4d50d87); /* 27 */
GG(b, c, d, a, pX[8], S24, 0x455a14ed); /* 28 */
GG(a, b, c, d, pX[13], S21, 0xa9e3e905); /* 29 */
GG(d, a, b, c, pX[2], S22, 0xfcefa3f8); /* 30 */
GG(c, d, a, b, pX[7], S23, 0x676f02d9); /* 31 */
GG(b, c, d, a, pX[12], S24, 0x8d2a4c8a); /* 32 */
/* Round 3 */
HH(a, b, c, d, pX[5], S31, 0xfffa3942); /* 33 */
HH(d, a, b, c, pX[8], S32, 0x8771f681); /* 34 */
HH(c, d, a, b, pX[11], S33, 0x6d9d6122); /* 35 */
HH(b, c, d, a, pX[14], S34, 0xfde5380c); /* 36 */
HH(a, b, c, d, pX[1], S31, 0xa4beea44); /* 37 */
HH(d, a, b, c, pX[4], S32, 0x4bdecfa9); /* 38 */
HH(c, d, a, b, pX[7], S33, 0xf6bb4b60); /* 39 */
HH(b, c, d, a, pX[10], S34, 0xbebfbc70); /* 40 */
HH(a, b, c, d, pX[13], S31, 0x289b7ec6); /* 41 */
HH(d, a, b, c, pX[0], S32, 0xeaa127fa); /* 42 */
HH(c, d, a, b, pX[3], S33, 0xd4ef3085); /* 43 */
HH(b, c, d, a, pX[6], S34, 0x4881d05); /* 44 */
HH(a, b, c, d, pX[9], S31, 0xd9d4d039); /* 45 */
HH(d, a, b, c, pX[12], S32, 0xe6db99e5); /* 46 */
HH(c, d, a, b, pX[15], S33, 0x1fa27cf8); /* 47 */
HH(b, c, d, a, pX[2], S34, 0xc4ac5665); /* 48 */
/* Round 4 */
II(a, b, c, d, pX[0], S41, 0xf4292244); /* 49 */
II(d, a, b, c, pX[7], S42, 0x432aff97); /* 50 */
II(c, d, a, b, pX[14], S43, 0xab9423a7); /* 51 */
II(b, c, d, a, pX[5], S44, 0xfc93a039); /* 52 */
II(a, b, c, d, pX[12], S41, 0x655b59c3); /* 53 */
II(d, a, b, c, pX[3], S42, 0x8f0ccc92); /* 54 */
II(c, d, a, b, pX[10], S43, 0xffeff47d); /* 55 */
II(b, c, d, a, pX[1], S44, 0x85845dd1); /* 56 */
II(a, b, c, d, pX[8], S41, 0x6fa87e4f); /* 57 */
II(d, a, b, c, pX[15], S42, 0xfe2ce6e0); /* 58 */
II(c, d, a, b, pX[6], S43, 0xa3014314); /* 59 */
II(b, c, d, a, pX[13], S44, 0x4e0811a1); /* 60 */
II(a, b, c, d, pX[4], S41, 0xf7537e82); /* 61 */
II(d, a, b, c, pX[11], S42, 0xbd3af235); /* 62 */
II(c, d, a, b, pX[2], S43, 0x2ad7d2bb); /* 63 */
II(b, c, d, a, pX[9], S44, 0xeb86d391); /* 64 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
memcpy(pOutHash, state, 16);
}

36
drivers/d3d12/dxil_hash.h Normal file
View File

@@ -0,0 +1,36 @@
/**************************************************************************/
/* dxil_hash.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void compute_dxil_hash(const BYTE *pData, UINT byteCount, BYTE *pOutHash);

View File

@@ -0,0 +1,348 @@
/**************************************************************************/
/* rendering_context_driver_d3d12.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "rendering_context_driver_d3d12.h"
#include "d3d12_hooks.h"
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/string/ustring.h"
#include "core/templates/local_vector.h"
#include "core/version.h"
#include "servers/rendering/rendering_device.h"
GODOT_GCC_WARNING_PUSH
GODOT_GCC_WARNING_IGNORE("-Wmissing-field-initializers")
GODOT_GCC_WARNING_IGNORE("-Wnon-virtual-dtor")
GODOT_GCC_WARNING_IGNORE("-Wshadow")
GODOT_GCC_WARNING_IGNORE("-Wswitch")
GODOT_CLANG_WARNING_PUSH
GODOT_CLANG_WARNING_IGNORE("-Wmissing-field-initializers")
GODOT_CLANG_WARNING_IGNORE("-Wnon-virtual-dtor")
GODOT_CLANG_WARNING_IGNORE("-Wstring-plus-int")
GODOT_CLANG_WARNING_IGNORE("-Wswitch")
GODOT_MSVC_WARNING_PUSH
#include <dxcapi.h>
GODOT_GCC_WARNING_POP
GODOT_CLANG_WARNING_POP
#if !defined(_MSC_VER)
#include <guiddef.h>
#include <dxguids.h>
#endif
// Note: symbols are not available in MinGW and old MSVC import libraries.
// GUID values from https://github.com/microsoft/DirectX-Headers/blob/7a9f4d06911d30eecb56a4956dab29dcca2709ed/include/directx/d3d12.idl#L5877-L5881
const GUID CLSID_D3D12DeviceFactoryGodot = { 0x114863bf, 0xc386, 0x4aee, { 0xb3, 0x9d, 0x8f, 0x0b, 0xbb, 0x06, 0x29, 0x55 } };
const GUID CLSID_D3D12DebugGodot = { 0xf2352aeb, 0xdd84, 0x49fe, { 0xb9, 0x7b, 0xa9, 0xdc, 0xfd, 0xcc, 0x1b, 0x4f } };
const GUID CLSID_D3D12SDKConfigurationGodot = { 0x7cda6aca, 0xa03e, 0x49c8, { 0x94, 0x58, 0x03, 0x34, 0xd2, 0x0e, 0x07, 0xce } };
#ifdef PIX_ENABLED
#if defined(__GNUC__)
#define _MSC_VER 1800
#endif
#define USE_PIX
#include "WinPixEventRuntime/pix3.h"
#if defined(__GNUC__)
#undef _MSC_VER
#endif
#endif
RenderingContextDriverD3D12::RenderingContextDriverD3D12() {}
RenderingContextDriverD3D12::~RenderingContextDriverD3D12() {
// Let's release manually everything that may still be holding
// onto the DLLs before freeing them.
device_factory.Reset();
dxgi_factory.Reset();
if (lib_d3d12) {
FreeLibrary(lib_d3d12);
}
if (lib_dxgi) {
FreeLibrary(lib_dxgi);
}
#ifdef DCOMP_ENABLED
if (lib_dcomp) {
FreeLibrary(lib_dcomp);
}
#endif
}
Error RenderingContextDriverD3D12::_init_device_factory() {
uint32_t agility_sdk_version = GLOBAL_GET("rendering/rendering_device/d3d12/agility_sdk_version");
String agility_sdk_path = String(".\\") + Engine::get_singleton()->get_architecture_name();
lib_d3d12 = LoadLibraryW(L"D3D12.dll");
ERR_FAIL_NULL_V(lib_d3d12, ERR_CANT_CREATE);
lib_dxgi = LoadLibraryW(L"DXGI.dll");
ERR_FAIL_NULL_V(lib_dxgi, ERR_CANT_CREATE);
#ifdef DCOMP_ENABLED
lib_dcomp = LoadLibraryW(L"Dcomp.dll");
ERR_FAIL_NULL_V(lib_dcomp, ERR_CANT_CREATE);
#endif
// Note: symbol is not available in MinGW import library.
PFN_D3D12_GET_INTERFACE d3d_D3D12GetInterface = (PFN_D3D12_GET_INTERFACE)(void *)GetProcAddress(lib_d3d12, "D3D12GetInterface");
if (!d3d_D3D12GetInterface) {
return OK; // Fallback to the system loader.
}
ID3D12SDKConfiguration *sdk_config = nullptr;
if (SUCCEEDED(d3d_D3D12GetInterface(CLSID_D3D12SDKConfigurationGodot, IID_PPV_ARGS(&sdk_config)))) {
ID3D12SDKConfiguration1 *sdk_config1 = nullptr;
if (SUCCEEDED(sdk_config->QueryInterface(&sdk_config1))) {
if (SUCCEEDED(sdk_config1->CreateDeviceFactory(agility_sdk_version, agility_sdk_path.ascii().get_data(), IID_PPV_ARGS(device_factory.GetAddressOf())))) {
d3d_D3D12GetInterface(CLSID_D3D12DeviceFactoryGodot, IID_PPV_ARGS(device_factory.GetAddressOf()));
} else if (SUCCEEDED(sdk_config1->CreateDeviceFactory(agility_sdk_version, ".\\", IID_PPV_ARGS(device_factory.GetAddressOf())))) {
d3d_D3D12GetInterface(CLSID_D3D12DeviceFactoryGodot, IID_PPV_ARGS(device_factory.GetAddressOf()));
}
sdk_config1->Release();
}
sdk_config->Release();
}
return OK;
}
Error RenderingContextDriverD3D12::_initialize_debug_layers() {
ComPtr<ID3D12Debug> debug_controller;
HRESULT res;
if (device_factory) {
res = device_factory->GetConfigurationInterface(CLSID_D3D12DebugGodot, IID_PPV_ARGS(&debug_controller));
} else {
PFN_D3D12_GET_DEBUG_INTERFACE d3d_D3D12GetDebugInterface = (PFN_D3D12_GET_DEBUG_INTERFACE)(void *)GetProcAddress(lib_d3d12, "D3D12GetDebugInterface");
ERR_FAIL_NULL_V(d3d_D3D12GetDebugInterface, ERR_CANT_CREATE);
res = d3d_D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller));
}
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_QUERY_FAILED);
debug_controller->EnableDebugLayer();
return OK;
}
Error RenderingContextDriverD3D12::_initialize_devices() {
const UINT dxgi_factory_flags = use_validation_layers() ? DXGI_CREATE_FACTORY_DEBUG : 0;
typedef HRESULT(WINAPI * PFN_DXGI_CREATE_DXGI_FACTORY2)(UINT, REFIID, void **);
PFN_DXGI_CREATE_DXGI_FACTORY2 dxgi_CreateDXGIFactory2 = (PFN_DXGI_CREATE_DXGI_FACTORY2)(void *)GetProcAddress(lib_dxgi, "CreateDXGIFactory2");
ERR_FAIL_NULL_V(dxgi_CreateDXGIFactory2, ERR_CANT_CREATE);
HRESULT res = dxgi_CreateDXGIFactory2(dxgi_factory_flags, IID_PPV_ARGS(&dxgi_factory));
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE);
// Enumerate all possible adapters.
LocalVector<IDXGIAdapter1 *> adapters;
IDXGIAdapter1 *adapter = nullptr;
do {
adapter = create_adapter(adapters.size());
if (adapter != nullptr) {
adapters.push_back(adapter);
}
} while (adapter != nullptr);
ERR_FAIL_COND_V_MSG(adapters.is_empty(), ERR_CANT_CREATE, "Adapters enumeration reported zero accessible devices.");
// Fill the device descriptions with the adapters.
driver_devices.resize(adapters.size());
for (uint32_t i = 0; i < adapters.size(); ++i) {
DXGI_ADAPTER_DESC1 desc = {};
adapters[i]->GetDesc1(&desc);
Device &device = driver_devices[i];
device.name = desc.Description;
device.vendor = desc.VendorId;
device.workarounds = Workarounds();
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
device.type = DEVICE_TYPE_CPU;
} else {
const bool has_dedicated_vram = desc.DedicatedVideoMemory > 0;
device.type = has_dedicated_vram ? DEVICE_TYPE_DISCRETE_GPU : DEVICE_TYPE_INTEGRATED_GPU;
}
}
// Release all created adapters.
for (uint32_t i = 0; i < adapters.size(); ++i) {
adapters[i]->Release();
}
ComPtr<IDXGIFactory5> factory_5;
dxgi_factory.As(&factory_5);
if (factory_5 != nullptr) {
// The type is important as in general, sizeof(bool) != sizeof(BOOL).
BOOL feature_supported = FALSE;
res = factory_5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &feature_supported, sizeof(feature_supported));
if (SUCCEEDED(res)) {
tearing_supported = feature_supported;
} else {
ERR_PRINT("CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)res) + ".");
}
}
return OK;
}
bool RenderingContextDriverD3D12::use_validation_layers() const {
return Engine::get_singleton()->is_validation_layers_enabled();
}
Error RenderingContextDriverD3D12::initialize() {
Error err = _init_device_factory();
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
if (use_validation_layers()) {
err = _initialize_debug_layers();
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
}
err = _initialize_devices();
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
return OK;
}
const RenderingContextDriver::Device &RenderingContextDriverD3D12::device_get(uint32_t p_device_index) const {
DEV_ASSERT(p_device_index < driver_devices.size());
return driver_devices[p_device_index];
}
uint32_t RenderingContextDriverD3D12::device_get_count() const {
return driver_devices.size();
}
bool RenderingContextDriverD3D12::device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const {
// All devices should support presenting to any surface.
return true;
}
RenderingDeviceDriver *RenderingContextDriverD3D12::driver_create() {
return memnew(RenderingDeviceDriverD3D12(this));
}
void RenderingContextDriverD3D12::driver_free(RenderingDeviceDriver *p_driver) {
memdelete(p_driver);
}
RenderingContextDriver::SurfaceID RenderingContextDriverD3D12::surface_create(const void *p_platform_data) {
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
Surface *surface = memnew(Surface);
surface->hwnd = wpd->window;
return SurfaceID(surface);
}
void RenderingContextDriverD3D12::surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) {
Surface *surface = (Surface *)(p_surface);
surface->width = p_width;
surface->height = p_height;
surface->needs_resize = true;
}
void RenderingContextDriverD3D12::surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) {
Surface *surface = (Surface *)(p_surface);
surface->vsync_mode = p_vsync_mode;
surface->needs_resize = true;
}
DisplayServer::VSyncMode RenderingContextDriverD3D12::surface_get_vsync_mode(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->vsync_mode;
}
uint32_t RenderingContextDriverD3D12::surface_get_width(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->width;
}
uint32_t RenderingContextDriverD3D12::surface_get_height(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->height;
}
void RenderingContextDriverD3D12::surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) {
Surface *surface = (Surface *)(p_surface);
surface->needs_resize = p_needs_resize;
}
bool RenderingContextDriverD3D12::surface_get_needs_resize(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->needs_resize;
}
void RenderingContextDriverD3D12::surface_destroy(SurfaceID p_surface) {
Surface *surface = (Surface *)(p_surface);
memdelete(surface);
}
bool RenderingContextDriverD3D12::is_debug_utils_enabled() const {
#ifdef PIX_ENABLED
return true;
#else
return false;
#endif
}
IDXGIAdapter1 *RenderingContextDriverD3D12::create_adapter(uint32_t p_adapter_index) const {
ComPtr<IDXGIFactory6> factory_6;
dxgi_factory.As(&factory_6);
// TODO: Use IDXCoreAdapterList, which gives more comprehensive information.
IDXGIAdapter1 *adapter = nullptr;
if (factory_6) {
if (factory_6->EnumAdapterByGpuPreference(p_adapter_index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)) == DXGI_ERROR_NOT_FOUND) {
return nullptr;
}
} else {
if (dxgi_factory->EnumAdapters1(p_adapter_index, &adapter) == DXGI_ERROR_NOT_FOUND) {
return nullptr;
}
}
return adapter;
}
ID3D12DeviceFactory *RenderingContextDriverD3D12::device_factory_get() const {
return device_factory.Get();
}
IDXGIFactory2 *RenderingContextDriverD3D12::dxgi_factory_get() const {
return dxgi_factory.Get();
}
bool RenderingContextDriverD3D12::get_tearing_supported() const {
return tearing_supported;
}

View File

@@ -0,0 +1,118 @@
/**************************************************************************/
/* rendering_context_driver_d3d12.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/os/mutex.h"
#include "core/string/ustring.h"
#include "core/templates/rid_owner.h"
#include "rendering_device_driver_d3d12.h"
#include "servers/display_server.h"
#include "servers/rendering/rendering_context_driver.h"
#if defined(AS)
#undef AS
#endif
#ifdef DCOMP_ENABLED
#include <dcomp.h>
#endif
#include <d3dx12.h>
#include <dxgi1_6.h>
#include <wrl/client.h>
using Microsoft::WRL::ComPtr;
#define ARRAY_SIZE(a) std::size(a)
class RenderingContextDriverD3D12 : public RenderingContextDriver {
ComPtr<ID3D12DeviceFactory> device_factory;
ComPtr<IDXGIFactory2> dxgi_factory;
TightLocalVector<Device> driver_devices;
bool tearing_supported = false;
Error _init_device_factory();
Error _initialize_debug_layers();
Error _initialize_devices();
public:
virtual Error initialize() override;
virtual const Device &device_get(uint32_t p_device_index) const override;
virtual uint32_t device_get_count() const override;
virtual bool device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const override;
virtual RenderingDeviceDriver *driver_create() override;
virtual void driver_free(RenderingDeviceDriver *p_driver) override;
virtual SurfaceID surface_create(const void *p_platform_data) override;
virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) override;
virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) override;
virtual DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const override;
virtual uint32_t surface_get_width(SurfaceID p_surface) const override;
virtual uint32_t surface_get_height(SurfaceID p_surface) const override;
virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) override;
virtual bool surface_get_needs_resize(SurfaceID p_surface) const override;
virtual void surface_destroy(SurfaceID p_surface) override;
virtual bool is_debug_utils_enabled() const override;
// Platform-specific data for the Windows embedded in this driver.
struct WindowPlatformData {
HWND window;
};
// D3D12-only methods.
struct Surface {
HWND hwnd = nullptr;
uint32_t width = 0;
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
bool needs_resize = false;
#ifdef DCOMP_ENABLED
ComPtr<IDCompositionDevice> composition_device;
ComPtr<IDCompositionTarget> composition_target;
ComPtr<IDCompositionVisual> composition_visual;
#endif
};
HMODULE lib_d3d12 = nullptr;
HMODULE lib_dxgi = nullptr;
#ifdef DCOMP_ENABLED
HMODULE lib_dcomp = nullptr;
#endif
IDXGIAdapter1 *create_adapter(uint32_t p_adapter_index) const;
ID3D12DeviceFactory *device_factory_get() const;
IDXGIFactory2 *dxgi_factory_get() const;
bool get_tearing_supported() const;
bool use_validation_layers() const;
RenderingContextDriverD3D12();
virtual ~RenderingContextDriverD3D12() override;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,928 @@
/**************************************************************************/
/* rendering_device_driver_d3d12.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/templates/hash_map.h"
#include "core/templates/paged_allocator.h"
#include "core/templates/self_list.h"
#include "rendering_shader_container_d3d12.h"
#include "servers/rendering/rendering_device_driver.h"
#ifndef _MSC_VER
// Match current version used by MinGW, MSVC and Direct3D 12 headers use 500.
#define __REQUIRED_RPCNDR_H_VERSION__ 475
#endif
#include <d3dx12.h>
#include <dxgi1_6.h>
#define D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED
#include <D3D12MemAlloc.h>
#include <wrl/client.h>
#if defined(_MSC_VER) && defined(MemoryBarrier)
// Annoying define from winnt.h. Reintroduced by some of the headers above.
#undef MemoryBarrier
#endif
using Microsoft::WRL::ComPtr;
#ifdef DEV_ENABLED
#define CUSTOM_INFO_QUEUE_ENABLED 0
#endif
class RenderingContextDriverD3D12;
// Design principles:
// - D3D12 structs are zero-initialized and fields not requiring a non-zero value are omitted (except in cases where expresivity reasons apply).
class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
/*****************/
/**** GENERIC ****/
/*****************/
struct D3D12Format {
DXGI_FORMAT family = DXGI_FORMAT_UNKNOWN;
DXGI_FORMAT general_format = DXGI_FORMAT_UNKNOWN;
UINT swizzle = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
DXGI_FORMAT dsv_format = DXGI_FORMAT_UNKNOWN;
};
static const D3D12Format RD_TO_D3D12_FORMAT[RDD::DATA_FORMAT_MAX];
struct DeviceLimits {
uint64_t max_srvs_per_shader_stage = 0;
uint64_t max_cbvs_per_shader_stage = 0;
uint64_t max_samplers_across_all_stages = 0;
uint64_t max_uavs_across_all_stages = 0;
uint64_t timestamp_frequency = 0;
};
struct SubgroupCapabilities {
uint32_t size = 0;
bool wave_ops_supported = false;
uint32_t supported_stages_flags_rd() const;
uint32_t supported_operations_flags_rd() const;
};
struct ShaderCapabilities {
D3D_SHADER_MODEL shader_model = (D3D_SHADER_MODEL)0;
bool native_16bit_ops = false;
};
struct StorageBufferCapabilities {
bool storage_buffer_16_bit_access_is_supported = false;
};
struct FormatCapabilities {
bool relaxed_casting_supported = false;
};
struct BarrierCapabilities {
bool enhanced_barriers_supported = false;
};
struct MiscFeaturesSupport {
bool depth_bounds_supported = false;
};
RenderingContextDriverD3D12 *context_driver = nullptr;
RenderingContextDriver::Device context_device;
ComPtr<IDXGIAdapter> adapter;
DXGI_ADAPTER_DESC adapter_desc;
ComPtr<ID3D12Device> device;
DeviceLimits device_limits;
RDD::Capabilities device_capabilities;
uint32_t feature_level = 0; // Major * 10 + minor.
SubgroupCapabilities subgroup_capabilities;
RDD::MultiviewCapabilities multiview_capabilities;
FragmentShadingRateCapabilities fsr_capabilities;
FragmentDensityMapCapabilities fdm_capabilities;
ShaderCapabilities shader_capabilities;
StorageBufferCapabilities storage_buffer_capabilities;
FormatCapabilities format_capabilities;
BarrierCapabilities barrier_capabilities;
MiscFeaturesSupport misc_features_support;
RenderingShaderContainerFormatD3D12 shader_container_format;
String pipeline_cache_id;
class DescriptorsHeap {
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
ComPtr<ID3D12DescriptorHeap> heap;
uint32_t handle_size = 0;
public:
class Walker { // Texas Ranger.
friend class DescriptorsHeap;
uint32_t handle_size = 0;
uint32_t handle_count = 0;
D3D12_CPU_DESCRIPTOR_HANDLE first_cpu_handle = {};
D3D12_GPU_DESCRIPTOR_HANDLE first_gpu_handle = {};
uint32_t handle_index = 0;
public:
D3D12_CPU_DESCRIPTOR_HANDLE get_curr_cpu_handle();
D3D12_GPU_DESCRIPTOR_HANDLE get_curr_gpu_handle();
_FORCE_INLINE_ void rewind() { handle_index = 0; }
void advance(uint32_t p_count = 1);
uint32_t get_current_handle_index() const { return handle_index; }
uint32_t get_free_handles() { return handle_count - handle_index; }
bool is_at_eof() { return handle_index == handle_count; }
};
Error allocate(ID3D12Device *m_device, D3D12_DESCRIPTOR_HEAP_TYPE m_type, uint32_t m_descriptor_count, bool p_for_gpu);
uint32_t get_descriptor_count() const { return desc.NumDescriptors; }
ID3D12DescriptorHeap *get_heap() const { return heap.Get(); }
Walker make_walker() const;
};
struct {
ComPtr<ID3D12CommandSignature> draw;
ComPtr<ID3D12CommandSignature> draw_indexed;
ComPtr<ID3D12CommandSignature> dispatch;
} indirect_cmd_signatures;
static void STDMETHODCALLTYPE _debug_message_func(D3D12_MESSAGE_CATEGORY p_category, D3D12_MESSAGE_SEVERITY p_severity, D3D12_MESSAGE_ID p_id, LPCSTR p_description, void *p_context);
void _set_object_name(ID3D12Object *p_object, String p_object_name);
Error _initialize_device();
Error _check_capabilities();
Error _get_device_limits();
Error _initialize_allocator();
Error _initialize_frames(uint32_t p_frame_count);
Error _initialize_command_signatures();
public:
Error initialize(uint32_t p_device_index, uint32_t p_frame_count) override final;
private:
/****************/
/**** MEMORY ****/
/****************/
ComPtr<D3D12MA::Allocator> allocator;
/******************/
/**** RESOURCE ****/
/******************/
struct ResourceInfo {
struct States {
// As many subresources as mipmaps * layers; planes (for depth-stencil) are tracked together.
TightLocalVector<D3D12_RESOURCE_STATES> subresource_states; // Used only if not a view.
uint32_t last_batch_with_uav_barrier = 0;
};
ID3D12Resource *resource = nullptr; // Non-null even if not owned.
struct {
ComPtr<ID3D12Resource> resource;
ComPtr<D3D12MA::Allocation> allocation;
States states;
} owner_info; // All empty if the resource is not owned.
States *states_ptr = nullptr; // Own or from another if it doesn't own the D3D12 resource.
};
struct BarrierRequest {
static const uint32_t MAX_GROUPS = 4;
// Maybe this is too much data to have it locally. Benchmarking may reveal that
// cache would be used better by having a maximum of local subresource masks and beyond
// that have an allocated vector with the rest.
static const uint32_t MAX_SUBRESOURCES = 4096;
ID3D12Resource *dx_resource = nullptr;
uint8_t subres_mask_qwords = 0;
uint8_t planes = 0;
struct Group {
D3D12_RESOURCE_STATES states = {};
static_assert(MAX_SUBRESOURCES % 64 == 0);
uint64_t subres_mask[MAX_SUBRESOURCES / 64] = {};
} groups[MAX_GROUPS];
uint8_t groups_count = 0;
static const D3D12_RESOURCE_STATES DELETED_GROUP = D3D12_RESOURCE_STATES(0xFFFFFFFFU);
};
struct CommandBufferInfo;
void _resource_transition_batch(CommandBufferInfo *p_command_buffer, ResourceInfo *p_resource, uint32_t p_subresource, uint32_t p_num_planes, D3D12_RESOURCE_STATES p_new_state);
void _resource_transitions_flush(CommandBufferInfo *p_command_buffer);
/*****************/
/**** BUFFERS ****/
/*****************/
struct BufferInfo : public ResourceInfo {
DataFormat texel_format = DATA_FORMAT_MAX;
uint64_t size = 0;
struct {
bool usable_as_uav : 1;
} flags = {};
};
public:
virtual BufferID buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type) override final;
virtual bool buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) override final;
virtual void buffer_free(BufferID p_buffer) override final;
virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final;
virtual uint8_t *buffer_map(BufferID p_buffer) override final;
virtual void buffer_unmap(BufferID p_buffer) override final;
virtual uint64_t buffer_get_device_address(BufferID p_buffer) override final;
/*****************/
/**** TEXTURE ****/
/*****************/
private:
struct TextureInfo : public ResourceInfo {
DataFormat format = DATA_FORMAT_MAX;
CD3DX12_RESOURCE_DESC desc = {};
uint32_t base_layer = 0;
uint32_t layers = 0;
uint32_t base_mip = 0;
uint32_t mipmaps = 0;
struct {
D3D12_SHADER_RESOURCE_VIEW_DESC srv;
D3D12_UNORDERED_ACCESS_VIEW_DESC uav;
} view_descs = {};
TextureInfo *main_texture = nullptr;
UINT mapped_subresource = UINT_MAX;
SelfList<TextureInfo> pending_clear{ this };
#ifdef DEBUG_ENABLED
bool created_from_extension = false;
#endif
};
SelfList<TextureInfo>::List textures_pending_clear;
HashMap<DXGI_FORMAT, uint32_t> format_sample_counts_mask_cache;
Mutex format_sample_counts_mask_cache_mutex;
uint32_t _find_max_common_supported_sample_count(VectorView<DXGI_FORMAT> p_formats);
UINT _compute_component_mapping(const TextureView &p_view);
UINT _compute_plane_slice(DataFormat p_format, BitField<TextureAspectBits> p_aspect_bits);
UINT _compute_plane_slice(DataFormat p_format, TextureAspect p_aspect);
UINT _compute_subresource_from_layers(TextureInfo *p_texture, const TextureSubresourceLayers &p_layers, uint32_t p_layer_offset);
void _discard_texture_subresources(const TextureInfo *p_tex_info, const CommandBufferInfo *p_cmd_buf_info);
protected:
virtual bool _unordered_access_supported_by_format(DataFormat p_format);
public:
virtual TextureID texture_create(const TextureFormat &p_format, const TextureView &p_view) override final;
virtual TextureID texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil, uint32_t p_mipmaps) override final;
virtual TextureID texture_create_shared(TextureID p_original_texture, const TextureView &p_view) override final;
virtual TextureID texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) override final;
virtual void texture_free(TextureID p_texture) override final;
virtual uint64_t texture_get_allocation_size(TextureID p_texture) override final;
virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) override final;
virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) override final;
virtual void texture_unmap(TextureID p_texture) override final;
virtual BitField<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final;
virtual bool texture_can_make_shared_with_format(TextureID p_texture, DataFormat p_format, bool &r_raw_reinterpretation) override final;
private:
TextureID _texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps);
public:
/*****************/
/**** SAMPLER ****/
/*****************/
private:
LocalVector<D3D12_SAMPLER_DESC> samplers;
public:
virtual SamplerID sampler_create(const SamplerState &p_state) final override;
virtual void sampler_free(SamplerID p_sampler) final override;
virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) override final;
/**********************/
/**** VERTEX ARRAY ****/
/**********************/
private:
struct VertexFormatInfo {
TightLocalVector<D3D12_INPUT_ELEMENT_DESC> input_elem_descs;
TightLocalVector<UINT> vertex_buffer_strides;
};
public:
virtual VertexFormatID vertex_format_create(VectorView<VertexAttribute> p_vertex_attribs) override final;
virtual void vertex_format_free(VertexFormatID p_vertex_format) override final;
/******************/
/**** BARRIERS ****/
/******************/
virtual void command_pipeline_barrier(
CommandBufferID p_cmd_buffer,
BitField<PipelineStageBits> p_src_stages,
BitField<PipelineStageBits> p_dst_stages,
VectorView<RDD::MemoryBarrier> p_memory_barriers,
VectorView<RDD::BufferBarrier> p_buffer_barriers,
VectorView<RDD::TextureBarrier> p_texture_barriers) override final;
private:
/****************/
/**** FENCES ****/
/****************/
struct FenceInfo {
ComPtr<ID3D12Fence> d3d_fence = nullptr;
HANDLE event_handle = nullptr;
UINT64 fence_value = 0;
};
public:
virtual FenceID fence_create() override;
virtual Error fence_wait(FenceID p_fence) override;
virtual void fence_free(FenceID p_fence) override;
private:
/********************/
/**** SEMAPHORES ****/
/********************/
struct SemaphoreInfo {
ComPtr<ID3D12Fence> d3d_fence = nullptr;
UINT64 fence_value = 0;
};
virtual SemaphoreID semaphore_create() override;
virtual void semaphore_free(SemaphoreID p_semaphore) override;
/******************/
/**** COMMANDS ****/
/******************/
// ----- QUEUE FAMILY -----
virtual CommandQueueFamilyID command_queue_family_get(BitField<CommandQueueFamilyBits> p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface = 0) override;
private:
// ----- QUEUE -----
struct CommandQueueInfo {
ComPtr<ID3D12CommandQueue> d3d_queue;
};
public:
virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override;
virtual Error command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) override;
virtual void command_queue_free(CommandQueueID p_cmd_queue) override;
private:
// ----- POOL -----
struct CommandPoolInfo {
CommandQueueFamilyID queue_family;
CommandBufferType buffer_type = COMMAND_BUFFER_TYPE_PRIMARY;
// Since there are no command pools in D3D12, we need to track the command buffers created by this pool
// so that we can free them when the pool is freed.
SelfList<CommandBufferInfo>::List command_buffers;
};
public:
virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override final;
virtual bool command_pool_reset(CommandPoolID p_cmd_pool) override final;
virtual void command_pool_free(CommandPoolID p_cmd_pool) override final;
// ----- BUFFER -----
private:
// Belongs to RENDERING-SUBPASS, but needed here.
struct FramebufferInfo;
struct RenderPassInfo;
struct RenderPassState {
uint32_t current_subpass = UINT32_MAX;
const FramebufferInfo *fb_info = nullptr;
const RenderPassInfo *pass_info = nullptr;
CD3DX12_RECT region_rect = {};
bool region_is_all = false;
const VertexFormatInfo *vf_info = nullptr;
D3D12_VERTEX_BUFFER_VIEW vertex_buffer_views[8] = {};
uint32_t vertex_buffer_count = 0;
};
// Leveraging knowledge of actual usage and D3D12 specifics (namely, command lists from the same allocator
// can't be freely begun and ended), an allocator per list works better.
struct CommandBufferInfo {
// Store a self list reference to be used by the command pool.
SelfList<CommandBufferInfo> command_buffer_info_elem{ this };
ComPtr<ID3D12CommandAllocator> cmd_allocator;
ComPtr<ID3D12GraphicsCommandList> cmd_list;
ID3D12PipelineState *graphics_pso = nullptr;
ID3D12PipelineState *compute_pso = nullptr;
uint32_t graphics_root_signature_crc = 0;
uint32_t compute_root_signature_crc = 0;
RenderPassState render_pass_state;
bool descriptor_heaps_set = false;
HashMap<ResourceInfo::States *, BarrierRequest> res_barriers_requests;
LocalVector<D3D12_RESOURCE_BARRIER> res_barriers;
uint32_t res_barriers_count = 0;
uint32_t res_barriers_batch = 0;
};
public:
virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override final;
virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) override final;
virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) override final;
virtual void command_buffer_end(CommandBufferID p_cmd_buffer) override final;
virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> p_secondary_cmd_buffers) override final;
private:
/********************/
/**** SWAP CHAIN ****/
/********************/
struct SwapChain {
ComPtr<IDXGISwapChain3> d3d_swap_chain;
RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID();
UINT present_flags = 0;
UINT sync_interval = 1;
UINT creation_flags = 0;
RenderPassID render_pass;
TightLocalVector<ID3D12Resource *> render_targets;
TightLocalVector<TextureInfo> render_targets_info;
TightLocalVector<FramebufferID> framebuffers;
RDD::DataFormat data_format = DATA_FORMAT_MAX;
};
void _swap_chain_release(SwapChain *p_swap_chain);
void _swap_chain_release_buffers(SwapChain *p_swap_chain);
public:
virtual SwapChainID swap_chain_create(RenderingContextDriver::SurfaceID p_surface) override;
virtual Error swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) override;
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override;
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override;
virtual void swap_chain_free(SwapChainID p_swap_chain) override;
/*********************/
/**** FRAMEBUFFER ****/
/*********************/
private:
struct FramebufferInfo {
bool is_screen = false;
Size2i size;
TightLocalVector<uint32_t> attachments_handle_inds; // RTV heap index for color; DSV heap index for DSV.
DescriptorsHeap rtv_heap;
DescriptorsHeap dsv_heap; // Used only if not for screen and some depth-stencil attachments.
TightLocalVector<TextureID> attachments; // Color and depth-stencil. Used if not screen.
TextureID vrs_attachment;
};
D3D12_RENDER_TARGET_VIEW_DESC _make_rtv_for_texture(const TextureInfo *p_texture_info, uint32_t p_mipmap_offset, uint32_t p_layer_offset, uint32_t p_layers, bool p_add_bases = true);
D3D12_UNORDERED_ACCESS_VIEW_DESC _make_ranged_uav_for_texture(const TextureInfo *p_texture_info, uint32_t p_mipmap_offset, uint32_t p_layer_offset, uint32_t p_layers, bool p_add_bases = true);
D3D12_DEPTH_STENCIL_VIEW_DESC _make_dsv_for_texture(const TextureInfo *p_texture_info);
FramebufferID _framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height, bool p_is_screen);
public:
virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) override final;
virtual void framebuffer_free(FramebufferID p_framebuffer) override final;
/****************/
/**** SHADER ****/
/****************/
private:
static const uint32_t ROOT_SIGNATURE_SIZE = 256;
static const uint32_t PUSH_CONSTANT_SIZE = 128; // Mimicking Vulkan.
enum {
// We can only aim to set a maximum here, since depending on the shader
// there may be more or less root signature free for descriptor tables.
// Therefore, we'll have to rely on the final check at runtime, when building
// the root signature structure for a given shader.
// To be precise, these may be present or not, and their size vary statically:
// - Push constant (we'll assume this is always present to avoid reserving much
// more space for descriptor sets than needed for almost any imaginable case,
// given that most shader templates feature push constants).
// - NIR-DXIL runtime data.
MAX_UNIFORM_SETS = (ROOT_SIGNATURE_SIZE - PUSH_CONSTANT_SIZE) / sizeof(uint32_t),
};
struct ShaderInfo {
uint32_t dxil_push_constant_size = 0;
uint32_t nir_runtime_data_root_param_idx = UINT32_MAX;
bool is_compute = false;
struct UniformBindingInfo {
uint32_t stages = 0; // Actual shader stages using the uniform (0 if totally optimized out).
ResourceClass res_class = RES_CLASS_INVALID;
UniformType type = UNIFORM_TYPE_MAX;
uint32_t length = UINT32_MAX;
#ifdef DEV_ENABLED
bool writable = false;
#endif
struct RootSignatureLocation {
uint32_t root_param_idx = UINT32_MAX;
uint32_t range_idx = UINT32_MAX;
};
struct {
RootSignatureLocation resource;
RootSignatureLocation sampler;
} root_sig_locations;
};
struct UniformSet {
TightLocalVector<UniformBindingInfo> bindings;
struct {
uint32_t resources = 0;
uint32_t samplers = 0;
} num_root_params;
};
TightLocalVector<UniformSet> sets;
struct SpecializationConstant {
uint32_t constant_id = UINT32_MAX;
uint32_t int_value = UINT32_MAX;
uint64_t stages_bit_offsets[D3D12_BITCODE_OFFSETS_NUM_STAGES] = {};
};
TightLocalVector<SpecializationConstant> specialization_constants;
uint32_t spirv_specialization_constants_ids_mask = 0;
HashMap<ShaderStage, Vector<uint8_t>> stages_bytecode;
ComPtr<ID3D12RootSignature> root_signature;
ComPtr<ID3D12RootSignatureDeserializer> root_signature_deserializer;
const D3D12_ROOT_SIGNATURE_DESC *root_signature_desc = nullptr; // Owned by the deserializer.
uint32_t root_signature_crc = 0;
};
bool _shader_apply_specialization_constants(
const ShaderInfo *p_shader_info,
VectorView<PipelineSpecializationConstant> p_specialization_constants,
HashMap<ShaderStage, Vector<uint8_t>> &r_final_stages_bytecode);
public:
virtual ShaderID shader_create_from_container(const Ref<RenderingShaderContainer> &p_shader_container, const Vector<ImmutableSampler> &p_immutable_samplers) override final;
virtual uint32_t shader_get_layout_hash(ShaderID p_shader) override final;
virtual void shader_free(ShaderID p_shader) override final;
virtual void shader_destroy_modules(ShaderID p_shader) override final;
/*********************/
/**** UNIFORM SET ****/
/*********************/
private:
struct RootDescriptorTable {
uint32_t root_param_idx = UINT32_MAX;
D3D12_GPU_DESCRIPTOR_HANDLE start_gpu_handle = {};
};
struct UniformSetInfo {
struct {
DescriptorsHeap resources;
DescriptorsHeap samplers;
} desc_heaps;
struct StateRequirement {
ResourceInfo *resource = nullptr;
bool is_buffer = false;
D3D12_RESOURCE_STATES states = {};
uint64_t shader_uniform_idx_mask = 0;
};
TightLocalVector<StateRequirement> resource_states;
struct RecentBind {
uint64_t segment_serial = 0;
uint32_t root_signature_crc = 0;
struct {
TightLocalVector<RootDescriptorTable> resources;
TightLocalVector<RootDescriptorTable> samplers;
} root_tables;
int uses = 0;
} recent_binds[4]; // A better amount may be empirically found.
#ifdef DEV_ENABLED
// Filthy, but useful for dev.
struct ResourceDescInfo {
D3D12_DESCRIPTOR_RANGE_TYPE type;
D3D12_SRV_DIMENSION srv_dimension;
};
TightLocalVector<ResourceDescInfo> resources_desc_info;
#endif
};
public:
virtual UniformSetID uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index, int p_linear_pool_index) override final;
virtual void uniform_set_free(UniformSetID p_uniform_set) override final;
// ----- COMMANDS -----
virtual void command_uniform_set_prepare_for_use(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
private:
void _command_check_descriptor_sets(CommandBufferID p_cmd_buffer);
void _command_bind_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index, bool p_for_compute);
void _command_bind_uniform_sets(CommandBufferID p_cmd_buffer, VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count, bool p_for_compute);
public:
/******************/
/**** TRANSFER ****/
/******************/
virtual void command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) override final;
virtual void command_copy_buffer(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, BufferID p_dst_buffer, VectorView<BufferCopyRegion> p_regions) override final;
virtual void command_copy_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<TextureCopyRegion> p_regions) override final;
virtual void command_resolve_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap) override final;
virtual void command_clear_color_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, const Color &p_color, const TextureSubresourceRange &p_subresources) override final;
public:
virtual void command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<BufferTextureCopyRegion> p_regions) override final;
virtual void command_copy_texture_to_buffer(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, BufferID p_dst_buffer, VectorView<BufferTextureCopyRegion> p_regions) override final;
/******************/
/**** PIPELINE ****/
/******************/
struct RenderPipelineInfo {
const VertexFormatInfo *vf_info = nullptr;
struct {
D3D12_PRIMITIVE_TOPOLOGY primitive_topology = {};
Color blend_constant;
float depth_bounds_min = 0.0f;
float depth_bounds_max = 0.0f;
uint32_t stencil_reference = 0;
} dyn_params;
};
struct PipelineInfo {
ID3D12PipelineState *pso = nullptr;
const ShaderInfo *shader_info = nullptr;
RenderPipelineInfo render_info;
};
virtual void pipeline_free(PipelineID p_pipeline) override final;
public:
// ----- BINDING -----
virtual void command_bind_push_constants(CommandBufferID p_cmd_buffer, ShaderID p_shader, uint32_t p_dst_first_index, VectorView<uint32_t> p_data) override final;
// ----- CACHE -----
virtual bool pipeline_cache_create(const Vector<uint8_t> &p_data) override final;
virtual void pipeline_cache_free() override final;
virtual size_t pipeline_cache_query_size() override final;
virtual Vector<uint8_t> pipeline_cache_serialize() override final;
/*******************/
/**** RENDERING ****/
/*******************/
// ----- SUBPASS -----
private:
struct RenderPassInfo {
TightLocalVector<Attachment> attachments;
TightLocalVector<Subpass> subpasses;
uint32_t view_count = 0;
uint32_t max_supported_sample_count = 0;
};
public:
virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count, AttachmentReference p_fragment_density_map_attachment) override final;
virtual void render_pass_free(RenderPassID p_render_pass) override final;
// ----- COMMANDS -----
virtual void command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_clear_values) override final;
private:
void _end_render_pass(CommandBufferID p_cmd_buffer);
public:
virtual void command_end_render_pass(CommandBufferID p_cmd_buffer) override final;
virtual void command_next_render_subpass(CommandBufferID p_cmd_buffer, CommandBufferType p_cmd_buffer_type) override final;
virtual void command_render_set_viewport(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_viewports) override final;
virtual void command_render_set_scissor(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_scissors) override final;
virtual void command_render_clear_attachments(CommandBufferID p_cmd_buffer, VectorView<AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) override final;
// Binding.
virtual void command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
virtual void command_bind_render_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
virtual void command_bind_render_uniform_sets(CommandBufferID p_cmd_buffer, VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count) override final;
// Drawing.
virtual void command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) override final;
virtual void command_render_draw_indexed(CommandBufferID p_cmd_buffer, uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index, int32_t p_vertex_offset, uint32_t p_first_instance) override final;
virtual void command_render_draw_indexed_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
virtual void command_render_draw_indexed_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;
virtual void command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
virtual void command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;
// Buffer binding.
virtual void command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) override final;
virtual void command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) override final;
private:
void _bind_vertex_buffers(CommandBufferInfo *p_cmd_buf_info);
public:
// Dynamic state.
virtual void command_render_set_blend_constants(CommandBufferID p_cmd_buffer, const Color &p_constants) override final;
virtual void command_render_set_line_width(CommandBufferID p_cmd_buffer, float p_width) override final;
// ----- PIPELINE -----
public:
virtual PipelineID render_pipeline_create(
ShaderID p_shader,
VertexFormatID p_vertex_format,
RenderPrimitive p_render_primitive,
PipelineRasterizationState p_rasterization_state,
PipelineMultisampleState p_multisample_state,
PipelineDepthStencilState p_depth_stencil_state,
PipelineColorBlendState p_blend_state,
VectorView<int32_t> p_color_attachments,
BitField<PipelineDynamicStateFlags> p_dynamic_state,
RenderPassID p_render_pass,
uint32_t p_render_subpass,
VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
/*****************/
/**** COMPUTE ****/
/*****************/
// ----- COMMANDS -----
// Binding.
virtual void command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
virtual void command_bind_compute_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
virtual void command_bind_compute_uniform_sets(CommandBufferID p_cmd_buffer, VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count) override final;
// Dispatching.
virtual void command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override final;
virtual void command_compute_dispatch_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset) override final;
// ----- PIPELINE -----
virtual PipelineID compute_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
/*****************/
/**** QUERIES ****/
/*****************/
// ----- TIMESTAMP -----
private:
struct TimestampQueryPoolInfo {
ComPtr<ID3D12QueryHeap> query_heap;
uint32_t query_count = 0;
ComPtr<D3D12MA::Allocation> results_buffer_allocation;
};
public:
// Basic.
virtual QueryPoolID timestamp_query_pool_create(uint32_t p_query_count) override final;
virtual void timestamp_query_pool_free(QueryPoolID p_pool_id) override final;
virtual void timestamp_query_pool_get_results(QueryPoolID p_pool_id, uint32_t p_query_count, uint64_t *r_results) override final;
virtual uint64_t timestamp_query_result_to_time(uint64_t p_result) override final;
// Commands.
virtual void command_timestamp_query_pool_reset(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_query_count) override final;
virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) override final;
/****************/
/**** LABELS ****/
/****************/
virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final;
virtual void command_end_label(CommandBufferID p_cmd_buffer) override final;
/****************/
/**** DEBUG *****/
/****************/
virtual void command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) override final;
/********************/
/**** SUBMISSION ****/
/********************/
private:
struct FrameInfo {
struct {
DescriptorsHeap resources;
DescriptorsHeap samplers;
DescriptorsHeap aux;
DescriptorsHeap rtv;
} desc_heaps;
struct {
DescriptorsHeap::Walker resources;
DescriptorsHeap::Walker samplers;
DescriptorsHeap::Walker aux;
DescriptorsHeap::Walker rtv;
} desc_heap_walkers;
struct {
bool resources = false;
bool samplers = false;
bool aux = false;
bool rtv = false;
} desc_heaps_exhausted_reported;
CD3DX12_CPU_DESCRIPTOR_HANDLE null_rtv_handle = {}; // For [[MANUAL_SUBPASSES]].
uint32_t segment_serial = 0;
#ifdef DEV_ENABLED
uint32_t uniform_set_reused = 0;
#endif
};
TightLocalVector<FrameInfo> frames;
uint32_t frame_idx = 0;
uint32_t frames_drawn = 0;
uint32_t segment_serial = 0;
bool segment_begun = false;
HashMap<uint64_t, bool> has_comp_alpha;
public:
virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final;
virtual void end_segment() override final;
/**************/
/**** MISC ****/
/**************/
virtual void set_object_name(ObjectType p_type, ID p_driver_id, const String &p_name) override final;
virtual uint64_t get_resource_native_handle(DriverResource p_type, ID p_driver_id) override final;
virtual uint64_t get_total_memory_used() override final;
virtual uint64_t get_lazily_memory_used() override final;
virtual uint64_t limit_get(Limit p_limit) override final;
virtual uint64_t api_trait_get(ApiTrait p_trait) override final;
virtual bool has_feature(Features p_feature) override final;
virtual const MultiviewCapabilities &get_multiview_capabilities() override final;
virtual const FragmentShadingRateCapabilities &get_fragment_shading_rate_capabilities() override final;
virtual const FragmentDensityMapCapabilities &get_fragment_density_map_capabilities() override final;
virtual String get_api_name() const override final;
virtual String get_api_version() const override final;
virtual String get_pipeline_cache_uuid() const override final;
virtual const Capabilities &get_capabilities() const override final;
virtual const RenderingShaderContainerFormat &get_shader_container_format() const override final;
virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final;
static bool is_in_developer_mode();
private:
/*********************/
/**** BOOKKEEPING ****/
/*********************/
using VersatileResource = VersatileResourceTemplate<
BufferInfo,
TextureInfo,
TextureInfo,
TextureInfo,
VertexFormatInfo,
CommandBufferInfo,
FramebufferInfo,
ShaderInfo,
UniformSetInfo,
RenderPassInfo,
TimestampQueryPoolInfo>;
PagedAllocator<VersatileResource, true> resources_allocator;
/******************/
public:
RenderingDeviceDriverD3D12(RenderingContextDriverD3D12 *p_context_driver);
virtual ~RenderingDeviceDriverD3D12();
};

View File

@@ -0,0 +1,901 @@
/**************************************************************************/
/* rendering_shader_container_d3d12.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "rendering_shader_container_d3d12.h"
#include "core/templates/sort_array.h"
#include "dxil_hash.h"
#include <zlib.h>
#ifndef _MSC_VER
// Match current version used by MinGW, MSVC and Direct3D 12 headers use 500.
#define __REQUIRED_RPCNDR_H_VERSION__ 475
#endif
#include <d3dx12.h>
#include <dxgi1_6.h>
#define D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED
#include <D3D12MemAlloc.h>
#include <wrl/client.h>
#if defined(_MSC_VER) && defined(MemoryBarrier)
// Annoying define from winnt.h. Reintroduced by some of the headers above.
#undef MemoryBarrier
#endif
GODOT_GCC_WARNING_PUSH
GODOT_GCC_WARNING_IGNORE("-Wimplicit-fallthrough")
GODOT_GCC_WARNING_IGNORE("-Wlogical-not-parentheses")
GODOT_GCC_WARNING_IGNORE("-Wmissing-field-initializers")
GODOT_GCC_WARNING_IGNORE("-Wnon-virtual-dtor")
GODOT_GCC_WARNING_IGNORE("-Wshadow")
GODOT_GCC_WARNING_IGNORE("-Wswitch")
GODOT_CLANG_WARNING_PUSH
GODOT_CLANG_WARNING_IGNORE("-Wimplicit-fallthrough")
GODOT_CLANG_WARNING_IGNORE("-Wlogical-not-parentheses")
GODOT_CLANG_WARNING_IGNORE("-Wmissing-field-initializers")
GODOT_CLANG_WARNING_IGNORE("-Wnon-virtual-dtor")
GODOT_CLANG_WARNING_IGNORE("-Wstring-plus-int")
GODOT_CLANG_WARNING_IGNORE("-Wswitch")
GODOT_MSVC_WARNING_PUSH
GODOT_MSVC_WARNING_IGNORE(4200) // "nonstandard extension used: zero-sized array in struct/union".
GODOT_MSVC_WARNING_IGNORE(4806) // "'&': unsafe operation: no value of type 'bool' promoted to type 'uint32_t' can equal the given constant".
#include <nir_spirv.h>
#include <nir_to_dxil.h>
#include <spirv_to_dxil.h>
extern "C" {
#include <dxil_spirv_nir.h>
}
GODOT_GCC_WARNING_POP
GODOT_CLANG_WARNING_POP
GODOT_MSVC_WARNING_POP
static D3D12_SHADER_VISIBILITY stages_to_d3d12_visibility(uint32_t p_stages_mask) {
switch (p_stages_mask) {
case RenderingDeviceCommons::SHADER_STAGE_VERTEX_BIT:
return D3D12_SHADER_VISIBILITY_VERTEX;
case RenderingDeviceCommons::SHADER_STAGE_FRAGMENT_BIT:
return D3D12_SHADER_VISIBILITY_PIXEL;
default:
return D3D12_SHADER_VISIBILITY_ALL;
}
}
uint32_t RenderingDXIL::patch_specialization_constant(
RenderingDeviceCommons::PipelineSpecializationConstantType p_type,
const void *p_value,
const uint64_t (&p_stages_bit_offsets)[D3D12_BITCODE_OFFSETS_NUM_STAGES],
HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_stages_bytecodes,
bool p_is_first_patch) {
uint32_t patch_val = 0;
switch (p_type) {
case RenderingDeviceCommons::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT: {
uint32_t int_value = *((const int *)p_value);
ERR_FAIL_COND_V(int_value & (1 << 31), 0);
patch_val = int_value;
} break;
case RenderingDeviceCommons::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL: {
bool bool_value = *((const bool *)p_value);
patch_val = (uint32_t)bool_value;
} break;
case RenderingDeviceCommons::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT: {
uint32_t int_value = *((const int *)p_value);
ERR_FAIL_COND_V(int_value & (1 << 31), 0);
patch_val = (int_value >> 1);
} break;
}
// For VBR encoding to encode the number of bits we expect (32), we need to set the MSB unconditionally.
// However, signed VBR moves the MSB to the LSB, so setting the MSB to 1 wouldn't help. Therefore,
// the bit we set to 1 is the one at index 30.
patch_val |= (1 << 30);
patch_val <<= 1; // What signed VBR does.
auto tamper_bits = [](uint8_t *p_start, uint64_t p_bit_offset, uint64_t p_tb_value) -> uint64_t {
uint64_t original = 0;
uint32_t curr_input_byte = p_bit_offset / 8;
uint8_t curr_input_bit = p_bit_offset % 8;
auto get_curr_input_bit = [&]() -> bool {
return ((p_start[curr_input_byte] >> curr_input_bit) & 1);
};
auto move_to_next_input_bit = [&]() {
if (curr_input_bit == 7) {
curr_input_bit = 0;
curr_input_byte++;
} else {
curr_input_bit++;
}
};
auto tamper_input_bit = [&](bool p_new_bit) {
p_start[curr_input_byte] &= ~((uint8_t)1 << curr_input_bit);
if (p_new_bit) {
p_start[curr_input_byte] |= (uint8_t)1 << curr_input_bit;
}
};
uint8_t value_bit_idx = 0;
for (uint32_t i = 0; i < 5; i++) { // 32 bits take 5 full bytes in VBR.
for (uint32_t j = 0; j < 7; j++) {
bool input_bit = get_curr_input_bit();
original |= (uint64_t)(input_bit ? 1 : 0) << value_bit_idx;
tamper_input_bit((p_tb_value >> value_bit_idx) & 1);
move_to_next_input_bit();
value_bit_idx++;
}
#ifdef DEV_ENABLED
bool input_bit = get_curr_input_bit();
DEV_ASSERT((i < 4 && input_bit) || (i == 4 && !input_bit));
#endif
move_to_next_input_bit();
}
return original;
};
uint32_t stages_patched_mask = 0;
for (int stage = 0; stage < RenderingDeviceCommons::SHADER_STAGE_MAX; stage++) {
if (!r_stages_bytecodes.has((RenderingDeviceCommons::ShaderStage)stage)) {
continue;
}
uint64_t offset = p_stages_bit_offsets[RenderingShaderContainerD3D12::SHADER_STAGES_BIT_OFFSET_INDICES[stage]];
if (offset == 0) {
// This constant does not appear at this stage.
continue;
}
Vector<uint8_t> &bytecode = r_stages_bytecodes[(RenderingDeviceCommons::ShaderStage)stage];
#ifdef DEV_ENABLED
uint64_t orig_patch_val = tamper_bits(bytecode.ptrw(), offset, patch_val);
// Checking against the value the NIR patch should have set.
DEV_ASSERT(!p_is_first_patch || ((orig_patch_val >> 1) & GODOT_NIR_SC_SENTINEL_MAGIC_MASK) == GODOT_NIR_SC_SENTINEL_MAGIC);
uint64_t readback_patch_val = tamper_bits(bytecode.ptrw(), offset, patch_val);
DEV_ASSERT(readback_patch_val == patch_val);
#else
tamper_bits(bytecode.ptrw(), offset, patch_val);
#endif
stages_patched_mask |= (1 << stage);
}
return stages_patched_mask;
}
void RenderingDXIL::sign_bytecode(RenderingDeviceCommons::ShaderStage p_stage, Vector<uint8_t> &r_dxil_blob) {
uint8_t *w = r_dxil_blob.ptrw();
compute_dxil_hash(w + 20, r_dxil_blob.size() - 20, w + 4);
}
// RenderingShaderContainerD3D12
uint32_t RenderingShaderContainerD3D12::_format() const {
return 0x43443344;
}
uint32_t RenderingShaderContainerD3D12::_format_version() const {
return FORMAT_VERSION;
}
uint32_t RenderingShaderContainerD3D12::_from_bytes_reflection_extra_data(const uint8_t *p_bytes) {
reflection_data_d3d12 = *(const ReflectionDataD3D12 *)(p_bytes);
return sizeof(ReflectionDataD3D12);
}
uint32_t RenderingShaderContainerD3D12::_from_bytes_reflection_binding_uniform_extra_data_start(const uint8_t *p_bytes) {
reflection_binding_set_uniforms_data_d3d12.resize(reflection_binding_set_uniforms_data.size());
return 0;
}
uint32_t RenderingShaderContainerD3D12::_from_bytes_reflection_binding_uniform_extra_data(const uint8_t *p_bytes, uint32_t p_index) {
reflection_binding_set_uniforms_data_d3d12.ptrw()[p_index] = *(const ReflectionBindingDataD3D12 *)(p_bytes);
return sizeof(ReflectionBindingDataD3D12);
}
uint32_t RenderingShaderContainerD3D12::_from_bytes_reflection_specialization_extra_data_start(const uint8_t *p_bytes) {
reflection_specialization_data_d3d12.resize(reflection_specialization_data.size());
return 0;
}
uint32_t RenderingShaderContainerD3D12::_from_bytes_reflection_specialization_extra_data(const uint8_t *p_bytes, uint32_t p_index) {
reflection_specialization_data_d3d12.ptrw()[p_index] = *(const ReflectionSpecializationDataD3D12 *)(p_bytes);
return sizeof(ReflectionSpecializationDataD3D12);
}
uint32_t RenderingShaderContainerD3D12::_from_bytes_footer_extra_data(const uint8_t *p_bytes) {
ContainerFooterD3D12 footer = *(const ContainerFooterD3D12 *)(p_bytes);
root_signature_crc = footer.root_signature_crc;
root_signature_bytes.resize(footer.root_signature_length);
memcpy(root_signature_bytes.ptrw(), p_bytes + sizeof(ContainerFooterD3D12), root_signature_bytes.size());
return sizeof(ContainerFooterD3D12) + footer.root_signature_length;
}
uint32_t RenderingShaderContainerD3D12::_to_bytes_reflection_extra_data(uint8_t *p_bytes) const {
if (p_bytes != nullptr) {
*(ReflectionDataD3D12 *)(p_bytes) = reflection_data_d3d12;
}
return sizeof(ReflectionDataD3D12);
}
uint32_t RenderingShaderContainerD3D12::_to_bytes_reflection_binding_uniform_extra_data(uint8_t *p_bytes, uint32_t p_index) const {
if (p_bytes != nullptr) {
*(ReflectionBindingDataD3D12 *)(p_bytes) = reflection_binding_set_uniforms_data_d3d12[p_index];
}
return sizeof(ReflectionBindingDataD3D12);
}
uint32_t RenderingShaderContainerD3D12::_to_bytes_reflection_specialization_extra_data(uint8_t *p_bytes, uint32_t p_index) const {
if (p_bytes != nullptr) {
*(ReflectionSpecializationDataD3D12 *)(p_bytes) = reflection_specialization_data_d3d12[p_index];
}
return sizeof(ReflectionSpecializationDataD3D12);
}
uint32_t RenderingShaderContainerD3D12::_to_bytes_footer_extra_data(uint8_t *p_bytes) const {
if (p_bytes != nullptr) {
ContainerFooterD3D12 &footer = *(ContainerFooterD3D12 *)(p_bytes);
footer.root_signature_length = root_signature_bytes.size();
footer.root_signature_crc = root_signature_crc;
memcpy(p_bytes + sizeof(ContainerFooterD3D12), root_signature_bytes.ptr(), root_signature_bytes.size());
}
return sizeof(ContainerFooterD3D12) + root_signature_bytes.size();
}
#if NIR_ENABLED
bool RenderingShaderContainerD3D12::_convert_spirv_to_nir(const Vector<RenderingDeviceCommons::ShaderStageSPIRVData> &p_spirv, const nir_shader_compiler_options *p_compiler_options, HashMap<int, nir_shader *> &r_stages_nir_shaders, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed) {
r_stages_processed.clear();
dxil_spirv_runtime_conf dxil_runtime_conf = {};
dxil_runtime_conf.runtime_data_cbv.base_shader_register = RUNTIME_DATA_REGISTER;
dxil_runtime_conf.push_constant_cbv.base_shader_register = ROOT_CONSTANT_REGISTER;
dxil_runtime_conf.zero_based_vertex_instance_id = true;
dxil_runtime_conf.zero_based_compute_workgroup_id = true;
dxil_runtime_conf.declared_read_only_images_as_srvs = true;
// Making this explicit to let maintainers know that in practice this didn't improve performance,
// probably because data generated by one shader and consumed by another one forces the resource
// to transition from UAV to SRV, and back, instead of being an UAV all the time.
// In case someone wants to try, care must be taken so in case of incompatible bindings across stages
// happen as a result, all the stages are re-translated. That can happen if, for instance, a stage only
// uses an allegedly writable resource only for reading but the next stage doesn't.
dxil_runtime_conf.inferred_read_only_images_as_srvs = false;
// Translate SPIR-V to NIR.
for (int64_t i = 0; i < p_spirv.size(); i++) {
RenderingDeviceCommons::ShaderStage stage = p_spirv[i].shader_stage;
RenderingDeviceCommons::ShaderStage stage_flag = (RenderingDeviceCommons::ShaderStage)(1 << stage);
r_stages.push_back(stage);
r_stages_processed.set_flag(stage_flag);
const char *entry_point = "main";
static const gl_shader_stage SPIRV_TO_MESA_STAGES[RenderingDeviceCommons::SHADER_STAGE_MAX] = {
MESA_SHADER_VERTEX, // SHADER_STAGE_VERTEX
MESA_SHADER_FRAGMENT, // SHADER_STAGE_FRAGMENT
MESA_SHADER_TESS_CTRL, // SHADER_STAGE_TESSELATION_CONTROL
MESA_SHADER_TESS_EVAL, // SHADER_STAGE_TESSELATION_EVALUATION
MESA_SHADER_COMPUTE, // SHADER_STAGE_COMPUTE
};
nir_shader *shader = spirv_to_nir(
(const uint32_t *)(p_spirv[i].spirv.ptr()),
p_spirv[i].spirv.size() / sizeof(uint32_t),
nullptr,
0,
SPIRV_TO_MESA_STAGES[stage],
entry_point,
dxil_spirv_nir_get_spirv_options(),
p_compiler_options);
ERR_FAIL_NULL_V_MSG(shader, false, "Shader translation (step 1) at stage " + String(RenderingDeviceCommons::SHADER_STAGE_NAMES[stage]) + " failed.");
#ifdef DEV_ENABLED
nir_validate_shader(shader, "Validate before feeding NIR to the DXIL compiler");
#endif
if (stage == RenderingDeviceCommons::SHADER_STAGE_VERTEX) {
dxil_runtime_conf.yz_flip.y_mask = 0xffff;
dxil_runtime_conf.yz_flip.mode = DXIL_SPIRV_Y_FLIP_UNCONDITIONAL;
} else {
dxil_runtime_conf.yz_flip.y_mask = 0;
dxil_runtime_conf.yz_flip.mode = DXIL_SPIRV_YZ_FLIP_NONE;
}
dxil_spirv_nir_prep(shader);
bool requires_runtime_data = false;
dxil_spirv_nir_passes(shader, &dxil_runtime_conf, &requires_runtime_data);
r_stages_nir_shaders[stage] = shader;
}
// Link NIR shaders.
for (int i = RenderingDeviceCommons::SHADER_STAGE_MAX - 1; i >= 0; i--) {
if (!r_stages_nir_shaders.has(i)) {
continue;
}
nir_shader *shader = r_stages_nir_shaders[i];
nir_shader *prev_shader = nullptr;
for (int j = i - 1; j >= 0; j--) {
if (r_stages_nir_shaders.has(j)) {
prev_shader = r_stages_nir_shaders[j];
break;
}
}
// There is a bug in the Direct3D runtime during creation of a PSO with view instancing. If a fragment
// shader uses front/back face detection (SV_IsFrontFace), its signature must include the pixel position
// builtin variable (SV_Position), otherwise an Internal Runtime error will occur.
if (i == RenderingDeviceCommons::SHADER_STAGE_FRAGMENT) {
const bool use_front_face =
nir_find_variable_with_location(shader, nir_var_shader_in, VARYING_SLOT_FACE) ||
(shader->info.inputs_read & VARYING_BIT_FACE) ||
nir_find_variable_with_location(shader, nir_var_system_value, SYSTEM_VALUE_FRONT_FACE) ||
BITSET_TEST(shader->info.system_values_read, SYSTEM_VALUE_FRONT_FACE);
const bool use_position =
nir_find_variable_with_location(shader, nir_var_shader_in, VARYING_SLOT_POS) ||
(shader->info.inputs_read & VARYING_BIT_POS) ||
nir_find_variable_with_location(shader, nir_var_system_value, SYSTEM_VALUE_FRAG_COORD) ||
BITSET_TEST(shader->info.system_values_read, SYSTEM_VALUE_FRAG_COORD);
if (use_front_face && !use_position) {
nir_variable *const pos = nir_variable_create(shader, nir_var_shader_in, glsl_vec4_type(), "gl_FragCoord");
pos->data.location = VARYING_SLOT_POS;
shader->info.inputs_read |= VARYING_BIT_POS;
}
}
if (prev_shader) {
bool requires_runtime_data = {};
dxil_spirv_nir_link(shader, prev_shader, &dxil_runtime_conf, &requires_runtime_data);
}
}
return true;
}
struct GodotNirCallbackUserData {
RenderingShaderContainerD3D12 *container;
RenderingDeviceCommons::ShaderStage stage;
};
static dxil_shader_model shader_model_d3d_to_dxil(D3D_SHADER_MODEL p_d3d_shader_model) {
static_assert(SHADER_MODEL_6_0 == 0x60000);
static_assert(SHADER_MODEL_6_3 == 0x60003);
static_assert(D3D_SHADER_MODEL_6_0 == 0x60);
static_assert(D3D_SHADER_MODEL_6_3 == 0x63);
return (dxil_shader_model)((p_d3d_shader_model >> 4) * 0x10000 + (p_d3d_shader_model & 0xf));
}
bool RenderingShaderContainerD3D12::_convert_nir_to_dxil(const HashMap<int, nir_shader *> &p_stages_nir_shaders, BitField<RenderingDeviceCommons::ShaderStage> p_stages_processed, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs) {
// Translate NIR to DXIL.
for (KeyValue<int, nir_shader *> it : p_stages_nir_shaders) {
RenderingDeviceCommons::ShaderStage stage = (RenderingDeviceCommons::ShaderStage)(it.key);
GodotNirCallbackUserData godot_nir_callback_user_data;
godot_nir_callback_user_data.container = this;
godot_nir_callback_user_data.stage = stage;
GodotNirCallbacks godot_nir_callbacks = {};
godot_nir_callbacks.data = &godot_nir_callback_user_data;
godot_nir_callbacks.report_resource = _nir_report_resource;
godot_nir_callbacks.report_sc_bit_offset_fn = _nir_report_sc_bit_offset;
godot_nir_callbacks.report_bitcode_bit_offset_fn = _nir_report_bitcode_bit_offset;
nir_to_dxil_options nir_to_dxil_options = {};
nir_to_dxil_options.environment = DXIL_ENVIRONMENT_VULKAN;
nir_to_dxil_options.shader_model_max = shader_model_d3d_to_dxil(D3D_SHADER_MODEL(REQUIRED_SHADER_MODEL));
nir_to_dxil_options.validator_version_max = NO_DXIL_VALIDATION;
nir_to_dxil_options.godot_nir_callbacks = &godot_nir_callbacks;
dxil_logger logger = {};
logger.log = [](void *p_priv, const char *p_msg) {
#ifdef DEBUG_ENABLED
print_verbose(p_msg);
#endif
};
blob dxil_blob = {};
bool ok = nir_to_dxil(it.value, &nir_to_dxil_options, &logger, &dxil_blob);
ERR_FAIL_COND_V_MSG(!ok, false, "Shader translation at stage " + String(RenderingDeviceCommons::SHADER_STAGE_NAMES[stage]) + " failed.");
Vector<uint8_t> blob_copy;
blob_copy.resize(dxil_blob.size);
memcpy(blob_copy.ptrw(), dxil_blob.data, dxil_blob.size);
blob_finish(&dxil_blob);
r_dxil_blobs.insert(stage, blob_copy);
}
return true;
}
bool RenderingShaderContainerD3D12::_convert_spirv_to_dxil(const Vector<RenderingDeviceCommons::ShaderStageSPIRVData> &p_spirv, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed) {
r_dxil_blobs.clear();
HashMap<int, nir_shader *> stages_nir_shaders;
auto free_nir_shaders = [&]() {
for (KeyValue<int, nir_shader *> &E : stages_nir_shaders) {
ralloc_free(E.value);
}
stages_nir_shaders.clear();
};
// This structure must live as long as the shaders are alive.
nir_shader_compiler_options compiler_options = *dxil_get_nir_compiler_options();
compiler_options.lower_base_vertex = false;
// This is based on spirv2dxil.c. May need updates when it changes.
// Also, this has to stay around until after linking.
if (!_convert_spirv_to_nir(p_spirv, &compiler_options, stages_nir_shaders, r_stages, r_stages_processed)) {
free_nir_shaders();
return false;
}
if (!_convert_nir_to_dxil(stages_nir_shaders, r_stages_processed, r_dxil_blobs)) {
free_nir_shaders();
return false;
}
free_nir_shaders();
return true;
}
bool RenderingShaderContainerD3D12::_generate_root_signature(BitField<RenderingDeviceCommons::ShaderStage> p_stages_processed) {
// Root (push) constants.
LocalVector<D3D12_ROOT_PARAMETER1> root_params;
if (reflection_data_d3d12.dxil_push_constant_stages) {
CD3DX12_ROOT_PARAMETER1 push_constant;
push_constant.InitAsConstants(
reflection_data.push_constant_size / sizeof(uint32_t),
ROOT_CONSTANT_REGISTER,
0,
stages_to_d3d12_visibility(reflection_data_d3d12.dxil_push_constant_stages));
root_params.push_back(push_constant);
}
// NIR-DXIL runtime data.
if (reflection_data_d3d12.nir_runtime_data_root_param_idx == 1) { // Set above to 1 when discovering runtime data is needed.
DEV_ASSERT(!reflection_data.is_compute); // Could be supported if needed, but it's pointless as of now.
reflection_data_d3d12.nir_runtime_data_root_param_idx = root_params.size();
CD3DX12_ROOT_PARAMETER1 nir_runtime_data;
nir_runtime_data.InitAsConstants(
sizeof(dxil_spirv_vertex_runtime_data) / sizeof(uint32_t),
RUNTIME_DATA_REGISTER,
0,
D3D12_SHADER_VISIBILITY_VERTEX);
root_params.push_back(nir_runtime_data);
}
// Descriptor tables (up to two per uniform set, for resources and/or samplers).
// These have to stay around until serialization!
struct TraceableDescriptorTable {
uint32_t stages_mask = {};
Vector<D3D12_DESCRIPTOR_RANGE1> ranges;
Vector<RootSignatureLocation *> root_signature_locations;
};
uint32_t binding_start = 0;
Vector<TraceableDescriptorTable> resource_tables_maps;
Vector<TraceableDescriptorTable> sampler_tables_maps;
for (uint32_t i = 0; i < reflection_binding_set_uniforms_count.size(); i++) {
bool first_resource_in_set = true;
bool first_sampler_in_set = true;
uint32_t uniform_count = reflection_binding_set_uniforms_count[i];
for (uint32_t j = 0; j < uniform_count; j++) {
const ReflectionBindingData &uniform = reflection_binding_set_uniforms_data[binding_start + j];
ReflectionBindingDataD3D12 &uniform_d3d12 = reflection_binding_set_uniforms_data_d3d12.ptrw()[binding_start + j];
bool really_used = uniform_d3d12.dxil_stages != 0;
#ifdef DEV_ENABLED
bool anybody_home = (ResourceClass)(uniform_d3d12.resource_class) != RES_CLASS_INVALID || uniform_d3d12.has_sampler;
DEV_ASSERT(anybody_home == really_used);
#endif
if (!really_used) {
continue; // Existed in SPIR-V; went away in DXIL.
}
auto insert_range = [](D3D12_DESCRIPTOR_RANGE_TYPE p_range_type,
uint32_t p_num_descriptors,
uint32_t p_dxil_register,
uint32_t p_dxil_stages_mask,
RootSignatureLocation *p_root_sig_locations,
Vector<TraceableDescriptorTable> &r_tables,
bool &r_first_in_set) {
if (r_first_in_set) {
r_tables.resize(r_tables.size() + 1);
r_first_in_set = false;
}
TraceableDescriptorTable &table = r_tables.write[r_tables.size() - 1];
table.stages_mask |= p_dxil_stages_mask;
CD3DX12_DESCRIPTOR_RANGE1 range;
// Due to the aliasing hack for SRV-UAV of different families,
// we can be causing an unintended change of data (sometimes the validation layers catch it).
D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE;
if (p_range_type == D3D12_DESCRIPTOR_RANGE_TYPE_SRV || p_range_type == D3D12_DESCRIPTOR_RANGE_TYPE_UAV) {
flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE;
} else if (p_range_type == D3D12_DESCRIPTOR_RANGE_TYPE_CBV) {
flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE;
}
range.Init(p_range_type, p_num_descriptors, p_dxil_register, 0, flags);
table.ranges.push_back(range);
table.root_signature_locations.push_back(p_root_sig_locations);
};
uint32_t num_descriptors = 1;
D3D12_DESCRIPTOR_RANGE_TYPE resource_range_type = {};
switch ((ResourceClass)(uniform_d3d12.resource_class)) {
case RES_CLASS_INVALID: {
num_descriptors = uniform.length;
DEV_ASSERT(uniform_d3d12.has_sampler);
} break;
case RES_CLASS_CBV: {
resource_range_type = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
DEV_ASSERT(!uniform_d3d12.has_sampler);
} break;
case RES_CLASS_SRV: {
resource_range_type = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
num_descriptors = MAX(1u, uniform.length); // An unbound R/O buffer is reflected as zero-size.
} break;
case RES_CLASS_UAV: {
resource_range_type = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
num_descriptors = MAX(1u, uniform.length); // An unbound R/W buffer is reflected as zero-size.
DEV_ASSERT(!uniform_d3d12.has_sampler);
} break;
}
uint32_t dxil_register = i * GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER + uniform.binding * GODOT_NIR_BINDING_MULTIPLIER;
if (uniform_d3d12.resource_class != RES_CLASS_INVALID) {
insert_range(
resource_range_type,
num_descriptors,
dxil_register,
uniform_d3d12.dxil_stages,
&uniform_d3d12.root_signature_locations[RS_LOC_TYPE_RESOURCE],
resource_tables_maps,
first_resource_in_set);
}
if (uniform_d3d12.has_sampler) {
insert_range(
D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER,
num_descriptors,
dxil_register,
uniform_d3d12.dxil_stages,
&uniform_d3d12.root_signature_locations[RS_LOC_TYPE_SAMPLER],
sampler_tables_maps,
first_sampler_in_set);
}
}
binding_start += uniform_count;
}
auto make_descriptor_tables = [&root_params](const Vector<TraceableDescriptorTable> &p_tables) {
for (const TraceableDescriptorTable &table : p_tables) {
D3D12_SHADER_VISIBILITY visibility = stages_to_d3d12_visibility(table.stages_mask);
DEV_ASSERT(table.ranges.size() == table.root_signature_locations.size());
for (int i = 0; i < table.ranges.size(); i++) {
// By now we know very well which root signature location corresponds to the pointed uniform.
table.root_signature_locations[i]->root_param_index = root_params.size();
table.root_signature_locations[i]->range_index = i;
}
CD3DX12_ROOT_PARAMETER1 root_table;
root_table.InitAsDescriptorTable(table.ranges.size(), table.ranges.ptr(), visibility);
root_params.push_back(root_table);
}
};
make_descriptor_tables(resource_tables_maps);
make_descriptor_tables(sampler_tables_maps);
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC root_sig_desc = {};
D3D12_ROOT_SIGNATURE_FLAGS root_sig_flags =
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS;
if (!p_stages_processed.has_flag(RenderingDeviceCommons::SHADER_STAGE_VERTEX_BIT)) {
root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS;
}
if (!p_stages_processed.has_flag(RenderingDeviceCommons::SHADER_STAGE_FRAGMENT_BIT)) {
root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS;
}
if (reflection_data.vertex_input_mask) {
root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
}
root_sig_desc.Init_1_1(root_params.size(), root_params.ptr(), 0, nullptr, root_sig_flags);
// Create and store the root signature and its CRC32.
ID3DBlob *error_blob = nullptr;
ID3DBlob *root_sig_blob = nullptr;
HRESULT res = D3DX12SerializeVersionedRootSignature(HMODULE(lib_d3d12), &root_sig_desc, D3D_ROOT_SIGNATURE_VERSION_1_1, &root_sig_blob, &error_blob);
if (SUCCEEDED(res)) {
root_signature_bytes.resize(root_sig_blob->GetBufferSize());
memcpy(root_signature_bytes.ptrw(), root_sig_blob->GetBufferPointer(), root_sig_blob->GetBufferSize());
root_signature_crc = crc32(0, nullptr, 0);
root_signature_crc = crc32(root_signature_crc, (const Bytef *)root_sig_blob->GetBufferPointer(), root_sig_blob->GetBufferSize());
return true;
} else {
if (root_sig_blob != nullptr) {
root_sig_blob->Release();
}
String error_string;
if (error_blob != nullptr) {
error_string = vformat("Serialization of root signature failed with error 0x%08ux and the following message:\n%s", uint32_t(res), String::ascii(Span((char *)error_blob->GetBufferPointer(), error_blob->GetBufferSize())));
error_blob->Release();
} else {
error_string = vformat("Serialization of root signature failed with error 0x%08ux", uint32_t(res));
}
ERR_FAIL_V_MSG(false, error_string);
}
}
void RenderingShaderContainerD3D12::_nir_report_resource(uint32_t p_register, uint32_t p_space, uint32_t p_dxil_type, void *p_data) {
const GodotNirCallbackUserData &user_data = *(GodotNirCallbackUserData *)p_data;
// Types based on Mesa's dxil_container.h.
static const uint32_t DXIL_RES_SAMPLER = 1;
static const ResourceClass DXIL_TYPE_TO_CLASS[] = {
RES_CLASS_INVALID, // DXIL_RES_INVALID
RES_CLASS_INVALID, // DXIL_RES_SAMPLER
RES_CLASS_CBV, // DXIL_RES_CBV
RES_CLASS_SRV, // DXIL_RES_SRV_TYPED
RES_CLASS_SRV, // DXIL_RES_SRV_RAW
RES_CLASS_SRV, // DXIL_RES_SRV_STRUCTURED
RES_CLASS_UAV, // DXIL_RES_UAV_TYPED
RES_CLASS_UAV, // DXIL_RES_UAV_RAW
RES_CLASS_UAV, // DXIL_RES_UAV_STRUCTURED
RES_CLASS_INVALID, // DXIL_RES_UAV_STRUCTURED_WITH_COUNTER
};
DEV_ASSERT(p_dxil_type < ARRAY_SIZE(DXIL_TYPE_TO_CLASS));
ResourceClass resource_class = DXIL_TYPE_TO_CLASS[p_dxil_type];
if (p_register == ROOT_CONSTANT_REGISTER && p_space == 0) {
DEV_ASSERT(resource_class == RES_CLASS_CBV);
user_data.container->reflection_data_d3d12.dxil_push_constant_stages |= (1 << user_data.stage);
} else if (p_register == RUNTIME_DATA_REGISTER && p_space == 0) {
DEV_ASSERT(resource_class == RES_CLASS_CBV);
user_data.container->reflection_data_d3d12.nir_runtime_data_root_param_idx = 1; // Temporary, to be determined later.
} else {
DEV_ASSERT(p_space == 0);
uint32_t set = p_register / GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER;
uint32_t binding = (p_register % GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER) / GODOT_NIR_BINDING_MULTIPLIER;
DEV_ASSERT(set < (uint32_t)user_data.container->reflection_binding_set_uniforms_count.size());
uint32_t binding_start = 0;
for (uint32_t i = 0; i < set; i++) {
binding_start += user_data.container->reflection_binding_set_uniforms_count[i];
}
[[maybe_unused]] bool found = false;
for (uint32_t i = 0; i < user_data.container->reflection_binding_set_uniforms_count[set]; i++) {
const ReflectionBindingData &uniform = user_data.container->reflection_binding_set_uniforms_data[binding_start + i];
ReflectionBindingDataD3D12 &uniform_d3d12 = user_data.container->reflection_binding_set_uniforms_data_d3d12.ptrw()[binding_start + i];
if (uniform.binding != binding) {
continue;
}
uniform_d3d12.dxil_stages |= (1 << user_data.stage);
if (resource_class != RES_CLASS_INVALID) {
DEV_ASSERT(uniform_d3d12.resource_class == (uint32_t)RES_CLASS_INVALID || uniform_d3d12.resource_class == (uint32_t)resource_class);
uniform_d3d12.resource_class = resource_class;
} else if (p_dxil_type == DXIL_RES_SAMPLER) {
uniform_d3d12.has_sampler = (uint32_t)true;
} else {
DEV_ASSERT(false && "Unknown resource class.");
}
found = true;
}
DEV_ASSERT(found);
}
}
void RenderingShaderContainerD3D12::_nir_report_sc_bit_offset(uint32_t p_sc_id, uint64_t p_bit_offset, void *p_data) {
const GodotNirCallbackUserData &user_data = *(GodotNirCallbackUserData *)p_data;
[[maybe_unused]] bool found = false;
for (int64_t i = 0; i < user_data.container->reflection_specialization_data.size(); i++) {
const ReflectionSpecializationData &sc = user_data.container->reflection_specialization_data[i];
ReflectionSpecializationDataD3D12 &sc_d3d12 = user_data.container->reflection_specialization_data_d3d12.ptrw()[i];
if (sc.constant_id != p_sc_id) {
continue;
}
uint32_t offset_idx = SHADER_STAGES_BIT_OFFSET_INDICES[user_data.stage];
DEV_ASSERT(sc_d3d12.stages_bit_offsets[offset_idx] == 0);
sc_d3d12.stages_bit_offsets[offset_idx] = p_bit_offset;
found = true;
break;
}
DEV_ASSERT(found);
}
void RenderingShaderContainerD3D12::_nir_report_bitcode_bit_offset(uint64_t p_bit_offset, void *p_data) {
DEV_ASSERT(p_bit_offset % 8 == 0);
const GodotNirCallbackUserData &user_data = *(GodotNirCallbackUserData *)p_data;
uint32_t offset_idx = SHADER_STAGES_BIT_OFFSET_INDICES[user_data.stage];
for (int64_t i = 0; i < user_data.container->reflection_specialization_data.size(); i++) {
ReflectionSpecializationDataD3D12 &sc_d3d12 = user_data.container->reflection_specialization_data_d3d12.ptrw()[i];
if (sc_d3d12.stages_bit_offsets[offset_idx] == 0) {
// This SC has been optimized out from this stage.
continue;
}
sc_d3d12.stages_bit_offsets[offset_idx] += p_bit_offset;
}
}
#endif
void RenderingShaderContainerD3D12::_set_from_shader_reflection_post(const String &p_shader_name, const RenderingDeviceCommons::ShaderReflection &p_reflection) {
reflection_binding_set_uniforms_data_d3d12.resize(reflection_binding_set_uniforms_data.size());
reflection_specialization_data_d3d12.resize(reflection_specialization_data.size());
// Sort bindings inside each uniform set. This guarantees the root signature will be generated in the correct order.
SortArray<ReflectionBindingData> sorter;
uint32_t binding_start = 0;
for (uint32_t i = 0; i < reflection_binding_set_uniforms_count.size(); i++) {
uint32_t uniform_count = reflection_binding_set_uniforms_count[i];
if (uniform_count > 0) {
sorter.sort(&reflection_binding_set_uniforms_data.ptrw()[binding_start], uniform_count);
binding_start += uniform_count;
}
}
}
bool RenderingShaderContainerD3D12::_set_code_from_spirv(const Vector<RenderingDeviceCommons::ShaderStageSPIRVData> &p_spirv) {
#if NIR_ENABLED
reflection_data_d3d12.nir_runtime_data_root_param_idx = UINT32_MAX;
for (int64_t i = 0; i < reflection_specialization_data.size(); i++) {
DEV_ASSERT(reflection_specialization_data[i].constant_id < (sizeof(reflection_data_d3d12.spirv_specialization_constants_ids_mask) * 8) && "Constant IDs with values above 31 are not supported.");
reflection_data_d3d12.spirv_specialization_constants_ids_mask |= (1 << reflection_specialization_data[i].constant_id);
}
// Translate SPIR-V shaders to DXIL, and collect shader info from the new representation.
HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> dxil_blobs;
Vector<RenderingDeviceCommons::ShaderStage> stages;
BitField<RenderingDeviceCommons::ShaderStage> stages_processed = {};
if (!_convert_spirv_to_dxil(p_spirv, dxil_blobs, stages, stages_processed)) {
return false;
}
// Patch with default values of specialization constants.
DEV_ASSERT(reflection_specialization_data.size() == reflection_specialization_data_d3d12.size());
for (int32_t i = 0; i < reflection_specialization_data.size(); i++) {
const ReflectionSpecializationData &sc = reflection_specialization_data[i];
const ReflectionSpecializationDataD3D12 &sc_d3d12 = reflection_specialization_data_d3d12[i];
RenderingDXIL::patch_specialization_constant((RenderingDeviceCommons::PipelineSpecializationConstantType)(sc.type), &sc.int_value, sc_d3d12.stages_bit_offsets, dxil_blobs, true);
}
// Sign.
uint32_t shader_index = 0;
for (KeyValue<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &E : dxil_blobs) {
RenderingDXIL::sign_bytecode(E.key, E.value);
}
// Store compressed DXIL blobs as the shaders.
shaders.resize(p_spirv.size());
for (int64_t i = 0; i < shaders.size(); i++) {
const PackedByteArray &dxil_bytes = dxil_blobs[stages[i]];
RenderingShaderContainer::Shader &shader = shaders.ptrw()[i];
uint32_t compressed_size = 0;
shader.shader_stage = stages[i];
shader.code_decompressed_size = dxil_bytes.size();
shader.code_compressed_bytes.resize(dxil_bytes.size());
bool compressed = compress_code(dxil_bytes.ptr(), dxil_bytes.size(), shader.code_compressed_bytes.ptrw(), &compressed_size, &shader.code_compression_flags);
ERR_FAIL_COND_V_MSG(!compressed, false, vformat("Failed to compress native code to native for SPIR-V #%d.", shader_index));
shader.code_compressed_bytes.resize(compressed_size);
}
if (!_generate_root_signature(stages_processed)) {
return false;
}
return true;
#else
ERR_FAIL_V_MSG(false, "Shader compilation is not supported at runtime without NIR.");
#endif
}
RenderingShaderContainerD3D12::RenderingShaderContainerD3D12() {
// Default empty constructor.
}
RenderingShaderContainerD3D12::RenderingShaderContainerD3D12(void *p_lib_d3d12) {
lib_d3d12 = p_lib_d3d12;
}
RenderingShaderContainerD3D12::ShaderReflectionD3D12 RenderingShaderContainerD3D12::get_shader_reflection_d3d12() const {
ShaderReflectionD3D12 reflection;
reflection.spirv_specialization_constants_ids_mask = reflection_data_d3d12.spirv_specialization_constants_ids_mask;
reflection.dxil_push_constant_stages = reflection_data_d3d12.dxil_push_constant_stages;
reflection.nir_runtime_data_root_param_idx = reflection_data_d3d12.nir_runtime_data_root_param_idx;
reflection.reflection_specialization_data_d3d12 = reflection_specialization_data_d3d12;
reflection.root_signature_bytes = root_signature_bytes;
reflection.root_signature_crc = root_signature_crc;
// Transform data vector into a vector of vectors that's easier to user.
uint32_t uniform_index = 0;
reflection.reflection_binding_set_uniforms_d3d12.resize(reflection_binding_set_uniforms_count.size());
for (int64_t i = 0; i < reflection.reflection_binding_set_uniforms_d3d12.size(); i++) {
Vector<ReflectionBindingDataD3D12> &uniforms = reflection.reflection_binding_set_uniforms_d3d12.ptrw()[i];
uniforms.resize(reflection_binding_set_uniforms_count[i]);
for (int64_t j = 0; j < uniforms.size(); j++) {
uniforms.ptrw()[j] = reflection_binding_set_uniforms_data_d3d12[uniform_index];
uniform_index++;
}
}
return reflection;
}
// RenderingShaderContainerFormatD3D12
void RenderingShaderContainerFormatD3D12::set_lib_d3d12(void *p_lib_d3d12) {
lib_d3d12 = p_lib_d3d12;
}
Ref<RenderingShaderContainer> RenderingShaderContainerFormatD3D12::create_container() const {
return memnew(RenderingShaderContainerD3D12(lib_d3d12));
}
RenderingDeviceCommons::ShaderLanguageVersion RenderingShaderContainerFormatD3D12::get_shader_language_version() const {
// NIR-DXIL is Vulkan 1.1-conformant.
return SHADER_LANGUAGE_VULKAN_VERSION_1_1;
}
RenderingDeviceCommons::ShaderSpirvVersion RenderingShaderContainerFormatD3D12::get_shader_spirv_version() const {
// The SPIR-V part of Mesa supports 1.6, but:
// - SPIRV-Reflect won't be able to parse the compute workgroup size.
// - We want to play it safe with NIR-DXIL.
return SHADER_SPIRV_VERSION_1_5;
}
RenderingShaderContainerFormatD3D12::RenderingShaderContainerFormatD3D12() {}
RenderingShaderContainerFormatD3D12::~RenderingShaderContainerFormatD3D12() {}

View File

@@ -0,0 +1,179 @@
/**************************************************************************/
/* rendering_shader_container_d3d12.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "servers/rendering/rendering_shader_container.h"
#define NIR_ENABLED 1
#ifdef SHADER_BAKER_RUNTIME_ENABLED
#undef NIR_ENABLED
#endif
#include "d3d12_godot_nir_bridge.h"
#define D3D12_BITCODE_OFFSETS_NUM_STAGES 3
#if NIR_ENABLED
struct nir_shader;
struct nir_shader_compiler_options;
#endif
enum RootSignatureLocationType {
RS_LOC_TYPE_RESOURCE,
RS_LOC_TYPE_SAMPLER,
};
enum ResourceClass {
RES_CLASS_INVALID,
RES_CLASS_CBV,
RES_CLASS_SRV,
RES_CLASS_UAV,
};
struct RenderingDXIL {
static uint32_t patch_specialization_constant(
RenderingDeviceCommons::PipelineSpecializationConstantType p_type,
const void *p_value,
const uint64_t (&p_stages_bit_offsets)[D3D12_BITCODE_OFFSETS_NUM_STAGES],
HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_stages_bytecodes,
bool p_is_first_patch);
static void sign_bytecode(RenderingDeviceCommons::ShaderStage p_stage, Vector<uint8_t> &r_dxil_blob);
};
class RenderingShaderContainerD3D12 : public RenderingShaderContainer {
GDSOFTCLASS(RenderingShaderContainerD3D12, RenderingShaderContainer);
public:
static constexpr uint32_t REQUIRED_SHADER_MODEL = 0x62; // D3D_SHADER_MODEL_6_2
static constexpr uint32_t ROOT_CONSTANT_REGISTER = GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER * (RenderingDeviceCommons::MAX_UNIFORM_SETS + 1);
static constexpr uint32_t RUNTIME_DATA_REGISTER = GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER * (RenderingDeviceCommons::MAX_UNIFORM_SETS + 2);
static constexpr uint32_t FORMAT_VERSION = 1;
static constexpr uint32_t SHADER_STAGES_BIT_OFFSET_INDICES[RenderingDeviceCommons::SHADER_STAGE_MAX] = {
0, // SHADER_STAGE_VERTEX
1, // SHADER_STAGE_FRAGMENT
UINT32_MAX, // SHADER_STAGE_TESSELATION_CONTROL
UINT32_MAX, // SHADER_STAGE_TESSELATION_EVALUATION
2, // SHADER_STAGE_COMPUTE
};
struct RootSignatureLocation {
uint32_t root_param_index = UINT32_MAX;
uint32_t range_index = UINT32_MAX;
};
struct ReflectionBindingDataD3D12 {
uint32_t resource_class = 0;
uint32_t has_sampler = 0;
uint32_t dxil_stages = 0;
RootSignatureLocation root_signature_locations[2];
};
struct ReflectionSpecializationDataD3D12 {
uint64_t stages_bit_offsets[D3D12_BITCODE_OFFSETS_NUM_STAGES] = {};
};
protected:
struct ReflectionDataD3D12 {
uint32_t spirv_specialization_constants_ids_mask = 0;
uint32_t dxil_push_constant_stages = 0;
uint32_t nir_runtime_data_root_param_idx = 0;
};
struct ContainerFooterD3D12 {
uint32_t root_signature_length = 0;
uint32_t root_signature_crc = 0;
};
void *lib_d3d12 = nullptr;
ReflectionDataD3D12 reflection_data_d3d12;
Vector<ReflectionBindingDataD3D12> reflection_binding_set_uniforms_data_d3d12;
Vector<ReflectionSpecializationDataD3D12> reflection_specialization_data_d3d12;
Vector<uint8_t> root_signature_bytes;
uint32_t root_signature_crc = 0;
#if NIR_ENABLED
bool _convert_spirv_to_nir(const Vector<RenderingDeviceCommons::ShaderStageSPIRVData> &p_spirv, const nir_shader_compiler_options *p_compiler_options, HashMap<int, nir_shader *> &r_stages_nir_shaders, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed);
bool _convert_nir_to_dxil(const HashMap<int, nir_shader *> &p_stages_nir_shaders, BitField<RenderingDeviceCommons::ShaderStage> p_stages_processed, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs);
bool _convert_spirv_to_dxil(const Vector<RenderingDeviceCommons::ShaderStageSPIRVData> &p_spirv, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed);
bool _generate_root_signature(BitField<RenderingDeviceCommons::ShaderStage> p_stages_processed);
// GodotNirCallbacks.
static void _nir_report_resource(uint32_t p_register, uint32_t p_space, uint32_t p_dxil_type, void *p_data);
static void _nir_report_sc_bit_offset(uint32_t p_sc_id, uint64_t p_bit_offset, void *p_data);
static void _nir_report_bitcode_bit_offset(uint64_t p_bit_offset, void *p_data);
#endif
// RenderingShaderContainer overrides.
virtual uint32_t _format() const override;
virtual uint32_t _format_version() const override;
virtual uint32_t _from_bytes_reflection_extra_data(const uint8_t *p_bytes) override;
virtual uint32_t _from_bytes_reflection_binding_uniform_extra_data_start(const uint8_t *p_bytes) override;
virtual uint32_t _from_bytes_reflection_binding_uniform_extra_data(const uint8_t *p_bytes, uint32_t p_index) override;
virtual uint32_t _from_bytes_reflection_specialization_extra_data_start(const uint8_t *p_bytes) override;
virtual uint32_t _from_bytes_reflection_specialization_extra_data(const uint8_t *p_bytes, uint32_t p_index) override;
virtual uint32_t _from_bytes_footer_extra_data(const uint8_t *p_bytes) override;
virtual uint32_t _to_bytes_reflection_extra_data(uint8_t *p_bytes) const override;
virtual uint32_t _to_bytes_reflection_binding_uniform_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;
virtual uint32_t _to_bytes_reflection_specialization_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;
virtual uint32_t _to_bytes_footer_extra_data(uint8_t *p_bytes) const override;
virtual void _set_from_shader_reflection_post(const String &p_shader_name, const RenderingDeviceCommons::ShaderReflection &p_reflection) override;
virtual bool _set_code_from_spirv(const Vector<RenderingDeviceCommons::ShaderStageSPIRVData> &p_spirv) override;
public:
struct ShaderReflectionD3D12 {
uint32_t spirv_specialization_constants_ids_mask = 0;
uint32_t dxil_push_constant_stages = 0;
uint32_t nir_runtime_data_root_param_idx = 0;
Vector<Vector<ReflectionBindingDataD3D12>> reflection_binding_set_uniforms_d3d12;
Vector<ReflectionSpecializationDataD3D12> reflection_specialization_data_d3d12;
Vector<uint8_t> root_signature_bytes;
uint32_t root_signature_crc = 0;
};
RenderingShaderContainerD3D12();
RenderingShaderContainerD3D12(void *p_lib_d3d12);
ShaderReflectionD3D12 get_shader_reflection_d3d12() const;
};
class RenderingShaderContainerFormatD3D12 : public RenderingShaderContainerFormat {
protected:
void *lib_d3d12 = nullptr;
public:
void set_lib_d3d12(void *p_lib_d3d12);
virtual Ref<RenderingShaderContainer> create_container() const override;
virtual ShaderLanguageVersion get_shader_language_version() const override;
virtual ShaderSpirvVersion get_shader_spirv_version() const override;
RenderingShaderContainerFormatD3D12();
virtual ~RenderingShaderContainerFormatD3D12();
};