Adding physics.
This commit is contained in:
parent
3fdee3d9b6
commit
807e61a887
586 changed files with 118826 additions and 4 deletions
12
.gitattributes
vendored
12
.gitattributes
vendored
|
|
@ -26,9 +26,16 @@
|
||||||
*.exe filter=lfs diff=lfs merge=lfs -text
|
*.exe filter=lfs diff=lfs merge=lfs -text
|
||||||
*.dll filter=lfs diff=lfs merge=lfs -text
|
*.dll filter=lfs diff=lfs merge=lfs -text
|
||||||
*.dat filter=lfs diff=lfs merge=lfs -text
|
*.dat filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
||||||
|
#
|
||||||
# Singe game and menu descriptors are Lua text, not binary.
|
# Singe game and menu descriptors are Lua text, not binary.
|
||||||
|
#
|
||||||
games.dat !filter !diff !merge text
|
games.dat !filter !diff !merge text
|
||||||
menu.dat !filter !diff !merge text
|
menu.dat !filter !diff !merge text
|
||||||
|
|
||||||
|
#
|
||||||
|
# Fonts
|
||||||
|
#
|
||||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
@ -75,3 +82,8 @@ menu.dat !filter !diff !merge text
|
||||||
*.lzma filter=lfs diff=lfs merge=lfs -text
|
*.lzma filter=lfs diff=lfs merge=lfs -text
|
||||||
*.gz filter=lfs diff=lfs merge=lfs -text
|
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||||
*.tar filter=lfs diff=lfs merge=lfs -text
|
*.tar filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
||||||
|
#
|
||||||
|
# Model
|
||||||
|
#
|
||||||
|
*.glb filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.22)
|
||||||
project(singe2 VERSION 3.00 LANGUAGES C)
|
project(singe2 VERSION 3.00 LANGUAGES C CXX)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
@ -273,6 +273,8 @@ set(SINGE_SOURCE
|
||||||
src/main.h
|
src/main.h
|
||||||
src/pack.c
|
src/pack.c
|
||||||
src/pack.h
|
src/pack.h
|
||||||
|
src/physics.h
|
||||||
|
src/physicsJolt.cpp
|
||||||
src/model.c
|
src/model.c
|
||||||
src/model.h
|
src/model.h
|
||||||
src/math3d.c
|
src/math3d.c
|
||||||
|
|
@ -622,12 +624,25 @@ foreach(imageLib libjpeg.a libpng16.a)
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
# Jolt Physics comes with the compile definitions it was built with (SIMD level, layer bits) through
|
||||||
|
# its exported target; the one C++ file in the tree (physicsJolt.cpp) needs them. Its C++ runtime is
|
||||||
|
# zig's own libc++, linked statically, or the host's libstdc++ under gcc; macOS already lists -lc++.
|
||||||
|
find_package(Jolt CONFIG REQUIRED PATHS ${BUILD_DIR}/lib/cmake/Jolt NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
|
||||||
|
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
|
||||||
|
if(NOT KANGAROO_OS STREQUAL "macos")
|
||||||
|
if(SINGE_ZIG_TARGET)
|
||||||
|
list(APPEND SYSTEM_LIBS -lc++)
|
||||||
|
else()
|
||||||
|
list(APPEND SYSTEM_LIBS -lstdc++)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# System libraries follow the static ones so their symbols resolve for everything above.
|
# System libraries follow the static ones so their symbols resolve for everything above.
|
||||||
# Apple's linker has no --start-group, so the list is repeated there instead.
|
# Apple's linker has no --start-group, so the list is repeated there instead.
|
||||||
if(KANGAROO_OS STREQUAL "macos")
|
if(KANGAROO_OS STREQUAL "macos")
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ${STATIC_LIBS} ${STATIC_LIBS} ${STATIC_LIBS} ${SYSTEM_LIBS} -pthread -lm)
|
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ${STATIC_LIBS} ${STATIC_LIBS} ${STATIC_LIBS} Jolt::Jolt ${SYSTEM_LIBS} -pthread -lm)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE -Wl,--start-group ${STATIC_LIBS} -Wl,--end-group ${SYSTEM_LIBS} -pthread -lm)
|
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE -Wl,--start-group ${STATIC_LIBS} -Wl,--end-group Jolt::Jolt ${SYSTEM_LIBS} -pthread -lm)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
1
LICENSES
1
LICENSES
|
|
@ -9,6 +9,7 @@ cgltf MIT https://github.com/jkuhlmann/cgltf
|
||||||
copas MIT https://lunarmodules.github.io/copas
|
copas MIT https://lunarmodules.github.io/copas
|
||||||
ffmpeg LGPL-2.1 https://ffmpeg.org
|
ffmpeg LGPL-2.1 https://ffmpeg.org
|
||||||
freetype FTL https://freetype.org
|
freetype FTL https://freetype.org
|
||||||
|
JoltPhysics MIT https://github.com/jrouwe/JoltPhysics
|
||||||
json.lua MIT https://github.com/rxi/json.lua
|
json.lua MIT https://github.com/rxi/json.lua
|
||||||
libjpeg-turbo IJG https://libjpeg-turbo.org
|
libjpeg-turbo IJG https://libjpeg-turbo.org
|
||||||
libogg BSD-3-Clause https://xiph.org/ogg
|
libogg BSD-3-Clause https://xiph.org/ogg
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,22 @@ singeCmakeProject(zstd ${SB_THIRDPARTY}/zstd/build/cmake "" "-DZSTD_BUILD_SHARED
|
||||||
|
|
||||||
singeCmakeProject(SDL3 ${SB_THIRDPARTY}/SDL3 "" "-DSDL_SHARED=off;-DSDL_STATIC=on;-DSDL_TESTS=off;-DSDL_EXAMPLES=off" "${SB_ENV}")
|
singeCmakeProject(SDL3 ${SB_THIRDPARTY}/SDL3 "" "-DSDL_SHARED=off;-DSDL_STATIC=on;-DSDL_TESTS=off;-DSDL_EXAMPLES=off" "${SB_ENV}")
|
||||||
|
|
||||||
|
# ===== Jolt Physics (C++; static, single precision, no tests or samples) =====
|
||||||
|
# The x86 baseline is SSE4.1/4.2 so a shipped binary runs on anything from 2008 on; AVX and the
|
||||||
|
# bit-count instructions would silently raise it. No link-time optimisation through the cross
|
||||||
|
# compiler, no warnings-as-errors with a clang Jolt has not met, nothing but the library; the GPU
|
||||||
|
# compute paths (hair and soft bodies on DX12, Vulkan, Metal or a CPU fallback) would need a shader
|
||||||
|
# compiler at build time and are not used.
|
||||||
|
set(joltOptions
|
||||||
|
-DTARGET_UNIT_TESTS=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_PERFORMANCE_TEST=OFF -DTARGET_SAMPLES=OFF -DTARGET_VIEWER=OFF
|
||||||
|
-DJPH_BUILD_SHARED_LIBS=OFF -DDOUBLE_PRECISION=OFF -DCROSS_PLATFORM_DETERMINISTIC=OFF
|
||||||
|
-DUSE_SSE4_1=ON -DUSE_SSE4_2=ON -DUSE_AVX=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_F16C=OFF -DUSE_FMADD=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF
|
||||||
|
-DINTERPROCEDURAL_OPTIMIZATION=OFF -DENABLE_ALL_WARNINGS=OFF -DGENERATE_DEBUG_SYMBOLS=OFF
|
||||||
|
-DDEBUG_RENDERER_IN_DEBUG_AND_RELEASE=OFF -DFLOATING_POINT_EXCEPTIONS_ENABLED=OFF -DCPP_EXCEPTIONS_ENABLED=OFF -DCPP_RTTI_ENABLED=OFF
|
||||||
|
-DJPH_USE_DX12=OFF -DJPH_USE_VK=OFF -DJPH_USE_MTL=OFF -DJPH_USE_CPU_COMPUTE=OFF
|
||||||
|
)
|
||||||
|
singeCmakeProject(Jolt ${SB_THIRDPARTY}/JoltPhysics/Build "" "${joltOptions}" "${SB_ENV}")
|
||||||
|
|
||||||
set(sdl3Dir -DSDL3_DIR=${SB_PREFIX}/lib/cmake/SDL3)
|
set(sdl3Dir -DSDL3_DIR=${SB_PREFIX}/lib/cmake/SDL3)
|
||||||
|
|
||||||
singeCmakeProject(SDL3_image ${SB_THIRDPARTY}/SDL3_image "SDL3;zlib"
|
singeCmakeProject(SDL3_image ${SB_THIRDPARTY}/SDL3_image "SDL3;zlib"
|
||||||
|
|
@ -299,7 +315,7 @@ set(singeBinary ${SB_PREFIX}/singe)
|
||||||
ExternalProject_Add(singe
|
ExternalProject_Add(singe
|
||||||
SOURCE_DIR ${CMAKE_SOURCE_DIR}
|
SOURCE_DIR ${CMAKE_SOURCE_DIR}
|
||||||
BINARY_DIR ${singeBinary}
|
BINARY_DIR ${singeBinary}
|
||||||
DEPENDS zlib zstd SDL3 SDL3_image SDL3_mixer SDL3_ttf openssl ffmpeg
|
DEPENDS zlib zstd SDL3 SDL3_image SDL3_mixer SDL3_ttf openssl ffmpeg Jolt
|
||||||
CONFIGURE_COMMAND ${SB_ENV} ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR} -B ${singeBinary} ${SB_CMAKE_ARGS} -DSINGE_SUPERBUILD=OFF -DKANGAROO_OS=${KANGAROO_OS} -DKANGAROO_ARCH=${KANGAROO_ARCH} -DSINGE_TREE=${SINGE_TREE}
|
CONFIGURE_COMMAND ${SB_ENV} ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR} -B ${singeBinary} ${SB_CMAKE_ARGS} -DSINGE_SUPERBUILD=OFF -DKANGAROO_OS=${KANGAROO_OS} -DKANGAROO_ARCH=${KANGAROO_ARCH} -DSINGE_TREE=${SINGE_TREE}
|
||||||
BUILD_COMMAND ${SB_ENV} ${CMAKE_COMMAND} --build ${singeBinary} --parallel ${SB_JOBS}
|
BUILD_COMMAND ${SB_ENV} ${CMAKE_COMMAND} --build ${singeBinary} --parallel ${SB_JOBS}
|
||||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${singeBinary}/${singeBinaryBase}${CMAKE_EXECUTABLE_SUFFIX} ${CMAKE_SOURCE_DIR}/.builddir/${singeBinaryName}
|
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${singeBinary}/${singeBinaryBase}${CMAKE_EXECUTABLE_SUFFIX} ${CMAKE_SOURCE_DIR}/.builddir/${singeBinaryName}
|
||||||
|
|
|
||||||
81
src/physics.h
Normal file
81
src/physics.h
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Singe 3
|
||||||
|
* Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 3
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PHYSICS_H
|
||||||
|
#define PHYSICS_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "math3d.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// The physics world (Jolt Physics behind this C interface; see physicsJolt.cpp). A body lives on
|
||||||
|
// a scene node: static bodies stay where the node was when they were made, kinematic bodies follow
|
||||||
|
// their node each frame, dynamic bodies drive their node.
|
||||||
|
|
||||||
|
typedef enum BodyTypeE {
|
||||||
|
BODY_STATIC = 0,
|
||||||
|
BODY_DYNAMIC = 1,
|
||||||
|
BODY_KINEMATIC = 2
|
||||||
|
} BodyTypeE;
|
||||||
|
|
||||||
|
typedef enum ShapeTypeE {
|
||||||
|
SHAPE_BOX = 0, // width, height, depth
|
||||||
|
SHAPE_SPHERE = 1, // radius
|
||||||
|
SHAPE_CAPSULE = 2, // radius, height (caps included), along Y
|
||||||
|
SHAPE_CYLINDER = 3, // radius, height, along Y
|
||||||
|
SHAPE_HULL = 4, // a convex hull of the node's mesh (stage 4)
|
||||||
|
SHAPE_MESH = 5 // the node's mesh triangles, static only (stage 4)
|
||||||
|
} ShapeTypeE;
|
||||||
|
|
||||||
|
|
||||||
|
bool bodyApplyForce(int32_t node, Vec3T force, const Vec3T *at);
|
||||||
|
bool bodyApplyImpulse(int32_t node, Vec3T impulse, const Vec3T *at);
|
||||||
|
bool bodyDelete(int32_t node);
|
||||||
|
bool bodyExists(int32_t node);
|
||||||
|
Vec3T bodyGetAngularVelocity(int32_t node);
|
||||||
|
Vec3T bodyGetVelocity(int32_t node);
|
||||||
|
bool bodyIsResting(int32_t node);
|
||||||
|
bool bodyNew(int32_t node, BodyTypeE type, ShapeTypeE shape, float a, float b, float c);
|
||||||
|
bool bodySetAngularVelocity(int32_t node, Vec3T velocity);
|
||||||
|
bool bodySetBounce(int32_t node, float bounce);
|
||||||
|
bool bodySetEnabled(int32_t node, bool enabled);
|
||||||
|
bool bodySetFriction(int32_t node, float friction);
|
||||||
|
bool bodySetMass(int32_t node, float kilograms);
|
||||||
|
bool bodySetVelocity(int32_t node, Vec3T velocity);
|
||||||
|
bool physicsAvailable(void);
|
||||||
|
bool physicsInit(void);
|
||||||
|
void physicsQuit(void);
|
||||||
|
void physicsSetEnabled(bool enabled);
|
||||||
|
void physicsSetGravity(Vec3T gravity);
|
||||||
|
void physicsUpdate(bool advance);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
596
src/physicsJolt.cpp
Normal file
596
src/physicsJolt.cpp
Normal file
|
|
@ -0,0 +1,596 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Singe 3
|
||||||
|
* Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 3
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Jolt Physics behind the C interface in physics.h: the only C++ in Singe. Nothing of Jolt's
|
||||||
|
// crosses the header; the engine sees plain C functions and node handles.
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
#include <Jolt/RegisterTypes.h>
|
||||||
|
#include <Jolt/Core/Factory.h>
|
||||||
|
#include <Jolt/Core/TempAllocator.h>
|
||||||
|
#include <Jolt/Core/JobSystemThreadPool.h>
|
||||||
|
#include <Jolt/Physics/PhysicsSettings.h>
|
||||||
|
#include <Jolt/Physics/PhysicsSystem.h>
|
||||||
|
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||||
|
#include <Jolt/Physics/Body/BodyLock.h>
|
||||||
|
#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h>
|
||||||
|
#include <Jolt/Physics/Collision/ObjectLayer.h>
|
||||||
|
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||||
|
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||||
|
#include <Jolt/Physics/Collision/Shape/CylinderShape.h>
|
||||||
|
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||||
|
extern "C" {
|
||||||
|
#include "util.h"
|
||||||
|
#include "scene.h"
|
||||||
|
}
|
||||||
|
#include "physics.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_BODIES 4096
|
||||||
|
#define MAX_BODY_PAIRS 4096
|
||||||
|
#define MAX_CONTACTS 8192
|
||||||
|
#define TEMP_ALLOCATOR_BYTES (16 * 1024 * 1024)
|
||||||
|
#define MIN_JOB_THREADS 1
|
||||||
|
#define STEP_SECONDS (1.0 / 60.0)
|
||||||
|
#define MAX_STEPS_PER_FRAME 4
|
||||||
|
#define MIN_DIMENSION 0.001f
|
||||||
|
#define DEFAULT_FRICTION 0.5f
|
||||||
|
#define DEFAULT_BOUNCE 0.1f
|
||||||
|
#define NO_HANDLE -1
|
||||||
|
|
||||||
|
|
||||||
|
// Two object layers: what never moves and what may. Static bodies never collide with each other.
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const JPH::ObjectLayer LAYER_NON_MOVING = 0;
|
||||||
|
const JPH::ObjectLayer LAYER_MOVING = 1;
|
||||||
|
const JPH::BroadPhaseLayer BROAD_NON_MOVING(0);
|
||||||
|
const JPH::BroadPhaseLayer BROAD_MOVING(1);
|
||||||
|
const JPH::uint BROAD_COUNT = 2;
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectPairFilterT final : public JPH::ObjectLayerPairFilter {
|
||||||
|
public:
|
||||||
|
bool ShouldCollide(JPH::ObjectLayer a, JPH::ObjectLayer b) const override {
|
||||||
|
return (a == LAYER_MOVING) || (b == LAYER_MOVING);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class BroadPhaseLayersT final : public JPH::BroadPhaseLayerInterface {
|
||||||
|
public:
|
||||||
|
JPH::uint GetNumBroadPhaseLayers() const override {
|
||||||
|
return BROAD_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH::BroadPhaseLayer GetBroadPhaseLayer(JPH::ObjectLayer layer) const override {
|
||||||
|
return (layer == LAYER_NON_MOVING) ? BROAD_NON_MOVING : BROAD_MOVING;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
|
||||||
|
const char *GetBroadPhaseLayerName(JPH::BroadPhaseLayer layer) const override {
|
||||||
|
return (layer == BROAD_NON_MOVING) ? "NON_MOVING" : "MOVING";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectVsBroadPhaseFilterT final : public JPH::ObjectVsBroadPhaseLayerFilter {
|
||||||
|
public:
|
||||||
|
bool ShouldCollide(JPH::ObjectLayer layer, JPH::BroadPhaseLayer broad) const override {
|
||||||
|
return (layer == LAYER_MOVING) || (broad == BROAD_MOVING);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// A body on a scene node.
|
||||||
|
struct BodyRecordT {
|
||||||
|
int32_t node;
|
||||||
|
uint32_t generation; // The node's, so a reused handle is not mistaken for this body
|
||||||
|
JPH::BodyID id;
|
||||||
|
BodyTypeE type;
|
||||||
|
bool enabled; // In the world (bodySetEnabled)
|
||||||
|
bool used;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct WorldT {
|
||||||
|
JPH::TempAllocatorImpl *tempAllocator;
|
||||||
|
JPH::JobSystemThreadPool *jobs;
|
||||||
|
BroadPhaseLayersT broadPhaseLayers;
|
||||||
|
ObjectVsBroadPhaseFilterT objectVsBroadPhase;
|
||||||
|
ObjectPairFilterT objectPairs;
|
||||||
|
JPH::PhysicsSystem *system;
|
||||||
|
BodyRecordT *bodies;
|
||||||
|
int32_t bodyCount;
|
||||||
|
double accumulator; // Seconds owed to the fixed step
|
||||||
|
uint64_t lastTick;
|
||||||
|
bool enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
WorldT *_world = nullptr;
|
||||||
|
|
||||||
|
|
||||||
|
BodyRecordT *_find(int32_t node);
|
||||||
|
JPH::Quat _fromQuat(QuatT q);
|
||||||
|
JPH::Vec3 _fromVec3(Vec3T v);
|
||||||
|
void _release(BodyRecordT *record);
|
||||||
|
QuatT _toQuat(JPH::Quat q);
|
||||||
|
Vec3T _toVec3(JPH::Vec3 v);
|
||||||
|
void _trace(const char *fmt, ...);
|
||||||
|
|
||||||
|
|
||||||
|
// The record for a node's body, or NULL. A record whose node was deleted (or reused) is
|
||||||
|
// released on the way.
|
||||||
|
BodyRecordT *_find(int32_t node) {
|
||||||
|
int32_t x;
|
||||||
|
|
||||||
|
if (_world == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
for (x = 0; x < _world->bodyCount; x++) {
|
||||||
|
BodyRecordT *record = &_world->bodies[x];
|
||||||
|
|
||||||
|
if (!record->used || (record->node != node)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!nodeValid(node) || (nodeGetGeneration(node) != record->generation)) {
|
||||||
|
_release(record);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JPH::Quat _fromQuat(QuatT q) {
|
||||||
|
return JPH::Quat(q.x, q.y, q.z, q.w).Normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JPH::Vec3 _fromVec3(Vec3T v) {
|
||||||
|
return JPH::Vec3(v.x, v.y, v.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Takes the body out of the world and frees its slot.
|
||||||
|
void _release(BodyRecordT *record) {
|
||||||
|
JPH::BodyInterface &bodies = _world->system->GetBodyInterface();
|
||||||
|
|
||||||
|
if (record->enabled) {
|
||||||
|
bodies.RemoveBody(record->id);
|
||||||
|
}
|
||||||
|
bodies.DestroyBody(record->id);
|
||||||
|
memset(record, 0, sizeof(*record));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QuatT _toQuat(JPH::Quat q) {
|
||||||
|
QuatT out = { q.GetX(), q.GetY(), q.GetZ(), q.GetW() };
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vec3T _toVec3(JPH::Vec3 v) {
|
||||||
|
return vec3(v.GetX(), v.GetY(), v.GetZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Jolt's trace goes to Singe's.
|
||||||
|
void _trace(const char *fmt, ...) {
|
||||||
|
char buffer[1024];
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
utilTrace("Physics: %s", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ===== Bodies =====
|
||||||
|
|
||||||
|
// A force for this step, at the centre of mass or a world point.
|
||||||
|
bool bodyApplyForce(int32_t node, Vec3T force, const Vec3T *at) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if ((record == nullptr) || (record->type != BODY_DYNAMIC)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (at != nullptr) {
|
||||||
|
_world->system->GetBodyInterface().AddForce(record->id, _fromVec3(force), JPH::RVec3(at->x, at->y, at->z));
|
||||||
|
} else {
|
||||||
|
_world->system->GetBodyInterface().AddForce(record->id, _fromVec3(force));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// An instant change of momentum, at the centre of mass or a world point.
|
||||||
|
bool bodyApplyImpulse(int32_t node, Vec3T impulse, const Vec3T *at) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if ((record == nullptr) || (record->type != BODY_DYNAMIC)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (at != nullptr) {
|
||||||
|
_world->system->GetBodyInterface().AddImpulse(record->id, _fromVec3(impulse), JPH::RVec3(at->x, at->y, at->z));
|
||||||
|
} else {
|
||||||
|
_world->system->GetBodyInterface().AddImpulse(record->id, _fromVec3(impulse));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool bodyDelete(int32_t node) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if (record == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_release(record);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool bodyExists(int32_t node) {
|
||||||
|
return _find(node) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vec3T bodyGetAngularVelocity(int32_t node) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if (record == nullptr) {
|
||||||
|
return vec3(0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
return _toVec3(_world->system->GetBodyInterface().GetAngularVelocity(record->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vec3T bodyGetVelocity(int32_t node) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if (record == nullptr) {
|
||||||
|
return vec3(0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
return _toVec3(_world->system->GetBodyInterface().GetLinearVelocity(record->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Asleep: a dynamic body that has come to rest (static and disabled bodies count as resting).
|
||||||
|
bool bodyIsResting(int32_t node) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if (record == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !record->enabled || !_world->system->GetBodyInterface().IsActive(record->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Gives the node a body with a shape sized by a, b, c (see ShapeTypeE) and the node's world scale,
|
||||||
|
// placed where the node is now. One body per node; a second call replaces the first.
|
||||||
|
bool bodyNew(int32_t node, BodyTypeE type, ShapeTypeE shape, float a, float b, float c) {
|
||||||
|
BodyRecordT *record;
|
||||||
|
JPH::RefConst<JPH::Shape> joltShape;
|
||||||
|
Vec3T position;
|
||||||
|
QuatT rotation;
|
||||||
|
Vec3T scale;
|
||||||
|
float radius;
|
||||||
|
float height;
|
||||||
|
int32_t x;
|
||||||
|
|
||||||
|
if ((_world == nullptr) || !nodeValid(node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bodyDelete(node);
|
||||||
|
sceneUpdateTransforms();
|
||||||
|
nodeGetWorldTransform(node, &position, &rotation, &scale);
|
||||||
|
switch (shape) {
|
||||||
|
case SHAPE_BOX:
|
||||||
|
joltShape = new JPH::BoxShape(JPH::Vec3(SDL_max(a * scale.x, MIN_DIMENSION) / 2.0f, SDL_max(b * scale.y, MIN_DIMENSION) / 2.0f, SDL_max(c * scale.z, MIN_DIMENSION) / 2.0f), 0.0f);
|
||||||
|
break;
|
||||||
|
case SHAPE_SPHERE:
|
||||||
|
joltShape = new JPH::SphereShape(SDL_max(a * SDL_max(scale.x, SDL_max(scale.y, scale.z)), MIN_DIMENSION));
|
||||||
|
break;
|
||||||
|
case SHAPE_CAPSULE:
|
||||||
|
radius = SDL_max(a * SDL_max(scale.x, scale.z), MIN_DIMENSION);
|
||||||
|
height = SDL_max(b * scale.y, MIN_DIMENSION);
|
||||||
|
joltShape = new JPH::CapsuleShape(SDL_max(height / 2.0f - radius, MIN_DIMENSION), radius);
|
||||||
|
break;
|
||||||
|
case SHAPE_CYLINDER:
|
||||||
|
radius = SDL_max(a * SDL_max(scale.x, scale.z), MIN_DIMENSION);
|
||||||
|
height = SDL_max(b * scale.y, MIN_DIMENSION);
|
||||||
|
joltShape = new JPH::CylinderShape(height / 2.0f, radius);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
utilTrace("Physics: shape %d is not available yet.", (int32_t)shape);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (x = 0; x < _world->bodyCount; x++) {
|
||||||
|
if (!_world->bodies[x].used) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (x == _world->bodyCount) {
|
||||||
|
_world->bodies = (BodyRecordT *)SDL_realloc(_world->bodies, sizeof(BodyRecordT) * (size_t)(_world->bodyCount + 1));
|
||||||
|
if (_world->bodies == nullptr) {
|
||||||
|
utilDie("Out of memory allocating a physics body.");
|
||||||
|
}
|
||||||
|
_world->bodyCount++;
|
||||||
|
}
|
||||||
|
record = &_world->bodies[x];
|
||||||
|
memset(record, 0, sizeof(*record));
|
||||||
|
{
|
||||||
|
JPH::EMotionType motion = (type == BODY_STATIC) ? JPH::EMotionType::Static : ((type == BODY_KINEMATIC) ? JPH::EMotionType::Kinematic : JPH::EMotionType::Dynamic);
|
||||||
|
JPH::ObjectLayer layer = (type == BODY_STATIC) ? LAYER_NON_MOVING : LAYER_MOVING;
|
||||||
|
JPH::BodyCreationSettings settings(joltShape, JPH::RVec3(position.x, position.y, position.z), _fromQuat(rotation), motion, layer);
|
||||||
|
|
||||||
|
settings.mFriction = DEFAULT_FRICTION;
|
||||||
|
settings.mRestitution = DEFAULT_BOUNCE;
|
||||||
|
settings.mUserData = (JPH::uint64)(uint32_t)node;
|
||||||
|
record->id = _world->system->GetBodyInterface().CreateAndAddBody(settings, JPH::EActivation::Activate);
|
||||||
|
}
|
||||||
|
if (record->id.IsInvalid()) {
|
||||||
|
utilTrace("Physics: unable to create a body (too many?).");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
record->node = node;
|
||||||
|
record->generation = nodeGetGeneration(node);
|
||||||
|
record->type = type;
|
||||||
|
record->enabled = true;
|
||||||
|
record->used = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool bodySetAngularVelocity(int32_t node, Vec3T velocity) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if ((record == nullptr) || (record->type == BODY_STATIC)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_world->system->GetBodyInterface().SetAngularVelocity(record->id, _fromVec3(velocity));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 0 stops dead, 1 bounces back with everything it arrived with.
|
||||||
|
bool bodySetBounce(int32_t node, float bounce) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if (record == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_world->system->GetBodyInterface().SetRestitution(record->id, SDL_clamp(bounce, 0.0f, 1.0f));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Takes the body out of the world (it stops colliding and moving) and puts it back.
|
||||||
|
bool bodySetEnabled(int32_t node, bool enabled) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if (record == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (enabled && !record->enabled) {
|
||||||
|
_world->system->GetBodyInterface().AddBody(record->id, JPH::EActivation::Activate);
|
||||||
|
} else if (!enabled && record->enabled) {
|
||||||
|
_world->system->GetBodyInterface().RemoveBody(record->id);
|
||||||
|
}
|
||||||
|
record->enabled = enabled;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool bodySetFriction(int32_t node, float friction) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if (record == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_world->system->GetBodyInterface().SetFriction(record->id, SDL_max(friction, 0.0f));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Rescales the body's mass and inertia (dynamic bodies only).
|
||||||
|
bool bodySetMass(int32_t node, float kilograms) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if ((record == nullptr) || (record->type != BODY_DYNAMIC) || (kilograms <= 0.0f)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
JPH::BodyLockWrite lock(_world->system->GetBodyLockInterface(), record->id);
|
||||||
|
|
||||||
|
if (!lock.Succeeded()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lock.GetBody().GetMotionProperties()->ScaleToMass(kilograms);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool bodySetVelocity(int32_t node, Vec3T velocity) {
|
||||||
|
BodyRecordT *record = _find(node);
|
||||||
|
|
||||||
|
if ((record == nullptr) || (record->type == BODY_STATIC)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_world->system->GetBodyInterface().SetLinearVelocity(record->id, _fromVec3(velocity));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ===== World =====
|
||||||
|
|
||||||
|
bool physicsAvailable(void) {
|
||||||
|
return _world != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Brings Jolt up: allocators, the type factory, a job pool sized to the machine, and an empty
|
||||||
|
// world. Refuses (returning false, 3D physics unavailable) on an x86 without SSE4.1 and 4.2, the
|
||||||
|
// level the library was compiled for, rather than faulting on the first instruction.
|
||||||
|
bool physicsInit(void) {
|
||||||
|
int32_t threads;
|
||||||
|
|
||||||
|
if (_world != nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#if defined(JPH_USE_SSE4_2)
|
||||||
|
if (!SDL_HasSSE41() || !SDL_HasSSE42()) {
|
||||||
|
utilTrace("Physics: this CPU lacks SSE4.1/4.2; physics is unavailable.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
JPH::RegisterDefaultAllocator();
|
||||||
|
JPH::Trace = _trace;
|
||||||
|
JPH::Factory::sInstance = new JPH::Factory();
|
||||||
|
JPH::RegisterTypes();
|
||||||
|
_world = new WorldT();
|
||||||
|
_world->tempAllocator = new JPH::TempAllocatorImpl(TEMP_ALLOCATOR_BYTES);
|
||||||
|
threads = SDL_max(SDL_GetNumLogicalCPUCores() - 1, MIN_JOB_THREADS);
|
||||||
|
_world->jobs = new JPH::JobSystemThreadPool(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsBarriers, threads);
|
||||||
|
_world->system = new JPH::PhysicsSystem();
|
||||||
|
_world->system->Init(MAX_BODIES, 0, MAX_BODY_PAIRS, MAX_CONTACTS, _world->broadPhaseLayers, _world->objectVsBroadPhase, _world->objectPairs);
|
||||||
|
_world->enabled = true;
|
||||||
|
utilTrace("Physics: Jolt %d.%d.%d ready, %d job thread%s", JPH_VERSION_MAJOR, JPH_VERSION_MINOR, JPH_VERSION_PATCH, threads, (threads == 1) ? "" : "s");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void physicsQuit(void) {
|
||||||
|
int32_t x;
|
||||||
|
|
||||||
|
if (_world == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (x = 0; x < _world->bodyCount; x++) {
|
||||||
|
if (_world->bodies[x].used) {
|
||||||
|
_release(&_world->bodies[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_free(_world->bodies);
|
||||||
|
delete _world->system;
|
||||||
|
delete _world->jobs;
|
||||||
|
delete _world->tempAllocator;
|
||||||
|
delete _world;
|
||||||
|
_world = nullptr;
|
||||||
|
JPH::UnregisterTypes();
|
||||||
|
delete JPH::Factory::sInstance;
|
||||||
|
JPH::Factory::sInstance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Pauses the simulation (bodies hold still) without losing it.
|
||||||
|
void physicsSetEnabled(bool enabled) {
|
||||||
|
if (_world != nullptr) {
|
||||||
|
_world->enabled = enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void physicsSetGravity(Vec3T gravity) {
|
||||||
|
if (_world != nullptr) {
|
||||||
|
_world->system->SetGravity(_fromVec3(gravity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Once per frame, after animation: kinematic bodies are moved to their nodes, the world steps at
|
||||||
|
// a fixed rate for the time that has passed (at most a few steps, and none while the game is
|
||||||
|
// paused or physics is disabled), and dynamic bodies drive their nodes.
|
||||||
|
void physicsUpdate(bool advance) {
|
||||||
|
uint64_t now;
|
||||||
|
int32_t steps = 0;
|
||||||
|
int32_t x;
|
||||||
|
Vec3T position;
|
||||||
|
QuatT rotation;
|
||||||
|
Vec3T scale;
|
||||||
|
|
||||||
|
if (_world == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
now = SDL_GetTicksNS();
|
||||||
|
if (advance && _world->enabled && (_world->lastTick != 0)) {
|
||||||
|
_world->accumulator += (double)(now - _world->lastTick) / 1e9;
|
||||||
|
}
|
||||||
|
_world->lastTick = now;
|
||||||
|
if (_world->accumulator > STEP_SECONDS * MAX_STEPS_PER_FRAME) {
|
||||||
|
_world->accumulator = STEP_SECONDS * MAX_STEPS_PER_FRAME;
|
||||||
|
}
|
||||||
|
if (_world->accumulator < STEP_SECONDS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sceneUpdateTransforms();
|
||||||
|
{
|
||||||
|
JPH::BodyInterface &bodies = _world->system->GetBodyInterface();
|
||||||
|
float dt = (float)(STEP_SECONDS * (int32_t)(_world->accumulator / STEP_SECONDS));
|
||||||
|
|
||||||
|
// Kinematic bodies go where their nodes went, with the velocity that implies.
|
||||||
|
for (x = 0; x < _world->bodyCount; x++) {
|
||||||
|
BodyRecordT *record = &_world->bodies[x];
|
||||||
|
|
||||||
|
if (!record->used || (record->type != BODY_KINEMATIC) || !record->enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!nodeValid(record->node) || (nodeGetGeneration(record->node) != record->generation)) {
|
||||||
|
_release(record);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nodeGetWorldTransform(record->node, &position, &rotation, &scale);
|
||||||
|
bodies.MoveKinematic(record->id, JPH::RVec3(position.x, position.y, position.z), _fromQuat(rotation), dt);
|
||||||
|
}
|
||||||
|
while ((_world->accumulator >= STEP_SECONDS) && (steps < MAX_STEPS_PER_FRAME)) {
|
||||||
|
_world->system->Update((float)STEP_SECONDS, 1, _world->tempAllocator, _world->jobs);
|
||||||
|
_world->accumulator -= STEP_SECONDS;
|
||||||
|
steps++;
|
||||||
|
}
|
||||||
|
// Dynamic bodies drive their nodes.
|
||||||
|
for (x = 0; x < _world->bodyCount; x++) {
|
||||||
|
BodyRecordT *record = &_world->bodies[x];
|
||||||
|
JPH::RVec3 where;
|
||||||
|
JPH::Quat how;
|
||||||
|
|
||||||
|
if (!record->used || (record->type != BODY_DYNAMIC) || !record->enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!nodeValid(record->node) || (nodeGetGeneration(record->node) != record->generation)) {
|
||||||
|
_release(record);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bodies.GetPositionAndRotation(record->id, where, how);
|
||||||
|
nodeSetWorldTransform(record->node, vec3((float)where.GetX(), (float)where.GetY(), (float)where.GetZ()), _toQuat(how));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/scene.c
51
src/scene.c
|
|
@ -1989,6 +1989,16 @@ Vec3T nodeGetWorldPosition(int32_t node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The node's world-space position, rotation and scale as of the last transform update.
|
||||||
|
bool nodeGetWorldTransform(int32_t node, Vec3T *position, QuatT *rotation, Vec3T *scale) {
|
||||||
|
if (!nodeValid(node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mat4Decompose(_scene.nodes[node].world, position, rotation, scale);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Points the node's -Z at a world-space target (the camera and lights look down -Z). Only the
|
// Points the node's -Z at a world-space target (the camera and lights look down -Z). Only the
|
||||||
// node's own rotation changes, in its parent's space.
|
// node's own rotation changes, in its parent's space.
|
||||||
bool nodeLookAt(int32_t node, Vec3T target) {
|
bool nodeLookAt(int32_t node, Vec3T target) {
|
||||||
|
|
@ -2167,6 +2177,36 @@ bool nodeSetVisible(int32_t node, bool visible) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Places the node at a world-space position and rotation by converting through its parent's
|
||||||
|
// world transform (what a physics body needs to drive a node under any parent). Scale is kept.
|
||||||
|
bool nodeSetWorldTransform(int32_t node, Vec3T position, QuatT rotation) {
|
||||||
|
NodeT *n;
|
||||||
|
Mat4T parentInverse;
|
||||||
|
Vec3T parentPosition;
|
||||||
|
QuatT parentRotation;
|
||||||
|
Vec3T parentScale;
|
||||||
|
QuatT parentInverseRotation;
|
||||||
|
|
||||||
|
if (!nodeValid(node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
n = &_scene.nodes[node];
|
||||||
|
if ((n->parent == NO_HANDLE) || (n->parent == SCENE_ROOT_NODE) || !mat4Invert(_scene.nodes[n->parent].world, &parentInverse)) {
|
||||||
|
n->translation = position;
|
||||||
|
n->rotation = quatNormalize(rotation);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
mat4Decompose(_scene.nodes[n->parent].world, &parentPosition, &parentRotation, &parentScale);
|
||||||
|
parentInverseRotation = parentRotation;
|
||||||
|
parentInverseRotation.x = -parentInverseRotation.x;
|
||||||
|
parentInverseRotation.y = -parentInverseRotation.y;
|
||||||
|
parentInverseRotation.z = -parentInverseRotation.z;
|
||||||
|
n->translation = mat4TransformPoint(parentInverse, position);
|
||||||
|
n->rotation = quatNormalize(quatMultiply(parentInverseRotation, rotation));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool nodeValid(int32_t node) {
|
bool nodeValid(int32_t node) {
|
||||||
return (node >= 0) && (node < _scene.nodeCount) && _scene.nodes[node].used;
|
return (node >= 0) && (node < _scene.nodeCount) && _scene.nodes[node].used;
|
||||||
}
|
}
|
||||||
|
|
@ -2644,6 +2684,17 @@ Vec3T sceneUnproject(float x, float y, float distance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Rebuilds every node's world matrix now (sceneRender does it too); physics needs them before the
|
||||||
|
// step, after animation has moved the nodes.
|
||||||
|
void sceneUpdateTransforms(void) {
|
||||||
|
Mat4T identity = mat4Identity();
|
||||||
|
|
||||||
|
if (_scene.device != NULL) {
|
||||||
|
_updateWorld(SCENE_ROOT_NODE, &identity, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Once per frame before sceneRender: copies every player a material shows into that feed's RGBA
|
// Once per frame before sceneRender: copies every player a material shows into that feed's RGBA
|
||||||
// target with the 2D renderer (which converts YUV on the way), then flushes the renderer so the
|
// target with the 2D renderer (which converts YUV on the way), then flushes the renderer so the
|
||||||
// copies are queued ahead of the scene's own command buffer.
|
// copies are queued ahead of the scene's own command buffer.
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ void sceneComputeNormals(SceneVertexT *vertices, int32_t vertexCount, co
|
||||||
void sceneSetBackground(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
void sceneSetBackground(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||||
void sceneSetShadowSize(int32_t size);
|
void sceneSetShadowSize(int32_t size);
|
||||||
Vec3T sceneUnproject(float x, float y, float distance);
|
Vec3T sceneUnproject(float x, float y, float distance);
|
||||||
|
void sceneUpdateTransforms(void);
|
||||||
void sceneUpdateVideo(SceneVideoSourceFn source);
|
void sceneUpdateVideo(SceneVideoSourceFn source);
|
||||||
|
|
||||||
bool cameraSet(int32_t node);
|
bool cameraSet(int32_t node);
|
||||||
|
|
@ -114,6 +115,7 @@ Vec3T nodeGetPosition(int32_t node);
|
||||||
QuatT nodeGetRotation(int32_t node);
|
QuatT nodeGetRotation(int32_t node);
|
||||||
Vec3T nodeGetScale(int32_t node);
|
Vec3T nodeGetScale(int32_t node);
|
||||||
Vec3T nodeGetWorldPosition(int32_t node);
|
Vec3T nodeGetWorldPosition(int32_t node);
|
||||||
|
bool nodeGetWorldTransform(int32_t node, Vec3T *position, QuatT *rotation, Vec3T *scale);
|
||||||
bool nodeLookAt(int32_t node, Vec3T target);
|
bool nodeLookAt(int32_t node, Vec3T target);
|
||||||
bool nodeMove(int32_t node, Vec3T delta);
|
bool nodeMove(int32_t node, Vec3T delta);
|
||||||
int32_t nodeNew(int32_t parent);
|
int32_t nodeNew(int32_t parent);
|
||||||
|
|
@ -126,6 +128,7 @@ bool nodeSetRotation(int32_t node, QuatT rotation);
|
||||||
bool nodeSetScale(int32_t node, Vec3T scale);
|
bool nodeSetScale(int32_t node, Vec3T scale);
|
||||||
bool nodeSetSkin(int32_t node, const int32_t *joints, const Mat4T *inverseBind, int32_t count);
|
bool nodeSetSkin(int32_t node, const int32_t *joints, const Mat4T *inverseBind, int32_t count);
|
||||||
bool nodeSetVisible(int32_t node, bool visible);
|
bool nodeSetVisible(int32_t node, bool visible);
|
||||||
|
bool nodeSetWorldTransform(int32_t node, Vec3T position, QuatT rotation);
|
||||||
bool nodeValid(int32_t node);
|
bool nodeValid(int32_t node);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
236
src/singe.c
236
src/singe.c
|
|
@ -60,6 +60,7 @@ LSEC_API int luaopen_ssl_config(lua_State *L);
|
||||||
#include "vfs.h"
|
#include "vfs.h"
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
|
#include "physics.h"
|
||||||
#include "videoPlayer.h"
|
#include "videoPlayer.h"
|
||||||
#include "singe.h"
|
#include "singe.h"
|
||||||
|
|
||||||
|
|
@ -437,6 +438,7 @@ static QuatT _argEuler(lua_State *L, const char *method, int32_t index);
|
||||||
static float *_argFloatTable(lua_State *L, const char *method, int32_t index, int32_t *count);
|
static float *_argFloatTable(lua_State *L, const char *method, int32_t index, int32_t *count);
|
||||||
static FontT *_argFont(lua_State *L, const char *method, int32_t index);
|
static FontT *_argFont(lua_State *L, const char *method, int32_t index);
|
||||||
static int32_t _argInteger(lua_State *L, const char *method, int32_t index);
|
static int32_t _argInteger(lua_State *L, const char *method, int32_t index);
|
||||||
|
static int32_t _argBody(lua_State *L, const char *method, int32_t index);
|
||||||
static int32_t _argMaterial(lua_State *L, const char *method, int32_t index);
|
static int32_t _argMaterial(lua_State *L, const char *method, int32_t index);
|
||||||
static int32_t _argMesh(lua_State *L, const char *method, int32_t index);
|
static int32_t _argMesh(lua_State *L, const char *method, int32_t index);
|
||||||
static int32_t _argNode(lua_State *L, const char *method, int32_t index);
|
static int32_t _argNode(lua_State *L, const char *method, int32_t index);
|
||||||
|
|
@ -526,6 +528,19 @@ static int32_t apiAnimationPlay(lua_State *L);
|
||||||
static int32_t apiAnimationResume(lua_State *L);
|
static int32_t apiAnimationResume(lua_State *L);
|
||||||
static int32_t apiAnimationSetTime(lua_State *L);
|
static int32_t apiAnimationSetTime(lua_State *L);
|
||||||
static int32_t apiAnimationStop(lua_State *L);
|
static int32_t apiAnimationStop(lua_State *L);
|
||||||
|
static int32_t apiBodyApplyForce(lua_State *L);
|
||||||
|
static int32_t apiBodyApplyImpulse(lua_State *L);
|
||||||
|
static int32_t apiBodyDelete(lua_State *L);
|
||||||
|
static int32_t apiBodyGetAngularVelocity(lua_State *L);
|
||||||
|
static int32_t apiBodyGetVelocity(lua_State *L);
|
||||||
|
static int32_t apiBodyIsResting(lua_State *L);
|
||||||
|
static int32_t apiBodyNew(lua_State *L);
|
||||||
|
static int32_t apiBodySetAngularVelocity(lua_State *L);
|
||||||
|
static int32_t apiBodySetBounce(lua_State *L);
|
||||||
|
static int32_t apiBodySetEnabled(lua_State *L);
|
||||||
|
static int32_t apiBodySetFriction(lua_State *L);
|
||||||
|
static int32_t apiBodySetMass(lua_State *L);
|
||||||
|
static int32_t apiBodySetVelocity(lua_State *L);
|
||||||
static int32_t apiCameraSet(lua_State *L);
|
static int32_t apiCameraSet(lua_State *L);
|
||||||
static int32_t apiCameraSetOrthographic(lua_State *L);
|
static int32_t apiCameraSetOrthographic(lua_State *L);
|
||||||
static int32_t apiCameraSetPerspective(lua_State *L);
|
static int32_t apiCameraSetPerspective(lua_State *L);
|
||||||
|
|
@ -635,6 +650,8 @@ static int32_t apiOverlayLine(lua_State *L);
|
||||||
static int32_t apiOverlayPlot(lua_State *L);
|
static int32_t apiOverlayPlot(lua_State *L);
|
||||||
static int32_t apiOverlayPrint(lua_State *L);
|
static int32_t apiOverlayPrint(lua_State *L);
|
||||||
static int32_t apiOverlaySetResolution(lua_State *L);
|
static int32_t apiOverlaySetResolution(lua_State *L);
|
||||||
|
static int32_t apiPhysicsSetEnabled(lua_State *L);
|
||||||
|
static int32_t apiPhysicsSetGravity(lua_State *L);
|
||||||
static int32_t apiSceneEnable(lua_State *L);
|
static int32_t apiSceneEnable(lua_State *L);
|
||||||
static int32_t apiSceneGetSize(lua_State *L);
|
static int32_t apiSceneGetSize(lua_State *L);
|
||||||
static int32_t apiSceneProject(lua_State *L);
|
static int32_t apiSceneProject(lua_State *L);
|
||||||
|
|
@ -801,6 +818,17 @@ static int32_t _argInteger(lua_State *L, const char *method, int32_t index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A node that carries a physics body, checked.
|
||||||
|
static int32_t _argBody(lua_State *L, const char *method, int32_t index) {
|
||||||
|
int32_t node = _argNode(L, method, index);
|
||||||
|
|
||||||
|
if (!bodyExists(node)) {
|
||||||
|
_luaDie(L, method, "Node %d has no body.", node);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// A material handle argument, checked.
|
// A material handle argument, checked.
|
||||||
static int32_t _argMaterial(lua_State *L, const char *method, int32_t index) {
|
static int32_t _argMaterial(lua_State *L, const char *method, int32_t index) {
|
||||||
int32_t material = _argInteger(L, method, index);
|
int32_t material = _argInteger(L, method, index);
|
||||||
|
|
@ -2153,6 +2181,25 @@ static void _pushConstants(lua_State *L) {
|
||||||
lua_setglobal(L, "LIGHT_POINT");
|
lua_setglobal(L, "LIGHT_POINT");
|
||||||
lua_pushinteger(L, LIGHT_SPOT);
|
lua_pushinteger(L, LIGHT_SPOT);
|
||||||
lua_setglobal(L, "LIGHT_SPOT");
|
lua_setglobal(L, "LIGHT_SPOT");
|
||||||
|
// Physics bodies and shapes
|
||||||
|
lua_pushinteger(L, BODY_STATIC);
|
||||||
|
lua_setglobal(L, "BODY_STATIC");
|
||||||
|
lua_pushinteger(L, BODY_DYNAMIC);
|
||||||
|
lua_setglobal(L, "BODY_DYNAMIC");
|
||||||
|
lua_pushinteger(L, BODY_KINEMATIC);
|
||||||
|
lua_setglobal(L, "BODY_KINEMATIC");
|
||||||
|
lua_pushinteger(L, SHAPE_BOX);
|
||||||
|
lua_setglobal(L, "SHAPE_BOX");
|
||||||
|
lua_pushinteger(L, SHAPE_SPHERE);
|
||||||
|
lua_setglobal(L, "SHAPE_SPHERE");
|
||||||
|
lua_pushinteger(L, SHAPE_CAPSULE);
|
||||||
|
lua_setglobal(L, "SHAPE_CAPSULE");
|
||||||
|
lua_pushinteger(L, SHAPE_CYLINDER);
|
||||||
|
lua_setglobal(L, "SHAPE_CYLINDER");
|
||||||
|
lua_pushinteger(L, SHAPE_HULL);
|
||||||
|
lua_setglobal(L, "SHAPE_HULL");
|
||||||
|
lua_pushinteger(L, SHAPE_MESH);
|
||||||
|
lua_setglobal(L, "SHAPE_MESH");
|
||||||
|
|
||||||
lua_pushinteger(L, -1);
|
lua_pushinteger(L, -1);
|
||||||
lua_setglobal(L, "SOUND_ERROR_INVALID");
|
lua_setglobal(L, "SOUND_ERROR_INVALID");
|
||||||
|
|
@ -2704,6 +2751,161 @@ static int32_t apiAnimationStop(lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bodyApplyForce(node, fx, fy, fz [, px, py, pz]): this step, at the centre or a world point
|
||||||
|
static int32_t apiBodyApplyForce(lua_State *L) {
|
||||||
|
int32_t node;
|
||||||
|
Vec3T force;
|
||||||
|
Vec3T at;
|
||||||
|
|
||||||
|
_argCheck(L, "bodyApplyForce", 4, 7);
|
||||||
|
node = _argBody(L, "bodyApplyForce", 1);
|
||||||
|
force = _argVec3(L, "bodyApplyForce", 2);
|
||||||
|
if (lua_gettop(L) >= 7) {
|
||||||
|
at = _argVec3(L, "bodyApplyForce", 5);
|
||||||
|
bodyApplyForce(node, force, &at);
|
||||||
|
} else {
|
||||||
|
bodyApplyForce(node, force, NULL);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bodyApplyImpulse(node, ix, iy, iz [, px, py, pz]): an instant change of momentum
|
||||||
|
static int32_t apiBodyApplyImpulse(lua_State *L) {
|
||||||
|
int32_t node;
|
||||||
|
Vec3T impulse;
|
||||||
|
Vec3T at;
|
||||||
|
|
||||||
|
_argCheck(L, "bodyApplyImpulse", 4, 7);
|
||||||
|
node = _argBody(L, "bodyApplyImpulse", 1);
|
||||||
|
impulse = _argVec3(L, "bodyApplyImpulse", 2);
|
||||||
|
if (lua_gettop(L) >= 7) {
|
||||||
|
at = _argVec3(L, "bodyApplyImpulse", 5);
|
||||||
|
bodyApplyImpulse(node, impulse, &at);
|
||||||
|
} else {
|
||||||
|
bodyApplyImpulse(node, impulse, NULL);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bodyDelete(node)
|
||||||
|
static int32_t apiBodyDelete(lua_State *L) {
|
||||||
|
_argCheck(L, "bodyDelete", 1, 1);
|
||||||
|
bodyDelete(_argBody(L, "bodyDelete", 1));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// x, y, z = bodyGetAngularVelocity(node): radians per second about each axis
|
||||||
|
static int32_t apiBodyGetAngularVelocity(lua_State *L) {
|
||||||
|
_argCheck(L, "bodyGetAngularVelocity", 1, 1);
|
||||||
|
return _pushVec3(L, bodyGetAngularVelocity(_argBody(L, "bodyGetAngularVelocity", 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// x, y, z = bodyGetVelocity(node): units per second
|
||||||
|
static int32_t apiBodyGetVelocity(lua_State *L) {
|
||||||
|
_argCheck(L, "bodyGetVelocity", 1, 1);
|
||||||
|
return _pushVec3(L, bodyGetVelocity(_argBody(L, "bodyGetVelocity", 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// resting = bodyIsResting(node): asleep
|
||||||
|
static int32_t apiBodyIsResting(lua_State *L) {
|
||||||
|
_argCheck(L, "bodyIsResting", 1, 1);
|
||||||
|
lua_pushboolean(L, bodyIsResting(_argBody(L, "bodyIsResting", 1)));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bodyNew(node, type, shape, a [, b [, c]]): BODY_* and SHAPE_*; a, b, c size the shape
|
||||||
|
static int32_t apiBodyNew(lua_State *L) {
|
||||||
|
int32_t node;
|
||||||
|
int32_t type;
|
||||||
|
int32_t shape;
|
||||||
|
float dims[3] = { 0.0f, 0.0f, 0.0f };
|
||||||
|
int32_t x;
|
||||||
|
|
||||||
|
_argCheck(L, "bodyNew", 3, 6);
|
||||||
|
node = _argNode(L, "bodyNew", 1);
|
||||||
|
type = _argInteger(L, "bodyNew", 2);
|
||||||
|
shape = _argInteger(L, "bodyNew", 3);
|
||||||
|
if ((type < BODY_STATIC) || (type > BODY_KINEMATIC)) {
|
||||||
|
_luaDie(L, "bodyNew", "Unknown body type %d.", type);
|
||||||
|
}
|
||||||
|
if ((shape < SHAPE_BOX) || (shape > SHAPE_MESH)) {
|
||||||
|
_luaDie(L, "bodyNew", "Unknown shape %d.", shape);
|
||||||
|
}
|
||||||
|
for (x = 0; x < 3; x++) {
|
||||||
|
if (lua_gettop(L) >= 4 + x) {
|
||||||
|
dims[x] = (float)_argNumber(L, "bodyNew", 4 + x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!physicsAvailable()) {
|
||||||
|
_luaDie(L, "bodyNew", "Physics is not available on this machine.");
|
||||||
|
}
|
||||||
|
if (!bodyNew(node, (BodyTypeE)type, (ShapeTypeE)shape, dims[0], dims[1], dims[2])) {
|
||||||
|
_luaDie(L, "bodyNew", "Unable to create the body.");
|
||||||
|
}
|
||||||
|
_luaTrace(L, "bodyNew", "node %d type %d shape %d", node, type, shape);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bodySetAngularVelocity(node, x, y, z)
|
||||||
|
static int32_t apiBodySetAngularVelocity(lua_State *L) {
|
||||||
|
_argCheck(L, "bodySetAngularVelocity", 4, 4);
|
||||||
|
bodySetAngularVelocity(_argBody(L, "bodySetAngularVelocity", 1), _argVec3(L, "bodySetAngularVelocity", 2));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bodySetBounce(node, 0..1)
|
||||||
|
static int32_t apiBodySetBounce(lua_State *L) {
|
||||||
|
_argCheck(L, "bodySetBounce", 2, 2);
|
||||||
|
bodySetBounce(_argBody(L, "bodySetBounce", 1), (float)_argNumber(L, "bodySetBounce", 2));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bodySetEnabled(node, bool): out of the world and back
|
||||||
|
static int32_t apiBodySetEnabled(lua_State *L) {
|
||||||
|
_argCheck(L, "bodySetEnabled", 2, 2);
|
||||||
|
bodySetEnabled(_argBody(L, "bodySetEnabled", 1), _argBoolean(L, "bodySetEnabled", 2));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bodySetFriction(node, friction)
|
||||||
|
static int32_t apiBodySetFriction(lua_State *L) {
|
||||||
|
_argCheck(L, "bodySetFriction", 2, 2);
|
||||||
|
bodySetFriction(_argBody(L, "bodySetFriction", 1), (float)_argNumber(L, "bodySetFriction", 2));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bodySetMass(node, kilograms): dynamic bodies
|
||||||
|
static int32_t apiBodySetMass(lua_State *L) {
|
||||||
|
int32_t node;
|
||||||
|
|
||||||
|
_argCheck(L, "bodySetMass", 2, 2);
|
||||||
|
node = _argBody(L, "bodySetMass", 1);
|
||||||
|
if (!bodySetMass(node, (float)_argNumber(L, "bodySetMass", 2))) {
|
||||||
|
_luaDie(L, "bodySetMass", "Node %d is not a dynamic body, or the mass is not positive.", node);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bodySetVelocity(node, x, y, z)
|
||||||
|
static int32_t apiBodySetVelocity(lua_State *L) {
|
||||||
|
_argCheck(L, "bodySetVelocity", 4, 4);
|
||||||
|
bodySetVelocity(_argBody(L, "bodySetVelocity", 1), _argVec3(L, "bodySetVelocity", 2));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Any node can be the camera (it looks down its own -Z); -1 restores the default view.
|
// Any node can be the camera (it looks down its own -Z); -1 restores the default view.
|
||||||
static int32_t apiCameraSet(lua_State *L) {
|
static int32_t apiCameraSet(lua_State *L) {
|
||||||
int32_t node;
|
int32_t node;
|
||||||
|
|
@ -4426,6 +4628,22 @@ static int32_t apiOverlaySetResolution(lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// physicsSetEnabled(bool): pauses the simulation without losing it
|
||||||
|
static int32_t apiPhysicsSetEnabled(lua_State *L) {
|
||||||
|
_argCheck(L, "physicsSetEnabled", 1, 1);
|
||||||
|
physicsSetEnabled(_argBoolean(L, "physicsSetEnabled", 1));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// physicsSetGravity(x, y, z): default 0, -9.81, 0
|
||||||
|
static int32_t apiPhysicsSetGravity(lua_State *L) {
|
||||||
|
_argCheck(L, "physicsSetGravity", 3, 3);
|
||||||
|
physicsSetGravity(_argVec3(L, "physicsSetGravity", 1));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// scriptExecute(config) Runs another script after this one ends.
|
// scriptExecute(config) Runs another script after this one ends.
|
||||||
// Turns the 3D layer on or off.
|
// Turns the 3D layer on or off.
|
||||||
static int32_t apiSceneEnable(lua_State *L) {
|
static int32_t apiSceneEnable(lua_State *L) {
|
||||||
|
|
@ -5852,6 +6070,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, SDL_GPUDevice *device, Co
|
||||||
_global.window = window;
|
_global.window = window;
|
||||||
_global.renderer = renderer;
|
_global.renderer = renderer;
|
||||||
sceneInit(device, renderer);
|
sceneInit(device, renderer);
|
||||||
|
physicsInit();
|
||||||
|
|
||||||
// Local copy of config
|
// Local copy of config
|
||||||
_global.conf = cloneConf(conf);
|
_global.conf = cloneConf(conf);
|
||||||
|
|
@ -5942,6 +6161,19 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, SDL_GPUDevice *device, Co
|
||||||
lua_register(_global.luaContext, "animationResume", apiAnimationResume); // 3.00
|
lua_register(_global.luaContext, "animationResume", apiAnimationResume); // 3.00
|
||||||
lua_register(_global.luaContext, "animationSetTime", apiAnimationSetTime); // 3.00
|
lua_register(_global.luaContext, "animationSetTime", apiAnimationSetTime); // 3.00
|
||||||
lua_register(_global.luaContext, "animationStop", apiAnimationStop); // 3.00
|
lua_register(_global.luaContext, "animationStop", apiAnimationStop); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodyApplyForce", apiBodyApplyForce); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodyApplyImpulse", apiBodyApplyImpulse); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodyDelete", apiBodyDelete); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodyGetAngularVelocity", apiBodyGetAngularVelocity); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodyGetVelocity", apiBodyGetVelocity); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodyIsResting", apiBodyIsResting); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodyNew", apiBodyNew); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodySetAngularVelocity", apiBodySetAngularVelocity); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodySetBounce", apiBodySetBounce); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodySetEnabled", apiBodySetEnabled); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodySetFriction", apiBodySetFriction); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodySetMass", apiBodySetMass); // 3.00
|
||||||
|
lua_register(_global.luaContext, "bodySetVelocity", apiBodySetVelocity); // 3.00
|
||||||
lua_register(_global.luaContext, "colorBackground", apiColorBackground); // 1.xx
|
lua_register(_global.luaContext, "colorBackground", apiColorBackground); // 1.xx
|
||||||
lua_register(_global.luaContext, "colorForeground", apiColorForeground); // 1.xx
|
lua_register(_global.luaContext, "colorForeground", apiColorForeground); // 1.xx
|
||||||
|
|
||||||
|
|
@ -6059,6 +6291,8 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, SDL_GPUDevice *device, Co
|
||||||
lua_register(_global.luaContext, "overlayPrint", apiOverlayPrint); // 1.xx
|
lua_register(_global.luaContext, "overlayPrint", apiOverlayPrint); // 1.xx
|
||||||
lua_register(_global.luaContext, "overlaySetResolution", apiOverlaySetResolution); // 2.00
|
lua_register(_global.luaContext, "overlaySetResolution", apiOverlaySetResolution); // 2.00
|
||||||
|
|
||||||
|
lua_register(_global.luaContext, "physicsSetEnabled", apiPhysicsSetEnabled); // 3.00
|
||||||
|
lua_register(_global.luaContext, "physicsSetGravity", apiPhysicsSetGravity); // 3.00
|
||||||
lua_register(_global.luaContext, "sceneEnable", apiSceneEnable); // 3.00
|
lua_register(_global.luaContext, "sceneEnable", apiSceneEnable); // 3.00
|
||||||
lua_register(_global.luaContext, "sceneGetSize", apiSceneGetSize); // 3.00
|
lua_register(_global.luaContext, "sceneGetSize", apiSceneGetSize); // 3.00
|
||||||
lua_register(_global.luaContext, "sceneProject", apiSceneProject); // 3.00
|
lua_register(_global.luaContext, "sceneProject", apiSceneProject); // 3.00
|
||||||
|
|
@ -6614,6 +6848,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, SDL_GPUDevice *device, Co
|
||||||
}
|
}
|
||||||
// 3D scene
|
// 3D scene
|
||||||
modelUpdate(!_global.frozen);
|
modelUpdate(!_global.frozen);
|
||||||
|
physicsUpdate(!_global.frozen);
|
||||||
sceneUpdateVideo(_sceneVideoSource);
|
sceneUpdateVideo(_sceneVideoSource);
|
||||||
sceneTexture = sceneRender();
|
sceneTexture = sceneRender();
|
||||||
if (sceneTexture != NULL) {
|
if (sceneTexture != NULL) {
|
||||||
|
|
@ -6662,6 +6897,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, SDL_GPUDevice *device, Co
|
||||||
_progTrace("Destroying overlay");
|
_progTrace("Destroying overlay");
|
||||||
SDL_DestroyTexture(_global.pauseTexture);
|
SDL_DestroyTexture(_global.pauseTexture);
|
||||||
modelQuit();
|
modelQuit();
|
||||||
|
physicsQuit();
|
||||||
sceneQuit();
|
sceneQuit();
|
||||||
SDL_DestroyTexture(_global.overlayTexture);
|
SDL_DestroyTexture(_global.overlayTexture);
|
||||||
SDL_DestroySurface(_global.overlay);
|
SDL_DestroySurface(_global.overlay);
|
||||||
|
|
|
||||||
BIN
testScripts/Box.png
(Stored with Git LFS)
Executable file
BIN
testScripts/Box.png
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
testScripts/Models/BoxAnimated.glb
(Stored with Git LFS)
Normal file
BIN
testScripts/Models/BoxAnimated.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
testScripts/Models/Duck.glb
(Stored with Git LFS)
Normal file
BIN
testScripts/Models/Duck.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
testScripts/Models/Fox.glb
(Stored with Git LFS)
Normal file
BIN
testScripts/Models/Fox.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
26
testScripts/README.md
Normal file
26
testScripts/README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Test scripts
|
||||||
|
|
||||||
|
The Lua scripts used to verify the 3D scene and physics work, one per stage
|
||||||
|
of PLAN.md sections 18 and 19, with everything they load: the Khronos
|
||||||
|
Duck, Fox and BoxAnimated sample models under Models/, and Box.png. The
|
||||||
|
disc and the font are the engine's own, addressed as
|
||||||
|
Singe/menuBackground.mkv and Singe/FreeSansBold.ttf like any engine
|
||||||
|
asset. This directory is a complete Singe game directory. Run one from the singe directory with
|
||||||
|
|
||||||
|
.builddir/Singe-v3.00-Linux-x86_64 -w -d data -v Singe/menuBackground.mkv testScripts/scene6.singe
|
||||||
|
|
||||||
|
or pack it (singe --pack testScripts testScripts.game) and run an entry
|
||||||
|
with --entry N (the numbers follow games.dat). Every script takes a
|
||||||
|
screenshot or two and quits by itself; the results are in screenshots/.
|
||||||
|
|
||||||
|
| Script | Entry | Stage | What it shows |
|
||||||
|
|-----------------|-------|-------|------------------------------------------------------------|
|
||||||
|
| scenetest.singe | 1 | 18.1 | The 3D layer: a translucent colour over the disc, text on top |
|
||||||
|
| scene2.singe | 2 | 18.2 | Primitives, materials, lights, camera, sceneProject label |
|
||||||
|
| scene3.singe | 3 | 18.3 | glTF models: two Ducks, BoxAnimated, loose and packed |
|
||||||
|
| scene4.singe | 4 | 18.4 | Node animation at three speeds, one paused |
|
||||||
|
| scene5.singe | 5 | 18.5 | Skinning: three Foxes in Survey, Walk and Run |
|
||||||
|
| scene6.singe | 6 | 18.6 | The disc and a loaded video as materials, sceneUnproject |
|
||||||
|
| scene7.singe | 7 | 18.6b | Shadows: sun, sun and spot together, a point light inside |
|
||||||
|
| scene8.singe | 8 | 18 | An arcade cabinet from primitives with the disc on screen |
|
||||||
|
| scene9.singe | 9 | 19.2 | Physics: a crate stack, a ball, a kinematic paddle |
|
||||||
101
testScripts/games.dat
Normal file
101
testScripts/games.dat
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
GAMES = {
|
||||||
|
{
|
||||||
|
TITLE = "Scene1",
|
||||||
|
SCRIPT = "testScripts/scenetest.singe",
|
||||||
|
VIDEO = "Singe/menuBackground.mkv",
|
||||||
|
DESCRIPTION = "Stage 1 of the 3D scene: a translucent layer over the disc.",
|
||||||
|
YEAR = 2026,
|
||||||
|
GENRE = "Test",
|
||||||
|
PLATFORM = "Singe",
|
||||||
|
DEVELOPER = "Test",
|
||||||
|
PUBLISHER = "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TITLE = "Scene2",
|
||||||
|
SCRIPT = "testScripts/scene2.singe",
|
||||||
|
VIDEO = "Singe/menuBackground.mkv",
|
||||||
|
DESCRIPTION = "Stage 2: primitives, materials, lights.",
|
||||||
|
YEAR = 2026,
|
||||||
|
GENRE = "Test",
|
||||||
|
PLATFORM = "Singe",
|
||||||
|
DEVELOPER = "Test",
|
||||||
|
PUBLISHER = "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TITLE = "Scene3",
|
||||||
|
SCRIPT = "testScripts/scene3.singe",
|
||||||
|
VIDEO = "Singe/menuBackground.mkv",
|
||||||
|
DESCRIPTION = "Stage 3: glTF models.",
|
||||||
|
YEAR = 2026,
|
||||||
|
GENRE = "Test",
|
||||||
|
PLATFORM = "Singe",
|
||||||
|
DEVELOPER = "Test",
|
||||||
|
PUBLISHER = "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TITLE = "Scene4",
|
||||||
|
SCRIPT = "testScripts/scene4.singe",
|
||||||
|
VIDEO = "Singe/menuBackground.mkv",
|
||||||
|
DESCRIPTION = "Stage 4: node animation.",
|
||||||
|
YEAR = 2026,
|
||||||
|
GENRE = "Test",
|
||||||
|
PLATFORM = "Singe",
|
||||||
|
DEVELOPER = "Test",
|
||||||
|
PUBLISHER = "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TITLE = "Scene5",
|
||||||
|
SCRIPT = "testScripts/scene5.singe",
|
||||||
|
VIDEO = "Singe/menuBackground.mkv",
|
||||||
|
DESCRIPTION = "Stage 5: skinning.",
|
||||||
|
YEAR = 2026,
|
||||||
|
GENRE = "Test",
|
||||||
|
PLATFORM = "Singe",
|
||||||
|
DEVELOPER = "Test",
|
||||||
|
PUBLISHER = "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TITLE = "Scene6",
|
||||||
|
SCRIPT = "testScripts/scene6.singe",
|
||||||
|
VIDEO = "Singe/menuBackground.mkv",
|
||||||
|
DESCRIPTION = "Stage 6: video materials, unproject, antialiasing.",
|
||||||
|
YEAR = 2026,
|
||||||
|
GENRE = "Test",
|
||||||
|
PLATFORM = "Singe",
|
||||||
|
DEVELOPER = "Test",
|
||||||
|
PUBLISHER = "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TITLE = "Scene7",
|
||||||
|
SCRIPT = "testScripts/scene7.singe",
|
||||||
|
VIDEO = "Singe/menuBackground.mkv",
|
||||||
|
DESCRIPTION = "Stage 6b: shadows.",
|
||||||
|
YEAR = 2026,
|
||||||
|
GENRE = "Test",
|
||||||
|
PLATFORM = "Singe",
|
||||||
|
DEVELOPER = "Test",
|
||||||
|
PUBLISHER = "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TITLE = "Cabinet",
|
||||||
|
SCRIPT = "testScripts/scene8.singe",
|
||||||
|
VIDEO = "Singe/menuBackground.mkv",
|
||||||
|
DESCRIPTION = "An arcade cabinet built from primitives, the disc on its screen.",
|
||||||
|
YEAR = 2026,
|
||||||
|
GENRE = "Test",
|
||||||
|
PLATFORM = "Singe",
|
||||||
|
DEVELOPER = "Test",
|
||||||
|
PUBLISHER = "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TITLE = "Physics",
|
||||||
|
SCRIPT = "testScripts/scene9.singe",
|
||||||
|
VIDEO = "Singe/menuBackground.mkv",
|
||||||
|
DESCRIPTION = "A crate stack, a ball and a kinematic paddle on Jolt Physics.",
|
||||||
|
YEAR = 2026,
|
||||||
|
GENRE = "Test",
|
||||||
|
PLATFORM = "Singe",
|
||||||
|
DEVELOPER = "Test",
|
||||||
|
PUBLISHER = "Test",
|
||||||
|
},
|
||||||
|
}
|
||||||
82
testScripts/scene2.singe
Normal file
82
testScripts/scene2.singe
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
-- Stage 2 test: a lit spinning cube, a textured plane and a glass sphere over the disc, with
|
||||||
|
-- overlay text pinned to the cube through sceneProject.
|
||||||
|
local font = fontLoad("Singe/FreeSansBold.ttf", 28)
|
||||||
|
local boxArt = spriteLoad("testScripts/Box.png")
|
||||||
|
local frames = 0
|
||||||
|
|
||||||
|
fontSelect(font)
|
||||||
|
discPlay()
|
||||||
|
sceneEnable(true)
|
||||||
|
sceneSetBackground(0, 0, 0, 0)
|
||||||
|
sceneSetAmbient(40, 40, 60)
|
||||||
|
|
||||||
|
local red = materialNew()
|
||||||
|
materialSetColor(red, 220, 40, 40)
|
||||||
|
materialSetRoughness(red, 0.3)
|
||||||
|
materialSetMetallic(red, 0.2)
|
||||||
|
|
||||||
|
local tiled = materialNew()
|
||||||
|
materialSetTexture(tiled, boxArt)
|
||||||
|
|
||||||
|
local glass = materialNew()
|
||||||
|
materialSetColor(glass, 80, 160, 255, 110)
|
||||||
|
materialSetBlend(glass, true)
|
||||||
|
materialSetRoughness(glass, 0.1)
|
||||||
|
|
||||||
|
local gold = materialNew()
|
||||||
|
materialSetColor(gold, 255, 200, 60)
|
||||||
|
materialSetMetallic(gold, 1.0)
|
||||||
|
materialSetRoughness(gold, 0.35)
|
||||||
|
|
||||||
|
local cube = nodeNew()
|
||||||
|
nodeSetMesh(cube, meshBox(1.2, 1.2, 1.2), red)
|
||||||
|
nodeSetPosition(cube, -1.6, 0.3, 0)
|
||||||
|
|
||||||
|
local floor = nodeNew()
|
||||||
|
nodeSetMesh(floor, meshPlane(6, 4), tiled)
|
||||||
|
nodeSetPosition(floor, 0, -1, -1)
|
||||||
|
|
||||||
|
local ball = nodeNew()
|
||||||
|
nodeSetMesh(ball, meshSphere(0.7, 32), glass)
|
||||||
|
nodeSetPosition(ball, 1.4, 0.2, 0.5)
|
||||||
|
|
||||||
|
local ring = nodeNew()
|
||||||
|
nodeSetMesh(ring, meshTorus(0.8, 0.2, 40), gold)
|
||||||
|
nodeSetPosition(ring, 1.4, 0.2, 0.5)
|
||||||
|
|
||||||
|
local cone = nodeNew()
|
||||||
|
nodeSetMesh(cone, meshCone(0.4, 0.9, 24), gold)
|
||||||
|
nodeSetPosition(cone, 0, -0.55, 1.2)
|
||||||
|
|
||||||
|
local sun = lightNew(LIGHT_DIRECTIONAL)
|
||||||
|
nodeSetPosition(sun, 2, 4, 3)
|
||||||
|
nodeLookAt(sun, 0, 0, 0)
|
||||||
|
lightSetIntensity(sun, 1.2)
|
||||||
|
|
||||||
|
local lamp = lightNew(LIGHT_POINT)
|
||||||
|
nodeSetPosition(lamp, -2, 1.5, 2)
|
||||||
|
lightSetColor(lamp, 120, 200, 255)
|
||||||
|
lightSetIntensity(lamp, 6)
|
||||||
|
lightSetRange(lamp, 8)
|
||||||
|
|
||||||
|
local camera = nodeNew()
|
||||||
|
nodeSetPosition(camera, 0, 1.6, 5.5)
|
||||||
|
nodeLookAt(camera, 0, 0, 0)
|
||||||
|
cameraSet(camera)
|
||||||
|
cameraSetPerspective(55, 0.1, 100)
|
||||||
|
|
||||||
|
function onOverlayUpdate()
|
||||||
|
frames = frames + 1
|
||||||
|
nodeRotate(cube, 0.8, 1.7, 0)
|
||||||
|
nodeRotate(ring, 0, 2.0, 1.0)
|
||||||
|
overlayClear()
|
||||||
|
local x, y, depth, inFront = sceneProject(nodeGetWorldPosition(cube))
|
||||||
|
fontPrint(x - 40, y - 70, "cube")
|
||||||
|
fontPrint(20, 20, "Stage 2, frame " .. frames)
|
||||||
|
if frames == 90 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 120 then
|
||||||
|
singeQuit()
|
||||||
|
end
|
||||||
|
end
|
||||||
53
testScripts/scene3.singe
Normal file
53
testScripts/scene3.singe
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
-- Stage 3 test: glTF models. The Khronos Duck (textured) twice at different scales, and
|
||||||
|
-- BoxAnimated (two meshes, one nested), over the disc.
|
||||||
|
local font = fontLoad("Singe/FreeSansBold.ttf", 28)
|
||||||
|
local frames = 0
|
||||||
|
|
||||||
|
fontSelect(font)
|
||||||
|
discPlay()
|
||||||
|
sceneEnable(true)
|
||||||
|
sceneSetBackground(0, 0, 0, 0)
|
||||||
|
sceneSetAmbient(60, 60, 70)
|
||||||
|
|
||||||
|
local duckModel = modelLoad("testScripts/Models/Duck.glb")
|
||||||
|
local boxModel = modelLoad("testScripts/Models/BoxAnimated.glb")
|
||||||
|
|
||||||
|
local duck = modelInstance(duckModel)
|
||||||
|
nodeSetPosition(duck, -1.5, -1, 0)
|
||||||
|
nodeSetScale(duck, 1.0)
|
||||||
|
|
||||||
|
local duck2 = modelInstance(duckModel)
|
||||||
|
nodeSetPosition(duck2, 1.8, -1, -0.5)
|
||||||
|
nodeSetScale(duck2, 0.6)
|
||||||
|
nodeSetRotation(duck2, 0, -60, 0)
|
||||||
|
|
||||||
|
local boxes = modelInstance(boxModel)
|
||||||
|
nodeSetPosition(boxes, 0.2, -1, 1.2)
|
||||||
|
nodeSetScale(boxes, 0.4)
|
||||||
|
|
||||||
|
local sun = lightNew(LIGHT_DIRECTIONAL)
|
||||||
|
nodeSetPosition(sun, 2, 4, 3)
|
||||||
|
nodeLookAt(sun, 0, 0, 0)
|
||||||
|
lightSetIntensity(sun, 1.5)
|
||||||
|
|
||||||
|
local camera = nodeNew()
|
||||||
|
nodeSetPosition(camera, 0, 1.2, 5)
|
||||||
|
nodeLookAt(camera, 0, 0, 0)
|
||||||
|
cameraSet(camera)
|
||||||
|
|
||||||
|
debugPrint("duck children " .. #nodeGetChildren(duck) .. " boxes children " .. #nodeGetChildren(boxes) .. " box anims " .. #modelGetAnimations(boxModel))
|
||||||
|
local inner = nodeFind("inner_box", boxes)
|
||||||
|
debugPrint("inner_box " .. tostring(inner))
|
||||||
|
|
||||||
|
function onOverlayUpdate()
|
||||||
|
frames = frames + 1
|
||||||
|
nodeRotate(duck, 0, 1.5, 0)
|
||||||
|
overlayClear()
|
||||||
|
fontPrint(20, 20, "Stage 3, frame " .. frames)
|
||||||
|
if frames == 60 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 80 then
|
||||||
|
singeQuit()
|
||||||
|
end
|
||||||
|
end
|
||||||
53
testScripts/scene4.singe
Normal file
53
testScripts/scene4.singe
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
-- Stage 4 test: node animation. BoxAnimated's lid rotates and the inner box rises; two
|
||||||
|
-- instances at different speeds, a third paused at a fixed time.
|
||||||
|
local font = fontLoad("Singe/FreeSansBold.ttf", 28)
|
||||||
|
local frames = 0
|
||||||
|
|
||||||
|
fontSelect(font)
|
||||||
|
discPlay()
|
||||||
|
sceneEnable(true)
|
||||||
|
sceneSetBackground(0, 0, 0, 0)
|
||||||
|
sceneSetAmbient(60, 60, 70)
|
||||||
|
|
||||||
|
local boxModel = modelLoad("testScripts/Models/BoxAnimated.glb")
|
||||||
|
local names = modelGetAnimations(boxModel)
|
||||||
|
debugPrint("animations " .. #names .. " first '" .. tostring(names[1]) .. "'")
|
||||||
|
|
||||||
|
local a = modelInstance(boxModel)
|
||||||
|
nodeSetPosition(a, -2.2, -1, 0)
|
||||||
|
nodeSetScale(a, 0.5)
|
||||||
|
animationPlay(a, 1, true)
|
||||||
|
|
||||||
|
local b = modelInstance(boxModel)
|
||||||
|
nodeSetPosition(b, 0.2, -1, 0)
|
||||||
|
nodeSetScale(b, 0.5)
|
||||||
|
animationPlay(b, 1, true, 3.0)
|
||||||
|
|
||||||
|
local c = modelInstance(boxModel)
|
||||||
|
nodeSetPosition(c, 2.6, -1, 0)
|
||||||
|
nodeSetScale(c, 0.5)
|
||||||
|
animationPlay(c, 1, false)
|
||||||
|
animationSetTime(c, 1.5)
|
||||||
|
animationPause(c)
|
||||||
|
|
||||||
|
local sun = lightNew(LIGHT_DIRECTIONAL)
|
||||||
|
nodeSetPosition(sun, 2, 4, 3)
|
||||||
|
nodeLookAt(sun, 0, 0, 0)
|
||||||
|
lightSetIntensity(sun, 1.5)
|
||||||
|
|
||||||
|
local camera = nodeNew()
|
||||||
|
nodeSetPosition(camera, 0, 1.5, 6)
|
||||||
|
nodeLookAt(camera, 0, 0, 0)
|
||||||
|
cameraSet(camera)
|
||||||
|
|
||||||
|
function onOverlayUpdate()
|
||||||
|
frames = frames + 1
|
||||||
|
overlayClear()
|
||||||
|
fontPrint(20, 20, string.format("Stage 4, frame %d a=%.2f b=%.2f c=%.2f playing=%s", frames, animationGetTime(a), animationGetTime(b), animationGetTime(c), tostring(animationIsPlaying(c))))
|
||||||
|
if frames == 30 or frames == 75 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 90 then
|
||||||
|
singeQuit()
|
||||||
|
end
|
||||||
|
end
|
||||||
45
testScripts/scene5.singe
Normal file
45
testScripts/scene5.singe
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
-- Stage 5 test: skinning. Three Foxes: Survey, Walk and Run.
|
||||||
|
local font = fontLoad("Singe/FreeSansBold.ttf", 28)
|
||||||
|
local frames = 0
|
||||||
|
|
||||||
|
fontSelect(font)
|
||||||
|
discPlay()
|
||||||
|
sceneEnable(true)
|
||||||
|
sceneSetBackground(0, 0, 0, 0)
|
||||||
|
sceneSetAmbient(70, 70, 80)
|
||||||
|
|
||||||
|
local foxModel = modelLoad("testScripts/Models/Fox.glb")
|
||||||
|
local names = modelGetAnimations(foxModel)
|
||||||
|
debugPrint("fox animations: " .. table.concat(names, ", "))
|
||||||
|
|
||||||
|
local foxes = {}
|
||||||
|
for i, name in ipairs(names) do
|
||||||
|
local fox = modelInstance(foxModel)
|
||||||
|
nodeSetPosition(fox, (i - 2) * 2.4, -1, 0)
|
||||||
|
nodeSetScale(fox, 0.018)
|
||||||
|
nodeSetRotation(fox, 0, -30, 0)
|
||||||
|
animationPlay(fox, name, true)
|
||||||
|
foxes[i] = fox
|
||||||
|
end
|
||||||
|
|
||||||
|
local sun = lightNew(LIGHT_DIRECTIONAL)
|
||||||
|
nodeSetPosition(sun, 2, 4, 3)
|
||||||
|
nodeLookAt(sun, 0, 0, 0)
|
||||||
|
lightSetIntensity(sun, 1.4)
|
||||||
|
|
||||||
|
local camera = nodeNew()
|
||||||
|
nodeSetPosition(camera, 0, 1.2, 6)
|
||||||
|
nodeLookAt(camera, 0, 0, 0)
|
||||||
|
cameraSet(camera)
|
||||||
|
|
||||||
|
function onOverlayUpdate()
|
||||||
|
frames = frames + 1
|
||||||
|
overlayClear()
|
||||||
|
fontPrint(20, 20, "Stage 5, frame " .. frames)
|
||||||
|
if frames == 40 or frames == 70 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 80 then
|
||||||
|
singeQuit()
|
||||||
|
end
|
||||||
|
end
|
||||||
75
testScripts/scene6.singe
Normal file
75
testScripts/scene6.singe
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
-- Stage 6 test: the disc as a material on a tilted screen, a loaded video on a sphere, a small
|
||||||
|
-- marker placed with sceneUnproject at the overlay's centre, and antialiasing.
|
||||||
|
local font = fontLoad("Singe/FreeSansBold.ttf", 28)
|
||||||
|
local clip = videoLoad("Singe/menuBackground.mkv")
|
||||||
|
local frames = 0
|
||||||
|
|
||||||
|
fontSelect(font)
|
||||||
|
discPlay()
|
||||||
|
videoPlay(clip)
|
||||||
|
sceneEnable(true)
|
||||||
|
sceneSetBackground(20, 20, 40, 255)
|
||||||
|
sceneSetAmbient(80, 80, 90)
|
||||||
|
|
||||||
|
local screen = materialNew()
|
||||||
|
materialSetVideo(screen)
|
||||||
|
materialSetUnlit(screen, true)
|
||||||
|
|
||||||
|
local ballSkin = materialNew()
|
||||||
|
materialSetVideo(ballSkin, clip)
|
||||||
|
|
||||||
|
local cabinet = materialNew()
|
||||||
|
materialSetColor(cabinet, 60, 60, 70)
|
||||||
|
materialSetRoughness(cabinet, 0.6)
|
||||||
|
|
||||||
|
local marker = materialNew()
|
||||||
|
materialSetColor(marker, 255, 60, 60)
|
||||||
|
materialSetEmissive(marker, 120, 0, 0)
|
||||||
|
|
||||||
|
local body = nodeNew()
|
||||||
|
nodeSetMesh(body, meshBox(3.2, 2.4, 0.4), cabinet)
|
||||||
|
nodeSetPosition(body, -0.6, 0.2, -0.3)
|
||||||
|
nodeSetRotation(body, 0, 25, 0)
|
||||||
|
|
||||||
|
local tv = nodeNew(body)
|
||||||
|
nodeSetMesh(tv, meshPlane(3.0, 2.2), screen)
|
||||||
|
nodeSetPosition(tv, 0, 0, 0.21)
|
||||||
|
nodeSetRotation(tv, 90, 0, 0)
|
||||||
|
|
||||||
|
local ball = nodeNew()
|
||||||
|
nodeSetMesh(ball, meshSphere(0.7, 48), ballSkin)
|
||||||
|
nodeSetPosition(ball, 2.0, -0.2, 0.8)
|
||||||
|
|
||||||
|
local width, height = sceneGetSize()
|
||||||
|
local cx, cy, cz = sceneUnproject(width / 2, height / 2, 4.0)
|
||||||
|
local dot = nodeNew()
|
||||||
|
nodeSetMesh(dot, meshSphere(0.08, 16), marker)
|
||||||
|
|
||||||
|
local sun = lightNew(LIGHT_DIRECTIONAL)
|
||||||
|
nodeSetPosition(sun, 2, 4, 3)
|
||||||
|
nodeLookAt(sun, 0, 0, 0)
|
||||||
|
lightSetIntensity(sun, 1.3)
|
||||||
|
|
||||||
|
local camera = nodeNew()
|
||||||
|
nodeSetPosition(camera, 0, 0.8, 5.5)
|
||||||
|
nodeLookAt(camera, 0, 0, 0)
|
||||||
|
cameraSet(camera)
|
||||||
|
|
||||||
|
function onOverlayUpdate()
|
||||||
|
frames = frames + 1
|
||||||
|
nodeRotate(ball, 0, 1.2, 0)
|
||||||
|
if frames == 2 then
|
||||||
|
-- After the first render the camera matrices exist; place the marker 4 units in at the centre.
|
||||||
|
cx, cy, cz = sceneUnproject(width / 2, height / 2, 4.0)
|
||||||
|
nodeSetPosition(dot, cx, cy, cz)
|
||||||
|
debugPrint(string.format("unproject centre -> %.2f %.2f %.2f", cx, cy, cz))
|
||||||
|
end
|
||||||
|
overlayClear()
|
||||||
|
fontPrint(20, 20, "Stage 6, frame " .. frames)
|
||||||
|
if frames == 80 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 100 then
|
||||||
|
singeQuit()
|
||||||
|
end
|
||||||
|
end
|
||||||
96
testScripts/scene7.singe
Normal file
96
testScripts/scene7.singe
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
-- Stage 6b test: shadows. A floor, a cube, a duck and a walking fox under a directional sun
|
||||||
|
-- (first screenshot), then the same scene lit by a spot light overhead (second screenshot).
|
||||||
|
local font = fontLoad("Singe/FreeSansBold.ttf", 28)
|
||||||
|
local frames = 0
|
||||||
|
|
||||||
|
fontSelect(font)
|
||||||
|
discPlay()
|
||||||
|
sceneEnable(true)
|
||||||
|
sceneSetBackground(30, 30, 50, 255)
|
||||||
|
sceneSetAmbient(50, 50, 60)
|
||||||
|
|
||||||
|
local grey = materialNew()
|
||||||
|
materialSetColor(grey, 200, 200, 200)
|
||||||
|
materialSetRoughness(grey, 0.9)
|
||||||
|
|
||||||
|
local red = materialNew()
|
||||||
|
materialSetColor(red, 220, 50, 50)
|
||||||
|
|
||||||
|
local floor = nodeNew()
|
||||||
|
nodeSetMesh(floor, meshPlane(10, 10), grey)
|
||||||
|
nodeSetPosition(floor, 0, -1, 0)
|
||||||
|
|
||||||
|
local wall = nodeNew()
|
||||||
|
nodeSetMesh(wall, meshBox(10, 4, 0.2), grey)
|
||||||
|
nodeSetPosition(wall, 0, 1, -3)
|
||||||
|
|
||||||
|
local cube = nodeNew()
|
||||||
|
nodeSetMesh(cube, meshBox(1, 1, 1), red)
|
||||||
|
nodeSetPosition(cube, -2, -0.5, 0)
|
||||||
|
nodeSetRotation(cube, 0, 30, 0)
|
||||||
|
|
||||||
|
local duck = modelInstance(modelLoad("testScripts/Models/Duck.glb"))
|
||||||
|
nodeSetPosition(duck, 0.5, -1, 0.5)
|
||||||
|
nodeSetRotation(duck, 0, -40, 0)
|
||||||
|
|
||||||
|
local fox = modelInstance(modelLoad("testScripts/Models/Fox.glb"))
|
||||||
|
nodeSetPosition(fox, 2.5, -1, 0)
|
||||||
|
nodeSetScale(fox, 0.015)
|
||||||
|
nodeSetRotation(fox, 0, -110, 0)
|
||||||
|
animationPlay(fox, "Walk", true)
|
||||||
|
|
||||||
|
local sun = lightNew(LIGHT_DIRECTIONAL)
|
||||||
|
nodeSetPosition(sun, 3, 6, 4)
|
||||||
|
nodeLookAt(sun, 0, 0, 0)
|
||||||
|
lightSetIntensity(sun, 1.4)
|
||||||
|
lightSetShadow(sun, true)
|
||||||
|
|
||||||
|
local lamp = lightNew(LIGHT_SPOT)
|
||||||
|
nodeSetPosition(lamp, 0, 5, 1.5)
|
||||||
|
nodeLookAt(lamp, 0, -1, 0)
|
||||||
|
lightSetIntensity(lamp, 40)
|
||||||
|
lightSetCone(lamp, 30, 42)
|
||||||
|
nodeSetVisible(lamp, false)
|
||||||
|
|
||||||
|
local bulb = lightNew(LIGHT_POINT)
|
||||||
|
nodeSetPosition(bulb, 0.8, 0.4, 1.2)
|
||||||
|
lightSetColor(bulb, 255, 220, 180)
|
||||||
|
lightSetIntensity(bulb, 12)
|
||||||
|
lightSetRange(bulb, 12)
|
||||||
|
nodeSetVisible(bulb, false)
|
||||||
|
|
||||||
|
local camera = nodeNew()
|
||||||
|
nodeSetPosition(camera, 0, 2.5, 7)
|
||||||
|
nodeLookAt(camera, 0, -0.3, 0)
|
||||||
|
cameraSet(camera)
|
||||||
|
|
||||||
|
function onOverlayUpdate()
|
||||||
|
frames = frames + 1
|
||||||
|
overlayClear()
|
||||||
|
fontPrint(20, 20, "Shadows, frame " .. frames)
|
||||||
|
if frames == 40 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 45 then
|
||||||
|
-- Sun and spot casting at once.
|
||||||
|
nodeSetVisible(lamp, true)
|
||||||
|
lightSetShadow(lamp, true)
|
||||||
|
end
|
||||||
|
if frames == 80 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 85 then
|
||||||
|
-- A bulb inside the scene: cube-map shadows in every direction.
|
||||||
|
nodeSetVisible(sun, false)
|
||||||
|
nodeSetVisible(lamp, false)
|
||||||
|
nodeSetVisible(bulb, true)
|
||||||
|
lightSetShadow(bulb, true)
|
||||||
|
sceneSetAmbient(25, 25, 30)
|
||||||
|
end
|
||||||
|
if frames == 120 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 130 then
|
||||||
|
singeQuit()
|
||||||
|
end
|
||||||
|
end
|
||||||
93
testScripts/scene8.singe
Normal file
93
testScripts/scene8.singe
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
-- An arcade cabinet built from primitives, with the laserdisc playing on its screen.
|
||||||
|
local font = fontLoad("Singe/FreeSansBold.ttf", 28)
|
||||||
|
local frames = 0
|
||||||
|
|
||||||
|
fontSelect(font)
|
||||||
|
discPlay()
|
||||||
|
sceneEnable(true)
|
||||||
|
sceneSetBackground(18, 18, 30, 255)
|
||||||
|
sceneSetAmbient(70, 70, 85)
|
||||||
|
|
||||||
|
local black = materialNew()
|
||||||
|
materialSetColor(black, 25, 25, 28)
|
||||||
|
materialSetRoughness(black, 0.55)
|
||||||
|
|
||||||
|
local trim = materialNew()
|
||||||
|
materialSetColor(trim, 230, 60, 40)
|
||||||
|
materialSetRoughness(trim, 0.4)
|
||||||
|
|
||||||
|
local panelLook = materialNew()
|
||||||
|
materialSetColor(panelLook, 40, 40, 60)
|
||||||
|
materialSetRoughness(panelLook, 0.5)
|
||||||
|
|
||||||
|
local screen = materialNew()
|
||||||
|
materialSetVideo(screen)
|
||||||
|
materialSetUnlit(screen, true)
|
||||||
|
|
||||||
|
local marqueeLook = materialNew()
|
||||||
|
materialSetColor(marqueeLook, 255, 210, 80)
|
||||||
|
materialSetEmissive(marqueeLook, 120, 90, 20)
|
||||||
|
|
||||||
|
local floorLook = materialNew()
|
||||||
|
materialSetColor(floorLook, 120, 110, 100)
|
||||||
|
materialSetRoughness(floorLook, 0.9)
|
||||||
|
|
||||||
|
local floor = nodeNew()
|
||||||
|
nodeSetMesh(floor, meshPlane(12, 12), floorLook)
|
||||||
|
nodeSetPosition(floor, 0, -1.8, 0)
|
||||||
|
|
||||||
|
-- The cabinet: one parent node so the whole thing can be turned.
|
||||||
|
local cabinet = nodeNew()
|
||||||
|
nodeSetPosition(cabinet, 0, 0, 0)
|
||||||
|
nodeSetRotation(cabinet, 0, -28, 0)
|
||||||
|
|
||||||
|
local function part(parent, mesh, material, x, y, z, rx, ry, rz)
|
||||||
|
local n = nodeNew(parent)
|
||||||
|
nodeSetMesh(n, mesh, material)
|
||||||
|
nodeSetPosition(n, x, y, z)
|
||||||
|
nodeSetRotation(n, rx or 0, ry or 0, rz or 0)
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
|
||||||
|
part(cabinet, meshBox(1.6, 1.4, 1.3), black, 0, -1.1, 0) -- base
|
||||||
|
part(cabinet, meshBox(1.6, 1.9, 1.0), black, 0, 0.55, -0.15) -- body behind the screen
|
||||||
|
part(cabinet, meshBox(1.6, 0.5, 1.3), black, 0, 1.75, 0) -- head
|
||||||
|
part(cabinet, meshBox(1.5, 0.36, 0.05), marqueeLook, 0, 1.75, 0.66) -- marquee
|
||||||
|
part(cabinet, meshBox(1.6, 0.12, 0.7), panelLook, 0, -0.45, 0.55, -12) -- control panel
|
||||||
|
part(cabinet, meshCylinder(0.05, 0.28, 16), trim, -0.35, -0.28, 0.55, -12) -- joystick
|
||||||
|
part(cabinet, meshSphere(0.09, 16), trim, -0.35, -0.13, 0.58) -- joystick ball
|
||||||
|
part(cabinet, meshCylinder(0.07, 0.04, 16), trim, 0.2, -0.36, 0.5, -12) -- button
|
||||||
|
part(cabinet, meshCylinder(0.07, 0.04, 16), trim, 0.42, -0.36, 0.5, -12) -- button
|
||||||
|
part(cabinet, meshBox(0.06, 1.9, 0.08), trim, -0.77, 0.55, 0.38) -- side trim
|
||||||
|
part(cabinet, meshBox(0.06, 1.9, 0.08), trim, 0.77, 0.55, 0.38)
|
||||||
|
part(cabinet, meshBox(1.42, 1.1, 0.06), black, 0, 0.6, 0.34, -8) -- bezel
|
||||||
|
part(cabinet, meshPlane(1.3, 0.98), screen, 0, 0.6, 0.38, 82) -- the screen, tilted back a little
|
||||||
|
|
||||||
|
local sun = lightNew(LIGHT_DIRECTIONAL)
|
||||||
|
nodeSetPosition(sun, -3, 5, 4)
|
||||||
|
nodeLookAt(sun, 0, 0, 0)
|
||||||
|
lightSetIntensity(sun, 1.1)
|
||||||
|
lightSetShadow(sun, true)
|
||||||
|
|
||||||
|
local glow = lightNew(LIGHT_POINT)
|
||||||
|
nodeSetPosition(glow, 0.4, 0.9, 1.2)
|
||||||
|
lightSetColor(glow, 180, 200, 255)
|
||||||
|
lightSetIntensity(glow, 3)
|
||||||
|
lightSetRange(glow, 6)
|
||||||
|
|
||||||
|
local camera = nodeNew()
|
||||||
|
nodeSetPosition(camera, 1.6, 0.9, 4.6)
|
||||||
|
nodeLookAt(camera, 0, 0.2, 0)
|
||||||
|
cameraSet(camera)
|
||||||
|
cameraSetPerspective(45, 0.1, 100)
|
||||||
|
|
||||||
|
function onOverlayUpdate()
|
||||||
|
frames = frames + 1
|
||||||
|
overlayClear()
|
||||||
|
if frames == 22 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 30 then
|
||||||
|
singeQuit()
|
||||||
|
end
|
||||||
|
end
|
||||||
94
testScripts/scene9.singe
Normal file
94
testScripts/scene9.singe
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
-- Physics stage 2: a stack of crates and a ball dropped onto the floor in front of the cabinet,
|
||||||
|
-- then a kinematic paddle sweeps through the stack.
|
||||||
|
local font = fontLoad("Singe/FreeSansBold.ttf", 28)
|
||||||
|
local frames = 0
|
||||||
|
|
||||||
|
fontSelect(font)
|
||||||
|
discPlay()
|
||||||
|
sceneEnable(true)
|
||||||
|
sceneSetBackground(18, 18, 30, 255)
|
||||||
|
sceneSetAmbient(70, 70, 85)
|
||||||
|
|
||||||
|
local floorLook = materialNew()
|
||||||
|
materialSetColor(floorLook, 120, 110, 100)
|
||||||
|
materialSetRoughness(floorLook, 0.9)
|
||||||
|
local crateLook = materialNew()
|
||||||
|
materialSetColor(crateLook, 200, 140, 60)
|
||||||
|
materialSetRoughness(crateLook, 0.7)
|
||||||
|
local ballLook = materialNew()
|
||||||
|
materialSetColor(ballLook, 60, 140, 230)
|
||||||
|
materialSetRoughness(ballLook, 0.3)
|
||||||
|
local paddleLook = materialNew()
|
||||||
|
materialSetColor(paddleLook, 230, 60, 40)
|
||||||
|
local screen = materialNew()
|
||||||
|
materialSetVideo(screen)
|
||||||
|
materialSetUnlit(screen, true)
|
||||||
|
local black = materialNew()
|
||||||
|
materialSetColor(black, 25, 25, 28)
|
||||||
|
|
||||||
|
local floor = nodeNew()
|
||||||
|
nodeSetMesh(floor, meshBox(14, 0.2, 14), floorLook)
|
||||||
|
nodeSetPosition(floor, 0, -1.9, 0)
|
||||||
|
bodyNew(floor, BODY_STATIC, SHAPE_BOX, 14, 0.2, 14)
|
||||||
|
|
||||||
|
-- A slab of a cabinet at the back with the disc on it, as a static body.
|
||||||
|
local cabinet = nodeNew()
|
||||||
|
nodeSetMesh(cabinet, meshBox(2.2, 3.0, 0.8), black)
|
||||||
|
nodeSetPosition(cabinet, 0, -0.3, -2.5)
|
||||||
|
bodyNew(cabinet, BODY_STATIC, SHAPE_BOX, 2.2, 3.0, 0.8)
|
||||||
|
local tv = nodeNew(cabinet)
|
||||||
|
nodeSetMesh(tv, meshPlane(1.8, 1.35), screen)
|
||||||
|
nodeSetPosition(tv, 0, 0.5, 0.41)
|
||||||
|
nodeSetRotation(tv, 90, 0, 0)
|
||||||
|
|
||||||
|
local crates = {}
|
||||||
|
for i = 1, 6 do
|
||||||
|
local crate = nodeNew()
|
||||||
|
nodeSetMesh(crate, meshBox(0.6, 0.6, 0.6), crateLook)
|
||||||
|
nodeSetPosition(crate, -0.6 + (i % 2) * 0.05, -1.5 + (i - 1) * 0.62, 0.2)
|
||||||
|
nodeSetRotation(crate, 0, (i - 1) * 7, 0)
|
||||||
|
bodyNew(crate, BODY_DYNAMIC, SHAPE_BOX, 0.6, 0.6, 0.6)
|
||||||
|
bodySetFriction(crate, 0.6)
|
||||||
|
crates[i] = crate
|
||||||
|
end
|
||||||
|
|
||||||
|
local ball = nodeNew()
|
||||||
|
nodeSetMesh(ball, meshSphere(0.35, 32), ballLook)
|
||||||
|
nodeSetPosition(ball, 1.2, 3.0, 0.6)
|
||||||
|
bodyNew(ball, BODY_DYNAMIC, SHAPE_SPHERE, 0.35)
|
||||||
|
bodySetBounce(ball, 0.6)
|
||||||
|
bodySetMass(ball, 2)
|
||||||
|
|
||||||
|
local paddle = nodeNew()
|
||||||
|
nodeSetMesh(paddle, meshBox(0.3, 1.2, 1.6), paddleLook)
|
||||||
|
nodeSetPosition(paddle, -4.5, -1.2, 0.2)
|
||||||
|
bodyNew(paddle, BODY_KINEMATIC, SHAPE_BOX, 0.3, 1.2, 1.6)
|
||||||
|
|
||||||
|
local sun = lightNew(LIGHT_DIRECTIONAL)
|
||||||
|
nodeSetPosition(sun, -3, 6, 4)
|
||||||
|
nodeLookAt(sun, 0, 0, 0)
|
||||||
|
lightSetIntensity(sun, 1.2)
|
||||||
|
lightSetShadow(sun, true)
|
||||||
|
|
||||||
|
local camera = nodeNew()
|
||||||
|
nodeSetPosition(camera, 3.5, 1.8, 6.5)
|
||||||
|
nodeLookAt(camera, 0, -0.4, 0)
|
||||||
|
cameraSet(camera)
|
||||||
|
cameraSetPerspective(50, 0.1, 100)
|
||||||
|
|
||||||
|
function onOverlayUpdate()
|
||||||
|
frames = frames + 1
|
||||||
|
overlayClear()
|
||||||
|
local bx, by, bz = nodeGetWorldPosition(ball)
|
||||||
|
fontPrint(20, 20, string.format("Physics %d ball y=%.1f %s", frames, by, bodyIsResting(ball) and "resting" or "moving"))
|
||||||
|
if frames >= 70 and frames < 160 then
|
||||||
|
-- The paddle sweeps through the stack from the left.
|
||||||
|
nodeMove(paddle, 0.07, 0, 0)
|
||||||
|
end
|
||||||
|
if frames == 40 or frames == 120 or frames == 200 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 210 then
|
||||||
|
singeQuit()
|
||||||
|
end
|
||||||
|
end
|
||||||
25
testScripts/scenetest.singe
Normal file
25
testScripts/scenetest.singe
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
-- Stage 1 test: the 3D layer clears to translucent green over the playing disc; overlay text sits on top.
|
||||||
|
local font = fontLoad("Singe/FreeSansBold.ttf", 28)
|
||||||
|
local frames = 0
|
||||||
|
|
||||||
|
fontSelect(font)
|
||||||
|
discPlay()
|
||||||
|
|
||||||
|
function onOverlayUpdate()
|
||||||
|
frames = frames + 1
|
||||||
|
overlayClear()
|
||||||
|
fontPrint(20, 20, "Overlay text above the scene, frame " .. frames)
|
||||||
|
if frames == 30 then
|
||||||
|
sceneEnable(true)
|
||||||
|
sceneSetBackground(0, 255, 0, 96)
|
||||||
|
end
|
||||||
|
if frames == 20 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 90 then
|
||||||
|
singeScreenshot()
|
||||||
|
end
|
||||||
|
if frames == 150 then
|
||||||
|
singeQuit()
|
||||||
|
end
|
||||||
|
end
|
||||||
501
thirdparty/JoltPhysics/Build/CMakeLists.txt
vendored
Normal file
501
thirdparty/JoltPhysics/Build/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,501 @@
|
||||||
|
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
|
||||||
|
|
||||||
|
project(JoltPhysics VERSION 5.6.0 LANGUAGES CXX)
|
||||||
|
|
||||||
|
# When turning this option on, the library will be compiled using assertions. By default asserts are enabled in Debug build.
|
||||||
|
option(USE_ASSERTS "Enable asserts" OFF)
|
||||||
|
|
||||||
|
# When turning this option on, the library will be compiled using doubles for positions. This allows for much bigger worlds.
|
||||||
|
option(DOUBLE_PRECISION "Use double precision math" OFF)
|
||||||
|
|
||||||
|
# When turning this option on, the library will be compiled with debug symbols
|
||||||
|
option(GENERATE_DEBUG_SYMBOLS "Generate debug symbols" ON)
|
||||||
|
|
||||||
|
# Which type of debug symbols to generate, e.g. using source-map when compiling with emscripten makes compilation a lot faster
|
||||||
|
set(JPH_DEBUG_SYMBOL_FORMAT "" CACHE STRING "Which type of debug symbols to generate")
|
||||||
|
|
||||||
|
# When turning this option on, the library will override the default CMAKE_CXX_FLAGS_DEBUG/RELEASE values, otherwise they will use the platform defaults
|
||||||
|
option(OVERRIDE_CXX_FLAGS "Override CMAKE_CXX_FLAGS_DEBUG/RELEASE" ON)
|
||||||
|
|
||||||
|
# When turning this option on, the library will be compiled in such a way to attempt to keep the simulation deterministic across platforms
|
||||||
|
option(CROSS_PLATFORM_DETERMINISTIC "Cross platform deterministic" OFF)
|
||||||
|
|
||||||
|
# When turning this option on, the library will be compiled for ARM using the CROSS_COMPILE_ARM_TARGET architecture, requires compiling with clang
|
||||||
|
option(CROSS_COMPILE_ARM "Cross compile to the CROSS_COMPILE_ARM_TARGET architecture" OFF)
|
||||||
|
|
||||||
|
# When cross compiling to ARM this specifies which target to use. Can be 'aarch64-linux-gnu' for 64-bit or 'arm-linux-gnueabihf' for 32-bit
|
||||||
|
set(CROSS_COMPILE_ARM_TARGET "aarch64-linux-gnu" CACHE STRING "The target to use")
|
||||||
|
|
||||||
|
# When turning this option on, Jolt will be compiled as a shared library and public symbols will be exported.
|
||||||
|
option(JPH_BUILD_SHARED_LIBS "Compile Jolt as a shared library" ${BUILD_SHARED_LIBS})
|
||||||
|
|
||||||
|
# When turning this option on, the library will be compiled with interprocedural optimizations enabled, also known as link-time optimizations or link-time code generation.
|
||||||
|
# Note that if you turn this on you need to use SET_INTERPROCEDURAL_OPTIMIZATION() or set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) to enable LTO specifically for your own project as well.
|
||||||
|
# If you don't do this you may get an error: /usr/bin/ld: libJolt.a: error adding symbols: file format not recognized
|
||||||
|
option(INTERPROCEDURAL_OPTIMIZATION "Enable interprocedural optimizations" ON)
|
||||||
|
|
||||||
|
# When turning this on, in Debug and Release mode, the library will emit extra code to ensure that the 4th component of a 3-vector is kept the same as the 3rd component
|
||||||
|
# and will enable floating point exceptions during simulation to detect divisions by zero.
|
||||||
|
# Note that this currently only works using MSVC. Clang turns Float2 into a SIMD vector sometimes causing floating point exceptions (the option is ignored).
|
||||||
|
option(FLOATING_POINT_EXCEPTIONS_ENABLED "Enable floating point exceptions" ON)
|
||||||
|
|
||||||
|
# When turning this on, the library will be compiled with C++ exceptions enabled.
|
||||||
|
# This adds some overhead and Jolt doesn't use exceptions so by default it is off.
|
||||||
|
option(CPP_EXCEPTIONS_ENABLED "Enable C++ exceptions" OFF)
|
||||||
|
|
||||||
|
# When turning this on, the library will be compiled with C++ RTTI enabled.
|
||||||
|
# This adds some overhead and Jolt doesn't use RTTI so by default it is off.
|
||||||
|
option(CPP_RTTI_ENABLED "Enable C++ RTTI" OFF)
|
||||||
|
|
||||||
|
# Number of bits to use in ObjectLayer. Can be 16 or 32.
|
||||||
|
option(OBJECT_LAYER_BITS "Number of bits in ObjectLayer" 16)
|
||||||
|
|
||||||
|
# Select X86 processor features to use (if everything is off it will be SSE2 compatible)
|
||||||
|
option(USE_SSE4_1 "Enable SSE4.1" ON)
|
||||||
|
option(USE_SSE4_2 "Enable SSE4.2" ON)
|
||||||
|
option(USE_AVX "Enable AVX" ON)
|
||||||
|
option(USE_AVX2 "Enable AVX2" ON)
|
||||||
|
option(USE_AVX512 "Enable AVX512" OFF)
|
||||||
|
option(USE_LZCNT "Enable LZCNT" ON)
|
||||||
|
option(USE_TZCNT "Enable TZCNT" ON)
|
||||||
|
option(USE_F16C "Enable F16C" ON)
|
||||||
|
option(USE_FMADD "Enable FMADD" ON)
|
||||||
|
|
||||||
|
# Enable SIMD for the WASM build. Note that this is currently off by default since not all browsers support this.
|
||||||
|
# See: https://caniuse.com/?search=WebAssembly%20SIMD (Safari got support in March 2023 and was the last major browser to get support).
|
||||||
|
option(USE_WASM_SIMD "Enable SIMD for WASM" OFF)
|
||||||
|
|
||||||
|
# Enable 64 bit WASM instead of the default 32 bit WASM. Note that this currently requires special commandline flags in browsers and nodejs to enable.
|
||||||
|
# E.g. use 'node --experimental-wasm-memory64 UnitTests.js' to run the unit tests in nodejs in 64 bit.
|
||||||
|
option(JPH_USE_WASM64 "Enable 64 bit WASM" OFF)
|
||||||
|
|
||||||
|
# Enable all warnings
|
||||||
|
option(ENABLE_ALL_WARNINGS "Enable all warnings and warnings as errors" ON)
|
||||||
|
|
||||||
|
# Setting to periodically trace broadphase stats to help determine if the broadphase layer configuration is optimal
|
||||||
|
option(TRACK_BROADPHASE_STATS "Track Broadphase Stats" OFF)
|
||||||
|
|
||||||
|
# Setting to periodically trace narrowphase stats to help determine which collision queries could be optimized
|
||||||
|
option(TRACK_NARROWPHASE_STATS "Track Narrowphase Stats" OFF)
|
||||||
|
|
||||||
|
# Setting to track simulation timings per body
|
||||||
|
option(JPH_TRACK_SIMULATION_STATS "Track Simulation Stats" OFF)
|
||||||
|
|
||||||
|
# Enable the debug renderer in the Debug and Release builds. Note that DEBUG_RENDERER_IN_DISTRIBUTION will override this setting.
|
||||||
|
option(DEBUG_RENDERER_IN_DEBUG_AND_RELEASE "Enable debug renderer in Debug and Release builds" ON)
|
||||||
|
|
||||||
|
# Setting to enable the debug renderer in all builds.
|
||||||
|
# Note that enabling this reduces the performance of the library even if you're not drawing anything.
|
||||||
|
option(DEBUG_RENDERER_IN_DISTRIBUTION "Enable debug renderer in all builds" OFF)
|
||||||
|
|
||||||
|
# Enable the profiler in Debug and Release builds. Note that PROFILER_IN_DISTRIBUTION will override this setting.
|
||||||
|
option(PROFILER_IN_DEBUG_AND_RELEASE "Enable the profiler in Debug and Release builds" ON)
|
||||||
|
|
||||||
|
# Enable the profiler in all builds.
|
||||||
|
# Note that enabling this reduces the performance of the library.
|
||||||
|
option(PROFILER_IN_DISTRIBUTION "Enable the profiler in all builds" OFF)
|
||||||
|
|
||||||
|
# Ability to use the external profiler using CMake config. Defines preprocessor JPH_EXTERNAL_PROFILE.
|
||||||
|
# Use external profiler set using ProfileStartMeasurement and ProfileEndMeasurement to profile.
|
||||||
|
# Option is available only when profiling is enabled using PROFILER_IN_DEBUG_AND_RELEASE or PROFILER_IN_DISTRIBUTION.
|
||||||
|
option(JPH_USE_EXTERNAL_PROFILE "Use external profiler when profiling is enabled" OFF)
|
||||||
|
|
||||||
|
# Setting this option will force the library to use malloc/free instead of allowing the user to override the memory allocator
|
||||||
|
option(DISABLE_CUSTOM_ALLOCATOR "Disable support for a custom memory allocator" OFF)
|
||||||
|
|
||||||
|
# Setting this option will force the library to use the STL vector instead of the custom Array class
|
||||||
|
option(USE_STD_VECTOR "Use std::vector instead of own Array class" OFF)
|
||||||
|
|
||||||
|
# Setting this option will compile the ObjectStream class and RTTI attribute information
|
||||||
|
option(ENABLE_OBJECT_STREAM "Compile the ObjectStream class and RTTI attribute information" ON)
|
||||||
|
|
||||||
|
# Enable installation
|
||||||
|
option(ENABLE_INSTALL "Generate installation target" ON)
|
||||||
|
|
||||||
|
include(CMakeDependentOption)
|
||||||
|
|
||||||
|
# Ability to toggle between the static and DLL versions of the MSVC runtime library
|
||||||
|
# Windows Store only supports the DLL version
|
||||||
|
cmake_dependent_option(USE_STATIC_MSVC_RUNTIME_LIBRARY "Use the static MSVC runtime library" ON "MSVC;NOT WINDOWS_STORE" OFF)
|
||||||
|
|
||||||
|
# Option to compile with DirectX 12 compute
|
||||||
|
option(JPH_USE_DX12 "Use DirectX" ON)
|
||||||
|
|
||||||
|
# Option to compile with Vulkan compute
|
||||||
|
option(JPH_USE_VK "Use Vulkan" ON)
|
||||||
|
|
||||||
|
# Option to compile with Metal compute
|
||||||
|
option(JPH_USE_MTL "Use Metal" ON)
|
||||||
|
|
||||||
|
# Option to compile with CPU fallback compute
|
||||||
|
option(JPH_USE_CPU_COMPUTE "Use CPU Compute" ON)
|
||||||
|
|
||||||
|
# Option to enable shader debug symbols
|
||||||
|
option(JPH_SHADER_DEBUG_SYMBOLS "Shader Debug Symbols" OFF)
|
||||||
|
|
||||||
|
# Option to enable shader optimizations. When this is on, the shaders will be optimized for performance, otherwise they're suitable for debugging.
|
||||||
|
option(JPH_SHADER_OPTIMIZATION "Shader Optimization" ON)
|
||||||
|
|
||||||
|
# Determine which configurations exist
|
||||||
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) # Only do this when we're at the top level, see: https://gitlab.kitware.com/cmake/cmake/-/issues/24181
|
||||||
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
|
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Distribution")
|
||||||
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
|
||||||
|
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;ReleaseASAN;ReleaseUBSAN;ReleaseTSAN;ReleaseCoverage;Distribution")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# 64 bit architecture
|
||||||
|
set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE "x64")
|
||||||
|
|
||||||
|
# Set runtime library
|
||||||
|
if (USE_STATIC_MSVC_RUNTIME_LIBRARY)
|
||||||
|
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set general compiler flags
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /Gm- /MP /nologo /diagnostics:classic /FC /fp:except- /Zc:inline")
|
||||||
|
|
||||||
|
# Enable warnings
|
||||||
|
if (ENABLE_ALL_WARNINGS)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Wall /WX")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Optionally generate debug symbols
|
||||||
|
if (GENERATE_DEBUG_SYMBOLS)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT CPP_RTTI_ENABLED)
|
||||||
|
# Set compiler flag for disabling RTTI
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
|
||||||
|
else()
|
||||||
|
# Set compiler flag for enabling RTTI
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT CPP_EXCEPTIONS_ENABLED)
|
||||||
|
# Remove any existing compiler flag that enables exceptions
|
||||||
|
string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||||
|
|
||||||
|
# Disable warning about STL and compiler-generated types using noexcept when exceptions are disabled
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4577")
|
||||||
|
else()
|
||||||
|
# Enable exceptions
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set compiler flags for various configurations
|
||||||
|
if (OVERRIDE_CXX_FLAGS)
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "/GS /Od /Ob0 /RTC1")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "/GS- /Gy /O2 /Oi /Ot")
|
||||||
|
endif()
|
||||||
|
set(CMAKE_CXX_FLAGS_DISTRIBUTION "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASEASAN "-fsanitize=address /Od")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASEUBSAN "-fsanitize=undefined,implicit-conversion,float-divide-by-zero,local-bounds -fno-sanitize-recover=all")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASETSAN "${CMAKE_CXX_FLAGS_RELEASE} -fsanitize=thread")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASECOVERAGE "-fprofile-instr-generate -fcoverage-mapping")
|
||||||
|
|
||||||
|
# Set linker flags
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "/SUBSYSTEM:WINDOWS /ignore:4221")
|
||||||
|
if (GENERATE_DEBUG_SYMBOLS)
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
|
||||||
|
endif()
|
||||||
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
|
if (CROSS_PLATFORM_DETERMINISTIC)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise")
|
||||||
|
else()
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:fast") # Clang doesn't use fast math because it cannot be turned off inside a single compilation unit
|
||||||
|
endif()
|
||||||
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||||
|
# Fill in the path to the asan libraries
|
||||||
|
string(REGEX MATCH "^[0-9]+" CLANG_VERSION_MAJOR "${CMAKE_CXX_COMPILER_VERSION}")
|
||||||
|
set(CLANG_LIB_PATH "\"$(VSInstallDir)\\VC\\Tools\\Llvm\\x64\\lib\\clang\\${CLANG_VERSION_MAJOR}\\lib\\windows\"")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /showFilenames")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments") # Clang emits warnings about unused arguments such as /MP and /GL
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_RELEASEASAN "/SUBSYSTEM:CONSOLE /LIBPATH:${CLANG_LIB_PATH} clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_RELEASEUBSAN "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LIBPATH:${CLANG_LIB_PATH}")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_RELEASETSAN "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LIBPATH:${CLANG_LIB_PATH}")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_RELEASECOVERAGE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LIBPATH:${CLANG_LIB_PATH}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
# Enable warnings
|
||||||
|
if (ENABLE_ALL_WARNINGS)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Optionally generate debug symbols
|
||||||
|
if (GENERATE_DEBUG_SYMBOLS)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g${JPH_DEBUG_SYMBOL_FORMAT}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT CPP_RTTI_ENABLED)
|
||||||
|
# Set compiler flag for disabling RTTI
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||||
|
else()
|
||||||
|
# Set compiler flag for enabling RTTI
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frtti")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT CPP_EXCEPTIONS_ENABLED)
|
||||||
|
# Set compiler flag for disabling exception-handling
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||||
|
else()
|
||||||
|
# Set compiler flag for enabling exception-handling
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
|
# Also disable -Wstringop-overflow or it will generate false positives that can't be disabled from code when link-time optimizations are enabled
|
||||||
|
# Also disable -Wno-psabi to avoid messages of the form note: parameter passing for argument of type '...' changed in GCC 7.1
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-stringop-overflow -Wno-psabi")
|
||||||
|
else()
|
||||||
|
# Cross compiler flags
|
||||||
|
if (CROSS_COMPILE_ARM AND NOT ("${CROSS_COMPILE_ARM_TARGET}" STREQUAL ""))
|
||||||
|
set(CMAKE_CXX_FLAGS "--target=${CROSS_COMPILE_ARM_TARGET} ${CMAKE_CXX_FLAGS}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Do not use -ffast-math since it creates too many inaccuracies under clang
|
||||||
|
# FMA is not compatible with cross platform determinism
|
||||||
|
if (CROSS_PLATFORM_DETERMINISTIC OR NOT USE_FMADD OR EMSCRIPTEN)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffp-contract=off")
|
||||||
|
else()
|
||||||
|
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "^x86")
|
||||||
|
# On x86 our FMA intrinsics are preserved
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffp-contract=fast")
|
||||||
|
else()
|
||||||
|
# On ARM (and maybe other platforms) using 'fast' mode actually replaces a FMA intrinsic in Debug / when using LTO (?) with a 'mul' and an 'add' causing us to lose precision
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffp-contract=on")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# See https://github.com/jrouwe/JoltPhysics/issues/922. When compiling with DOUBLE_PRECISION=YES and CMAKE_OSX_DEPLOYMENT_TARGET=10.12 clang triggers a warning that we silence here.
|
||||||
|
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -faligned-allocation")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set compiler flags for various configurations
|
||||||
|
if (OVERRIDE_CXX_FLAGS)
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
||||||
|
endif()
|
||||||
|
set(CMAKE_CXX_FLAGS_DISTRIBUTION "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASEASAN "-fsanitize=address")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASEUBSAN "-fsanitize=undefined,implicit-conversion,float-divide-by-zero,local-bounds -fno-sanitize-recover=all")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASETSAN "${CMAKE_CXX_FLAGS_RELEASE} -fsanitize=thread")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASECOVERAGE "-O0 -DJPH_NO_FORCE_INLINE -fprofile-instr-generate -fcoverage-mapping")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set linker flags
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_DISTRIBUTION "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
|
||||||
|
|
||||||
|
# Enable link time optimization in Release and Distribution mode if requested and available
|
||||||
|
function(SET_INTERPROCEDURAL_OPTIMIZATION)
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE OFF PARENT_SCOPE)
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DISTRIBUTION OFF PARENT_SCOPE)
|
||||||
|
|
||||||
|
# On ARM, whole program optimization triggers an internal compiler error during code gen, so we don't turn it on
|
||||||
|
# When compiling as a shared lib with MinGW, turning on LTO causes errors of the form 'ld.exe: cannot export symbol X wrong type (4 vs 3)'
|
||||||
|
if (INTERPROCEDURAL_OPTIMIZATION
|
||||||
|
AND NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM64EC")
|
||||||
|
AND NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM64")
|
||||||
|
AND NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")
|
||||||
|
AND (NOT CROSS_COMPILE_ARM OR ("${CROSS_COMPILE_ARM_TARGET}" STREQUAL "aarch64-linux-gnu"))
|
||||||
|
AND NOT (MINGW AND JPH_BUILD_SHARED_LIBS))
|
||||||
|
include(CheckIPOSupported)
|
||||||
|
check_ipo_supported(RESULT IS_IPO_SUPPORTED OUTPUT IPO_CHECK_OUTPUT)
|
||||||
|
|
||||||
|
if (IS_IPO_SUPPORTED)
|
||||||
|
message("Interprocedural optimizations are turned on")
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON PARENT_SCOPE)
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DISTRIBUTION ON PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
message("Warning: Interprocedural optimizations are not supported for this target, turn off the option INTERPROCEDURAL_OPTIMIZATION to disable this warning")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
SET_INTERPROCEDURAL_OPTIMIZATION()
|
||||||
|
|
||||||
|
# Set repository root
|
||||||
|
set(PHYSICS_REPO_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../)
|
||||||
|
|
||||||
|
# Make Jolt Library
|
||||||
|
include(${PHYSICS_REPO_ROOT}/Jolt/Jolt.cmake)
|
||||||
|
if (XCODE)
|
||||||
|
# Ensure that we enable SSE4.2 for the x86_64 build, XCode builds multiple architectures
|
||||||
|
set_property(TARGET Jolt PROPERTY XCODE_ATTRIBUTE_OTHER_CPLUSPLUSFLAGS[arch=x86_64] "$(inherited) -msse4.2 -mpopcnt")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Install Jolt library and includes
|
||||||
|
if (ENABLE_INSTALL)
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
install(TARGETS Jolt
|
||||||
|
EXPORT JoltExport
|
||||||
|
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
foreach(SRC_FILE ${JOLT_PHYSICS_SRC_FILES})
|
||||||
|
string(REPLACE ${PHYSICS_REPO_ROOT} "" RELATIVE_SRC_FILE ${SRC_FILE})
|
||||||
|
get_filename_component(DESTINATION_PATH ${RELATIVE_SRC_FILE} DIRECTORY)
|
||||||
|
if (NOT RELATIVE_SRC_FILE MATCHES "\.cpp")
|
||||||
|
cmake_path(SET DST_FILE NORMALIZE "${CMAKE_INSTALL_INCLUDEDIR}/${DESTINATION_PATH}")
|
||||||
|
install(FILES ${SRC_FILE} DESTINATION ${DST_FILE})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
foreach(SRC_FILE ${JOLT_PHYSICS_DXIL_SHADERS} ${JOLT_PHYSICS_METAL_LIB} ${JOLT_PHYSICS_SPV_SHADERS})
|
||||||
|
string(REPLACE ${PHYSICS_REPO_ROOT} "" RELATIVE_SRC_FILE ${SRC_FILE})
|
||||||
|
get_filename_component(DESTINATION_PATH ${RELATIVE_SRC_FILE} DIRECTORY)
|
||||||
|
cmake_path(SET DST_FILE NORMALIZE "${CMAKE_INSTALL_DATADIR}/${DESTINATION_PATH}")
|
||||||
|
install(FILES ${SRC_FILE} DESTINATION ${DST_FILE})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Export Jolt library
|
||||||
|
export(TARGETS Jolt
|
||||||
|
NAMESPACE Jolt::
|
||||||
|
FILE JoltConfig.cmake)
|
||||||
|
install(EXPORT JoltExport
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Jolt/
|
||||||
|
NAMESPACE Jolt::
|
||||||
|
FILE JoltConfig.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check if we're the root CMakeLists.txt, if not we are included by another CMake file and we should disable everything except for the main library
|
||||||
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
|
# Ability to turn ON/OFF individual applications
|
||||||
|
option(TARGET_UNIT_TESTS "Build Unit Tests" ON)
|
||||||
|
option(TARGET_HELLO_WORLD "Build Hello World" ON)
|
||||||
|
option(TARGET_PERFORMANCE_TEST "Build Performance Test" ON)
|
||||||
|
option(TARGET_SAMPLES "Build Samples" ON)
|
||||||
|
option(TARGET_VIEWER "Build JoltViewer" ON)
|
||||||
|
|
||||||
|
if (TARGET_UNIT_TESTS)
|
||||||
|
# Create UnitTests executable
|
||||||
|
include(${PHYSICS_REPO_ROOT}/UnitTests/UnitTests.cmake)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
# Icon
|
||||||
|
set(JPH_ICON "${CMAKE_CURRENT_SOURCE_DIR}/macOS/icon.icns")
|
||||||
|
set_source_files_properties(${JPH_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||||
|
|
||||||
|
# macOS configuration
|
||||||
|
add_executable(UnitTests MACOSX_BUNDLE ${UNIT_TESTS_SRC_FILES} ${UNIT_TESTS_ASSETS} ${JPH_ICON})
|
||||||
|
|
||||||
|
# Make sure that all unit test assets move to the Resources folder in the package
|
||||||
|
foreach(ASSET_FILE ${UNIT_TESTS_ASSETS})
|
||||||
|
string(REPLACE ${PHYSICS_REPO_ROOT} "Resources" ASSET_DST ${ASSET_FILE})
|
||||||
|
get_filename_component(ASSET_DST ${ASSET_DST} DIRECTORY)
|
||||||
|
set_source_files_properties(${ASSET_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION ${ASSET_DST})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
set_property(TARGET UnitTests PROPERTY MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/iOS/UnitTestsInfo.plist")
|
||||||
|
set_property(TARGET UnitTests PROPERTY XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.joltphysics.unittests")
|
||||||
|
set_property(TARGET UnitTests PROPERTY BUILD_RPATH "/usr/local/lib" INSTALL_RPATH "/usr/local/lib") # to find the Vulkan shared lib
|
||||||
|
|
||||||
|
# Ensure that we enable SSE4.2 for the x86_64 build, XCode builds multiple architectures
|
||||||
|
set_property(TARGET UnitTests PROPERTY XCODE_ATTRIBUTE_OTHER_CPLUSPLUSFLAGS[arch=x86_64] "$(inherited) -msse4.2 -mpopcnt")
|
||||||
|
|
||||||
|
# Unit tests are in the app bundle on macOS
|
||||||
|
set(UNIT_TEST_COMMAND UnitTests.app/Contents/MacOS/UnitTests)
|
||||||
|
else()
|
||||||
|
add_executable(UnitTests ${UNIT_TESTS_SRC_FILES})
|
||||||
|
|
||||||
|
set(UNIT_TEST_COMMAND UnitTests)
|
||||||
|
endif()
|
||||||
|
target_include_directories(UnitTests PUBLIC ${UNIT_TESTS_ROOT})
|
||||||
|
target_link_libraries(UnitTests LINK_PUBLIC Jolt)
|
||||||
|
|
||||||
|
if (EMSCRIPTEN)
|
||||||
|
target_link_options(UnitTests PUBLIC -sSTACK_SIZE=1048576 -sINITIAL_MEMORY=134217728)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Code coverage doesn't work when using precompiled headers
|
||||||
|
if (CMAKE_GENERATOR STREQUAL "Ninja Multi-Config" AND MSVC)
|
||||||
|
# The Ninja Multi-Config generator errors out when selectively disabling precompiled headers for certain configurations.
|
||||||
|
# See: https://github.com/jrouwe/JoltPhysics/issues/1211
|
||||||
|
target_precompile_headers(UnitTests PRIVATE "${JOLT_PHYSICS_ROOT}/Jolt.h")
|
||||||
|
else()
|
||||||
|
target_precompile_headers(UnitTests PRIVATE "$<$<NOT:$<CONFIG:ReleaseCoverage>>:${JOLT_PHYSICS_ROOT}/Jolt.h>")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
target_link_options(UnitTests PUBLIC "/SUBSYSTEM:CONSOLE")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Register unit tests as a test so that it can be run with:
|
||||||
|
# ctest --output-on-failure
|
||||||
|
enable_testing()
|
||||||
|
add_test(UnitTests ${UNIT_TEST_COMMAND})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "WindowsStore")
|
||||||
|
if (TARGET_HELLO_WORLD)
|
||||||
|
# Example 'Hello World' application
|
||||||
|
include(${PHYSICS_REPO_ROOT}/HelloWorld/HelloWorld.cmake)
|
||||||
|
add_executable(HelloWorld ${HELLO_WORLD_SRC_FILES})
|
||||||
|
target_include_directories(HelloWorld PUBLIC ${HELLO_WORLD_ROOT})
|
||||||
|
target_link_libraries(HelloWorld LINK_PUBLIC Jolt)
|
||||||
|
if (MSVC)
|
||||||
|
target_link_options(HelloWorld PUBLIC "/SUBSYSTEM:CONSOLE")
|
||||||
|
endif()
|
||||||
|
if (EMSCRIPTEN)
|
||||||
|
target_link_options(HelloWorld PUBLIC -sSTACK_SIZE=1048576 -sINITIAL_MEMORY=134217728)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (TARGET_PERFORMANCE_TEST)
|
||||||
|
# Performance Test application
|
||||||
|
include(${PHYSICS_REPO_ROOT}/PerformanceTest/PerformanceTest.cmake)
|
||||||
|
add_executable(PerformanceTest ${PERFORMANCE_TEST_SRC_FILES})
|
||||||
|
target_include_directories(PerformanceTest PUBLIC ${PERFORMANCE_TEST_ROOT})
|
||||||
|
target_link_libraries(PerformanceTest LINK_PUBLIC Jolt)
|
||||||
|
if (MSVC)
|
||||||
|
target_link_options(PerformanceTest PUBLIC "/SUBSYSTEM:CONSOLE")
|
||||||
|
endif()
|
||||||
|
if (EMSCRIPTEN)
|
||||||
|
# Embed the assets for the RagdollScene
|
||||||
|
target_link_options(PerformanceTest PUBLIC "SHELL:--preload-file ${PHYSICS_REPO_ROOT}/Assets/Human.tof@/Assets/Human.tof")
|
||||||
|
target_link_options(PerformanceTest PUBLIC "SHELL:--preload-file ${PHYSICS_REPO_ROOT}/Assets/Human/dead_pose1.tof@/Assets/Human/dead_pose1.tof")
|
||||||
|
target_link_options(PerformanceTest PUBLIC "SHELL:--preload-file ${PHYSICS_REPO_ROOT}/Assets/terrain2.bof@/Assets/terrain2.bof")
|
||||||
|
target_link_options(PerformanceTest PUBLIC -sSTACK_SIZE=1048576 -sINITIAL_MEMORY=134217728)
|
||||||
|
endif()
|
||||||
|
set_property(TARGET PerformanceTest PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${PHYSICS_REPO_ROOT}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if ((WIN32 OR LINUX OR ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")) AND NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")) # ARM 32-bit is missing dinput8.lib
|
||||||
|
if (TARGET_SAMPLES OR TARGET_VIEWER)
|
||||||
|
include(${PHYSICS_REPO_ROOT}/TestFramework/TestFramework.cmake)
|
||||||
|
endif()
|
||||||
|
if (TARGET_SAMPLES)
|
||||||
|
if (TEST_FRAMEWORK_AVAILABLE)
|
||||||
|
include(${PHYSICS_REPO_ROOT}/Samples/Samples.cmake)
|
||||||
|
else()
|
||||||
|
message("Cannot build Samples because Vulkan/DirectX/Metal SDK is not available!")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if (TARGET_VIEWER)
|
||||||
|
if (TEST_FRAMEWORK_AVAILABLE)
|
||||||
|
include(${PHYSICS_REPO_ROOT}/JoltViewer/JoltViewer.cmake)
|
||||||
|
else()
|
||||||
|
message("Cannot build JoltViewer because Vulkan/DirectX/Metal SDK is not available!")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Copy DX12 runtime DLLs (dxcompiler.dll, dxil.dll) to the output directory of each application
|
||||||
|
if (JPH_DX12_RUNTIME_DLLS)
|
||||||
|
foreach(TARGET_NAME Samples JoltViewer UnitTests)
|
||||||
|
if (TARGET ${TARGET_NAME})
|
||||||
|
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${JPH_DX12_RUNTIME_DLLS}
|
||||||
|
$<TARGET_FILE_DIR:${TARGET_NAME}>
|
||||||
|
COMMENT "Copying DX12 runtime DLLs to ${TARGET_NAME} output directory")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
242
thirdparty/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.cpp
vendored
Normal file
242
thirdparty/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.cpp
vendored
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#include <Jolt/AABBTree/AABBTreeBuilder.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
uint AABBTreeBuilder::Node::GetMinDepth(const Array<Node> &inNodes) const
|
||||||
|
{
|
||||||
|
if (HasChildren())
|
||||||
|
{
|
||||||
|
uint left = inNodes[mChild[0]].GetMinDepth(inNodes);
|
||||||
|
uint right = inNodes[mChild[1]].GetMinDepth(inNodes);
|
||||||
|
return min(left, right) + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint AABBTreeBuilder::Node::GetMaxDepth(const Array<Node> &inNodes) const
|
||||||
|
{
|
||||||
|
if (HasChildren())
|
||||||
|
{
|
||||||
|
uint left = inNodes[mChild[0]].GetMaxDepth(inNodes);
|
||||||
|
uint right = inNodes[mChild[1]].GetMaxDepth(inNodes);
|
||||||
|
return max(left, right) + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint AABBTreeBuilder::Node::GetNodeCount(const Array<Node> &inNodes) const
|
||||||
|
{
|
||||||
|
if (HasChildren())
|
||||||
|
return inNodes[mChild[0]].GetNodeCount(inNodes) + inNodes[mChild[1]].GetNodeCount(inNodes) + 1;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint AABBTreeBuilder::Node::GetLeafNodeCount(const Array<Node> &inNodes) const
|
||||||
|
{
|
||||||
|
if (HasChildren())
|
||||||
|
return inNodes[mChild[0]].GetLeafNodeCount(inNodes) + inNodes[mChild[1]].GetLeafNodeCount(inNodes);
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint AABBTreeBuilder::Node::GetTriangleCountInTree(const Array<Node> &inNodes) const
|
||||||
|
{
|
||||||
|
if (HasChildren())
|
||||||
|
return inNodes[mChild[0]].GetTriangleCountInTree(inNodes) + inNodes[mChild[1]].GetTriangleCountInTree(inNodes);
|
||||||
|
else
|
||||||
|
return GetTriangleCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AABBTreeBuilder::Node::GetTriangleCountPerNode(const Array<Node> &inNodes, float &outAverage, uint &outMin, uint &outMax) const
|
||||||
|
{
|
||||||
|
outMin = INT_MAX;
|
||||||
|
outMax = 0;
|
||||||
|
outAverage = 0;
|
||||||
|
uint avg_divisor = 0;
|
||||||
|
GetTriangleCountPerNodeInternal(inNodes, outAverage, avg_divisor, outMin, outMax);
|
||||||
|
if (avg_divisor > 0)
|
||||||
|
outAverage /= avg_divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AABBTreeBuilder::Node::CalculateSAHCost(const Array<Node> &inNodes, float inCostTraversal, float inCostLeaf) const
|
||||||
|
{
|
||||||
|
float surface_area = mBounds.GetSurfaceArea();
|
||||||
|
return surface_area > 0.0f? CalculateSAHCostInternal(inNodes, inCostTraversal / surface_area, inCostLeaf / surface_area) : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AABBTreeBuilder::Node::GetNChildren(const Array<Node> &inNodes, uint inN, Array<const Node*> &outChildren) const
|
||||||
|
{
|
||||||
|
JPH_ASSERT(outChildren.empty());
|
||||||
|
|
||||||
|
// Check if there is anything to expand
|
||||||
|
if (!HasChildren())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Start with the children of this node
|
||||||
|
outChildren.push_back(&inNodes[mChild[0]]);
|
||||||
|
outChildren.push_back(&inNodes[mChild[1]]);
|
||||||
|
|
||||||
|
size_t next = 0;
|
||||||
|
bool all_triangles = true;
|
||||||
|
while (outChildren.size() < inN)
|
||||||
|
{
|
||||||
|
// If we have looped over all nodes, start over with the first node again
|
||||||
|
if (next >= outChildren.size())
|
||||||
|
{
|
||||||
|
// If there only triangle nodes left, we have to terminate
|
||||||
|
if (all_triangles)
|
||||||
|
return;
|
||||||
|
next = 0;
|
||||||
|
all_triangles = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to expand this node into its two children
|
||||||
|
const Node *to_expand = outChildren[next];
|
||||||
|
if (to_expand->HasChildren())
|
||||||
|
{
|
||||||
|
outChildren.erase(outChildren.begin() + next);
|
||||||
|
outChildren.push_back(&inNodes[to_expand->mChild[0]]);
|
||||||
|
outChildren.push_back(&inNodes[to_expand->mChild[1]]);
|
||||||
|
all_triangles = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float AABBTreeBuilder::Node::CalculateSAHCostInternal(const Array<Node> &inNodes, float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const
|
||||||
|
{
|
||||||
|
if (HasChildren())
|
||||||
|
return inCostTraversalDivSurfaceArea * mBounds.GetSurfaceArea()
|
||||||
|
+ inNodes[mChild[0]].CalculateSAHCostInternal(inNodes, inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea)
|
||||||
|
+ inNodes[mChild[1]].CalculateSAHCostInternal(inNodes, inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea);
|
||||||
|
else
|
||||||
|
return inCostLeafDivSurfaceArea * mBounds.GetSurfaceArea() * GetTriangleCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AABBTreeBuilder::Node::GetTriangleCountPerNodeInternal(const Array<Node> &inNodes, float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const
|
||||||
|
{
|
||||||
|
if (HasChildren())
|
||||||
|
{
|
||||||
|
inNodes[mChild[0]].GetTriangleCountPerNodeInternal(inNodes, outAverage, outAverageDivisor, outMin, outMax);
|
||||||
|
inNodes[mChild[1]].GetTriangleCountPerNodeInternal(inNodes, outAverage, outAverageDivisor, outMin, outMax);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outAverage += GetTriangleCount();
|
||||||
|
outAverageDivisor++;
|
||||||
|
outMin = min(outMin, GetTriangleCount());
|
||||||
|
outMax = max(outMax, GetTriangleCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AABBTreeBuilder::AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf) :
|
||||||
|
mTriangleSplitter(inSplitter),
|
||||||
|
mMaxTrianglesPerLeaf(inMaxTrianglesPerLeaf)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AABBTreeBuilder::Node *AABBTreeBuilder::Build(AABBTreeBuilderStats &outStats)
|
||||||
|
{
|
||||||
|
TriangleSplitter::Range initial = mTriangleSplitter.GetInitialRange();
|
||||||
|
|
||||||
|
// Worst case for number of nodes: 1 leaf node per triangle. At each level above, the number of nodes is half that of the level below.
|
||||||
|
// This means that at most we'll be allocating 2x the number of triangles in nodes.
|
||||||
|
mNodes.reserve(2 * initial.Count());
|
||||||
|
mTriangles.reserve(initial.Count());
|
||||||
|
|
||||||
|
// Build the tree
|
||||||
|
Node &root = mNodes[BuildInternal(initial)];
|
||||||
|
|
||||||
|
// Collect stats
|
||||||
|
float avg_triangles_per_leaf;
|
||||||
|
uint min_triangles_per_leaf, max_triangles_per_leaf;
|
||||||
|
root.GetTriangleCountPerNode(mNodes, avg_triangles_per_leaf, min_triangles_per_leaf, max_triangles_per_leaf);
|
||||||
|
|
||||||
|
mTriangleSplitter.GetStats(outStats.mSplitterStats);
|
||||||
|
|
||||||
|
outStats.mSAHCost = root.CalculateSAHCost(mNodes, 1.0f, 1.0f);
|
||||||
|
outStats.mMinDepth = root.GetMinDepth(mNodes);
|
||||||
|
outStats.mMaxDepth = root.GetMaxDepth(mNodes);
|
||||||
|
outStats.mNodeCount = root.GetNodeCount(mNodes);
|
||||||
|
outStats.mLeafNodeCount = root.GetLeafNodeCount(mNodes);
|
||||||
|
outStats.mMaxTrianglesPerLeaf = mMaxTrianglesPerLeaf;
|
||||||
|
outStats.mTreeMinTrianglesPerLeaf = min_triangles_per_leaf;
|
||||||
|
outStats.mTreeMaxTrianglesPerLeaf = max_triangles_per_leaf;
|
||||||
|
outStats.mTreeAvgTrianglesPerLeaf = avg_triangles_per_leaf;
|
||||||
|
|
||||||
|
return &root;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint AABBTreeBuilder::BuildInternal(const TriangleSplitter::Range &inTriangles)
|
||||||
|
{
|
||||||
|
// Check if there are too many triangles left
|
||||||
|
if (inTriangles.Count() > mMaxTrianglesPerLeaf)
|
||||||
|
{
|
||||||
|
// Split triangles in two batches
|
||||||
|
TriangleSplitter::Range left, right;
|
||||||
|
if (!mTriangleSplitter.Split(inTriangles, left, right))
|
||||||
|
{
|
||||||
|
// When the trace below triggers:
|
||||||
|
//
|
||||||
|
// This code builds a tree structure to accelerate collision detection.
|
||||||
|
// At top level it will start with all triangles in a mesh and then divides the triangles into two batches.
|
||||||
|
// This process repeats until until the batch size is smaller than mMaxTrianglePerLeaf.
|
||||||
|
//
|
||||||
|
// It uses a TriangleSplitter to find a good split. When this warning triggers, the splitter was not able
|
||||||
|
// to create a reasonable split for the triangles. This usually happens when the triangles in a batch are
|
||||||
|
// intersecting. They could also be overlapping when projected on the 3 coordinate axis.
|
||||||
|
//
|
||||||
|
// To solve this issue, you could try to pass your mesh through a mesh cleaning / optimization algorithm.
|
||||||
|
// You could also inspect the triangles that cause this issue and see if that part of the mesh can be fixed manually.
|
||||||
|
//
|
||||||
|
// When you do not fix this warning, the tree will be less efficient for collision detection, but it will still work.
|
||||||
|
JPH_IF_DEBUG(Trace("AABBTreeBuilder: Doing random split for %d triangles (max per node: %u)!", (int)inTriangles.Count(), mMaxTrianglesPerLeaf);)
|
||||||
|
int half = inTriangles.Count() / 2;
|
||||||
|
JPH_ASSERT(half > 0);
|
||||||
|
left = TriangleSplitter::Range(inTriangles.mBegin, inTriangles.mBegin + half);
|
||||||
|
right = TriangleSplitter::Range(inTriangles.mBegin + half, inTriangles.mEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively build
|
||||||
|
const uint node_index = (uint)mNodes.size();
|
||||||
|
mNodes.push_back(Node());
|
||||||
|
uint left_index = BuildInternal(left);
|
||||||
|
uint right_index = BuildInternal(right);
|
||||||
|
Node &node = mNodes[node_index];
|
||||||
|
node.mChild[0] = left_index;
|
||||||
|
node.mChild[1] = right_index;
|
||||||
|
node.mBounds = mNodes[node.mChild[0]].mBounds;
|
||||||
|
node.mBounds.Encapsulate(mNodes[node.mChild[1]].mBounds);
|
||||||
|
return node_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create leaf node
|
||||||
|
const uint node_index = (uint)mNodes.size();
|
||||||
|
mNodes.push_back(Node());
|
||||||
|
Node &node = mNodes.back();
|
||||||
|
node.mTrianglesBegin = (uint)mTriangles.size();
|
||||||
|
node.mNumTriangles = inTriangles.mEnd - inTriangles.mBegin;
|
||||||
|
const VertexList &v = mTriangleSplitter.GetVertices();
|
||||||
|
for (uint i = inTriangles.mBegin; i < inTriangles.mEnd; ++i)
|
||||||
|
{
|
||||||
|
const IndexedTriangle &t = mTriangleSplitter.GetTriangle(i);
|
||||||
|
mTriangles.push_back(t);
|
||||||
|
node.mBounds.Encapsulate(v, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
121
thirdparty/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.h
vendored
Normal file
121
thirdparty/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.h
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/TriangleSplitter/TriangleSplitter.h>
|
||||||
|
#include <Jolt/Geometry/AABox.h>
|
||||||
|
#include <Jolt/Core/NonCopyable.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct AABBTreeBuilderStats
|
||||||
|
{
|
||||||
|
///@name Splitter stats
|
||||||
|
TriangleSplitter::Stats mSplitterStats; ///< Stats returned by the triangle splitter algorithm
|
||||||
|
|
||||||
|
///@name Tree structure
|
||||||
|
float mSAHCost = 0.0f; ///< Surface Area Heuristic cost of this tree
|
||||||
|
int mMinDepth = 0; ///< Minimal depth of tree (number of nodes)
|
||||||
|
int mMaxDepth = 0; ///< Maximum depth of tree (number of nodes)
|
||||||
|
int mNodeCount = 0; ///< Number of nodes in the tree
|
||||||
|
int mLeafNodeCount = 0; ///< Number of leaf nodes (that contain triangles)
|
||||||
|
|
||||||
|
///@name Configured stats
|
||||||
|
int mMaxTrianglesPerLeaf = 0; ///< Configured max triangles per leaf
|
||||||
|
|
||||||
|
///@name Actual stats
|
||||||
|
int mTreeMinTrianglesPerLeaf = 0; ///< Minimal amount of triangles in a leaf
|
||||||
|
int mTreeMaxTrianglesPerLeaf = 0; ///< Maximal amount of triangles in a leaf
|
||||||
|
float mTreeAvgTrianglesPerLeaf = 0.0f; ///< Average amount of triangles in leaf nodes
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Helper class to build an AABB tree
|
||||||
|
class JPH_EXPORT AABBTreeBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// A node in the tree, contains the AABox for the tree and any child nodes or triangles
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Indicates that there is no child
|
||||||
|
static constexpr uint cInvalidNodeIndex = ~uint(0);
|
||||||
|
|
||||||
|
/// Get number of triangles in this node
|
||||||
|
inline uint GetTriangleCount() const { return mNumTriangles; }
|
||||||
|
|
||||||
|
/// Check if this node has any children
|
||||||
|
inline bool HasChildren() const { return mChild[0] != cInvalidNodeIndex || mChild[1] != cInvalidNodeIndex; }
|
||||||
|
|
||||||
|
/// Get child node
|
||||||
|
inline const Node * GetChild(uint inIdx, const Array<Node> &inNodes) const { return mChild[inIdx] != cInvalidNodeIndex? &inNodes[mChild[inIdx]] : nullptr; }
|
||||||
|
|
||||||
|
/// Min depth of tree
|
||||||
|
uint GetMinDepth(const Array<Node> &inNodes) const;
|
||||||
|
|
||||||
|
/// Max depth of tree
|
||||||
|
uint GetMaxDepth(const Array<Node> &inNodes) const;
|
||||||
|
|
||||||
|
/// Number of nodes in tree
|
||||||
|
uint GetNodeCount(const Array<Node> &inNodes) const;
|
||||||
|
|
||||||
|
/// Number of leaf nodes in tree
|
||||||
|
uint GetLeafNodeCount(const Array<Node> &inNodes) const;
|
||||||
|
|
||||||
|
/// Get triangle count in tree
|
||||||
|
uint GetTriangleCountInTree(const Array<Node> &inNodes) const;
|
||||||
|
|
||||||
|
/// Calculate min and max triangles per node
|
||||||
|
void GetTriangleCountPerNode(const Array<Node> &inNodes, float &outAverage, uint &outMin, uint &outMax) const;
|
||||||
|
|
||||||
|
/// Calculate the total cost of the tree using the surface area heuristic
|
||||||
|
float CalculateSAHCost(const Array<Node> &inNodes, float inCostTraversal, float inCostLeaf) const;
|
||||||
|
|
||||||
|
/// Recursively get children (breadth first) to get in total inN children (or less if there are no more)
|
||||||
|
void GetNChildren(const Array<Node> &inNodes, uint inN, Array<const Node *> &outChildren) const;
|
||||||
|
|
||||||
|
/// Bounding box
|
||||||
|
AABox mBounds;
|
||||||
|
|
||||||
|
/// Triangles (if no child nodes)
|
||||||
|
uint mTrianglesBegin; // Index into mTriangles
|
||||||
|
uint mNumTriangles = 0;
|
||||||
|
|
||||||
|
/// Child node indices (if no triangles)
|
||||||
|
uint mChild[2] = { cInvalidNodeIndex, cInvalidNodeIndex };
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class AABBTreeBuilder;
|
||||||
|
|
||||||
|
/// Recursive helper function to calculate cost of the tree
|
||||||
|
float CalculateSAHCostInternal(const Array<Node> &inNodes, float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const;
|
||||||
|
|
||||||
|
/// Recursive helper function to calculate min and max triangles per node
|
||||||
|
void GetTriangleCountPerNodeInternal(const Array<Node> &inNodes, float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
explicit AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf = 16);
|
||||||
|
|
||||||
|
/// Recursively build tree, returns the root node of the tree
|
||||||
|
Node * Build(AABBTreeBuilderStats &outStats);
|
||||||
|
|
||||||
|
/// Get all nodes
|
||||||
|
const Array<Node> & GetNodes() const { return mNodes; }
|
||||||
|
|
||||||
|
/// Get all triangles
|
||||||
|
const Array<IndexedTriangle> &GetTriangles() const { return mTriangles; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint BuildInternal(const TriangleSplitter::Range &inTriangles);
|
||||||
|
|
||||||
|
TriangleSplitter & mTriangleSplitter;
|
||||||
|
const uint mMaxTrianglesPerLeaf;
|
||||||
|
Array<Node> mNodes;
|
||||||
|
Array<IndexedTriangle> mTriangles;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
296
thirdparty/JoltPhysics/Jolt/AABBTree/AABBTreeToBuffer.h
vendored
Normal file
296
thirdparty/JoltPhysics/Jolt/AABBTree/AABBTreeToBuffer.h
vendored
Normal file
|
|
@ -0,0 +1,296 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/AABBTree/AABBTreeBuilder.h>
|
||||||
|
#include <Jolt/Core/ByteBuffer.h>
|
||||||
|
#include <Jolt/Geometry/IndexedTriangle.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Conversion algorithm that converts an AABB tree to an optimized binary buffer
|
||||||
|
template <class TriangleCodec, class NodeCodec>
|
||||||
|
class AABBTreeToBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Header for the tree
|
||||||
|
using NodeHeader = typename NodeCodec::Header;
|
||||||
|
|
||||||
|
/// Size in bytes of the header of the tree
|
||||||
|
static const int HeaderSize = NodeCodec::HeaderSize;
|
||||||
|
|
||||||
|
/// Maximum number of children per node in the tree
|
||||||
|
static const int NumChildrenPerNode = NodeCodec::NumChildrenPerNode;
|
||||||
|
|
||||||
|
/// Header for the triangles
|
||||||
|
using TriangleHeader = typename TriangleCodec::TriangleHeader;
|
||||||
|
|
||||||
|
/// Size in bytes of the header for the triangles
|
||||||
|
static const int TriangleHeaderSize = TriangleCodec::TriangleHeaderSize;
|
||||||
|
|
||||||
|
/// Convert AABB tree. Returns false if failed.
|
||||||
|
bool Convert(const Array<IndexedTriangle> &inTriangles, const Array<AABBTreeBuilder::Node> &inNodes, const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, bool inStoreUserData, const char *&outError)
|
||||||
|
{
|
||||||
|
typename NodeCodec::EncodingContext node_ctx;
|
||||||
|
typename TriangleCodec::EncodingContext tri_ctx(inVertices);
|
||||||
|
|
||||||
|
// Child nodes out of loop so we don't constantly realloc it
|
||||||
|
Array<const AABBTreeBuilder::Node *> child_nodes;
|
||||||
|
child_nodes.reserve(NumChildrenPerNode);
|
||||||
|
|
||||||
|
// First calculate how big the tree is going to be.
|
||||||
|
// Since the tree can be huge for very large meshes, we don't want
|
||||||
|
// to reallocate the buffer as it may cause out of memory situations.
|
||||||
|
// This loop mimics the construction loop below.
|
||||||
|
uint64 total_size = HeaderSize + TriangleHeaderSize;
|
||||||
|
size_t node_count = 1; // Start with root node
|
||||||
|
size_t to_process_max_size = 1; // Track size of queues so we can do a single reserve below
|
||||||
|
size_t to_process_triangles_max_size = 0;
|
||||||
|
{ // A scope to free the memory associated with to_estimate and to_estimate_triangles
|
||||||
|
Array<const AABBTreeBuilder::Node *> to_estimate;
|
||||||
|
Array<const AABBTreeBuilder::Node *> to_estimate_triangles;
|
||||||
|
to_estimate.push_back(inRoot);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
while (!to_estimate.empty())
|
||||||
|
{
|
||||||
|
// Get the next node to process
|
||||||
|
const AABBTreeBuilder::Node *node = to_estimate.back();
|
||||||
|
to_estimate.pop_back();
|
||||||
|
|
||||||
|
// Update total size
|
||||||
|
node_ctx.PrepareNodeAllocate(node, total_size);
|
||||||
|
|
||||||
|
if (node->HasChildren())
|
||||||
|
{
|
||||||
|
// Collect the first NumChildrenPerNode sub-nodes in the tree
|
||||||
|
child_nodes.clear(); // Won't free the memory
|
||||||
|
node->GetNChildren(inNodes, NumChildrenPerNode, child_nodes);
|
||||||
|
|
||||||
|
// Increment the number of nodes we're going to store
|
||||||
|
node_count += child_nodes.size();
|
||||||
|
|
||||||
|
// Insert in reverse order so we estimate left child first when taking nodes from the back
|
||||||
|
for (int idx = int(child_nodes.size()) - 1; idx >= 0; --idx)
|
||||||
|
{
|
||||||
|
// Store triangles in separate list so we process them last
|
||||||
|
const AABBTreeBuilder::Node *child = child_nodes[idx];
|
||||||
|
if (child->HasChildren())
|
||||||
|
{
|
||||||
|
to_estimate.push_back(child);
|
||||||
|
to_process_max_size = max(to_estimate.size(), to_process_max_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
to_estimate_triangles.push_back(child);
|
||||||
|
to_process_triangles_max_size = max(to_estimate_triangles.size(), to_process_triangles_max_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Update total size
|
||||||
|
tri_ctx.PreparePack(&inTriangles[node->mTrianglesBegin], node->mNumTriangles, inStoreUserData, total_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've got triangles to estimate, loop again with just the triangles
|
||||||
|
if (to_estimate_triangles.empty())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
to_estimate.swap(to_estimate_triangles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize the prepare stage for the triangle context
|
||||||
|
tri_ctx.FinalizePreparePack(total_size);
|
||||||
|
|
||||||
|
// Reserve the buffer
|
||||||
|
if (size_t(total_size) != total_size)
|
||||||
|
{
|
||||||
|
outError = "AABBTreeToBuffer: Out of memory!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mTree.reserve(size_t(total_size));
|
||||||
|
|
||||||
|
// Add headers
|
||||||
|
NodeHeader *header = HeaderSize > 0? mTree.Allocate<NodeHeader>() : nullptr;
|
||||||
|
TriangleHeader *triangle_header = TriangleHeaderSize > 0? mTree.Allocate<TriangleHeader>() : nullptr;
|
||||||
|
|
||||||
|
struct NodeData
|
||||||
|
{
|
||||||
|
const AABBTreeBuilder::Node * mNode = nullptr; // Node that this entry belongs to
|
||||||
|
Vec3 mNodeBoundsMin; // Quantized node bounds
|
||||||
|
Vec3 mNodeBoundsMax;
|
||||||
|
size_t mNodeStart = size_t(-1); // Start of node in mTree
|
||||||
|
size_t mTriangleStart = size_t(-1); // Start of the triangle data in mTree
|
||||||
|
size_t mChildNodeStart[NumChildrenPerNode]; // Start of the children of the node in mTree
|
||||||
|
size_t mChildTrianglesStart[NumChildrenPerNode]; // Start of the triangle data in mTree
|
||||||
|
size_t * mParentChildNodeStart = nullptr; // Where to store mNodeStart (to patch mChildNodeStart of my parent)
|
||||||
|
size_t * mParentTrianglesStart = nullptr; // Where to store mTriangleStart (to patch mChildTrianglesStart of my parent)
|
||||||
|
uint mNumChildren = 0; // Number of children
|
||||||
|
};
|
||||||
|
|
||||||
|
Array<NodeData *> to_process;
|
||||||
|
to_process.reserve(to_process_max_size);
|
||||||
|
Array<NodeData *> to_process_triangles;
|
||||||
|
to_process_triangles.reserve(to_process_triangles_max_size);
|
||||||
|
Array<NodeData> node_list;
|
||||||
|
node_list.reserve(node_count); // Needed to ensure that array is not reallocated, so we can keep pointers in the array
|
||||||
|
|
||||||
|
NodeData root;
|
||||||
|
root.mNode = inRoot;
|
||||||
|
root.mNodeBoundsMin = inRoot->mBounds.mMin;
|
||||||
|
root.mNodeBoundsMax = inRoot->mBounds.mMax;
|
||||||
|
node_list.push_back(root);
|
||||||
|
to_process.push_back(&node_list.back());
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
while (!to_process.empty())
|
||||||
|
{
|
||||||
|
// Get the next node to process
|
||||||
|
NodeData *node_data = to_process.back();
|
||||||
|
to_process.pop_back();
|
||||||
|
|
||||||
|
// Due to quantization box could have become bigger, not smaller
|
||||||
|
JPH_ASSERT(AABox(node_data->mNodeBoundsMin, node_data->mNodeBoundsMax).Contains(node_data->mNode->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");
|
||||||
|
|
||||||
|
// Collect the first NumChildrenPerNode sub-nodes in the tree
|
||||||
|
child_nodes.clear(); // Won't free the memory
|
||||||
|
node_data->mNode->GetNChildren(inNodes, NumChildrenPerNode, child_nodes);
|
||||||
|
node_data->mNumChildren = (uint)child_nodes.size();
|
||||||
|
|
||||||
|
// Fill in default child bounds
|
||||||
|
Vec3 child_bounds_min[NumChildrenPerNode], child_bounds_max[NumChildrenPerNode];
|
||||||
|
for (size_t i = 0; i < NumChildrenPerNode; ++i)
|
||||||
|
if (i < child_nodes.size())
|
||||||
|
{
|
||||||
|
child_bounds_min[i] = child_nodes[i]->mBounds.mMin;
|
||||||
|
child_bounds_max[i] = child_nodes[i]->mBounds.mMax;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
child_bounds_min[i] = Vec3::sZero();
|
||||||
|
child_bounds_max[i] = Vec3::sZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new node
|
||||||
|
node_data->mNodeStart = node_ctx.NodeAllocate(node_data->mNode, node_data->mNodeBoundsMin, node_data->mNodeBoundsMax, child_nodes, child_bounds_min, child_bounds_max, mTree, outError);
|
||||||
|
if (node_data->mNodeStart == size_t(-1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (node_data->mNode->HasChildren())
|
||||||
|
{
|
||||||
|
// Insert in reverse order so we process left child first when taking nodes from the back
|
||||||
|
for (int idx = int(child_nodes.size()) - 1; idx >= 0; --idx)
|
||||||
|
{
|
||||||
|
const AABBTreeBuilder::Node *child_node = child_nodes[idx];
|
||||||
|
|
||||||
|
// Due to quantization box could have become bigger, not smaller
|
||||||
|
JPH_ASSERT(AABox(child_bounds_min[idx], child_bounds_max[idx]).Contains(child_node->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");
|
||||||
|
|
||||||
|
// Add child to list of nodes to be processed
|
||||||
|
NodeData child;
|
||||||
|
child.mNode = child_node;
|
||||||
|
child.mNodeBoundsMin = child_bounds_min[idx];
|
||||||
|
child.mNodeBoundsMax = child_bounds_max[idx];
|
||||||
|
child.mParentChildNodeStart = &node_data->mChildNodeStart[idx];
|
||||||
|
child.mParentTrianglesStart = &node_data->mChildTrianglesStart[idx];
|
||||||
|
node_list.push_back(child);
|
||||||
|
|
||||||
|
// Store triangles in separate list so we process them last
|
||||||
|
if (child_node->HasChildren())
|
||||||
|
to_process.push_back(&node_list.back());
|
||||||
|
else
|
||||||
|
to_process_triangles.push_back(&node_list.back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add triangles
|
||||||
|
node_data->mTriangleStart = tri_ctx.Pack(&inTriangles[node_data->mNode->mTrianglesBegin], node_data->mNode->mNumTriangles, inStoreUserData, mTree, outError);
|
||||||
|
if (node_data->mTriangleStart == size_t(-1))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch offset into parent
|
||||||
|
if (node_data->mParentChildNodeStart != nullptr)
|
||||||
|
{
|
||||||
|
*node_data->mParentChildNodeStart = node_data->mNodeStart;
|
||||||
|
*node_data->mParentTrianglesStart = node_data->mTriangleStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've got triangles to process, loop again with just the triangles
|
||||||
|
if (to_process_triangles.empty())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
to_process.swap(to_process_triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that our reservation was correct (we don't know if we swapped the arrays or not)
|
||||||
|
JPH_ASSERT(to_process_max_size == to_process.capacity() || to_process_triangles_max_size == to_process.capacity());
|
||||||
|
JPH_ASSERT(to_process_max_size == to_process_triangles.capacity() || to_process_triangles_max_size == to_process_triangles.capacity());
|
||||||
|
|
||||||
|
// Finalize all nodes
|
||||||
|
for (NodeData &n : node_list)
|
||||||
|
if (!node_ctx.NodeFinalize(n.mNode, n.mNodeStart, n.mNumChildren, n.mChildNodeStart, n.mChildTrianglesStart, mTree, outError))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Finalize the triangles
|
||||||
|
tri_ctx.Finalize(inVertices, triangle_header, mTree);
|
||||||
|
|
||||||
|
// Validate that our reservations were correct
|
||||||
|
if (node_count != node_list.size())
|
||||||
|
{
|
||||||
|
outError = "Internal Error: Node memory estimate was incorrect, memory corruption!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (total_size != mTree.size())
|
||||||
|
{
|
||||||
|
outError = "Internal Error: Tree memory estimate was incorrect, memory corruption!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize the nodes
|
||||||
|
return node_ctx.Finalize(header, inRoot, node_list[0].mNodeStart, node_list[0].mTriangleStart, outError);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get resulting data
|
||||||
|
inline const ByteBuffer & GetBuffer() const
|
||||||
|
{
|
||||||
|
return mTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get resulting data
|
||||||
|
inline ByteBuffer & GetBuffer()
|
||||||
|
{
|
||||||
|
return mTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get header for tree
|
||||||
|
inline const NodeHeader * GetNodeHeader() const
|
||||||
|
{
|
||||||
|
return mTree.Get<NodeHeader>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get header for triangles
|
||||||
|
inline const TriangleHeader * GetTriangleHeader() const
|
||||||
|
{
|
||||||
|
return mTree.Get<TriangleHeader>(HeaderSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get root of resulting tree
|
||||||
|
inline const void * GetRoot() const
|
||||||
|
{
|
||||||
|
return mTree.Get<void>(HeaderSize + TriangleHeaderSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ByteBuffer mTree; ///< Resulting tree structure
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
323
thirdparty/JoltPhysics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h
vendored
Normal file
323
thirdparty/JoltPhysics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h
vendored
Normal file
|
|
@ -0,0 +1,323 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/ByteBuffer.h>
|
||||||
|
#include <Jolt/Math/HalfFloat.h>
|
||||||
|
#include <Jolt/AABBTree/AABBTreeBuilder.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class NodeCodecQuadTreeHalfFloat
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Number of child nodes of this node
|
||||||
|
static constexpr int NumChildrenPerNode = 4;
|
||||||
|
|
||||||
|
/// Header for the tree
|
||||||
|
struct Header
|
||||||
|
{
|
||||||
|
Float3 mRootBoundsMin;
|
||||||
|
Float3 mRootBoundsMax;
|
||||||
|
uint32 mRootProperties;
|
||||||
|
uint8 mBlockIDBits; ///< Number of bits to address a triangle block
|
||||||
|
uint8 mPadding[3] = { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable)
|
||||||
|
static constexpr int HeaderSize = sizeof(Header);
|
||||||
|
|
||||||
|
/// Stack size to use during DecodingContext::sWalkTree
|
||||||
|
static constexpr int StackSize = 128;
|
||||||
|
|
||||||
|
/// Node properties
|
||||||
|
enum : uint32
|
||||||
|
{
|
||||||
|
TRIANGLE_COUNT_BITS = 4,
|
||||||
|
TRIANGLE_COUNT_SHIFT = 28,
|
||||||
|
TRIANGLE_COUNT_MASK = (1 << TRIANGLE_COUNT_BITS) - 1,
|
||||||
|
OFFSET_BITS = 28,
|
||||||
|
OFFSET_MASK = (1 << OFFSET_BITS) - 1,
|
||||||
|
OFFSET_NON_SIGNIFICANT_BITS = 2,
|
||||||
|
OFFSET_NON_SIGNIFICANT_MASK = (1 << OFFSET_NON_SIGNIFICANT_BITS) - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Node structure
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
HalfFloat mBoundsMinX[4]; ///< 4 child bounding boxes
|
||||||
|
HalfFloat mBoundsMinY[4];
|
||||||
|
HalfFloat mBoundsMinZ[4];
|
||||||
|
HalfFloat mBoundsMaxX[4];
|
||||||
|
HalfFloat mBoundsMaxY[4];
|
||||||
|
HalfFloat mBoundsMaxZ[4];
|
||||||
|
uint32 mNodeProperties[4]; ///< 4 child node properties
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(Node) == 64, "Node should be 64 bytes");
|
||||||
|
|
||||||
|
/// This class encodes and compresses quad tree nodes
|
||||||
|
class EncodingContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Mimics the size a call to NodeAllocate() would add to the buffer
|
||||||
|
void PrepareNodeAllocate(const AABBTreeBuilder::Node *inNode, uint64 &ioBufferSize) const
|
||||||
|
{
|
||||||
|
// We don't emit nodes for leafs
|
||||||
|
if (!inNode->HasChildren())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Add size of node
|
||||||
|
ioBufferSize += sizeof(Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a new node for inNode.
|
||||||
|
/// Algorithm can modify the order of ioChildren to indicate in which order children should be compressed
|
||||||
|
/// Algorithm can enlarge the bounding boxes of the children during compression and returns these in outChildBoundsMin, outChildBoundsMax
|
||||||
|
/// inNodeBoundsMin, inNodeBoundsMax is the bounding box if inNode possibly widened by compressing the parent node
|
||||||
|
/// Returns size_t(-1) on error and reports the error in outError
|
||||||
|
size_t NodeAllocate(const AABBTreeBuilder::Node *inNode, Vec3Arg inNodeBoundsMin, Vec3Arg inNodeBoundsMax, Array<const AABBTreeBuilder::Node *> &ioChildren, Vec3 outChildBoundsMin[NumChildrenPerNode], Vec3 outChildBoundsMax[NumChildrenPerNode], ByteBuffer &ioBuffer, const char *&outError) const
|
||||||
|
{
|
||||||
|
// We don't emit nodes for leafs
|
||||||
|
if (!inNode->HasChildren())
|
||||||
|
return ioBuffer.size();
|
||||||
|
|
||||||
|
// Remember the start of the node
|
||||||
|
size_t node_start = ioBuffer.size();
|
||||||
|
|
||||||
|
// Fill in bounds
|
||||||
|
Node *node = ioBuffer.Allocate<Node>();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (i < ioChildren.size())
|
||||||
|
{
|
||||||
|
const AABBTreeBuilder::Node *this_node = ioChildren[i];
|
||||||
|
|
||||||
|
// Copy bounding box
|
||||||
|
node->mBoundsMinX[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetX());
|
||||||
|
node->mBoundsMinY[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetY());
|
||||||
|
node->mBoundsMinZ[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetZ());
|
||||||
|
node->mBoundsMaxX[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetX());
|
||||||
|
node->mBoundsMaxY[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetY());
|
||||||
|
node->mBoundsMaxZ[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetZ());
|
||||||
|
|
||||||
|
// Store triangle count
|
||||||
|
node->mNodeProperties[i] = this_node->GetTriangleCount() << TRIANGLE_COUNT_SHIFT;
|
||||||
|
if (this_node->GetTriangleCount() >= TRIANGLE_COUNT_MASK)
|
||||||
|
{
|
||||||
|
outError = "NodeCodecQuadTreeHalfFloat: Too many triangles";
|
||||||
|
return size_t(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Make this an invalid triangle node
|
||||||
|
node->mNodeProperties[i] = uint32(TRIANGLE_COUNT_MASK) << TRIANGLE_COUNT_SHIFT;
|
||||||
|
|
||||||
|
// Make bounding box invalid
|
||||||
|
node->mBoundsMinX[i] = HALF_FLT_MAX;
|
||||||
|
node->mBoundsMinY[i] = HALF_FLT_MAX;
|
||||||
|
node->mBoundsMinZ[i] = HALF_FLT_MAX;
|
||||||
|
node->mBoundsMaxX[i] = HALF_FLT_MAX;
|
||||||
|
node->mBoundsMaxY[i] = HALF_FLT_MAX;
|
||||||
|
node->mBoundsMaxZ[i] = HALF_FLT_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we don't keep track of the bounding box while descending the tree, we keep the root bounds at all levels for triangle compression
|
||||||
|
for (int i = 0; i < NumChildrenPerNode; ++i)
|
||||||
|
{
|
||||||
|
outChildBoundsMin[i] = inNodeBoundsMin;
|
||||||
|
outChildBoundsMax[i] = inNodeBoundsMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Once all nodes have been added, this call finalizes all nodes by patching in the offsets of the child nodes (that were added after the node itself was added)
|
||||||
|
bool NodeFinalize(const AABBTreeBuilder::Node *inNode, size_t inNodeStart, uint inNumChildren, const size_t *inChildrenNodeStart, const size_t *inChildrenTrianglesStart, ByteBuffer &ioBuffer, const char *&outError)
|
||||||
|
{
|
||||||
|
if (!inNode->HasChildren())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Node *node = ioBuffer.Get<Node>(inNodeStart);
|
||||||
|
for (uint i = 0; i < inNumChildren; ++i)
|
||||||
|
{
|
||||||
|
size_t offset;
|
||||||
|
if (node->mNodeProperties[i] != 0)
|
||||||
|
{
|
||||||
|
// This is a triangle block
|
||||||
|
offset = inChildrenTrianglesStart[i];
|
||||||
|
|
||||||
|
// Store highest block with triangles so we can count the number of bits we need
|
||||||
|
mHighestTriangleBlock = max(mHighestTriangleBlock, offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This is a node block
|
||||||
|
offset = inChildrenNodeStart[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store offset of next node / triangles
|
||||||
|
if (offset & OFFSET_NON_SIGNIFICANT_MASK)
|
||||||
|
{
|
||||||
|
outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-significant bits set";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
offset >>= OFFSET_NON_SIGNIFICANT_BITS;
|
||||||
|
if (offset > OFFSET_MASK)
|
||||||
|
{
|
||||||
|
outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
node->mNodeProperties[i] |= uint32(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Once all nodes have been finalized, this will finalize the header of the nodes
|
||||||
|
bool Finalize(Header *outHeader, const AABBTreeBuilder::Node *inRoot, size_t inRootNodeStart, size_t inRootTrianglesStart, const char *&outError) const
|
||||||
|
{
|
||||||
|
// Check if we can address the root node
|
||||||
|
size_t offset = inRoot->HasChildren()? inRootNodeStart : inRootTrianglesStart;
|
||||||
|
if (offset & OFFSET_NON_SIGNIFICANT_MASK)
|
||||||
|
{
|
||||||
|
outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-significant bits set";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
offset >>= OFFSET_NON_SIGNIFICANT_BITS;
|
||||||
|
if (offset > OFFSET_MASK)
|
||||||
|
{
|
||||||
|
outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the root has triangles, we need to take that offset instead since the mHighestTriangleBlock will be zero
|
||||||
|
size_t highest_triangle_block = inRootTrianglesStart != size_t(-1)? inRootTrianglesStart : mHighestTriangleBlock;
|
||||||
|
highest_triangle_block >>= OFFSET_NON_SIGNIFICANT_BITS;
|
||||||
|
|
||||||
|
inRoot->mBounds.mMin.StoreFloat3(&outHeader->mRootBoundsMin);
|
||||||
|
inRoot->mBounds.mMax.StoreFloat3(&outHeader->mRootBoundsMax);
|
||||||
|
outHeader->mRootProperties = uint32(offset) + (inRoot->GetTriangleCount() << TRIANGLE_COUNT_SHIFT);
|
||||||
|
outHeader->mBlockIDBits = uint8(32 - CountLeadingZeros(uint32(highest_triangle_block)));
|
||||||
|
if (inRoot->GetTriangleCount() >= TRIANGLE_COUNT_MASK)
|
||||||
|
{
|
||||||
|
outError = "NodeCodecQuadTreeHalfFloat: Too many triangles";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t mHighestTriangleBlock = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This class decodes and decompresses quad tree nodes
|
||||||
|
class DecodingContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Get the amount of bits needed to store an ID to a triangle block
|
||||||
|
inline static uint sTriangleBlockIDBits(const Header *inHeader)
|
||||||
|
{
|
||||||
|
return inHeader->mBlockIDBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a triangle block ID to the start of the triangle buffer
|
||||||
|
inline static const void * sGetTriangleBlockStart(const uint8 *inBufferStart, uint inTriangleBlockID)
|
||||||
|
{
|
||||||
|
return inBufferStart + (inTriangleBlockID << OFFSET_NON_SIGNIFICANT_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
JPH_INLINE explicit DecodingContext(const Header *inHeader)
|
||||||
|
{
|
||||||
|
// Start with the root node on the stack
|
||||||
|
mNodeStack[0] = inHeader->mRootProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitTriangles for each triangle encountered
|
||||||
|
template <class TriangleContext, class Visitor>
|
||||||
|
JPH_INLINE void WalkTree(const uint8 *inBufferStart, const TriangleContext &inTriangleContext, Visitor &ioVisitor)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Test if node contains triangles
|
||||||
|
uint32 node_properties = mNodeStack[mTop];
|
||||||
|
uint32 tri_count = node_properties >> TRIANGLE_COUNT_SHIFT;
|
||||||
|
if (tri_count == 0)
|
||||||
|
{
|
||||||
|
const Node *node = reinterpret_cast<const Node *>(inBufferStart + (node_properties << OFFSET_NON_SIGNIFICANT_BITS));
|
||||||
|
|
||||||
|
// Unpack bounds
|
||||||
|
#ifdef JPH_CPU_BIG_ENDIAN
|
||||||
|
Vec4 bounds_minx = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMinX[0] + (node->mBoundsMinX[1] << 16), node->mBoundsMinX[2] + (node->mBoundsMinX[3] << 16), 0, 0));
|
||||||
|
Vec4 bounds_miny = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMinY[0] + (node->mBoundsMinY[1] << 16), node->mBoundsMinY[2] + (node->mBoundsMinY[3] << 16), 0, 0));
|
||||||
|
Vec4 bounds_minz = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMinZ[0] + (node->mBoundsMinZ[1] << 16), node->mBoundsMinZ[2] + (node->mBoundsMinZ[3] << 16), 0, 0));
|
||||||
|
|
||||||
|
Vec4 bounds_maxx = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMaxX[0] + (node->mBoundsMaxX[1] << 16), node->mBoundsMaxX[2] + (node->mBoundsMaxX[3] << 16), 0, 0));
|
||||||
|
Vec4 bounds_maxy = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMaxY[0] + (node->mBoundsMaxY[1] << 16), node->mBoundsMaxY[2] + (node->mBoundsMaxY[3] << 16), 0, 0));
|
||||||
|
Vec4 bounds_maxz = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMaxZ[0] + (node->mBoundsMaxZ[1] << 16), node->mBoundsMaxZ[2] + (node->mBoundsMaxZ[3] << 16), 0, 0));
|
||||||
|
#else
|
||||||
|
UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMinX[0]));
|
||||||
|
Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy);
|
||||||
|
Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
|
||||||
|
|
||||||
|
UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMinZ[0]));
|
||||||
|
Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx);
|
||||||
|
Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
|
||||||
|
|
||||||
|
UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMaxY[0]));
|
||||||
|
Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz);
|
||||||
|
Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Load properties for 4 children
|
||||||
|
UVec4 properties = UVec4::sLoadInt4(&node->mNodeProperties[0]);
|
||||||
|
|
||||||
|
// Check which sub nodes to visit
|
||||||
|
int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, mTop);
|
||||||
|
|
||||||
|
// Push them onto the stack
|
||||||
|
JPH_ASSERT(mTop + 4 < StackSize);
|
||||||
|
properties.StoreInt4(&mNodeStack[mTop]);
|
||||||
|
mTop += num_results;
|
||||||
|
}
|
||||||
|
else if (tri_count != TRIANGLE_COUNT_MASK) // TRIANGLE_COUNT_MASK indicates a padding node, normally we shouldn't visit these nodes but when querying with a big enough box you could touch HALF_FLT_MAX (about 65K)
|
||||||
|
{
|
||||||
|
// Node contains triangles, do individual tests
|
||||||
|
uint32 triangle_block_id = node_properties & OFFSET_MASK;
|
||||||
|
const void *triangles = sGetTriangleBlockStart(inBufferStart, triangle_block_id);
|
||||||
|
|
||||||
|
ioVisitor.VisitTriangles(inTriangleContext, triangles, tri_count, triangle_block_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we're done
|
||||||
|
if (ioVisitor.ShouldAbort())
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Fetch next node until we find one that the visitor wants to see
|
||||||
|
do
|
||||||
|
--mTop;
|
||||||
|
while (mTop >= 0 && !ioVisitor.ShouldVisitNode(mTop));
|
||||||
|
}
|
||||||
|
while (mTop >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This can be used to have the visitor early out (ioVisitor.ShouldAbort() returns true) and later continue again (call WalkTree() again)
|
||||||
|
bool IsDoneWalking() const
|
||||||
|
{
|
||||||
|
return mTop < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32 mNodeStack[StackSize];
|
||||||
|
int mTop = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
555
thirdparty/JoltPhysics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h
vendored
Normal file
555
thirdparty/JoltPhysics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h
vendored
Normal file
|
|
@ -0,0 +1,555 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Geometry/RayTriangle.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Store vertices in 64 bits and indices in 8 bits + 8 bit of flags per triangle like this:
|
||||||
|
///
|
||||||
|
/// TriangleBlockHeader,
|
||||||
|
/// TriangleBlock (4 triangles and their flags in 16 bytes),
|
||||||
|
/// TriangleBlock...
|
||||||
|
/// [Optional] UserData (4 bytes per triangle)
|
||||||
|
///
|
||||||
|
/// Vertices are stored:
|
||||||
|
///
|
||||||
|
/// VertexData (1 vertex in 64 bits),
|
||||||
|
/// VertexData...
|
||||||
|
///
|
||||||
|
/// They're compressed relative to the bounding box as provided by the node codec.
|
||||||
|
class TriangleCodecIndexed8BitPackSOA4Flags
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class TriangleHeader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Float3 mOffset; ///< Offset of all vertices
|
||||||
|
Float3 mScale; ///< Scale of all vertices, vertex_position = mOffset + mScale * compressed_vertex_position
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable)
|
||||||
|
static constexpr int TriangleHeaderSize = sizeof(TriangleHeader);
|
||||||
|
|
||||||
|
/// If this codec could return a different offset than the current buffer size when calling Pack()
|
||||||
|
static constexpr bool ChangesOffsetOnPack = false;
|
||||||
|
|
||||||
|
/// Amount of bits per component
|
||||||
|
enum EComponentData : uint32
|
||||||
|
{
|
||||||
|
COMPONENT_BITS = 21,
|
||||||
|
COMPONENT_MASK = (1 << COMPONENT_BITS) - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Packed X and Y coordinate
|
||||||
|
enum EVertexXY : uint32
|
||||||
|
{
|
||||||
|
COMPONENT_X = 0,
|
||||||
|
COMPONENT_Y1 = COMPONENT_BITS,
|
||||||
|
COMPONENT_Y1_BITS = 32 - COMPONENT_BITS,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Packed Z and Y coordinate
|
||||||
|
enum EVertexZY : uint32
|
||||||
|
{
|
||||||
|
COMPONENT_Z = 0,
|
||||||
|
COMPONENT_Y2 = COMPONENT_BITS,
|
||||||
|
COMPONENT_Y2_BITS = 31 - COMPONENT_BITS,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A single packed vertex
|
||||||
|
struct VertexData
|
||||||
|
{
|
||||||
|
uint32 mVertexXY;
|
||||||
|
uint32 mVertexZY;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(VertexData) == 8, "Compiler added padding");
|
||||||
|
|
||||||
|
/// A block of 4 triangles
|
||||||
|
struct TriangleBlock
|
||||||
|
{
|
||||||
|
uint8 mIndices[3][4]; ///< 8 bit indices to triangle vertices for 4 triangles in the form mIndices[vertex][triangle] where vertex in [0, 2] and triangle in [0, 3]
|
||||||
|
uint8 mFlags[4]; ///< Triangle flags (could contain material and active edges)
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(TriangleBlock) == 16, "Compiler added padding");
|
||||||
|
|
||||||
|
enum ETriangleBlockHeaderFlags : uint32
|
||||||
|
{
|
||||||
|
OFFSET_TO_VERTICES_BITS = 29, ///< Offset from current block to start of vertices in bytes
|
||||||
|
OFFSET_TO_VERTICES_MASK = (1 << OFFSET_TO_VERTICES_BITS) - 1,
|
||||||
|
OFFSET_NON_SIGNIFICANT_BITS = 2, ///< The offset from the current block to the start of the vertices must be a multiple of 4 bytes
|
||||||
|
OFFSET_NON_SIGNIFICANT_MASK = (1 << OFFSET_NON_SIGNIFICANT_BITS) - 1,
|
||||||
|
OFFSET_TO_USERDATA_BITS = 3, ///< When user data is stored, this is the number of blocks to skip to get to the user data (0 = no user data)
|
||||||
|
OFFSET_TO_USERDATA_MASK = (1 << OFFSET_TO_USERDATA_BITS) - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A triangle header, will be followed by one or more TriangleBlocks
|
||||||
|
struct TriangleBlockHeader
|
||||||
|
{
|
||||||
|
const VertexData * GetVertexData() const { return reinterpret_cast<const VertexData *>(reinterpret_cast<const uint8 *>(this) + ((mFlags & OFFSET_TO_VERTICES_MASK) << OFFSET_NON_SIGNIFICANT_BITS)); }
|
||||||
|
const TriangleBlock * GetTriangleBlock() const { return reinterpret_cast<const TriangleBlock *>(reinterpret_cast<const uint8 *>(this) + sizeof(TriangleBlockHeader)); }
|
||||||
|
const uint32 * GetUserData() const { uint32 offset = mFlags >> OFFSET_TO_VERTICES_BITS; return offset == 0? nullptr : reinterpret_cast<const uint32 *>(GetTriangleBlock() + offset); }
|
||||||
|
|
||||||
|
uint32 mFlags;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(TriangleBlockHeader) == 4, "Compiler added padding");
|
||||||
|
|
||||||
|
/// This class is used to validate that the triangle data will not be degenerate after compression
|
||||||
|
class ValidationContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
ValidationContext(const IndexedTriangleList &inTriangles, const VertexList &inVertices) :
|
||||||
|
mVertices(inVertices)
|
||||||
|
{
|
||||||
|
// Only used the referenced triangles, just like EncodingContext::Finalize does
|
||||||
|
for (const IndexedTriangle &i : inTriangles)
|
||||||
|
for (uint32 idx : i.mIdx)
|
||||||
|
mBounds.Encapsulate(Vec3(inVertices[idx]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test if a triangle will be degenerate after quantization
|
||||||
|
bool IsDegenerate(const IndexedTriangle &inTriangle) const
|
||||||
|
{
|
||||||
|
// Quantize the triangle in the same way as EncodingContext::Finalize does
|
||||||
|
UVec4 quantized_vertex[3];
|
||||||
|
Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(mBounds.GetSize(), Vec3::sReplicate(1.0e-20f));
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
quantized_vertex[i] = ((Vec3(mVertices[inTriangle.mIdx[i]]) - mBounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt();
|
||||||
|
return quantized_vertex[0] == quantized_vertex[1] || quantized_vertex[1] == quantized_vertex[2] || quantized_vertex[0] == quantized_vertex[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const VertexList & mVertices;
|
||||||
|
AABox mBounds;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This class is used to encode and compress triangle data into a byte buffer
|
||||||
|
class EncodingContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Indicates a vertex hasn't been seen yet in the triangle list
|
||||||
|
static constexpr uint32 cNotFound = 0xffffffff;
|
||||||
|
|
||||||
|
/// Construct the encoding context
|
||||||
|
explicit EncodingContext(const VertexList &inVertices) :
|
||||||
|
mVertexMap(inVertices.size(), cNotFound)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mimics the size a call to Pack() would add to the buffer
|
||||||
|
void PreparePack(const IndexedTriangle *inTriangles, uint inNumTriangles, bool inStoreUserData, uint64 &ioBufferSize)
|
||||||
|
{
|
||||||
|
// Add triangle block header
|
||||||
|
ioBufferSize += sizeof(TriangleBlockHeader);
|
||||||
|
|
||||||
|
// Compute first vertex that this batch will use (ensuring there's enough room if none of the vertices are shared)
|
||||||
|
uint start_vertex = Clamp((int)mVertexCount - 256 + (int)inNumTriangles * 3, 0, (int)mVertexCount);
|
||||||
|
|
||||||
|
// Pack vertices
|
||||||
|
uint padded_triangle_count = AlignUp(inNumTriangles, 4);
|
||||||
|
for (uint t = 0; t < padded_triangle_count; t += 4)
|
||||||
|
{
|
||||||
|
// Add triangle block header
|
||||||
|
ioBufferSize += sizeof(TriangleBlock);
|
||||||
|
|
||||||
|
for (uint vertex_nr = 0; vertex_nr < 3; ++vertex_nr)
|
||||||
|
for (uint block_tri_idx = 0; block_tri_idx < 4; ++block_tri_idx)
|
||||||
|
{
|
||||||
|
// Fetch vertex index. Create degenerate triangles for padding triangles.
|
||||||
|
bool triangle_available = t + block_tri_idx < inNumTriangles;
|
||||||
|
uint32 src_vertex_index = triangle_available? inTriangles[t + block_tri_idx].mIdx[vertex_nr] : inTriangles[inNumTriangles - 1].mIdx[0];
|
||||||
|
|
||||||
|
// Check if we've seen this vertex before and if it is in the range that we can encode
|
||||||
|
uint32 &vertex_index = mVertexMap[src_vertex_index];
|
||||||
|
if (vertex_index == cNotFound || vertex_index < start_vertex)
|
||||||
|
{
|
||||||
|
// Add vertex
|
||||||
|
vertex_index = mVertexCount;
|
||||||
|
mVertexCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add user data
|
||||||
|
if (inStoreUserData)
|
||||||
|
ioBufferSize += inNumTriangles * sizeof(uint32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mimics the size the Finalize() call would add to ioBufferSize
|
||||||
|
void FinalizePreparePack(uint64 &ioBufferSize)
|
||||||
|
{
|
||||||
|
// Remember where the vertices are going to start in the output buffer
|
||||||
|
JPH_ASSERT(IsAligned(ioBufferSize, 4));
|
||||||
|
mVerticesStartIdx = size_t(ioBufferSize);
|
||||||
|
|
||||||
|
// Add vertices to buffer
|
||||||
|
ioBufferSize += uint64(mVertexCount) * sizeof(VertexData);
|
||||||
|
|
||||||
|
// Reserve the amount of memory we need for the vertices
|
||||||
|
mVertices.reserve(mVertexCount);
|
||||||
|
|
||||||
|
// Set vertex map back to 'not found'
|
||||||
|
for (uint32 &v : mVertexMap)
|
||||||
|
v = cNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pack the triangles in inContainer to ioBuffer. This stores the mMaterialIndex of a triangle in the 8 bit flags.
|
||||||
|
/// Returns size_t(-1) on error.
|
||||||
|
size_t Pack(const IndexedTriangle *inTriangles, uint inNumTriangles, bool inStoreUserData, ByteBuffer &ioBuffer, const char *&outError)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inNumTriangles > 0);
|
||||||
|
|
||||||
|
// Determine position of triangles start
|
||||||
|
size_t triangle_block_start = ioBuffer.size();
|
||||||
|
|
||||||
|
// Allocate triangle block header
|
||||||
|
TriangleBlockHeader *header = ioBuffer.Allocate<TriangleBlockHeader>();
|
||||||
|
|
||||||
|
// Compute first vertex that this batch will use (ensuring there's enough room if none of the vertices are shared)
|
||||||
|
uint start_vertex = Clamp((int)mVertices.size() - 256 + (int)inNumTriangles * 3, 0, (int)mVertices.size());
|
||||||
|
|
||||||
|
// Store the start vertex offset relative to TriangleBlockHeader
|
||||||
|
size_t offset_to_vertices = mVerticesStartIdx - triangle_block_start + size_t(start_vertex) * sizeof(VertexData);
|
||||||
|
if (offset_to_vertices & OFFSET_NON_SIGNIFICANT_MASK)
|
||||||
|
{
|
||||||
|
outError = "TriangleCodecIndexed8BitPackSOA4Flags: Internal Error: Offset has non-significant bits set";
|
||||||
|
return size_t(-1);
|
||||||
|
}
|
||||||
|
offset_to_vertices >>= OFFSET_NON_SIGNIFICANT_BITS;
|
||||||
|
if (offset_to_vertices > OFFSET_TO_VERTICES_MASK)
|
||||||
|
{
|
||||||
|
outError = "TriangleCodecIndexed8BitPackSOA4Flags: Offset to vertices doesn't fit. Too much data.";
|
||||||
|
return size_t(-1);
|
||||||
|
}
|
||||||
|
header->mFlags = uint32(offset_to_vertices);
|
||||||
|
|
||||||
|
// When we store user data we need to store the offset to the user data in TriangleBlocks
|
||||||
|
uint padded_triangle_count = AlignUp(inNumTriangles, 4);
|
||||||
|
if (inStoreUserData)
|
||||||
|
{
|
||||||
|
uint32 num_blocks = padded_triangle_count >> 2;
|
||||||
|
JPH_ASSERT(num_blocks <= OFFSET_TO_USERDATA_MASK);
|
||||||
|
header->mFlags |= num_blocks << OFFSET_TO_VERTICES_BITS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack vertices
|
||||||
|
for (uint t = 0; t < padded_triangle_count; t += 4)
|
||||||
|
{
|
||||||
|
TriangleBlock *block = ioBuffer.Allocate<TriangleBlock>();
|
||||||
|
for (uint vertex_nr = 0; vertex_nr < 3; ++vertex_nr)
|
||||||
|
for (uint block_tri_idx = 0; block_tri_idx < 4; ++block_tri_idx)
|
||||||
|
{
|
||||||
|
// Fetch vertex index. Create degenerate triangles for padding triangles.
|
||||||
|
bool triangle_available = t + block_tri_idx < inNumTriangles;
|
||||||
|
uint32 src_vertex_index = triangle_available? inTriangles[t + block_tri_idx].mIdx[vertex_nr] : inTriangles[inNumTriangles - 1].mIdx[0];
|
||||||
|
|
||||||
|
// Check if we've seen this vertex before and if it is in the range that we can encode
|
||||||
|
uint32 &vertex_index = mVertexMap[src_vertex_index];
|
||||||
|
if (vertex_index == cNotFound || vertex_index < start_vertex)
|
||||||
|
{
|
||||||
|
// Add vertex
|
||||||
|
vertex_index = (uint32)mVertices.size();
|
||||||
|
mVertices.push_back(src_vertex_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store vertex index
|
||||||
|
uint32 vertex_offset = vertex_index - start_vertex;
|
||||||
|
if (vertex_offset > 0xff)
|
||||||
|
{
|
||||||
|
outError = "TriangleCodecIndexed8BitPackSOA4Flags: Offset doesn't fit in 8 bit";
|
||||||
|
return size_t(-1);
|
||||||
|
}
|
||||||
|
block->mIndices[vertex_nr][block_tri_idx] = (uint8)vertex_offset;
|
||||||
|
|
||||||
|
// Store flags
|
||||||
|
uint32 flags = triangle_available? inTriangles[t + block_tri_idx].mMaterialIndex : 0;
|
||||||
|
if (flags > 0xff)
|
||||||
|
{
|
||||||
|
outError = "TriangleCodecIndexed8BitPackSOA4Flags: Material index doesn't fit in 8 bit";
|
||||||
|
return size_t(-1);
|
||||||
|
}
|
||||||
|
block->mFlags[block_tri_idx] = (uint8)flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store user data
|
||||||
|
if (inStoreUserData)
|
||||||
|
{
|
||||||
|
uint32 *user_data = ioBuffer.Allocate<uint32>(inNumTriangles);
|
||||||
|
for (uint t = 0; t < inNumTriangles; ++t)
|
||||||
|
user_data[t] = inTriangles[t].mUserData;
|
||||||
|
}
|
||||||
|
|
||||||
|
return triangle_block_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// After all triangles have been packed, this finalizes the header and triangle buffer
|
||||||
|
void Finalize(const VertexList &inVertices, TriangleHeader *ioHeader, ByteBuffer &ioBuffer) const
|
||||||
|
{
|
||||||
|
// Assert that our reservations were correct
|
||||||
|
JPH_ASSERT(mVertices.size() == mVertexCount);
|
||||||
|
JPH_ASSERT(ioBuffer.size() == mVerticesStartIdx);
|
||||||
|
|
||||||
|
// Check if anything to do
|
||||||
|
if (mVertices.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Calculate bounding box
|
||||||
|
AABox bounds;
|
||||||
|
for (uint32 v : mVertices)
|
||||||
|
bounds.Encapsulate(Vec3(inVertices[v]));
|
||||||
|
|
||||||
|
// Compress vertices
|
||||||
|
VertexData *vertices = ioBuffer.Allocate<VertexData>(mVertices.size());
|
||||||
|
Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(bounds.GetSize(), Vec3::sReplicate(1.0e-20f));
|
||||||
|
for (uint32 v : mVertices)
|
||||||
|
{
|
||||||
|
UVec4 c = ((Vec3(inVertices[v]) - bounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt();
|
||||||
|
JPH_ASSERT(c.GetX() <= COMPONENT_MASK);
|
||||||
|
JPH_ASSERT(c.GetY() <= COMPONENT_MASK);
|
||||||
|
JPH_ASSERT(c.GetZ() <= COMPONENT_MASK);
|
||||||
|
vertices->mVertexXY = c.GetX() + (c.GetY() << COMPONENT_Y1);
|
||||||
|
vertices->mVertexZY = c.GetZ() + ((c.GetY() >> COMPONENT_Y1_BITS) << COMPONENT_Y2);
|
||||||
|
++vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store decompression information
|
||||||
|
bounds.mMin.StoreFloat3(&ioHeader->mOffset);
|
||||||
|
(bounds.GetSize() / Vec3::sReplicate(COMPONENT_MASK)).StoreFloat3(&ioHeader->mScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using VertexMap = Array<uint32>;
|
||||||
|
|
||||||
|
uint32 mVertexCount = 0; ///< Number of vertices calculated during PreparePack
|
||||||
|
size_t mVerticesStartIdx = 0; ///< Start of the vertices in the output buffer, calculated during PreparePack
|
||||||
|
Array<uint32> mVertices; ///< Output vertices as an index into the original vertex list (inVertices), sorted according to occurrence
|
||||||
|
VertexMap mVertexMap; ///< Maps from the original mesh vertex index (inVertices) to the index in our output vertices (mVertices)
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This class is used to decode and decompress triangle data packed by the EncodingContext
|
||||||
|
class DecodingContext
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/// Private helper function to unpack the 1 vertex of 4 triangles (outX contains the x coordinate of triangle 0 .. 3 etc.)
|
||||||
|
JPH_INLINE void Unpack(const VertexData *inVertices, UVec4Arg inIndex, Vec4 &outX, Vec4 &outY, Vec4 &outZ) const
|
||||||
|
{
|
||||||
|
// Get compressed data
|
||||||
|
UVec4 c1 = UVec4::sGatherInt4<8>(&inVertices->mVertexXY, inIndex);
|
||||||
|
UVec4 c2 = UVec4::sGatherInt4<8>(&inVertices->mVertexZY, inIndex);
|
||||||
|
|
||||||
|
// Unpack the x y and z component
|
||||||
|
UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK));
|
||||||
|
UVec4 yc = UVec4::sOr(c1.LogicalShiftRight<COMPONENT_Y1>(), c2.LogicalShiftRight<COMPONENT_Y2>().LogicalShiftLeft<COMPONENT_Y1_BITS>());
|
||||||
|
UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK));
|
||||||
|
|
||||||
|
// Convert to float
|
||||||
|
outX = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX);
|
||||||
|
outY = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY);
|
||||||
|
outZ = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Private helper function to unpack 4 triangles from a triangle block
|
||||||
|
JPH_INLINE void Unpack(const TriangleBlock *inBlock, const VertexData *inVertices, Vec4 &outX1, Vec4 &outY1, Vec4 &outZ1, Vec4 &outX2, Vec4 &outY2, Vec4 &outZ2, Vec4 &outX3, Vec4 &outY3, Vec4 &outZ3) const
|
||||||
|
{
|
||||||
|
// Get the indices for the three vertices (reads 4 bytes extra, but these are the flags so that's ok)
|
||||||
|
UVec4 indices = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&inBlock->mIndices[0]));
|
||||||
|
UVec4 iv1 = indices.Expand4Byte0();
|
||||||
|
UVec4 iv2 = indices.Expand4Byte4();
|
||||||
|
UVec4 iv3 = indices.Expand4Byte8();
|
||||||
|
|
||||||
|
#ifdef JPH_CPU_BIG_ENDIAN
|
||||||
|
// On big endian systems we need to reverse the bytes
|
||||||
|
iv1 = iv1.Swizzle<SWIZZLE_W, SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>();
|
||||||
|
iv2 = iv2.Swizzle<SWIZZLE_W, SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>();
|
||||||
|
iv3 = iv3.Swizzle<SWIZZLE_W, SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Decompress the triangle data
|
||||||
|
Unpack(inVertices, iv1, outX1, outY1, outZ1);
|
||||||
|
Unpack(inVertices, iv2, outX2, outY2, outZ2);
|
||||||
|
Unpack(inVertices, iv3, outX3, outY3, outZ3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
JPH_INLINE explicit DecodingContext(const TriangleHeader *inHeader) :
|
||||||
|
mOffsetX(Vec4::sReplicate(inHeader->mOffset.x)),
|
||||||
|
mOffsetY(Vec4::sReplicate(inHeader->mOffset.y)),
|
||||||
|
mOffsetZ(Vec4::sReplicate(inHeader->mOffset.z)),
|
||||||
|
mScaleX(Vec4::sReplicate(inHeader->mScale.x)),
|
||||||
|
mScaleY(Vec4::sReplicate(inHeader->mScale.y)),
|
||||||
|
mScaleZ(Vec4::sReplicate(inHeader->mScale.z))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpacks triangles in the format t1v1,t1v2,t1v3, t2v1,t2v2,t2v3, ...
|
||||||
|
JPH_INLINE void Unpack(const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles) const
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inNumTriangles > 0);
|
||||||
|
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||||
|
const VertexData *vertices = header->GetVertexData();
|
||||||
|
const TriangleBlock *t = header->GetTriangleBlock();
|
||||||
|
const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);
|
||||||
|
|
||||||
|
int triangles_left = inNumTriangles;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Unpack the vertices for 4 triangles
|
||||||
|
Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z;
|
||||||
|
Unpack(t, vertices, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);
|
||||||
|
|
||||||
|
// Transpose it so we get normal vectors
|
||||||
|
Mat44 v1 = Mat44(v1x, v1y, v1z, Vec4::sZero()).Transposed();
|
||||||
|
Mat44 v2 = Mat44(v2x, v2y, v2z, Vec4::sZero()).Transposed();
|
||||||
|
Mat44 v3 = Mat44(v3x, v3y, v3z, Vec4::sZero()).Transposed();
|
||||||
|
|
||||||
|
// Store triangle data
|
||||||
|
for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left)
|
||||||
|
{
|
||||||
|
*outTriangles++ = v1.GetColumn3(i);
|
||||||
|
*outTriangles++ = v2.GetColumn3(i);
|
||||||
|
*outTriangles++ = v3.GetColumn3(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
++t;
|
||||||
|
}
|
||||||
|
while (t < end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests a ray against the packed triangles
|
||||||
|
JPH_INLINE float TestRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, const void *inTriangleStart, uint32 inNumTriangles, float inClosest, uint32 &outClosestTriangleIndex) const
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inNumTriangles > 0);
|
||||||
|
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||||
|
const VertexData *vertices = header->GetVertexData();
|
||||||
|
const TriangleBlock *t = header->GetTriangleBlock();
|
||||||
|
const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);
|
||||||
|
|
||||||
|
Vec4 closest = Vec4::sReplicate(inClosest);
|
||||||
|
UVec4 closest_triangle_idx = UVec4::sZero();
|
||||||
|
|
||||||
|
UVec4 start_triangle_idx = UVec4::sZero();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Unpack the vertices for 4 triangles
|
||||||
|
Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z;
|
||||||
|
Unpack(t, vertices, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);
|
||||||
|
|
||||||
|
// Perform ray vs triangle test
|
||||||
|
Vec4 distance = RayTriangle4(inRayOrigin, inRayDirection, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);
|
||||||
|
|
||||||
|
// Update closest with the smaller values
|
||||||
|
UVec4 smaller = Vec4::sLess(distance, closest);
|
||||||
|
closest = Vec4::sSelect(closest, distance, smaller);
|
||||||
|
|
||||||
|
// Update triangle index with the smallest values
|
||||||
|
UVec4 triangle_idx = start_triangle_idx + UVec4(0, 1, 2, 3);
|
||||||
|
closest_triangle_idx = UVec4::sSelect(closest_triangle_idx, triangle_idx, smaller);
|
||||||
|
|
||||||
|
// Next block
|
||||||
|
++t;
|
||||||
|
start_triangle_idx += UVec4::sReplicate(4);
|
||||||
|
}
|
||||||
|
while (t < end);
|
||||||
|
|
||||||
|
// Get the smallest component
|
||||||
|
Vec4::sSort4(closest, closest_triangle_idx);
|
||||||
|
outClosestTriangleIndex = closest_triangle_idx.GetX();
|
||||||
|
return closest.GetX();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode a single triangle
|
||||||
|
inline void GetTriangle(const void *inTriangleStart, uint32 inTriangleIdx, Vec3 &outV1, Vec3 &outV2, Vec3 &outV3) const
|
||||||
|
{
|
||||||
|
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||||
|
const VertexData *vertices = header->GetVertexData();
|
||||||
|
const TriangleBlock *block = header->GetTriangleBlock() + (inTriangleIdx >> 2);
|
||||||
|
uint32 block_triangle_idx = inTriangleIdx & 0b11;
|
||||||
|
|
||||||
|
// Get the 3 vertices
|
||||||
|
const VertexData &v1 = vertices[block->mIndices[0][block_triangle_idx]];
|
||||||
|
const VertexData &v2 = vertices[block->mIndices[1][block_triangle_idx]];
|
||||||
|
const VertexData &v3 = vertices[block->mIndices[2][block_triangle_idx]];
|
||||||
|
|
||||||
|
// Pack the vertices
|
||||||
|
UVec4 c1(v1.mVertexXY, v2.mVertexXY, v3.mVertexXY, 0);
|
||||||
|
UVec4 c2(v1.mVertexZY, v2.mVertexZY, v3.mVertexZY, 0);
|
||||||
|
|
||||||
|
// Unpack the x y and z component
|
||||||
|
UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK));
|
||||||
|
UVec4 yc = UVec4::sOr(c1.LogicalShiftRight<COMPONENT_Y1>(), c2.LogicalShiftRight<COMPONENT_Y2>().LogicalShiftLeft<COMPONENT_Y1_BITS>());
|
||||||
|
UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK));
|
||||||
|
|
||||||
|
// Convert to float
|
||||||
|
Vec4 vx = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX);
|
||||||
|
Vec4 vy = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY);
|
||||||
|
Vec4 vz = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ);
|
||||||
|
|
||||||
|
// Transpose it so we get normal vectors
|
||||||
|
Mat44 trans = Mat44(vx, vy, vz, Vec4::sZero()).Transposed();
|
||||||
|
outV1 = trans.GetAxisX();
|
||||||
|
outV2 = trans.GetAxisY();
|
||||||
|
outV3 = trans.GetAxisZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get user data for a triangle
|
||||||
|
JPH_INLINE uint32 GetUserData(const void *inTriangleStart, uint32 inTriangleIdx) const
|
||||||
|
{
|
||||||
|
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||||
|
const uint32 *user_data = header->GetUserData();
|
||||||
|
return user_data != nullptr? user_data[inTriangleIdx] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get flags for entire triangle block
|
||||||
|
JPH_INLINE static void sGetFlags(const void *inTriangleStart, uint32 inNumTriangles, uint8 *outTriangleFlags)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inNumTriangles > 0);
|
||||||
|
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||||
|
const TriangleBlock *t = header->GetTriangleBlock();
|
||||||
|
const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);
|
||||||
|
|
||||||
|
int triangles_left = inNumTriangles;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left)
|
||||||
|
*outTriangleFlags++ = t->mFlags[i];
|
||||||
|
|
||||||
|
++t;
|
||||||
|
}
|
||||||
|
while (t < end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get flags for a particular triangle
|
||||||
|
JPH_INLINE static uint8 sGetFlags(const void *inTriangleStart, int inTriangleIndex)
|
||||||
|
{
|
||||||
|
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||||
|
const TriangleBlock *first_block = header->GetTriangleBlock();
|
||||||
|
return first_block[inTriangleIndex >> 2].mFlags[inTriangleIndex & 0b11];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpacks triangles and flags, convenience function
|
||||||
|
JPH_INLINE void Unpack(const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles, uint8 *outTriangleFlags) const
|
||||||
|
{
|
||||||
|
Unpack(inTriangleStart, inNumTriangles, outTriangles);
|
||||||
|
sGetFlags(inTriangleStart, inNumTriangles, outTriangleFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vec4 mOffsetX;
|
||||||
|
Vec4 mOffsetY;
|
||||||
|
Vec4 mOffsetZ;
|
||||||
|
Vec4 mScaleX;
|
||||||
|
Vec4 mScaleY;
|
||||||
|
Vec4 mScaleZ;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
36
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeBufferCPU.cpp
vendored
Normal file
36
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeBufferCPU.cpp
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_CPU_COMPUTE
|
||||||
|
|
||||||
|
#include <Jolt/Compute/CPU/ComputeBufferCPU.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
ComputeBufferCPU::ComputeBufferCPU(EType inType, uint64 inSize, uint inStride, const void *inData) :
|
||||||
|
ComputeBuffer(inType, inSize, inStride)
|
||||||
|
{
|
||||||
|
size_t buffer_size = size_t(mSize) * mStride;
|
||||||
|
mData = Allocate(buffer_size);
|
||||||
|
if (inData != nullptr)
|
||||||
|
memcpy(mData, inData, buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferCPU::~ComputeBufferCPU()
|
||||||
|
{
|
||||||
|
Free(mData);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferResult ComputeBufferCPU::CreateReadBackBuffer() const
|
||||||
|
{
|
||||||
|
ComputeBufferResult result;
|
||||||
|
result.Set(const_cast<ComputeBufferCPU *>(this));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_CPU_COMPUTE
|
||||||
36
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeBufferCPU.h
vendored
Normal file
36
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeBufferCPU.h
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeBuffer.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_CPU_COMPUTE
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Buffer that can be used with the CPU compute system
|
||||||
|
class JPH_EXPORT ComputeBufferCPU final : public ComputeBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Constructor / destructor
|
||||||
|
ComputeBufferCPU(EType inType, uint64 inSize, uint inStride, const void *inData);
|
||||||
|
virtual ~ComputeBufferCPU() override;
|
||||||
|
|
||||||
|
ComputeBufferResult CreateReadBackBuffer() const override;
|
||||||
|
|
||||||
|
void * GetData() const { return mData; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void * MapInternal(EMode inMode) override { return mData; }
|
||||||
|
virtual void UnmapInternal() override { /* Nothing to do */ }
|
||||||
|
|
||||||
|
void * mData;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_CPU_COMPUTE
|
||||||
101
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeQueueCPU.cpp
vendored
Normal file
101
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeQueueCPU.cpp
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_CPU_COMPUTE
|
||||||
|
|
||||||
|
#include <Jolt/Compute/CPU/ComputeQueueCPU.h>
|
||||||
|
#include <Jolt/Compute/CPU/ComputeShaderCPU.h>
|
||||||
|
#include <Jolt/Compute/CPU/ComputeBufferCPU.h>
|
||||||
|
#include <Jolt/Compute/CPU/ShaderWrapper.h>
|
||||||
|
#include <Jolt/Compute/CPU/HLSLToCPP.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
ComputeQueueCPU::~ComputeQueueCPU()
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mShader == nullptr && mWrapper == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueCPU::SetShader(const ComputeShader *inShader)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mShader == nullptr && mWrapper == nullptr);
|
||||||
|
|
||||||
|
mShader = static_cast<const ComputeShaderCPU *>(inShader);
|
||||||
|
mWrapper = mShader->CreateWrapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueCPU::SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::ConstantBuffer);
|
||||||
|
const ComputeBufferCPU *buffer = static_cast<const ComputeBufferCPU *>(inBuffer);
|
||||||
|
mWrapper->Bind(inName, buffer->GetData(), buffer->GetSize() * buffer->GetStride());
|
||||||
|
|
||||||
|
mUsedBuffers.insert(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueCPU::SetBuffer(const char *inName, const ComputeBuffer *inBuffer)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::UploadBuffer || inBuffer->GetType() == ComputeBuffer::EType::Buffer || inBuffer->GetType() == ComputeBuffer::EType::RWBuffer);
|
||||||
|
const ComputeBufferCPU *buffer = static_cast<const ComputeBufferCPU *>(inBuffer);
|
||||||
|
mWrapper->Bind(inName, buffer->GetData(), buffer->GetSize() * buffer->GetStride());
|
||||||
|
|
||||||
|
mUsedBuffers.insert(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueCPU::SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::RWBuffer);
|
||||||
|
const ComputeBufferCPU *buffer = static_cast<const ComputeBufferCPU *>(inBuffer);
|
||||||
|
mWrapper->Bind(inName, buffer->GetData(), buffer->GetSize() * buffer->GetStride());
|
||||||
|
|
||||||
|
mUsedBuffers.insert(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueCPU::ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc)
|
||||||
|
{
|
||||||
|
/* Nothing to read back */
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueCPU::Dispatch(uint inThreadGroupsX, uint inThreadGroupsY, uint inThreadGroupsZ)
|
||||||
|
{
|
||||||
|
uint nx = inThreadGroupsX * mShader->GetGroupSizeX();
|
||||||
|
uint ny = inThreadGroupsY * mShader->GetGroupSizeY();
|
||||||
|
uint nz = inThreadGroupsZ * mShader->GetGroupSizeZ();
|
||||||
|
|
||||||
|
for (uint z = 0; z < nz; ++z)
|
||||||
|
for (uint y = 0; y < ny; ++y)
|
||||||
|
for (uint x = 0; x < nx; ++x)
|
||||||
|
{
|
||||||
|
HLSLToCPP::uint3 tid { x, y, z };
|
||||||
|
mWrapper->Main(tid);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete mWrapper;
|
||||||
|
mWrapper = nullptr;
|
||||||
|
|
||||||
|
mUsedBuffers.clear();
|
||||||
|
mShader = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueCPU::Execute()
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueCPU::Wait()
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_CPU_COMPUTE
|
||||||
43
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeQueueCPU.h
vendored
Normal file
43
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeQueueCPU.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeQueue.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_CPU_COMPUTE
|
||||||
|
|
||||||
|
#include <Jolt/Compute/CPU/ComputeShaderCPU.h>
|
||||||
|
#include <Jolt/Core/UnorderedSet.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// A command queue for the CPU compute system
|
||||||
|
class JPH_EXPORT ComputeQueueCPU final : public ComputeQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~ComputeQueueCPU() override;
|
||||||
|
|
||||||
|
// See: ComputeQueue
|
||||||
|
virtual void SetShader(const ComputeShader *inShader) override;
|
||||||
|
virtual void SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer) override;
|
||||||
|
virtual void SetBuffer(const char *inName, const ComputeBuffer *inBuffer) override;
|
||||||
|
virtual void SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier = EBarrier::Yes) override;
|
||||||
|
virtual void ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc) override;
|
||||||
|
virtual void Dispatch(uint inThreadGroupsX, uint inThreadGroupsY, uint inThreadGroupsZ) override;
|
||||||
|
virtual void Execute() override;
|
||||||
|
virtual void Wait() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefConst<ComputeShaderCPU> mShader = nullptr; ///< Current active shader
|
||||||
|
ShaderWrapper * mWrapper = nullptr; ///< The active shader wrapper
|
||||||
|
UnorderedSet<RefConst<ComputeBuffer>> mUsedBuffers; ///< Buffers that are in use by the current execution, these will be retained until execution is finished so that we don't free buffers that are in use
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_CPU_COMPUTE
|
||||||
42
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeShaderCPU.h
vendored
Normal file
42
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeShaderCPU.h
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeShader.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_CPU_COMPUTE
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class ShaderWrapper;
|
||||||
|
|
||||||
|
/// Compute shader handle for CPU compute
|
||||||
|
class JPH_EXPORT ComputeShaderCPU : public ComputeShader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
using CreateShader = ShaderWrapper *(*)();
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
ComputeShaderCPU(CreateShader inCreateShader, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) :
|
||||||
|
ComputeShader(inGroupSizeX, inGroupSizeY, inGroupSizeZ),
|
||||||
|
mCreateShader(inCreateShader)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an instance of the shader wrapper
|
||||||
|
ShaderWrapper * CreateWrapper() const
|
||||||
|
{
|
||||||
|
return mCreateShader();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CreateShader mCreateShader;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_CPU_COMPUTE
|
||||||
56
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeSystemCPU.cpp
vendored
Normal file
56
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeSystemCPU.cpp
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_CPU_COMPUTE
|
||||||
|
|
||||||
|
#include <Jolt/Compute/CPU/ComputeSystemCPU.h>
|
||||||
|
#include <Jolt/Compute/CPU/ComputeQueueCPU.h>
|
||||||
|
#include <Jolt/Compute/CPU/ComputeBufferCPU.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
JPH_IMPLEMENT_RTTI_VIRTUAL(ComputeSystemCPU)
|
||||||
|
{
|
||||||
|
JPH_ADD_BASE_CLASS(ComputeSystemCPU, ComputeSystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeShaderResult ComputeSystemCPU::CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ)
|
||||||
|
{
|
||||||
|
ComputeShaderResult result;
|
||||||
|
const ShaderRegistry::const_iterator it = mShaderRegistry.find(inName);
|
||||||
|
if (it == mShaderRegistry.end())
|
||||||
|
{
|
||||||
|
result.SetError("Compute shader not found");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.Set(new ComputeShaderCPU(it->second, inGroupSizeX, inGroupSizeY, inGroupSizeZ));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferResult ComputeSystemCPU::CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData)
|
||||||
|
{
|
||||||
|
ComputeBufferResult result;
|
||||||
|
result.Set(new ComputeBufferCPU(inType, inSize, inStride, inData));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeQueueResult ComputeSystemCPU::CreateComputeQueue()
|
||||||
|
{
|
||||||
|
ComputeQueueResult result;
|
||||||
|
result.Set(new ComputeQueueCPU());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeSystemResult CreateComputeSystemCPU()
|
||||||
|
{
|
||||||
|
ComputeSystemResult result;
|
||||||
|
result.Set(new ComputeSystemCPU());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_CPU_COMPUTE
|
||||||
52
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeSystemCPU.h
vendored
Normal file
52
thirdparty/JoltPhysics/Jolt/Compute/CPU/ComputeSystemCPU.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeSystem.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_CPU_COMPUTE
|
||||||
|
|
||||||
|
#include <Jolt/Core/UnorderedMap.h>
|
||||||
|
#include <Jolt/Compute/CPU/ComputeShaderCPU.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Interface to run a workload on the CPU
|
||||||
|
/// This is intended mainly for debugging purposes and is not optimized for performance
|
||||||
|
class JPH_EXPORT ComputeSystemCPU : public ComputeSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_DECLARE_RTTI_VIRTUAL(JPH_EXPORT, ComputeSystemCPU)
|
||||||
|
|
||||||
|
// See: ComputeSystem
|
||||||
|
virtual ComputeShaderResult CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) override;
|
||||||
|
virtual ComputeBufferResult CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData = nullptr) override;
|
||||||
|
virtual ComputeQueueResult CreateComputeQueue() override;
|
||||||
|
|
||||||
|
using CreateShader = ComputeShaderCPU::CreateShader;
|
||||||
|
|
||||||
|
void RegisterShader(const char *inName, CreateShader inCreateShader)
|
||||||
|
{
|
||||||
|
mShaderRegistry[inName] = inCreateShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using ShaderRegistry = UnorderedMap<string_view, CreateShader>;
|
||||||
|
ShaderRegistry mShaderRegistry;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal helpers
|
||||||
|
#define JPH_SHADER_WRAPPER_FUNCTION_NAME(name) RegisterShader##name
|
||||||
|
#define JPH_SHADER_WRAPPER_FUNCTION(sys, name) void JPH_EXPORT JPH_SHADER_WRAPPER_FUNCTION_NAME(name)(ComputeSystemCPU *sys)
|
||||||
|
|
||||||
|
/// Macro to declare a shader register function
|
||||||
|
#define JPH_DECLARE_REGISTER_SHADER(name) namespace JPH { class ComputeSystemCPU; JPH_SHADER_WRAPPER_FUNCTION(, name); }
|
||||||
|
|
||||||
|
/// Macro to register a shader
|
||||||
|
#define JPH_REGISTER_SHADER(sys, name) JPH::JPH_SHADER_WRAPPER_FUNCTION_NAME(name)(sys)
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_CPU_COMPUTE
|
||||||
525
thirdparty/JoltPhysics/Jolt/Compute/CPU/HLSLToCPP.h
vendored
Normal file
525
thirdparty/JoltPhysics/Jolt/Compute/CPU/HLSLToCPP.h
vendored
Normal file
|
|
@ -0,0 +1,525 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Emulates HLSL vector types and operations in C++.
|
||||||
|
/// Note doesn't emulate things like barriers and group shared memory.
|
||||||
|
namespace HLSLToCPP {
|
||||||
|
|
||||||
|
using std::sqrt;
|
||||||
|
using std::min;
|
||||||
|
using std::max;
|
||||||
|
using std::round;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// float2
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct float2
|
||||||
|
{
|
||||||
|
// Constructors
|
||||||
|
inline float2() = default;
|
||||||
|
constexpr float2(float inX, float inY) : x(inX), y(inY) { }
|
||||||
|
explicit constexpr float2(float inS) : x(inS), y(inS) { }
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr float2 & operator += (const float2 &inRHS) { x += inRHS.x; y += inRHS.y; return *this; }
|
||||||
|
constexpr float2 & operator -= (const float2 &inRHS) { x -= inRHS.x; y -= inRHS.y; return *this; }
|
||||||
|
constexpr float2 & operator *= (float inRHS) { x *= inRHS; y *= inRHS; return *this; }
|
||||||
|
constexpr float2 & operator /= (float inRHS) { x /= inRHS; y /= inRHS; return *this; }
|
||||||
|
constexpr float2 & operator *= (const float2 &inRHS) { x *= inRHS.x; y *= inRHS.y; return *this; }
|
||||||
|
constexpr float2 & operator /= (const float2 &inRHS) { x /= inRHS.x; y /= inRHS.y; return *this; }
|
||||||
|
|
||||||
|
// Equality
|
||||||
|
constexpr bool operator == (const float2 &inRHS) const { return x == inRHS.x && y == inRHS.y; }
|
||||||
|
constexpr bool operator != (const float2 &inRHS) const { return !(*this == inRHS); }
|
||||||
|
|
||||||
|
// Component access
|
||||||
|
const float & operator [] (uint inIndex) const { return (&x)[inIndex]; }
|
||||||
|
float & operator [] (uint inIndex) { return (&x)[inIndex]; }
|
||||||
|
|
||||||
|
// Swizzling (note return value is const to prevent assignment to swizzled results)
|
||||||
|
const float2 swizzle_xy() const { return float2(x, y); }
|
||||||
|
const float2 swizzle_yx() const { return float2(y, x); }
|
||||||
|
|
||||||
|
float x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr float2 operator - (const float2 &inA) { return float2(-inA.x, -inA.y); }
|
||||||
|
constexpr float2 operator + (const float2 &inA, const float2 &inB) { return float2(inA.x + inB.x, inA.y + inB.y); }
|
||||||
|
constexpr float2 operator - (const float2 &inA, const float2 &inB) { return float2(inA.x - inB.x, inA.y - inB.y); }
|
||||||
|
constexpr float2 operator * (const float2 &inA, const float2 &inB) { return float2(inA.x * inB.x, inA.y * inB.y); }
|
||||||
|
constexpr float2 operator / (const float2 &inA, const float2 &inB) { return float2(inA.x / inB.x, inA.y / inB.y); }
|
||||||
|
constexpr float2 operator * (const float2 &inA, float inS) { return float2(inA.x * inS, inA.y * inS); }
|
||||||
|
constexpr float2 operator * (float inS, const float2 &inA) { return inA * inS; }
|
||||||
|
constexpr float2 operator / (const float2 &inA, float inS) { return float2(inA.x / inS, inA.y / inS); }
|
||||||
|
|
||||||
|
// Dot product
|
||||||
|
constexpr float dot(const float2 &inA, const float2 &inB) { return inA.x * inB.x + inA.y * inB.y; }
|
||||||
|
|
||||||
|
// Min value
|
||||||
|
constexpr float2 min(const float2 &inA, const float2 &inB) { return float2(min(inA.x, inB.x), min(inA.y, inB.y)); }
|
||||||
|
|
||||||
|
// Max value
|
||||||
|
constexpr float2 max(const float2 &inA, const float2 &inB) { return float2(max(inA.x, inB.x), max(inA.y, inB.y)); }
|
||||||
|
|
||||||
|
// Length
|
||||||
|
inline float length(const float2 &inV) { return sqrt(dot(inV, inV)); }
|
||||||
|
|
||||||
|
// Normalization
|
||||||
|
inline float2 normalize(const float2 &inV) { return inV / length(inV); }
|
||||||
|
|
||||||
|
// Rounding to int
|
||||||
|
inline float2 round(const float2 &inV) { return float2(round(inV.x), round(inV.y)); }
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// float3
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct uint3;
|
||||||
|
|
||||||
|
struct float3
|
||||||
|
{
|
||||||
|
// Constructors
|
||||||
|
inline float3() = default;
|
||||||
|
constexpr float3(const float2 &inV, float inZ) : x(inV.x), y(inV.y), z(inZ) { }
|
||||||
|
constexpr float3(float inX, float inY, float inZ) : x(inX), y(inY), z(inZ) { }
|
||||||
|
explicit constexpr float3(float inS) : x(inS), y(inS), z(inS) { }
|
||||||
|
explicit constexpr float3(const uint3 &inV);
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr float3 & operator += (const float3 &inRHS) { x += inRHS.x; y += inRHS.y; z += inRHS.z; return *this; }
|
||||||
|
constexpr float3 & operator -= (const float3 &inRHS) { x -= inRHS.x; y -= inRHS.y; z -= inRHS.z; return *this; }
|
||||||
|
constexpr float3 & operator *= (float inRHS) { x *= inRHS; y *= inRHS; z *= inRHS; return *this; }
|
||||||
|
constexpr float3 & operator /= (float inRHS) { x /= inRHS; y /= inRHS; z /= inRHS; return *this; }
|
||||||
|
constexpr float3 & operator *= (const float3 &inRHS) { x *= inRHS.x; y *= inRHS.y; z *= inRHS.z; return *this; }
|
||||||
|
constexpr float3 & operator /= (const float3 &inRHS) { x /= inRHS.x; y /= inRHS.y; z /= inRHS.z; return *this; }
|
||||||
|
|
||||||
|
// Equality
|
||||||
|
constexpr bool operator == (const float3 &inRHS) const { return x == inRHS.x && y == inRHS.y && z == inRHS.z; }
|
||||||
|
constexpr bool operator != (const float3 &inRHS) const { return !(*this == inRHS); }
|
||||||
|
|
||||||
|
// Component access
|
||||||
|
const float & operator [] (uint inIndex) const { return (&x)[inIndex]; }
|
||||||
|
float & operator [] (uint inIndex) { return (&x)[inIndex]; }
|
||||||
|
|
||||||
|
// Swizzling (note return value is const to prevent assignment to swizzled results)
|
||||||
|
const float2 swizzle_xy() const { return float2(x, y); }
|
||||||
|
const float2 swizzle_yx() const { return float2(y, x); }
|
||||||
|
const float3 swizzle_xyz() const { return float3(x, y, z); }
|
||||||
|
const float3 swizzle_xzy() const { return float3(x, z, y); }
|
||||||
|
const float3 swizzle_yxz() const { return float3(y, x, z); }
|
||||||
|
const float3 swizzle_yzx() const { return float3(y, z, x); }
|
||||||
|
const float3 swizzle_zxy() const { return float3(z, x, y); }
|
||||||
|
const float3 swizzle_zyx() const { return float3(z, y, x); }
|
||||||
|
|
||||||
|
float x, y, z;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr float3 operator - (const float3 &inA) { return float3(-inA.x, -inA.y, -inA.z); }
|
||||||
|
constexpr float3 operator + (const float3 &inA, const float3 &inB) { return float3(inA.x + inB.x, inA.y + inB.y, inA.z + inB.z); }
|
||||||
|
constexpr float3 operator - (const float3 &inA, const float3 &inB) { return float3(inA.x - inB.x, inA.y - inB.y, inA.z - inB.z); }
|
||||||
|
constexpr float3 operator * (const float3 &inA, const float3 &inB) { return float3(inA.x * inB.x, inA.y * inB.y, inA.z * inB.z); }
|
||||||
|
constexpr float3 operator / (const float3 &inA, const float3 &inB) { return float3(inA.x / inB.x, inA.y / inB.y, inA.z / inB.z); }
|
||||||
|
constexpr float3 operator * (const float3 &inA, float inS) { return float3(inA.x * inS, inA.y * inS, inA.z * inS); }
|
||||||
|
constexpr float3 operator * (float inS, const float3 &inA) { return inA * inS; }
|
||||||
|
constexpr float3 operator / (const float3 &inA, float inS) { return float3(inA.x / inS, inA.y / inS, inA.z / inS); }
|
||||||
|
|
||||||
|
// Dot product
|
||||||
|
constexpr float dot(const float3 &inA, const float3 &inB) { return inA.x * inB.x + inA.y * inB.y + inA.z * inB.z; }
|
||||||
|
|
||||||
|
// Min value
|
||||||
|
constexpr float3 min(const float3 &inA, const float3 &inB) { return float3(min(inA.x, inB.x), min(inA.y, inB.y), min(inA.z, inB.z)); }
|
||||||
|
|
||||||
|
// Max value
|
||||||
|
constexpr float3 max(const float3 &inA, const float3 &inB) { return float3(max(inA.x, inB.x), max(inA.y, inB.y), max(inA.z, inB.z)); }
|
||||||
|
|
||||||
|
// Length
|
||||||
|
inline float length(const float3 &inV) { return sqrt(dot(inV, inV)); }
|
||||||
|
|
||||||
|
// Normalization
|
||||||
|
inline float3 normalize(const float3 &inV) { return inV / length(inV); }
|
||||||
|
|
||||||
|
// Rounding to int
|
||||||
|
inline float3 round(const float3 &inV) { return float3(round(inV.x), round(inV.y), round(inV.z)); }
|
||||||
|
|
||||||
|
// Cross product
|
||||||
|
constexpr float3 cross(const float3 &inA, const float3 &inB) { return float3(inA.y * inB.z - inA.z * inB.y, inA.z * inB.x - inA.x * inB.z, inA.x * inB.y - inA.y * inB.x); }
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// float4
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct int4;
|
||||||
|
|
||||||
|
struct float4
|
||||||
|
{
|
||||||
|
// Constructors
|
||||||
|
inline float4() = default;
|
||||||
|
constexpr float4(const float3 &inV, float inW) : x(inV.x), y(inV.y), z(inV.z), w(inW) { }
|
||||||
|
constexpr float4(float inX, float inY, float inZ, float inW) : x(inX), y(inY), z(inZ), w(inW) { }
|
||||||
|
explicit constexpr float4(float inS) : x(inS), y(inS), z(inS), w(inS) { }
|
||||||
|
explicit constexpr float4(const int4 &inV);
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr float4 & operator += (const float4 &inRHS) { x += inRHS.x; y += inRHS.y; z += inRHS.z; w += inRHS.w; return *this; }
|
||||||
|
constexpr float4 & operator -= (const float4 &inRHS) { x -= inRHS.x; y -= inRHS.y; z -= inRHS.z; w -= inRHS.w; return *this; }
|
||||||
|
constexpr float4 & operator *= (float inRHS) { x *= inRHS; y *= inRHS; z *= inRHS; w *= inRHS; return *this; }
|
||||||
|
constexpr float4 & operator /= (float inRHS) { x /= inRHS; y /= inRHS; z /= inRHS; w /= inRHS; return *this; }
|
||||||
|
constexpr float4 & operator *= (const float4 &inRHS) { x *= inRHS.x; y *= inRHS.y; z *= inRHS.z; w *= inRHS.w; return *this; }
|
||||||
|
constexpr float4 & operator /= (const float4 &inRHS) { x /= inRHS.x; y /= inRHS.y; z /= inRHS.z; w /= inRHS.w; return *this; }
|
||||||
|
|
||||||
|
// Equality
|
||||||
|
constexpr bool operator == (const float4 &inRHS) const { return x == inRHS.x && y == inRHS.y && z == inRHS.z && w == inRHS.w; }
|
||||||
|
constexpr bool operator != (const float4 &inRHS) const { return !(*this == inRHS); }
|
||||||
|
|
||||||
|
// Component access
|
||||||
|
const float & operator [] (uint inIndex) const { return (&x)[inIndex]; }
|
||||||
|
float & operator [] (uint inIndex) { return (&x)[inIndex]; }
|
||||||
|
|
||||||
|
// Swizzling (note return value is const to prevent assignment to swizzled results)
|
||||||
|
const float2 swizzle_xy() const { return float2(x, y); }
|
||||||
|
const float2 swizzle_yx() const { return float2(y, x); }
|
||||||
|
const float3 swizzle_xyz() const { return float3(x, y, z); }
|
||||||
|
const float3 swizzle_xzy() const { return float3(x, z, y); }
|
||||||
|
const float3 swizzle_yxz() const { return float3(y, x, z); }
|
||||||
|
const float3 swizzle_yzx() const { return float3(y, z, x); }
|
||||||
|
const float3 swizzle_zxy() const { return float3(z, x, y); }
|
||||||
|
const float3 swizzle_zyx() const { return float3(z, y, x); }
|
||||||
|
const float4 swizzle_xywz() const { return float4(x, y, w, z); }
|
||||||
|
const float4 swizzle_xwyz() const { return float4(x, w, y, z); }
|
||||||
|
const float4 swizzle_wxyz() const { return float4(w, x, y, z); }
|
||||||
|
|
||||||
|
float x, y, z, w;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr float4 operator - (const float4 &inA) { return float4(-inA.x, -inA.y, -inA.z, -inA.w); }
|
||||||
|
constexpr float4 operator + (const float4 &inA, const float4 &inB) { return float4(inA.x + inB.x, inA.y + inB.y, inA.z + inB.z, inA.w + inB.w); }
|
||||||
|
constexpr float4 operator - (const float4 &inA, const float4 &inB) { return float4(inA.x - inB.x, inA.y - inB.y, inA.z - inB.z, inA.w - inB.w); }
|
||||||
|
constexpr float4 operator * (const float4 &inA, const float4 &inB) { return float4(inA.x * inB.x, inA.y * inB.y, inA.z * inB.z, inA.w * inB.w); }
|
||||||
|
constexpr float4 operator / (const float4 &inA, const float4 &inB) { return float4(inA.x / inB.x, inA.y / inB.y, inA.z / inB.z, inA.w / inB.w); }
|
||||||
|
constexpr float4 operator * (const float4 &inA, float inS) { return float4(inA.x * inS, inA.y * inS, inA.z * inS, inA.w * inS); }
|
||||||
|
constexpr float4 operator * (float inS, const float4 &inA) { return inA * inS; }
|
||||||
|
constexpr float4 operator / (const float4 &inA, float inS) { return float4(inA.x / inS, inA.y / inS, inA.z / inS, inA.w / inS); }
|
||||||
|
|
||||||
|
// Dot product
|
||||||
|
constexpr float dot(const float4 &inA, const float4 &inB) { return inA.x * inB.x + inA.y * inB.y + inA.z * inB.z + inA.w * inB.w; }
|
||||||
|
|
||||||
|
// Min value
|
||||||
|
constexpr float4 min(const float4 &inA, const float4 &inB) { return float4(min(inA.x, inB.x), min(inA.y, inB.y), min(inA.z, inB.z), min(inA.w, inB.w)); }
|
||||||
|
|
||||||
|
// Max value
|
||||||
|
constexpr float4 max(const float4 &inA, const float4 &inB) { return float4(max(inA.x, inB.x), max(inA.y, inB.y), max(inA.z, inB.z), max(inA.w, inB.w)); }
|
||||||
|
|
||||||
|
// Length
|
||||||
|
inline float length(const float4 &inV) { return sqrt(dot(inV, inV)); }
|
||||||
|
|
||||||
|
// Normalization
|
||||||
|
inline float4 normalize(const float4 &inV) { return inV / length(inV); }
|
||||||
|
|
||||||
|
// Rounding to int
|
||||||
|
inline float4 round(const float4 &inV) { return float4(round(inV.x), round(inV.y), round(inV.z), round(inV.w)); }
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// uint3
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct uint3
|
||||||
|
{
|
||||||
|
inline uint3() = default;
|
||||||
|
constexpr uint3(uint32 inX, uint32 inY, uint32 inZ) : x(inX), y(inY), z(inZ) { }
|
||||||
|
explicit constexpr uint3(const float3 &inV) : x(uint32(inV.x)), y(uint32(inV.y)), z(uint32(inV.z)) { }
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr uint3 & operator += (const uint3 &inRHS) { x += inRHS.x; y += inRHS.y; z += inRHS.z; return *this; }
|
||||||
|
constexpr uint3 & operator -= (const uint3 &inRHS) { x -= inRHS.x; y -= inRHS.y; z -= inRHS.z; return *this; }
|
||||||
|
constexpr uint3 & operator *= (uint32 inRHS) { x *= inRHS; y *= inRHS; z *= inRHS; return *this; }
|
||||||
|
constexpr uint3 & operator /= (uint32 inRHS) { x /= inRHS; y /= inRHS; z /= inRHS; return *this; }
|
||||||
|
constexpr uint3 & operator *= (const uint3 &inRHS) { x *= inRHS.x; y *= inRHS.y; z *= inRHS.z; return *this; }
|
||||||
|
constexpr uint3 & operator /= (const uint3 &inRHS) { x /= inRHS.x; y /= inRHS.y; z /= inRHS.z; return *this; }
|
||||||
|
|
||||||
|
// Equality
|
||||||
|
constexpr bool operator == (const uint3 &inRHS) const { return x == inRHS.x && y == inRHS.y && z == inRHS.z; }
|
||||||
|
constexpr bool operator != (const uint3 &inRHS) const { return !(*this == inRHS); }
|
||||||
|
|
||||||
|
// Component access
|
||||||
|
const uint32 & operator [] (uint inIndex) const { return (&x)[inIndex]; }
|
||||||
|
uint32 & operator [] (uint inIndex) { return (&x)[inIndex]; }
|
||||||
|
|
||||||
|
// Swizzling (note return value is const to prevent assignment to swizzled results)
|
||||||
|
const uint3 swizzle_xyz() const { return uint3(x, y, z); }
|
||||||
|
const uint3 swizzle_xzy() const { return uint3(x, z, y); }
|
||||||
|
const uint3 swizzle_yxz() const { return uint3(y, x, z); }
|
||||||
|
const uint3 swizzle_yzx() const { return uint3(y, z, x); }
|
||||||
|
const uint3 swizzle_zxy() const { return uint3(z, x, y); }
|
||||||
|
const uint3 swizzle_zyx() const { return uint3(z, y, x); }
|
||||||
|
|
||||||
|
uint32 x, y, z;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr uint3 operator + (const uint3 &inA, const uint3 &inB) { return uint3(inA.x + inB.x, inA.y + inB.y, inA.z + inB.z); }
|
||||||
|
constexpr uint3 operator - (const uint3 &inA, const uint3 &inB) { return uint3(inA.x - inB.x, inA.y - inB.y, inA.z - inB.z); }
|
||||||
|
constexpr uint3 operator * (const uint3 &inA, const uint3 &inB) { return uint3(inA.x * inB.x, inA.y * inB.y, inA.z * inB.z); }
|
||||||
|
constexpr uint3 operator / (const uint3 &inA, const uint3 &inB) { return uint3(inA.x / inB.x, inA.y / inB.y, inA.z / inB.z); }
|
||||||
|
constexpr uint3 operator * (const uint3 &inA, uint32 inS) { return uint3(inA.x * inS, inA.y * inS, inA.z * inS); }
|
||||||
|
constexpr uint3 operator * (uint32 inS, const uint3 &inA) { return inA * inS; }
|
||||||
|
constexpr uint3 operator / (const uint3 &inA, uint32 inS) { return uint3(inA.x / inS, inA.y / inS, inA.z / inS); }
|
||||||
|
|
||||||
|
// Dot product
|
||||||
|
constexpr uint32 dot(const uint3 &inA, const uint3 &inB) { return inA.x * inB.x + inA.y * inB.y + inA.z * inB.z; }
|
||||||
|
|
||||||
|
// Min value
|
||||||
|
constexpr uint3 min(const uint3 &inA, const uint3 &inB) { return uint3(min(inA.x, inB.x), min(inA.y, inB.y), min(inA.z, inB.z)); }
|
||||||
|
|
||||||
|
// Max value
|
||||||
|
constexpr uint3 max(const uint3 &inA, const uint3 &inB) { return uint3(max(inA.x, inB.x), max(inA.y, inB.y), max(inA.z, inB.z)); }
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// uint4
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct uint4
|
||||||
|
{
|
||||||
|
// Constructors
|
||||||
|
inline uint4() = default;
|
||||||
|
constexpr uint4(const uint3 &inV, uint32 inW) : x(inV.x), y(inV.y), z(inV.z), w(inW) { }
|
||||||
|
constexpr uint4(uint32 inX, uint32 inY, uint32 inZ, uint32 inW) : x(inX), y(inY), z(inZ), w(inW) { }
|
||||||
|
explicit constexpr uint4(uint32 inS) : x(inS), y(inS), z(inS), w(inS) { }
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr uint4 & operator += (const uint4 &inRHS) { x += inRHS.x; y += inRHS.y; z += inRHS.z; w += inRHS.w; return *this; }
|
||||||
|
constexpr uint4 & operator -= (const uint4 &inRHS) { x -= inRHS.x; y -= inRHS.y; z -= inRHS.z; w -= inRHS.w; return *this; }
|
||||||
|
constexpr uint4 & operator *= (uint32 inRHS) { x *= inRHS; y *= inRHS; z *= inRHS; w *= inRHS; return *this; }
|
||||||
|
constexpr uint4 & operator /= (uint32 inRHS) { x /= inRHS; y /= inRHS; z /= inRHS; w /= inRHS; return *this; }
|
||||||
|
constexpr uint4 & operator *= (const uint4 &inRHS) { x *= inRHS.x; y *= inRHS.y; z *= inRHS.z; w *= inRHS.w; return *this; }
|
||||||
|
constexpr uint4 & operator /= (const uint4 &inRHS) { x /= inRHS.x; y /= inRHS.y; z /= inRHS.z; w /= inRHS.w; return *this; }
|
||||||
|
|
||||||
|
// Equality
|
||||||
|
constexpr bool operator == (const uint4 &inRHS) const { return x == inRHS.x && y == inRHS.y && z == inRHS.z && w == inRHS.w; }
|
||||||
|
constexpr bool operator != (const uint4 &inRHS) const { return !(*this == inRHS); }
|
||||||
|
|
||||||
|
// Component access
|
||||||
|
const uint32 & operator [] (uint inIndex) const { return (&x)[inIndex]; }
|
||||||
|
uint32 & operator [] (uint inIndex) { return (&x)[inIndex]; }
|
||||||
|
|
||||||
|
// Swizzling (note return value is const to prevent assignment to swizzled results)
|
||||||
|
const uint3 swizzle_xyz() const { return uint3(x, y, z); }
|
||||||
|
const uint3 swizzle_xzy() const { return uint3(x, z, y); }
|
||||||
|
const uint3 swizzle_yxz() const { return uint3(y, x, z); }
|
||||||
|
const uint3 swizzle_yzx() const { return uint3(y, z, x); }
|
||||||
|
const uint3 swizzle_zxy() const { return uint3(z, x, y); }
|
||||||
|
const uint3 swizzle_zyx() const { return uint3(z, y, x); }
|
||||||
|
const uint4 swizzle_xywz() const { return uint4(x, y, w, z); }
|
||||||
|
const uint4 swizzle_xwyz() const { return uint4(x, w, y, z); }
|
||||||
|
const uint4 swizzle_wxyz() const { return uint4(w, x, y, z); }
|
||||||
|
|
||||||
|
uint32 x, y, z, w;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr uint4 operator + (const uint4 &inA, const uint4 &inB) { return uint4(inA.x + inB.x, inA.y + inB.y, inA.z + inB.z, inA.w + inB.w); }
|
||||||
|
constexpr uint4 operator - (const uint4 &inA, const uint4 &inB) { return uint4(inA.x - inB.x, inA.y - inB.y, inA.z - inB.z, inA.w - inB.w); }
|
||||||
|
constexpr uint4 operator * (const uint4 &inA, const uint4 &inB) { return uint4(inA.x * inB.x, inA.y * inB.y, inA.z * inB.z, inA.w * inB.w); }
|
||||||
|
constexpr uint4 operator / (const uint4 &inA, const uint4 &inB) { return uint4(inA.x / inB.x, inA.y / inB.y, inA.z / inB.z, inA.w / inB.w); }
|
||||||
|
constexpr uint4 operator * (const uint4 &inA, uint32 inS) { return uint4(inA.x * inS, inA.y * inS, inA.z * inS, inA.w * inS); }
|
||||||
|
constexpr uint4 operator * (uint32 inS, const uint4 &inA) { return inA * inS; }
|
||||||
|
constexpr uint4 operator / (const uint4 &inA, uint32 inS) { return uint4(inA.x / inS, inA.y / inS, inA.z / inS, inA.w / inS); }
|
||||||
|
|
||||||
|
// Dot product
|
||||||
|
constexpr uint32 dot(const uint4 &inA, const uint4 &inB) { return inA.x * inB.x + inA.y * inB.y + inA.z * inB.z + inA.w * inB.w; }
|
||||||
|
|
||||||
|
// Min value
|
||||||
|
constexpr uint4 min(const uint4 &inA, const uint4 &inB) { return uint4(min(inA.x, inB.x), min(inA.y, inB.y), min(inA.z, inB.z), min(inA.w, inB.w)); }
|
||||||
|
|
||||||
|
// Max value
|
||||||
|
constexpr uint4 max(const uint4 &inA, const uint4 &inB) { return uint4(max(inA.x, inB.x), max(inA.y, inB.y), max(inA.z, inB.z), max(inA.w, inB.w)); }
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// int3
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct int3
|
||||||
|
{
|
||||||
|
inline int3() = default;
|
||||||
|
constexpr int3(int inX, int inY, int inZ) : x(inX), y(inY), z(inZ) { }
|
||||||
|
explicit constexpr int3(const float3 &inV) : x(int(inV.x)), y(int(inV.y)), z(int(inV.z)) { }
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr int3 & operator += (const int3 &inRHS) { x += inRHS.x; y += inRHS.y; z += inRHS.z; return *this; }
|
||||||
|
constexpr int3 & operator -= (const int3 &inRHS) { x -= inRHS.x; y -= inRHS.y; z -= inRHS.z; return *this; }
|
||||||
|
constexpr int3 & operator *= (int inRHS) { x *= inRHS; y *= inRHS; z *= inRHS; return *this; }
|
||||||
|
constexpr int3 & operator /= (int inRHS) { x /= inRHS; y /= inRHS; z /= inRHS; return *this; }
|
||||||
|
constexpr int3 & operator *= (const int3 &inRHS) { x *= inRHS.x; y *= inRHS.y; z *= inRHS.z; return *this; }
|
||||||
|
constexpr int3 & operator /= (const int3 &inRHS) { x /= inRHS.x; y /= inRHS.y; z /= inRHS.z; return *this; }
|
||||||
|
|
||||||
|
// Equality
|
||||||
|
constexpr bool operator == (const int3 &inRHS) const { return x == inRHS.x && y == inRHS.y && z == inRHS.z; }
|
||||||
|
constexpr bool operator != (const int3 &inRHS) const { return !(*this == inRHS); }
|
||||||
|
|
||||||
|
// Component access
|
||||||
|
const int & operator [] (uint inIndex) const { return (&x)[inIndex]; }
|
||||||
|
int & operator [] (uint inIndex) { return (&x)[inIndex]; }
|
||||||
|
|
||||||
|
// Swizzling (note return value is const to prevent assignment to swizzled results)
|
||||||
|
const int3 swizzle_xyz() const { return int3(x, y, z); }
|
||||||
|
const int3 swizzle_xzy() const { return int3(x, z, y); }
|
||||||
|
const int3 swizzle_yxz() const { return int3(y, x, z); }
|
||||||
|
const int3 swizzle_yzx() const { return int3(y, z, x); }
|
||||||
|
const int3 swizzle_zxy() const { return int3(z, x, y); }
|
||||||
|
const int3 swizzle_zyx() const { return int3(z, y, x); }
|
||||||
|
|
||||||
|
int x, y, z;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr int3 operator - (const int3 &inA) { return int3(-inA.x, -inA.y, -inA.z); }
|
||||||
|
constexpr int3 operator + (const int3 &inA, const int3 &inB) { return int3(inA.x + inB.x, inA.y + inB.y, inA.z + inB.z); }
|
||||||
|
constexpr int3 operator - (const int3 &inA, const int3 &inB) { return int3(inA.x - inB.x, inA.y - inB.y, inA.z - inB.z); }
|
||||||
|
constexpr int3 operator * (const int3 &inA, const int3 &inB) { return int3(inA.x * inB.x, inA.y * inB.y, inA.z * inB.z); }
|
||||||
|
constexpr int3 operator / (const int3 &inA, const int3 &inB) { return int3(inA.x / inB.x, inA.y / inB.y, inA.z / inB.z); }
|
||||||
|
constexpr int3 operator * (const int3 &inA, int inS) { return int3(inA.x * inS, inA.y * inS, inA.z * inS); }
|
||||||
|
constexpr int3 operator * (int inS, const int3 &inA) { return inA * inS; }
|
||||||
|
constexpr int3 operator / (const int3 &inA, int inS) { return int3(inA.x / inS, inA.y / inS, inA.z / inS); }
|
||||||
|
|
||||||
|
// Dot product
|
||||||
|
constexpr int dot(const int3 &inA, const int3 &inB) { return inA.x * inB.x + inA.y * inB.y + inA.z * inB.z; }
|
||||||
|
|
||||||
|
// Min value
|
||||||
|
constexpr int3 min(const int3 &inA, const int3 &inB) { return int3(min(inA.x, inB.x), min(inA.y, inB.y), min(inA.z, inB.z)); }
|
||||||
|
|
||||||
|
// Max value
|
||||||
|
constexpr int3 max(const int3 &inA, const int3 &inB) { return int3(max(inA.x, inB.x), max(inA.y, inB.y), max(inA.z, inB.z)); }
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// int4
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct int4
|
||||||
|
{
|
||||||
|
// Constructors
|
||||||
|
inline int4() = default;
|
||||||
|
constexpr int4(const int3 &inV, int inW) : x(inV.x), y(inV.y), z(inV.z), w(inW) { }
|
||||||
|
constexpr int4(int inX, int inY, int inZ, int inW) : x(inX), y(inY), z(inZ), w(inW) { }
|
||||||
|
explicit constexpr int4(int inS) : x(inS), y(inS), z(inS), w(inS) { }
|
||||||
|
explicit constexpr int4(const float4 &inV) : x(int(inV.x)), y(int(inV.y)), z(int(inV.z)), w(int(inV.w)) { }
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr int4 & operator += (const int4 &inRHS) { x += inRHS.x; y += inRHS.y; z += inRHS.z; w += inRHS.w; return *this; }
|
||||||
|
constexpr int4 & operator -= (const int4 &inRHS) { x -= inRHS.x; y -= inRHS.y; z -= inRHS.z; w -= inRHS.w; return *this; }
|
||||||
|
constexpr int4 & operator *= (int inRHS) { x *= inRHS; y *= inRHS; z *= inRHS; w *= inRHS; return *this; }
|
||||||
|
constexpr int4 & operator /= (int inRHS) { x /= inRHS; y /= inRHS; z /= inRHS; w /= inRHS; return *this; }
|
||||||
|
constexpr int4 & operator *= (const int4 &inRHS) { x *= inRHS.x; y *= inRHS.y; z *= inRHS.z; w *= inRHS.w; return *this; }
|
||||||
|
constexpr int4 & operator /= (const int4 &inRHS) { x /= inRHS.x; y /= inRHS.y; z /= inRHS.z; w /= inRHS.w; return *this; }
|
||||||
|
|
||||||
|
// Equality
|
||||||
|
constexpr bool operator == (const int4 &inRHS) const { return x == inRHS.x && y == inRHS.y && z == inRHS.z && w == inRHS.w; }
|
||||||
|
constexpr bool operator != (const int4 &inRHS) const { return !(*this == inRHS); }
|
||||||
|
|
||||||
|
// Component access
|
||||||
|
const int & operator [] (uint inIndex) const { return (&x)[inIndex]; }
|
||||||
|
int & operator [] (uint inIndex) { return (&x)[inIndex]; }
|
||||||
|
|
||||||
|
// Swizzling (note return value is const to prevent assignment to swizzled results)
|
||||||
|
const int3 swizzle_xyz() const { return int3(x, y, z); }
|
||||||
|
const int3 swizzle_xzy() const { return int3(x, z, y); }
|
||||||
|
const int3 swizzle_yxz() const { return int3(y, x, z); }
|
||||||
|
const int3 swizzle_yzx() const { return int3(y, z, x); }
|
||||||
|
const int3 swizzle_zxy() const { return int3(z, x, y); }
|
||||||
|
const int3 swizzle_zyx() const { return int3(z, y, x); }
|
||||||
|
const int4 swizzle_xywz() const { return int4(x, y, w, z); }
|
||||||
|
const int4 swizzle_xwyz() const { return int4(x, w, y, z); }
|
||||||
|
const int4 swizzle_wxyz() const { return int4(w, x, y, z); }
|
||||||
|
|
||||||
|
int x, y, z, w;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
constexpr int4 operator - (const int4 &inA) { return int4(-inA.x, -inA.y, -inA.z, -inA.w); }
|
||||||
|
constexpr int4 operator + (const int4 &inA, const int4 &inB) { return int4(inA.x + inB.x, inA.y + inB.y, inA.z + inB.z, inA.w + inB.w); }
|
||||||
|
constexpr int4 operator - (const int4 &inA, const int4 &inB) { return int4(inA.x - inB.x, inA.y - inB.y, inA.z - inB.z, inA.w - inB.w); }
|
||||||
|
constexpr int4 operator * (const int4 &inA, const int4 &inB) { return int4(inA.x * inB.x, inA.y * inB.y, inA.z * inB.z, inA.w * inB.w); }
|
||||||
|
constexpr int4 operator / (const int4 &inA, const int4 &inB) { return int4(inA.x / inB.x, inA.y / inB.y, inA.z / inB.z, inA.w / inB.w); }
|
||||||
|
constexpr int4 operator * (const int4 &inA, int inS) { return int4(inA.x * inS, inA.y * inS, inA.z * inS, inA.w * inS); }
|
||||||
|
constexpr int4 operator * (int inS, const int4 &inA) { return inA * inS; }
|
||||||
|
constexpr int4 operator / (const int4 &inA, int inS) { return int4(inA.x / inS, inA.y / inS, inA.z / inS, inA.w / inS); }
|
||||||
|
|
||||||
|
// Dot product
|
||||||
|
constexpr int dot(const int4 &inA, const int4 &inB) { return inA.x * inB.x + inA.y * inB.y + inA.z * inB.z + inA.w * inB.w; }
|
||||||
|
|
||||||
|
// Min value
|
||||||
|
constexpr int4 min(const int4 &inA, const int4 &inB) { return int4(min(inA.x, inB.x), min(inA.y, inB.y), min(inA.z, inB.z), min(inA.w, inB.w)); }
|
||||||
|
|
||||||
|
// Max value
|
||||||
|
constexpr int4 max(const int4 &inA, const int4 &inB) { return int4(max(inA.x, inB.x), max(inA.y, inB.y), max(inA.z, inB.z), max(inA.w, inB.w)); }
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Mat44
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct Mat44
|
||||||
|
{
|
||||||
|
// Constructors
|
||||||
|
inline Mat44() = default;
|
||||||
|
constexpr Mat44(const float4 &inC0, const float4 &inC1, const float4 &inC2, const float4 &inC3) : c { inC0, inC1, inC2, inC3 } { }
|
||||||
|
|
||||||
|
// Columns
|
||||||
|
float4 & operator [] (uint inIndex) { return c[inIndex]; }
|
||||||
|
const float4 & operator [] (uint inIndex) const { return c[inIndex]; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
float4 c[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Other types
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
using Quat = float4;
|
||||||
|
using Plane = float4;
|
||||||
|
|
||||||
|
// Clamp value
|
||||||
|
template <class T>
|
||||||
|
constexpr T clamp(const T &inValue, const T &inMinValue, const T &inMaxValue)
|
||||||
|
{
|
||||||
|
return min(max(inValue, inMinValue), inMaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atomic add
|
||||||
|
template <class T>
|
||||||
|
T JPH_AtomicAdd(T &ioT, const T &inValue)
|
||||||
|
{
|
||||||
|
std::atomic<T> *value = reinterpret_cast<std::atomic<T> *>(&ioT);
|
||||||
|
return value->fetch_add(inValue) + inValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitcast float4 to int4
|
||||||
|
inline int4 asint(const float4 &inV) { return int4(BitCast<int>(inV.x), BitCast<int>(inV.y), BitCast<int>(inV.z), BitCast<int>(inV.w)); }
|
||||||
|
|
||||||
|
// Functions that couldn't be declared earlier
|
||||||
|
constexpr float3::float3(const uint3 &inV) : x(float(inV.x)), y(float(inV.y)), z(float(inV.z)) { }
|
||||||
|
constexpr float4::float4(const int4 &inV) : x(float(inV.x)), y(float(inV.y)), z(float(inV.z)), w(float(inV.w)) { }
|
||||||
|
|
||||||
|
// Swizzle operators
|
||||||
|
#define xy swizzle_xy()
|
||||||
|
#define yx swizzle_yx()
|
||||||
|
#define xyz swizzle_xyz()
|
||||||
|
#define xzy swizzle_xzy()
|
||||||
|
#define yxz swizzle_yxz()
|
||||||
|
#define yzx swizzle_yzx()
|
||||||
|
#define zxy swizzle_zxy()
|
||||||
|
#define zyx swizzle_zyx()
|
||||||
|
#define xywz swizzle_xywz()
|
||||||
|
#define xwyz swizzle_xwyz()
|
||||||
|
#define wxyz swizzle_wxyz()
|
||||||
|
|
||||||
|
} // HLSLToCPP
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
29
thirdparty/JoltPhysics/Jolt/Compute/CPU/ShaderWrapper.h
vendored
Normal file
29
thirdparty/JoltPhysics/Jolt/Compute/CPU/ShaderWrapper.h
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_CPU_COMPUTE
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
namespace HLSLToCPP { struct uint3; }
|
||||||
|
|
||||||
|
/// Wraps a compute shader to allow calling it from C++
|
||||||
|
class ShaderWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Destructor
|
||||||
|
virtual ~ShaderWrapper() = default;
|
||||||
|
|
||||||
|
/// Bind buffer to shader
|
||||||
|
virtual void Bind(const char *inName, void *inData, uint64 inSize) = 0;
|
||||||
|
|
||||||
|
/// Execute a single shader thread
|
||||||
|
virtual void Main(const HLSLToCPP::uint3 &inThreadID) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_CPU_COMPUTE
|
||||||
75
thirdparty/JoltPhysics/Jolt/Compute/CPU/WrapShaderBegin.h
vendored
Normal file
75
thirdparty/JoltPhysics/Jolt/Compute/CPU/WrapShaderBegin.h
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Core/HashCombine.h>
|
||||||
|
#include <Jolt/Compute/CPU/ComputeSystemCPU.h>
|
||||||
|
#include <Jolt/Compute/CPU/ShaderWrapper.h>
|
||||||
|
#include <Jolt/Compute/CPU/HLSLToCPP.h>
|
||||||
|
|
||||||
|
/// @cond INTERNAL
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5031) // #pragma warning(pop): likely mismatch, popping warning state pushed in different file
|
||||||
|
|
||||||
|
#define JPH_SHADER_OVERRIDE_MACROS
|
||||||
|
#define JPH_SHADER_GENERATE_WRAPPER
|
||||||
|
|
||||||
|
#define JPH_SHADER_CONSTANT(type, name, value) inline static constexpr type name = value;
|
||||||
|
|
||||||
|
#define JPH_SHADER_CONSTANTS_BEGIN(type, name) struct type { alignas(16) int dummy; } name; // Ensure that the first constant is 16 byte aligned
|
||||||
|
#define JPH_SHADER_CONSTANTS_MEMBER(type, name) type c##name;
|
||||||
|
#define JPH_SHADER_CONSTANTS_END(type)
|
||||||
|
|
||||||
|
#define JPH_SHADER_BUFFER(type) const type *
|
||||||
|
#define JPH_SHADER_RW_BUFFER(type) type *
|
||||||
|
|
||||||
|
#define JPH_SHADER_BIND_BEGIN(name)
|
||||||
|
#define JPH_SHADER_BIND_END(name)
|
||||||
|
#define JPH_SHADER_BIND_BUFFER(type, name) const type *name = nullptr;
|
||||||
|
#define JPH_SHADER_BIND_RW_BUFFER(type, name) type *name = nullptr;
|
||||||
|
|
||||||
|
#define JPH_SHADER_FUNCTION_BEGIN(return_type, name, group_size_x, group_size_y, group_size_z) \
|
||||||
|
virtual void Main(
|
||||||
|
#define JPH_SHADER_PARAM_THREAD_ID(name) const HLSLToCPP::uint3 &name
|
||||||
|
#define JPH_SHADER_FUNCTION_END ) override
|
||||||
|
|
||||||
|
#define JPH_SHADER_STRUCT_BEGIN(name) struct name {
|
||||||
|
#define JPH_SHADER_STRUCT_MEMBER(type, name) type m##name;
|
||||||
|
#define JPH_SHADER_STRUCT_END(name) };
|
||||||
|
|
||||||
|
#define JPH_TO_STRING(name) JPH_TO_STRING2(name)
|
||||||
|
#define JPH_TO_STRING2(name) #name
|
||||||
|
|
||||||
|
#define JPH_SHADER_CLASS_NAME(name) JPH_SHADER_CLASS_NAME2(name)
|
||||||
|
#define JPH_SHADER_CLASS_NAME2(name) name##ShaderWrapper
|
||||||
|
|
||||||
|
#define JPH_IN(type) const type &
|
||||||
|
#define JPH_OUT(type) type &
|
||||||
|
#define JPH_IN_OUT(type) type &
|
||||||
|
|
||||||
|
// Namespace to prevent 'using' from leaking out
|
||||||
|
namespace ShaderWrappers {
|
||||||
|
|
||||||
|
using namespace HLSLToCPP;
|
||||||
|
|
||||||
|
class JPH_SHADER_CLASS_NAME(JPH_SHADER_NAME) : public ShaderWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Define types
|
||||||
|
using JPH_float = float;
|
||||||
|
using JPH_float3 = HLSLToCPP::float3;
|
||||||
|
using JPH_float4 = HLSLToCPP::float4;
|
||||||
|
using JPH_uint = uint;
|
||||||
|
using JPH_uint3 = HLSLToCPP::uint3;
|
||||||
|
using JPH_uint4 = HLSLToCPP::uint4;
|
||||||
|
using JPH_int = int;
|
||||||
|
using JPH_int3 = HLSLToCPP::int3;
|
||||||
|
using JPH_int4 = HLSLToCPP::int4;
|
||||||
|
using JPH_Quat = HLSLToCPP::Quat;
|
||||||
|
using JPH_Plane = HLSLToCPP::Plane;
|
||||||
|
using JPH_Mat44 = HLSLToCPP::Mat44;
|
||||||
|
|
||||||
|
// Now the shader code should be included followed by WrapShaderBindings.h
|
||||||
|
|
||||||
|
/// @endcond
|
||||||
40
thirdparty/JoltPhysics/Jolt/Compute/CPU/WrapShaderBindings.h
vendored
Normal file
40
thirdparty/JoltPhysics/Jolt/Compute/CPU/WrapShaderBindings.h
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
/// @cond INTERNAL
|
||||||
|
|
||||||
|
// First WrapShaderBegin.h should have been included, then the shader code
|
||||||
|
|
||||||
|
/// Bind a buffer to the shader
|
||||||
|
virtual void Bind(const char *inName, void *inData, uint64 inSize) override
|
||||||
|
{
|
||||||
|
// Don't redefine constants
|
||||||
|
#undef JPH_SHADER_CONSTANT
|
||||||
|
#define JPH_SHADER_CONSTANT(type, name, value)
|
||||||
|
|
||||||
|
// Don't redefine structs
|
||||||
|
#undef JPH_SHADER_STRUCT_BEGIN
|
||||||
|
#undef JPH_SHADER_STRUCT_MEMBER
|
||||||
|
#undef JPH_SHADER_STRUCT_END
|
||||||
|
#define JPH_SHADER_STRUCT_BEGIN(name)
|
||||||
|
#define JPH_SHADER_STRUCT_MEMBER(type, name)
|
||||||
|
#define JPH_SHADER_STRUCT_END(name)
|
||||||
|
|
||||||
|
// When a constant buffer is bound, copy the data into the members
|
||||||
|
#undef JPH_SHADER_CONSTANTS_BEGIN
|
||||||
|
#undef JPH_SHADER_CONSTANTS_MEMBER
|
||||||
|
#define JPH_SHADER_CONSTANTS_BEGIN(type, name) case HashString(#name): memcpy(&name + 1, inData, size_t(inSize)); break; // Very hacky way to get the address of the first constant and to copy the entire block of constants
|
||||||
|
#define JPH_SHADER_CONSTANTS_MEMBER(type, name)
|
||||||
|
|
||||||
|
// When a buffer is bound, set the pointer
|
||||||
|
#undef JPH_SHADER_BIND_BUFFER
|
||||||
|
#undef JPH_SHADER_BIND_RW_BUFFER
|
||||||
|
#define JPH_SHADER_BIND_BUFFER(type, name) case HashString(#name): name = (const type *)inData; break;
|
||||||
|
#define JPH_SHADER_BIND_RW_BUFFER(type, name) case HashString(#name): name = (type *)inData; break;
|
||||||
|
|
||||||
|
switch (HashString(inName))
|
||||||
|
{
|
||||||
|
// Now include the shader bindings followed by WrapShaderEnd.h
|
||||||
|
|
||||||
|
/// @endcond
|
||||||
61
thirdparty/JoltPhysics/Jolt/Compute/CPU/WrapShaderEnd.h
vendored
Normal file
61
thirdparty/JoltPhysics/Jolt/Compute/CPU/WrapShaderEnd.h
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
/// @cond INTERNAL
|
||||||
|
|
||||||
|
// WrapShaderBindings.h should have been included followed by the shader bindings
|
||||||
|
|
||||||
|
default:
|
||||||
|
JPH_ASSERT(false, "Buffer cannot be bound to this shader");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Factory function to create a shader wrapper for this shader
|
||||||
|
static ShaderWrapper * sCreate()
|
||||||
|
{
|
||||||
|
return new JPH_SHADER_CLASS_NAME(JPH_SHADER_NAME)();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // ShaderWrappers
|
||||||
|
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
// Stop clang from complaining that the register function is missing a prototype
|
||||||
|
JPH_SHADER_WRAPPER_FUNCTION(, JPH_SHADER_NAME);
|
||||||
|
|
||||||
|
/// Register this wrapper
|
||||||
|
JPH_SHADER_WRAPPER_FUNCTION(inComputeSystem, JPH_SHADER_NAME)
|
||||||
|
{
|
||||||
|
inComputeSystem->RegisterShader(JPH_TO_STRING(JPH_SHADER_NAME), ShaderWrappers::JPH_SHADER_CLASS_NAME(JPH_SHADER_NAME)::sCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef JPH_SHADER_OVERRIDE_MACROS
|
||||||
|
#undef JPH_SHADER_GENERATE_WRAPPER
|
||||||
|
#undef JPH_SHADER_CONSTANT
|
||||||
|
#undef JPH_SHADER_CONSTANTS_BEGIN
|
||||||
|
#undef JPH_SHADER_CONSTANTS_MEMBER
|
||||||
|
#undef JPH_SHADER_CONSTANTS_END
|
||||||
|
#undef JPH_SHADER_BUFFER
|
||||||
|
#undef JPH_SHADER_RW_BUFFER
|
||||||
|
#undef JPH_SHADER_BIND_BEGIN
|
||||||
|
#undef JPH_SHADER_BIND_END
|
||||||
|
#undef JPH_SHADER_BIND_BUFFER
|
||||||
|
#undef JPH_SHADER_BIND_RW_BUFFER
|
||||||
|
#undef JPH_SHADER_FUNCTION_BEGIN
|
||||||
|
#undef JPH_SHADER_PARAM_THREAD_ID
|
||||||
|
#undef JPH_SHADER_FUNCTION_END
|
||||||
|
#undef JPH_SHADER_STRUCT_BEGIN
|
||||||
|
#undef JPH_SHADER_STRUCT_MEMBER
|
||||||
|
#undef JPH_SHADER_STRUCT_END
|
||||||
|
#undef JPH_TO_STRING
|
||||||
|
#undef JPH_TO_STRING2
|
||||||
|
#undef JPH_SHADER_CLASS_NAME
|
||||||
|
#undef JPH_SHADER_CLASS_NAME2
|
||||||
|
#undef JPH_OUT
|
||||||
|
#undef JPH_IN_OUT
|
||||||
|
#undef JPH_SHADER_NAME
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
69
thirdparty/JoltPhysics/Jolt/Compute/ComputeBuffer.h
vendored
Normal file
69
thirdparty/JoltPhysics/Jolt/Compute/ComputeBuffer.h
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/Reference.h>
|
||||||
|
#include <Jolt/Core/NonCopyable.h>
|
||||||
|
#include <Jolt/Core/Result.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class ComputeBuffer;
|
||||||
|
using ComputeBufferResult = Result<Ref<ComputeBuffer>>;
|
||||||
|
|
||||||
|
/// Buffer that can be read from / written to by a compute shader
|
||||||
|
class JPH_EXPORT ComputeBuffer : public RefTarget<ComputeBuffer>, public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Type of buffer
|
||||||
|
enum class EType
|
||||||
|
{
|
||||||
|
UploadBuffer, ///< Buffer that can be written on the CPU and then uploaded to the GPU.
|
||||||
|
ReadbackBuffer, ///< Buffer to be sent from the GPU to the CPU, used to read back data.
|
||||||
|
ConstantBuffer, ///< A smallish buffer that is used to pass constants to a shader.
|
||||||
|
Buffer, ///< Buffer that can be read from by a shader. Must be initialized with data at construction time and is read only thereafter.
|
||||||
|
RWBuffer, ///< Buffer that can be read from and written to by a shader.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Constructor / Destructor
|
||||||
|
ComputeBuffer(EType inType, uint64 inSize, uint inStride) : mType(inType), mSize(inSize), mStride(inStride) { }
|
||||||
|
virtual ~ComputeBuffer() { JPH_ASSERT(!mIsMapped); }
|
||||||
|
|
||||||
|
/// Properties
|
||||||
|
EType GetType() const { return mType; }
|
||||||
|
uint64 GetSize() const { return mSize; }
|
||||||
|
uint GetStride() const { return mStride; }
|
||||||
|
|
||||||
|
/// Mode in which the buffer is accessed
|
||||||
|
enum class EMode
|
||||||
|
{
|
||||||
|
Read, ///< Read only access to the buffer
|
||||||
|
Write, ///< Write only access to the buffer (this will discard all previous data in the buffer)
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Map / unmap buffer (get pointer to data).
|
||||||
|
void * Map(EMode inMode) { JPH_ASSERT(!mIsMapped); JPH_IF_ENABLE_ASSERTS(mIsMapped = true;) return MapInternal(inMode); }
|
||||||
|
template <typename T> T * Map(EMode inMode) { JPH_ASSERT(!mIsMapped); JPH_IF_ENABLE_ASSERTS(mIsMapped = true;) JPH_ASSERT(sizeof(T) == mStride); return reinterpret_cast<T *>(MapInternal(inMode)); }
|
||||||
|
void Unmap() { JPH_ASSERT(mIsMapped); JPH_IF_ENABLE_ASSERTS(mIsMapped = false;) UnmapInternal(); }
|
||||||
|
|
||||||
|
/// Create a readback buffer of the same size and stride that can be used to read the data stored in this buffer on CPU.
|
||||||
|
/// Note that this could also be implemented as 'return this' in case the underlying implementation allows locking GPU data on CPU directly.
|
||||||
|
virtual ComputeBufferResult CreateReadBackBuffer() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
EType mType;
|
||||||
|
uint64 mSize;
|
||||||
|
uint mStride;
|
||||||
|
#ifdef JPH_ENABLE_ASSERTS
|
||||||
|
bool mIsMapped = false;
|
||||||
|
#endif // JPH_ENABLE_ASSERTS
|
||||||
|
|
||||||
|
virtual void * MapInternal(EMode inMode) = 0;
|
||||||
|
virtual void UnmapInternal() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
83
thirdparty/JoltPhysics/Jolt/Compute/ComputeQueue.h
vendored
Normal file
83
thirdparty/JoltPhysics/Jolt/Compute/ComputeQueue.h
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/Reference.h>
|
||||||
|
#include <Jolt/Core/NonCopyable.h>
|
||||||
|
#include <Jolt/Core/Result.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class ComputeShader;
|
||||||
|
class ComputeBuffer;
|
||||||
|
|
||||||
|
/// A command queue for executing compute workloads on the GPU.
|
||||||
|
///
|
||||||
|
/// Note that only a single thread should be using a ComputeQueue at any time (although an implementation could be made that is thread safe).
|
||||||
|
class JPH_EXPORT ComputeQueue : public RefTarget<ComputeQueue>, public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~ComputeQueue() = default;
|
||||||
|
|
||||||
|
/// Activate a shader. Shader must be set first before buffers can be bound.
|
||||||
|
/// After every Dispatch call, the shader must be set again and all buffers must be bound again.
|
||||||
|
virtual void SetShader(const ComputeShader *inShader) = 0;
|
||||||
|
|
||||||
|
/// If a barrier should be placed before accessing the buffer
|
||||||
|
enum class EBarrier
|
||||||
|
{
|
||||||
|
Yes,
|
||||||
|
No
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Bind a constant buffer to the shader. Note that the contents of the buffer cannot be modified until execution finishes.
|
||||||
|
/// A reference to the buffer is added to make sure it stays alive until execution finishes.
|
||||||
|
/// @param inName Name of the buffer as specified in the shader.
|
||||||
|
/// @param inBuffer The buffer to bind.
|
||||||
|
virtual void SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer) = 0;
|
||||||
|
|
||||||
|
/// Bind a read only buffer to the shader. Note that the contents of the buffer cannot be modified on CPU until execution finishes (only relevant for buffers of type UploadBuffer).
|
||||||
|
/// A reference to the buffer is added to make sure it stays alive until execution finishes.
|
||||||
|
/// @param inName Name of the buffer as specified in the shader.
|
||||||
|
/// @param inBuffer The buffer to bind.
|
||||||
|
virtual void SetBuffer(const char *inName, const ComputeBuffer *inBuffer) = 0;
|
||||||
|
|
||||||
|
/// Bind a read/write buffer to the shader.
|
||||||
|
/// A reference to the buffer is added to make sure it stays alive until execution finishes.
|
||||||
|
/// @param inName Name of the buffer as specified in the shader.
|
||||||
|
/// @param inBuffer The buffer to bind.
|
||||||
|
/// @param inBarrier If set to Yes, a barrier will be placed before accessing the buffer to ensure all previous writes to the buffer are visible.
|
||||||
|
virtual void SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier = EBarrier::Yes) = 0;
|
||||||
|
|
||||||
|
/// Dispatch a compute shader with the specified number of thread groups
|
||||||
|
virtual void Dispatch(uint inThreadGroupsX, uint inThreadGroupsY = 1, uint inThreadGroupsZ = 1) = 0;
|
||||||
|
|
||||||
|
/// Schedule buffer to be copied from GPU to CPU.
|
||||||
|
/// A reference to the buffers is added to make sure they stay alive until execution finishes.
|
||||||
|
virtual void ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc) = 0;
|
||||||
|
|
||||||
|
/// Execute accumulated command list.
|
||||||
|
/// No more commands can be added until Wait is called.
|
||||||
|
virtual void Execute() = 0;
|
||||||
|
|
||||||
|
/// After executing, this waits until execution is done.
|
||||||
|
/// This also makes sure that any readback operations have completed and the data is available on CPU.
|
||||||
|
virtual void Wait() = 0;
|
||||||
|
|
||||||
|
/// Execute and wait for the command list to finish
|
||||||
|
/// @see Execute, Wait
|
||||||
|
void ExecuteAndWait()
|
||||||
|
{
|
||||||
|
Execute();
|
||||||
|
Wait();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using ComputeQueueResult = Result<Ref<ComputeQueue>>;
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
41
thirdparty/JoltPhysics/Jolt/Compute/ComputeShader.h
vendored
Normal file
41
thirdparty/JoltPhysics/Jolt/Compute/ComputeShader.h
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/Reference.h>
|
||||||
|
#include <Jolt/Core/NonCopyable.h>
|
||||||
|
#include <Jolt/Core/Result.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Compute shader handle
|
||||||
|
class JPH_EXPORT ComputeShader : public RefTarget<ComputeShader>, public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Constructor / destructor
|
||||||
|
ComputeShader(uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) :
|
||||||
|
mGroupSizeX(inGroupSizeX),
|
||||||
|
mGroupSizeY(inGroupSizeY),
|
||||||
|
mGroupSizeZ(inGroupSizeZ)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual ~ComputeShader() = default;
|
||||||
|
|
||||||
|
/// Get group sizes
|
||||||
|
uint32 GetGroupSizeX() const { return mGroupSizeX; }
|
||||||
|
uint32 GetGroupSizeY() const { return mGroupSizeY; }
|
||||||
|
uint32 GetGroupSizeZ() const { return mGroupSizeZ; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32 mGroupSizeX;
|
||||||
|
uint32 mGroupSizeY;
|
||||||
|
uint32 mGroupSizeZ;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ComputeShaderResult = Result<Ref<ComputeShader>>;
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
15
thirdparty/JoltPhysics/Jolt/Compute/ComputeSystem.cpp
vendored
Normal file
15
thirdparty/JoltPhysics/Jolt/Compute/ComputeSystem.cpp
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeSystem.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
JPH_IMPLEMENT_RTTI_ABSTRACT_BASE(ComputeSystem)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
78
thirdparty/JoltPhysics/Jolt/Compute/ComputeSystem.h
vendored
Normal file
78
thirdparty/JoltPhysics/Jolt/Compute/ComputeSystem.h
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeShader.h>
|
||||||
|
#include <Jolt/Compute/ComputeBuffer.h>
|
||||||
|
#include <Jolt/Compute/ComputeQueue.h>
|
||||||
|
#include <Jolt/Core/RTTI.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Interface to run a workload on the GPU
|
||||||
|
class JPH_EXPORT ComputeSystem : public RefTarget<ComputeSystem>, public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_DECLARE_RTTI_ABSTRACT_BASE(JPH_EXPORT, ComputeSystem)
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~ComputeSystem() = default;
|
||||||
|
|
||||||
|
/// Compile a compute shader
|
||||||
|
virtual ComputeShaderResult CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY = 1, uint32 inGroupSizeZ = 1) = 0;
|
||||||
|
|
||||||
|
/// Create a buffer for use with a compute shader
|
||||||
|
virtual ComputeBufferResult CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData = nullptr) = 0;
|
||||||
|
|
||||||
|
/// Create a queue for executing compute shaders
|
||||||
|
virtual ComputeQueueResult CreateComputeQueue() = 0;
|
||||||
|
|
||||||
|
/// Callback used when loading shaders
|
||||||
|
using ShaderLoader = std::function<bool(const char *inName, Array<uint8> &outData, String &outError)>;
|
||||||
|
ShaderLoader mShaderLoader = [](const char *, Array<uint8> &, String &outError) { JPH_ASSERT(false, "Override this function"); outError = "Not implemented"; return false; };
|
||||||
|
};
|
||||||
|
|
||||||
|
using ComputeSystemResult = Result<Ref<ComputeSystem>>;
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
/// Factory function to create a compute system using Vulkan
|
||||||
|
extern JPH_EXPORT ComputeSystemResult CreateComputeSystemVK();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef JPH_USE_CPU_COMPUTE
|
||||||
|
/// Factory function to create a compute system that falls back to CPU.
|
||||||
|
/// This is intended mainly for debugging purposes and is not optimized for performance
|
||||||
|
extern JPH_EXPORT ComputeSystemResult CreateComputeSystemCPU();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef JPH_USE_DX12
|
||||||
|
|
||||||
|
/// Factory function to create a compute system using DirectX 12
|
||||||
|
extern JPH_EXPORT ComputeSystemResult CreateComputeSystemDX12();
|
||||||
|
|
||||||
|
/// Factory function to create the default compute system for this platform
|
||||||
|
inline ComputeSystemResult CreateComputeSystem() { return CreateComputeSystemDX12(); }
|
||||||
|
|
||||||
|
#elif defined(JPH_USE_MTL)
|
||||||
|
|
||||||
|
/// Factory function to create a compute system using Metal
|
||||||
|
extern JPH_EXPORT ComputeSystemResult CreateComputeSystemMTL();
|
||||||
|
|
||||||
|
/// Factory function to create the default compute system for this platform
|
||||||
|
inline ComputeSystemResult CreateComputeSystem() { return CreateComputeSystemMTL(); }
|
||||||
|
|
||||||
|
#elif defined(JPH_USE_VK)
|
||||||
|
|
||||||
|
/// Factory function to create the default compute system for this platform
|
||||||
|
inline ComputeSystemResult CreateComputeSystem() { return CreateComputeSystemVK(); }
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/// Fallback implementation when no compute system is available
|
||||||
|
inline ComputeSystemResult CreateComputeSystem() { ComputeSystemResult result; result.SetError("Not implemented"); return result; }
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
167
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeBufferDX12.cpp
vendored
Normal file
167
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeBufferDX12.cpp
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_DX12
|
||||||
|
|
||||||
|
#include <Jolt/Compute/DX12/ComputeBufferDX12.h>
|
||||||
|
#include <Jolt/Compute/DX12/ComputeSystemDX12.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
ComputeBufferDX12::ComputeBufferDX12(ComputeSystemDX12 *inComputeSystem, EType inType, uint64 inSize, uint inStride) :
|
||||||
|
ComputeBuffer(inType, inSize, inStride),
|
||||||
|
mComputeSystem(inComputeSystem)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeBufferDX12::Initialize(const void *inData)
|
||||||
|
{
|
||||||
|
uint64 buffer_size = mSize * mStride;
|
||||||
|
|
||||||
|
switch (mType)
|
||||||
|
{
|
||||||
|
case EType::UploadBuffer:
|
||||||
|
mBufferCPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||||
|
mBufferGPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||||
|
if (mBufferCPU == nullptr || mBufferGPU == nullptr)
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EType::ConstantBuffer:
|
||||||
|
mBufferCPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||||
|
if (mBufferCPU == nullptr)
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EType::ReadbackBuffer:
|
||||||
|
JPH_ASSERT(inData == nullptr, "Can't upload data to a readback buffer");
|
||||||
|
mBufferCPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_READBACK, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||||
|
if (mBufferCPU == nullptr)
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EType::Buffer:
|
||||||
|
JPH_ASSERT(inData != nullptr);
|
||||||
|
mBufferCPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||||
|
mBufferGPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||||
|
if (mBufferCPU == nullptr || mBufferGPU == nullptr)
|
||||||
|
return false;
|
||||||
|
mNeedsSync = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EType::RWBuffer:
|
||||||
|
if (inData != nullptr)
|
||||||
|
{
|
||||||
|
mBufferCPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||||
|
if (mBufferCPU == nullptr)
|
||||||
|
return false;
|
||||||
|
mNeedsSync = true;
|
||||||
|
}
|
||||||
|
mBufferGPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, buffer_size);
|
||||||
|
if (mBufferGPU == nullptr)
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy data to upload buffer
|
||||||
|
if (inData != nullptr)
|
||||||
|
{
|
||||||
|
void *data = nullptr;
|
||||||
|
D3D12_RANGE range = { 0, 0 }; // We're not going to read
|
||||||
|
mBufferCPU->Map(0, &range, &data);
|
||||||
|
memcpy(data, inData, size_t(buffer_size));
|
||||||
|
mBufferCPU->Unmap(0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeBufferDX12::Barrier(ID3D12GraphicsCommandList *inCommandList, D3D12_RESOURCE_STATES inTo) const
|
||||||
|
{
|
||||||
|
// Check if state changed
|
||||||
|
if (mCurrentState == inTo)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Only buffers in GPU memory can change state
|
||||||
|
if (mType != ComputeBuffer::EType::Buffer && mType != ComputeBuffer::EType::RWBuffer)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
D3D12_RESOURCE_BARRIER barrier;
|
||||||
|
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||||
|
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||||
|
barrier.Transition.pResource = GetResourceGPU();
|
||||||
|
barrier.Transition.StateBefore = mCurrentState;
|
||||||
|
barrier.Transition.StateAfter = inTo;
|
||||||
|
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||||
|
inCommandList->ResourceBarrier(1, &barrier);
|
||||||
|
|
||||||
|
mCurrentState = inTo;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeBufferDX12::RWBarrier(ID3D12GraphicsCommandList *inCommandList)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mCurrentState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||||
|
|
||||||
|
D3D12_RESOURCE_BARRIER barrier;
|
||||||
|
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
|
||||||
|
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||||
|
barrier.Transition.pResource = GetResourceGPU();
|
||||||
|
inCommandList->ResourceBarrier(1, &barrier);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeBufferDX12::SyncCPUToGPU(ID3D12GraphicsCommandList *inCommandList) const
|
||||||
|
{
|
||||||
|
if (!mNeedsSync)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Barrier(inCommandList, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||||
|
|
||||||
|
inCommandList->CopyResource(GetResourceGPU(), GetResourceCPU());
|
||||||
|
|
||||||
|
mNeedsSync = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ComputeBufferDX12::MapInternal(EMode inMode)
|
||||||
|
{
|
||||||
|
void *mapped_resource = nullptr;
|
||||||
|
|
||||||
|
switch (inMode)
|
||||||
|
{
|
||||||
|
case EMode::Read:
|
||||||
|
JPH_ASSERT(mType == EType::ReadbackBuffer);
|
||||||
|
if (HRFailed(mBufferCPU->Map(0, nullptr, &mapped_resource)))
|
||||||
|
return nullptr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EMode::Write:
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mType == EType::UploadBuffer || mType == EType::ConstantBuffer);
|
||||||
|
D3D12_RANGE range = { 0, 0 }; // We're not going to read
|
||||||
|
if (HRFailed(mBufferCPU->Map(0, &range, &mapped_resource)))
|
||||||
|
return nullptr;
|
||||||
|
mNeedsSync = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapped_resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeBufferDX12::UnmapInternal()
|
||||||
|
{
|
||||||
|
mBufferCPU->Unmap(0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferResult ComputeBufferDX12::CreateReadBackBuffer() const
|
||||||
|
{
|
||||||
|
return mComputeSystem->CreateComputeBuffer(EType::ReadbackBuffer, mSize, mStride);
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_DX12
|
||||||
51
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeBufferDX12.h
vendored
Normal file
51
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeBufferDX12.h
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeBuffer.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_DX12
|
||||||
|
|
||||||
|
#include <Jolt/Compute/DX12/IncludeDX12.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class ComputeSystemDX12;
|
||||||
|
|
||||||
|
/// Buffer that can be read from / written to by a compute shader
|
||||||
|
class JPH_EXPORT ComputeBufferDX12 final : public ComputeBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
ComputeBufferDX12(ComputeSystemDX12 *inComputeSystem, EType inType, uint64 inSize, uint inStride);
|
||||||
|
|
||||||
|
bool Initialize(const void *inData);
|
||||||
|
|
||||||
|
ID3D12Resource * GetResourceCPU() const { return mBufferCPU.Get(); }
|
||||||
|
ID3D12Resource * GetResourceGPU() const { return mBufferGPU.Get(); }
|
||||||
|
ComPtr<ID3D12Resource> ReleaseResourceCPU() const { return std::move(mBufferCPU); }
|
||||||
|
|
||||||
|
bool Barrier(ID3D12GraphicsCommandList *inCommandList, D3D12_RESOURCE_STATES inTo) const;
|
||||||
|
void RWBarrier(ID3D12GraphicsCommandList *inCommandList);
|
||||||
|
bool SyncCPUToGPU(ID3D12GraphicsCommandList *inCommandList) const;
|
||||||
|
|
||||||
|
ComputeBufferResult CreateReadBackBuffer() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void * MapInternal(EMode inMode) override;
|
||||||
|
virtual void UnmapInternal() override;
|
||||||
|
|
||||||
|
ComputeSystemDX12 * mComputeSystem;
|
||||||
|
mutable ComPtr<ID3D12Resource> mBufferCPU;
|
||||||
|
ComPtr<ID3D12Resource> mBufferGPU;
|
||||||
|
mutable bool mNeedsSync = false; ///< If this buffer needs to be synced from CPU to GPU
|
||||||
|
mutable D3D12_RESOURCE_STATES mCurrentState = D3D12_RESOURCE_STATE_COPY_DEST; ///< State of the GPU buffer so we can do proper barriers
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_DX12
|
||||||
221
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeQueueDX12.cpp
vendored
Normal file
221
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeQueueDX12.cpp
vendored
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_DX12
|
||||||
|
|
||||||
|
#include <Jolt/Compute/DX12/ComputeQueueDX12.h>
|
||||||
|
#include <Jolt/Compute/DX12/ComputeShaderDX12.h>
|
||||||
|
#include <Jolt/Compute/DX12/ComputeBufferDX12.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
ComputeQueueDX12::~ComputeQueueDX12()
|
||||||
|
{
|
||||||
|
Wait();
|
||||||
|
|
||||||
|
if (mFenceEvent != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(mFenceEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeQueueDX12::Initialize(ID3D12Device *inDevice, D3D12_COMMAND_LIST_TYPE inType, ComputeQueueResult &outResult)
|
||||||
|
{
|
||||||
|
D3D12_COMMAND_QUEUE_DESC queue_desc = {};
|
||||||
|
queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||||
|
queue_desc.Type = inType;
|
||||||
|
queue_desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH;
|
||||||
|
if (HRFailed(inDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&mCommandQueue)), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (HRFailed(inDevice->CreateCommandAllocator(inType, IID_PPV_ARGS(&mCommandAllocator)), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create the command list
|
||||||
|
if (HRFailed(inDevice->CreateCommandList(0, inType, mCommandAllocator.Get(), nullptr, IID_PPV_ARGS(&mCommandList)), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Command lists are created in the recording state, but there is nothing to record yet. The main loop expects it to be closed, so close it now
|
||||||
|
if (HRFailed(mCommandList->Close(), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create synchronization object
|
||||||
|
if (HRFailed(inDevice->CreateFence(mFenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Increment fence value so we don't skip waiting the first time a command list is executed
|
||||||
|
mFenceValue++;
|
||||||
|
|
||||||
|
// Create an event handle to use for frame synchronization
|
||||||
|
mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
|
if (HRFailed(HRESULT_FROM_WIN32(GetLastError()), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D12GraphicsCommandList *ComputeQueueDX12::Start()
|
||||||
|
{
|
||||||
|
JPH_ASSERT(!mIsExecuting);
|
||||||
|
|
||||||
|
if (!mIsStarted)
|
||||||
|
{
|
||||||
|
// Reset the allocator
|
||||||
|
if (HRFailed(mCommandAllocator->Reset()))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Reset the command list
|
||||||
|
if (HRFailed(mCommandList->Reset(mCommandAllocator.Get(), nullptr)))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Now we have started recording commands
|
||||||
|
mIsStarted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mCommandList.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueDX12::SetShader(const ComputeShader *inShader)
|
||||||
|
{
|
||||||
|
ID3D12GraphicsCommandList *command_list = Start();
|
||||||
|
mShader = static_cast<const ComputeShaderDX12 *>(inShader);
|
||||||
|
command_list->SetPipelineState(mShader->GetPipelineState());
|
||||||
|
command_list->SetComputeRootSignature(mShader->GetRootSignature());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueDX12::SyncCPUToGPU(const ComputeBufferDX12 *inBuffer)
|
||||||
|
{
|
||||||
|
// Ensure that any CPU writes are visible to the GPU
|
||||||
|
if (inBuffer->SyncCPUToGPU(mCommandList.Get())
|
||||||
|
&& (inBuffer->GetType() == ComputeBuffer::EType::Buffer || inBuffer->GetType() == ComputeBuffer::EType::RWBuffer))
|
||||||
|
{
|
||||||
|
// After the first upload, the CPU buffer is no longer needed for Buffer and RWBuffer types
|
||||||
|
mDelayedFreedBuffers.emplace_back(inBuffer->ReleaseResourceCPU());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueDX12::SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::ConstantBuffer);
|
||||||
|
|
||||||
|
ID3D12GraphicsCommandList *command_list = Start();
|
||||||
|
const ComputeBufferDX12 *buffer = static_cast<const ComputeBufferDX12 *>(inBuffer);
|
||||||
|
command_list->SetComputeRootConstantBufferView(mShader->NameToIndex(inName), buffer->GetResourceCPU()->GetGPUVirtualAddress());
|
||||||
|
|
||||||
|
mUsedBuffers.insert(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueDX12::SetBuffer(const char *inName, const ComputeBuffer *inBuffer)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::UploadBuffer || inBuffer->GetType() == ComputeBuffer::EType::Buffer || inBuffer->GetType() == ComputeBuffer::EType::RWBuffer);
|
||||||
|
|
||||||
|
ID3D12GraphicsCommandList *command_list = Start();
|
||||||
|
const ComputeBufferDX12 *buffer = static_cast<const ComputeBufferDX12 *>(inBuffer);
|
||||||
|
uint parameter_index = mShader->NameToIndex(inName);
|
||||||
|
SyncCPUToGPU(buffer);
|
||||||
|
buffer->Barrier(command_list, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
|
||||||
|
command_list->SetComputeRootShaderResourceView(parameter_index, buffer->GetResourceGPU()->GetGPUVirtualAddress());
|
||||||
|
|
||||||
|
mUsedBuffers.insert(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueDX12::SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::RWBuffer);
|
||||||
|
|
||||||
|
ID3D12GraphicsCommandList *command_list = Start();
|
||||||
|
ComputeBufferDX12 *buffer = static_cast<ComputeBufferDX12 *>(inBuffer);
|
||||||
|
uint parameter_index = mShader->NameToIndex(inName);
|
||||||
|
SyncCPUToGPU(buffer);
|
||||||
|
if (!buffer->Barrier(command_list, D3D12_RESOURCE_STATE_UNORDERED_ACCESS) && inBarrier == EBarrier::Yes)
|
||||||
|
buffer->RWBarrier(command_list);
|
||||||
|
command_list->SetComputeRootUnorderedAccessView(parameter_index, buffer->GetResourceGPU()->GetGPUVirtualAddress());
|
||||||
|
|
||||||
|
mUsedBuffers.insert(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueDX12::ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc)
|
||||||
|
{
|
||||||
|
if (inDst == nullptr || inSrc == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inDst->GetType() == ComputeBuffer::EType::ReadbackBuffer);
|
||||||
|
|
||||||
|
ID3D12GraphicsCommandList *command_list = Start();
|
||||||
|
ComputeBufferDX12 *dst = static_cast<ComputeBufferDX12 *>(inDst);
|
||||||
|
const ComputeBufferDX12 *src = static_cast<const ComputeBufferDX12 *>(inSrc);
|
||||||
|
dst->Barrier(command_list, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||||
|
src->Barrier(command_list, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
||||||
|
command_list->CopyResource(dst->GetResourceCPU(), src->GetResourceGPU());
|
||||||
|
|
||||||
|
mUsedBuffers.insert(src);
|
||||||
|
mUsedBuffers.insert(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueDX12::Dispatch(uint inThreadGroupsX, uint inThreadGroupsY, uint inThreadGroupsZ)
|
||||||
|
{
|
||||||
|
ID3D12GraphicsCommandList *command_list = Start();
|
||||||
|
command_list->Dispatch(inThreadGroupsX, inThreadGroupsY, inThreadGroupsZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueDX12::Execute()
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mIsStarted);
|
||||||
|
JPH_ASSERT(!mIsExecuting);
|
||||||
|
|
||||||
|
// Close the command list
|
||||||
|
if (HRFailed(mCommandList->Close()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Execute the command list
|
||||||
|
ID3D12CommandList *command_lists[] = { mCommandList.Get() };
|
||||||
|
mCommandQueue->ExecuteCommandLists((UINT)std::size(command_lists), command_lists);
|
||||||
|
|
||||||
|
// Schedule a Signal command in the queue
|
||||||
|
if (HRFailed(mCommandQueue->Signal(mFence.Get(), mFenceValue)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Clear the current shader
|
||||||
|
mShader = nullptr;
|
||||||
|
|
||||||
|
// Mark that we're executing
|
||||||
|
mIsExecuting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueDX12::Wait()
|
||||||
|
{
|
||||||
|
// Check if we've been started
|
||||||
|
if (mIsExecuting)
|
||||||
|
{
|
||||||
|
if (mFence->GetCompletedValue() < mFenceValue)
|
||||||
|
{
|
||||||
|
// Wait until the fence has been processed
|
||||||
|
if (HRFailed(mFence->SetEventOnCompletion(mFenceValue, mFenceEvent)))
|
||||||
|
return;
|
||||||
|
WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the fence value
|
||||||
|
mFenceValue++;
|
||||||
|
|
||||||
|
// Buffers can be freed now
|
||||||
|
mUsedBuffers.clear();
|
||||||
|
|
||||||
|
// Free buffers
|
||||||
|
mDelayedFreedBuffers.clear();
|
||||||
|
|
||||||
|
// Done executing
|
||||||
|
mIsExecuting = false;
|
||||||
|
mIsStarted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_DX12
|
||||||
61
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeQueueDX12.h
vendored
Normal file
61
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeQueueDX12.h
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_DX12
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeQueue.h>
|
||||||
|
#include <Jolt/Compute/DX12/ComputeShaderDX12.h>
|
||||||
|
#include <Jolt/Core/UnorderedSet.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class ComputeBufferDX12;
|
||||||
|
|
||||||
|
/// A command queue for DirectX for executing compute workloads on the GPU.
|
||||||
|
class JPH_EXPORT ComputeQueueDX12 final : public ComputeQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~ComputeQueueDX12() override;
|
||||||
|
|
||||||
|
/// Initialize the queue
|
||||||
|
bool Initialize(ID3D12Device *inDevice, D3D12_COMMAND_LIST_TYPE inType, ComputeQueueResult &outResult);
|
||||||
|
|
||||||
|
/// Start the command list (requires waiting until the previous one is finished)
|
||||||
|
ID3D12GraphicsCommandList * Start();
|
||||||
|
|
||||||
|
// See: ComputeQueue
|
||||||
|
virtual void SetShader(const ComputeShader *inShader) override;
|
||||||
|
virtual void SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer) override;
|
||||||
|
virtual void SetBuffer(const char *inName, const ComputeBuffer *inBuffer) override;
|
||||||
|
virtual void SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier = EBarrier::Yes) override;
|
||||||
|
virtual void ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc) override;
|
||||||
|
virtual void Dispatch(uint inThreadGroupsX, uint inThreadGroupsY, uint inThreadGroupsZ) override;
|
||||||
|
virtual void Execute() override;
|
||||||
|
virtual void Wait() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Copy the CPU buffer to the GPU buffer if needed
|
||||||
|
void SyncCPUToGPU(const ComputeBufferDX12 *inBuffer);
|
||||||
|
|
||||||
|
ComPtr<ID3D12CommandQueue> mCommandQueue; ///< The command queue that will hold command lists
|
||||||
|
ComPtr<ID3D12CommandAllocator> mCommandAllocator; ///< Allocator that holds the memory for the commands
|
||||||
|
ComPtr<ID3D12GraphicsCommandList> mCommandList; ///< The command list that will hold the render commands / state changes
|
||||||
|
HANDLE mFenceEvent = INVALID_HANDLE_VALUE; ///< Fence event, used to wait for rendering to complete
|
||||||
|
ComPtr<ID3D12Fence> mFence; ///< Fence object, used to signal the fence event
|
||||||
|
UINT64 mFenceValue = 0; ///< Current fence value, each time we need to wait we will signal the fence with this value, wait for it and then increase the value
|
||||||
|
RefConst<ComputeShaderDX12> mShader = nullptr; ///< Current active shader
|
||||||
|
bool mIsStarted = false; ///< If the command list has been started (reset) and is ready to record commands
|
||||||
|
bool mIsExecuting = false; ///< If a command list is currently executing on the queue
|
||||||
|
UnorderedSet<RefConst<ComputeBuffer>> mUsedBuffers; ///< Buffers that are in use by the current execution, these will be retained until execution is finished so that we don't free buffers that are in use
|
||||||
|
Array<ComPtr<ID3D12Resource>> mDelayedFreedBuffers; ///< Buffers freed during the execution
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_DX12
|
||||||
52
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeShaderDX12.h
vendored
Normal file
52
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeShaderDX12.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_DX12
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeShader.h>
|
||||||
|
#include <Jolt/Compute/DX12/IncludeDX12.h>
|
||||||
|
#include <Jolt/Core/UnorderedMap.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Compute shader handle for DirectX
|
||||||
|
class JPH_EXPORT ComputeShaderDX12 : public ComputeShader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
ComputeShaderDX12(ComPtr<ID3D12RootSignature> inRootSignature, ComPtr<ID3D12PipelineState> inPipelineState, Array<String> &&inBindingNames, UnorderedMap<string_view, uint> &&inNameToIndex, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) :
|
||||||
|
ComputeShader(inGroupSizeX, inGroupSizeY, inGroupSizeZ),
|
||||||
|
mRootSignature(inRootSignature),
|
||||||
|
mPipelineState(inPipelineState),
|
||||||
|
mBindingNames(std::move(inBindingNames)),
|
||||||
|
mNameToIndex(std::move(inNameToIndex))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get index of shader parameter
|
||||||
|
uint NameToIndex(const char *inName) const
|
||||||
|
{
|
||||||
|
UnorderedMap<string_view, uint>::const_iterator it = mNameToIndex.find(inName);
|
||||||
|
JPH_ASSERT(it != mNameToIndex.end());
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Getters
|
||||||
|
ID3D12PipelineState * GetPipelineState() const { return mPipelineState.Get(); }
|
||||||
|
ID3D12RootSignature * GetRootSignature() const { return mRootSignature.Get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComPtr<ID3D12RootSignature> mRootSignature; ///< The root signature for this shader
|
||||||
|
ComPtr<ID3D12PipelineState> mPipelineState; ///< The pipeline state object for this shader
|
||||||
|
Array<String> mBindingNames; ///< A list of binding names, mNameToIndex points to these strings
|
||||||
|
UnorderedMap<string_view, uint> mNameToIndex; ///< Maps names to indices for the shader parameters, using a string_view so we can do find() without an allocation
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_DX12
|
||||||
255
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeSystemDX12.cpp
vendored
Normal file
255
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeSystemDX12.cpp
vendored
Normal file
|
|
@ -0,0 +1,255 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_DX12
|
||||||
|
|
||||||
|
#include <Jolt/Compute/DX12/ComputeSystemDX12.h>
|
||||||
|
#include <Jolt/Compute/DX12/ComputeQueueDX12.h>
|
||||||
|
#include <Jolt/Compute/DX12/ComputeShaderDX12.h>
|
||||||
|
#include <Jolt/Compute/DX12/ComputeBufferDX12.h>
|
||||||
|
#include <Jolt/Core/StringTools.h>
|
||||||
|
#include <Jolt/Core/UnorderedMap.h>
|
||||||
|
#include <Jolt/Core/FPException.h>
|
||||||
|
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5204) // 'X': class has virtual functions, but its trivial destructor is not virtual; instances of objects derived from this class may not be destructed correctly
|
||||||
|
JPH_MSVC2026_PLUS_SUPPRESS_WARNING(4865) // wingdi.h(2806,1): '<unnamed-enum-DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER>': the underlying type will change from 'int' to '__int64' when '/Zc:enumTypes' is specified on the command line
|
||||||
|
#include <d3dcompiler.h>
|
||||||
|
#include <dxcapi.h>
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
#include <d3d12sdklayers.h>
|
||||||
|
#endif
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_END
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
JPH_IMPLEMENT_RTTI_VIRTUAL(ComputeSystemDX12)
|
||||||
|
{
|
||||||
|
JPH_ADD_BASE_CLASS(ComputeSystemDX12, ComputeSystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeSystemDX12::Initialize(ID3D12Device *inDevice, ComputeSystemResult &outResult)
|
||||||
|
{
|
||||||
|
mDevice = inDevice;
|
||||||
|
|
||||||
|
// Dynamically load dxcompiler.dll
|
||||||
|
HMODULE dxc_module = LoadLibraryA("dxcompiler.dll");
|
||||||
|
if (dxc_module == nullptr)
|
||||||
|
{
|
||||||
|
outResult.SetError("Failed to load dxcompiler.dll");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDxcCreateInstanceFn = GetProcAddress(dxc_module, "DxcCreateInstance");
|
||||||
|
JPH_ASSERT(mDxcCreateInstanceFn != nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeSystemDX12::Shutdown()
|
||||||
|
{
|
||||||
|
// Releasing the device can cause invalid floating point operation exceptions inside the DirectX driver
|
||||||
|
FPExceptionDisableInvalid disable_invalid;
|
||||||
|
JPH_UNUSED(disable_invalid);
|
||||||
|
|
||||||
|
mDevice.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
ComPtr<ID3D12Resource> ComputeSystemDX12::CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, D3D12_RESOURCE_FLAGS inFlags, uint64 inSize)
|
||||||
|
{
|
||||||
|
// Create a new resource
|
||||||
|
D3D12_RESOURCE_DESC desc;
|
||||||
|
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||||
|
desc.Alignment = 0;
|
||||||
|
desc.Width = inSize;
|
||||||
|
desc.Height = 1;
|
||||||
|
desc.DepthOrArraySize = 1;
|
||||||
|
desc.MipLevels = 1;
|
||||||
|
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||||
|
desc.SampleDesc.Count = 1;
|
||||||
|
desc.SampleDesc.Quality = 0;
|
||||||
|
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||||
|
desc.Flags = inFlags;
|
||||||
|
|
||||||
|
D3D12_HEAP_PROPERTIES heap_properties = {};
|
||||||
|
heap_properties.Type = inHeapType;
|
||||||
|
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||||
|
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||||
|
heap_properties.CreationNodeMask = 1;
|
||||||
|
heap_properties.VisibleNodeMask = 1;
|
||||||
|
|
||||||
|
ComPtr<ID3D12Resource> resource;
|
||||||
|
if (HRFailed(mDevice->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &desc, inResourceState, nullptr, IID_PPV_ARGS(&resource))))
|
||||||
|
return nullptr;
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeShaderResult ComputeSystemDX12::CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ)
|
||||||
|
{
|
||||||
|
ComputeShaderResult result;
|
||||||
|
|
||||||
|
// Read shader source file
|
||||||
|
Array<uint8> data;
|
||||||
|
String error;
|
||||||
|
String file_name = String(inName) + ".dxil";
|
||||||
|
if (!mShaderLoader(file_name.c_str(), data, error))
|
||||||
|
{
|
||||||
|
result.SetError(error);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create IDxcUtils object
|
||||||
|
ComPtr<IDxcUtils> utils;
|
||||||
|
reinterpret_cast<DxcCreateInstanceProc>(reinterpret_cast<void *>(mDxcCreateInstanceFn))(CLSID_DxcUtils, IID_PPV_ARGS(utils.GetAddressOf()));
|
||||||
|
|
||||||
|
// Get reflection data
|
||||||
|
DxcBuffer reflection_buffer = { data.data(), data.size(), 0 };
|
||||||
|
ComPtr<ID3D12ShaderReflection> reflector;
|
||||||
|
if (HRFailed(utils->CreateReflection(&reflection_buffer, IID_PPV_ARGS(reflector.GetAddressOf())), result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Get the shader description
|
||||||
|
D3D12_SHADER_DESC shader_desc;
|
||||||
|
if (HRFailed(reflector->GetDesc(&shader_desc), result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Verify that the group sizes match the shader's thread group size
|
||||||
|
UINT thread_group_size_x, thread_group_size_y, thread_group_size_z;
|
||||||
|
if (HRFailed(reflector->GetThreadGroupSize(&thread_group_size_x, &thread_group_size_y, &thread_group_size_z), result))
|
||||||
|
return result;
|
||||||
|
JPH_ASSERT(inGroupSizeX == thread_group_size_x, "Group size X mismatch");
|
||||||
|
JPH_ASSERT(inGroupSizeY == thread_group_size_y, "Group size Y mismatch");
|
||||||
|
JPH_ASSERT(inGroupSizeZ == thread_group_size_z, "Group size Z mismatch");
|
||||||
|
|
||||||
|
// Convert parameters to root signature description
|
||||||
|
Array<String> binding_names;
|
||||||
|
binding_names.reserve(shader_desc.BoundResources);
|
||||||
|
UnorderedMap<string_view, uint> name_to_index;
|
||||||
|
Array<D3D12_ROOT_PARAMETER1> root_params;
|
||||||
|
for (UINT i = 0; i < shader_desc.BoundResources; ++i)
|
||||||
|
{
|
||||||
|
D3D12_SHADER_INPUT_BIND_DESC bind_desc;
|
||||||
|
reflector->GetResourceBindingDesc(i, &bind_desc);
|
||||||
|
|
||||||
|
D3D12_ROOT_PARAMETER1 param = {};
|
||||||
|
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||||
|
|
||||||
|
switch (bind_desc.Type)
|
||||||
|
{
|
||||||
|
case D3D_SIT_CBUFFER:
|
||||||
|
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D3D_SIT_STRUCTURED:
|
||||||
|
case D3D_SIT_BYTEADDRESS:
|
||||||
|
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D3D_SIT_UAV_RWTYPED:
|
||||||
|
case D3D_SIT_UAV_RWSTRUCTURED:
|
||||||
|
case D3D_SIT_UAV_RWBYTEADDRESS:
|
||||||
|
case D3D_SIT_UAV_APPEND_STRUCTURED:
|
||||||
|
case D3D_SIT_UAV_CONSUME_STRUCTURED:
|
||||||
|
case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER:
|
||||||
|
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D3D_SIT_TBUFFER:
|
||||||
|
case D3D_SIT_TEXTURE:
|
||||||
|
case D3D_SIT_SAMPLER:
|
||||||
|
case D3D_SIT_RTACCELERATIONSTRUCTURE:
|
||||||
|
case D3D_SIT_UAV_FEEDBACKTEXTURE:
|
||||||
|
JPH_ASSERT(false, "Unsupported shader input type");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
param.Descriptor.RegisterSpace = bind_desc.Space;
|
||||||
|
param.Descriptor.ShaderRegister = bind_desc.BindPoint;
|
||||||
|
param.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_VOLATILE;
|
||||||
|
|
||||||
|
binding_names.push_back(bind_desc.Name); // Add all strings to a pool to keep them alive
|
||||||
|
name_to_index[string_view(binding_names.back())] = (uint)root_params.size();
|
||||||
|
root_params.push_back(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the root signature
|
||||||
|
D3D12_VERSIONED_ROOT_SIGNATURE_DESC root_sig_desc = {};
|
||||||
|
root_sig_desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
|
||||||
|
root_sig_desc.Desc_1_1.NumParameters = (UINT)root_params.size();
|
||||||
|
root_sig_desc.Desc_1_1.pParameters = root_params.data();
|
||||||
|
root_sig_desc.Desc_1_1.NumStaticSamplers = 0;
|
||||||
|
root_sig_desc.Desc_1_1.pStaticSamplers = nullptr;
|
||||||
|
root_sig_desc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
|
||||||
|
ComPtr<ID3DBlob> serialized_sig;
|
||||||
|
ComPtr<ID3DBlob> root_sig_error_blob;
|
||||||
|
if (FAILED(D3D12SerializeVersionedRootSignature(&root_sig_desc, &serialized_sig, &root_sig_error_blob)))
|
||||||
|
{
|
||||||
|
if (root_sig_error_blob)
|
||||||
|
{
|
||||||
|
error = StringFormat("Failed to create root signature: %s", (const char *)root_sig_error_blob->GetBufferPointer());
|
||||||
|
result.SetError(error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result.SetError("Failed to create root signature");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
ComPtr<ID3D12RootSignature> root_sig;
|
||||||
|
if (FAILED(mDevice->CreateRootSignature(0, serialized_sig->GetBufferPointer(), serialized_sig->GetBufferSize(), IID_PPV_ARGS(&root_sig))))
|
||||||
|
{
|
||||||
|
result.SetError("Failed to create root signature");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a pipeline state object from the root signature and the shader
|
||||||
|
ComPtr<ID3D12PipelineState> pipeline_state;
|
||||||
|
D3D12_COMPUTE_PIPELINE_STATE_DESC compute_state_desc = {};
|
||||||
|
compute_state_desc.pRootSignature = root_sig.Get();
|
||||||
|
compute_state_desc.CS = { data.data(), data.size() };
|
||||||
|
if (FAILED(mDevice->CreateComputePipelineState(&compute_state_desc, IID_PPV_ARGS(&pipeline_state))))
|
||||||
|
{
|
||||||
|
result.SetError("Failed to create compute pipeline state");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set name on DX12 objects for easier debugging
|
||||||
|
wchar_t w_name[1024];
|
||||||
|
size_t converted_chars = 0;
|
||||||
|
mbstowcs_s(&converted_chars, w_name, 1024, inName, _TRUNCATE);
|
||||||
|
pipeline_state->SetName(w_name);
|
||||||
|
|
||||||
|
result.Set(new ComputeShaderDX12(root_sig, pipeline_state, std::move(binding_names), std::move(name_to_index), inGroupSizeX, inGroupSizeY, inGroupSizeZ));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferResult ComputeSystemDX12::CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData)
|
||||||
|
{
|
||||||
|
ComputeBufferResult result;
|
||||||
|
|
||||||
|
Ref<ComputeBufferDX12> buffer = new ComputeBufferDX12(this, inType, inSize, inStride);
|
||||||
|
if (!buffer->Initialize(inData))
|
||||||
|
{
|
||||||
|
result.SetError("Failed to create compute buffer");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Set(buffer.GetPtr());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeQueueResult ComputeSystemDX12::CreateComputeQueue()
|
||||||
|
{
|
||||||
|
ComputeQueueResult result;
|
||||||
|
|
||||||
|
Ref<ComputeQueueDX12> queue = new ComputeQueueDX12();
|
||||||
|
if (!queue->Initialize(mDevice.Get(), D3D12_COMMAND_LIST_TYPE_COMPUTE, result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result.Set(queue.GetPtr());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_DX12
|
||||||
45
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeSystemDX12.h
vendored
Normal file
45
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeSystemDX12.h
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/UnorderedMap.h>
|
||||||
|
#include <Jolt/Compute/ComputeSystem.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_DX12
|
||||||
|
|
||||||
|
#include <Jolt/Compute/DX12/IncludeDX12.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Interface to run a workload on the GPU using DirectX 12.
|
||||||
|
/// Minimal implementation that can integrate with your own DirectX 12 setup.
|
||||||
|
class JPH_EXPORT ComputeSystemDX12 : public ComputeSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_DECLARE_RTTI_VIRTUAL(JPH_EXPORT, ComputeSystemDX12)
|
||||||
|
|
||||||
|
/// Initialize / shutdown
|
||||||
|
bool Initialize(ID3D12Device *inDevice, ComputeSystemResult &outResult);
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
// See: ComputeSystem
|
||||||
|
virtual ComputeShaderResult CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) override;
|
||||||
|
virtual ComputeBufferResult CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData = nullptr) override;
|
||||||
|
virtual ComputeQueueResult CreateComputeQueue() override;
|
||||||
|
|
||||||
|
/// Access to the DX12 device
|
||||||
|
ID3D12Device * GetDevice() const { return mDevice.Get(); }
|
||||||
|
|
||||||
|
// Function to create a ID3D12Resource on specified heap with specified state
|
||||||
|
ComPtr<ID3D12Resource> CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, D3D12_RESOURCE_FLAGS inFlags, uint64 inSize);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComPtr<ID3D12Device> mDevice;
|
||||||
|
FARPROC mDxcCreateInstanceFn = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_DX12
|
||||||
158
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeSystemDX12Impl.cpp
vendored
Normal file
158
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeSystemDX12Impl.cpp
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_DX12
|
||||||
|
|
||||||
|
#include <Jolt/Compute/DX12/ComputeSystemDX12Impl.h>
|
||||||
|
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
#include <d3d12sdklayers.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
JPH_IMPLEMENT_RTTI_VIRTUAL(ComputeSystemDX12Impl)
|
||||||
|
{
|
||||||
|
JPH_ADD_BASE_CLASS(ComputeSystemDX12Impl, ComputeSystemDX12)
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeSystemDX12Impl::~ComputeSystemDX12Impl()
|
||||||
|
{
|
||||||
|
Shutdown();
|
||||||
|
mDXGIFactory.Reset();
|
||||||
|
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
// Test for leaks
|
||||||
|
ComPtr<IDXGIDebug1> dxgi_debug;
|
||||||
|
if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgi_debug))))
|
||||||
|
dxgi_debug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeSystemDX12Impl::Initialize(ComputeSystemResult &outResult)
|
||||||
|
{
|
||||||
|
#if defined(JPH_DEBUG)
|
||||||
|
// Enable the D3D12 debug layer
|
||||||
|
ComPtr<ID3D12Debug> debug_controller;
|
||||||
|
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller))))
|
||||||
|
debug_controller->EnableDebugLayer();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Create DXGI factory
|
||||||
|
if (HRFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mDXGIFactory)), outResult))
|
||||||
|
{
|
||||||
|
outResult.SetError("Failed to create DXGI factory");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find adapter
|
||||||
|
ComPtr<IDXGIAdapter1> adapter;
|
||||||
|
ComPtr<ID3D12Device> device;
|
||||||
|
|
||||||
|
HRESULT result = E_FAIL;
|
||||||
|
|
||||||
|
// First check if we have the Windows 1803 IDXGIFactory6 interface
|
||||||
|
ComPtr<IDXGIFactory6> factory6;
|
||||||
|
if (SUCCEEDED(mDXGIFactory->QueryInterface(IID_PPV_ARGS(&factory6))))
|
||||||
|
{
|
||||||
|
for (int search_software = 0; search_software < 2 && device == nullptr; ++search_software)
|
||||||
|
for (UINT index = 0; factory6->EnumAdapterByGpuPreference(index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)) != DXGI_ERROR_NOT_FOUND; ++index)
|
||||||
|
{
|
||||||
|
DXGI_ADAPTER_DESC1 desc;
|
||||||
|
adapter->GetDesc1(&desc);
|
||||||
|
|
||||||
|
// We don't want software renderers in the first pass
|
||||||
|
int is_software = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0? 1 : 0;
|
||||||
|
if (search_software != is_software)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check to see whether the adapter supports Direct3D 12
|
||||||
|
#if defined(JPH_PLATFORM_WINDOWS) && defined(_DEBUG)
|
||||||
|
int prev_state = _CrtSetDbgFlag(0); // Temporarily disable leak detection as this call reports false positives
|
||||||
|
#endif
|
||||||
|
result = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device));
|
||||||
|
#if defined(JPH_PLATFORM_WINDOWS) && defined(_DEBUG)
|
||||||
|
_CrtSetDbgFlag(prev_state);
|
||||||
|
#endif
|
||||||
|
if (SUCCEEDED(result))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fall back to the older method that may not get the fastest GPU
|
||||||
|
for (int search_software = 0; search_software < 2 && device == nullptr; ++search_software)
|
||||||
|
for (UINT index = 0; mDXGIFactory->EnumAdapters1(index, &adapter) != DXGI_ERROR_NOT_FOUND; ++index)
|
||||||
|
{
|
||||||
|
DXGI_ADAPTER_DESC1 desc;
|
||||||
|
adapter->GetDesc1(&desc);
|
||||||
|
|
||||||
|
// We don't want software renderers in the first pass
|
||||||
|
int is_software = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0? 1 : 0;
|
||||||
|
if (search_software != is_software)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check to see whether the adapter supports Direct3D 12
|
||||||
|
#if defined(JPH_PLATFORM_WINDOWS) && defined(_DEBUG)
|
||||||
|
int prev_state = _CrtSetDbgFlag(0); // Temporarily disable leak detection as this call reports false positives
|
||||||
|
#endif
|
||||||
|
result = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device));
|
||||||
|
#if defined(JPH_PLATFORM_WINDOWS) && defined(_DEBUG)
|
||||||
|
_CrtSetDbgFlag(prev_state);
|
||||||
|
#endif
|
||||||
|
if (SUCCEEDED(result))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we managed to obtain a device
|
||||||
|
if (HRFailed(result, outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Initialize the compute interface
|
||||||
|
if (!ComputeSystemDX12::Initialize(device.Get(), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
// Enable breaking on errors
|
||||||
|
ComPtr<ID3D12InfoQueue> info_queue;
|
||||||
|
if (SUCCEEDED(device.As(&info_queue)))
|
||||||
|
{
|
||||||
|
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
|
||||||
|
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
|
||||||
|
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
|
||||||
|
|
||||||
|
// Disable an error that triggers on Windows 11 with a hybrid graphic system
|
||||||
|
// See: https://stackoverflow.com/questions/69805245/directx-12-application-is-crashing-in-windows-11
|
||||||
|
D3D12_MESSAGE_ID hide[] =
|
||||||
|
{
|
||||||
|
D3D12_MESSAGE_ID_RESOURCE_BARRIER_MISMATCHING_COMMAND_LIST_TYPE,
|
||||||
|
};
|
||||||
|
D3D12_INFO_QUEUE_FILTER filter = { };
|
||||||
|
filter.DenyList.NumIDs = static_cast<UINT>(std::size(hide));
|
||||||
|
filter.DenyList.pIDList = hide;
|
||||||
|
info_queue->AddStorageFilterEntries(&filter);
|
||||||
|
}
|
||||||
|
#endif // JPH_DEBUG
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeSystemResult CreateComputeSystemDX12()
|
||||||
|
{
|
||||||
|
ComputeSystemResult result;
|
||||||
|
|
||||||
|
Ref<ComputeSystemDX12Impl> compute = new ComputeSystemDX12Impl();
|
||||||
|
if (!compute->Initialize(result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result.Set(compute.GetPtr());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_DX12
|
||||||
33
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeSystemDX12Impl.h
vendored
Normal file
33
thirdparty/JoltPhysics/Jolt/Compute/DX12/ComputeSystemDX12Impl.h
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_DX12
|
||||||
|
|
||||||
|
#include <Jolt/Compute/DX12/ComputeSystemDX12.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Implementation of ComputeSystemDX12 that fully initializes DirectX 12
|
||||||
|
class JPH_EXPORT ComputeSystemDX12Impl : public ComputeSystemDX12
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_DECLARE_RTTI_VIRTUAL(JPH_EXPORT, ComputeSystemDX12Impl)
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~ComputeSystemDX12Impl() override;
|
||||||
|
|
||||||
|
/// Initialize the compute system
|
||||||
|
bool Initialize(ComputeSystemResult &outResult);
|
||||||
|
|
||||||
|
IDXGIFactory4 * GetDXGIFactory() const { return mDXGIFactory.Get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComPtr<IDXGIFactory4> mDXGIFactory;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_DX12
|
||||||
49
thirdparty/JoltPhysics/Jolt/Compute/DX12/IncludeDX12.h
vendored
Normal file
49
thirdparty/JoltPhysics/Jolt/Compute/DX12/IncludeDX12.h
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/IncludeWindows.h>
|
||||||
|
#include <Jolt/Core/StringTools.h>
|
||||||
|
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4265) // 'X': class has virtual functions, but its non-trivial destructor is not virtual; instances of this class may not be destructed correctly
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4625) // 'X': copy constructor was implicitly defined as deleted
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4626) // 'X': assignment operator was implicitly defined as deleted
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5204) // 'X': class has virtual functions, but its trivial destructor is not virtual; instances of objects derived from this class may not be destructed correctly
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5220) // 'X': a non-static data member with a volatile qualified type no longer implies
|
||||||
|
JPH_MSVC2026_PLUS_SUPPRESS_WARNING(4865) // wingdi.h(2806,1): '<unnamed-enum-DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER>': the underlying type will change from 'int' to '__int64' when '/Zc:enumTypes' is specified on the command line
|
||||||
|
#include <d3d12.h>
|
||||||
|
#include <dxgi1_6.h>
|
||||||
|
#include <dxgidebug.h>
|
||||||
|
#include <wrl.h>
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_END
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
using Microsoft::WRL::ComPtr;
|
||||||
|
|
||||||
|
template <class Result>
|
||||||
|
inline bool HRFailed(HRESULT inHR, Result &outResult)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(inHR))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
String error = StringFormat("Call failed with error code: %08X", inHR);
|
||||||
|
outResult.SetError(error);
|
||||||
|
JPH_ASSERT(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool HRFailed(HRESULT inHR)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(inHR))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Trace("Call failed with error code: %08X", inHR);
|
||||||
|
JPH_ASSERT(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
39
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeBufferMTL.h
vendored
Normal file
39
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeBufferMTL.h
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_MTL
|
||||||
|
|
||||||
|
#include <Jolt/Compute/MTL/ComputeSystemMTL.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Buffer that can be read from / written to by a compute shader
|
||||||
|
class JPH_EXPORT ComputeBufferMTL final : public ComputeBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
ComputeBufferMTL(ComputeSystemMTL *inComputeSystem, EType inType, uint64 inSize, uint inStride);
|
||||||
|
virtual ~ComputeBufferMTL() override;
|
||||||
|
|
||||||
|
bool Initialize(const void *inData);
|
||||||
|
|
||||||
|
virtual ComputeBufferResult CreateReadBackBuffer() const override;
|
||||||
|
|
||||||
|
id<MTLBuffer> GetBuffer() const { return mBuffer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void * MapInternal(EMode inMode) override;
|
||||||
|
virtual void UnmapInternal() override;
|
||||||
|
|
||||||
|
ComputeSystemMTL * mComputeSystem;
|
||||||
|
id<MTLBuffer> mBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_MTL
|
||||||
52
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeBufferMTL.mm
vendored
Normal file
52
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeBufferMTL.mm
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_MTL
|
||||||
|
|
||||||
|
#include <Jolt/Compute/MTL/ComputeBufferMTL.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
ComputeBufferMTL::ComputeBufferMTL(ComputeSystemMTL *inComputeSystem, EType inType, uint64 inSize, uint inStride) :
|
||||||
|
ComputeBuffer(inType, inSize, inStride),
|
||||||
|
mComputeSystem(inComputeSystem)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeBufferMTL::Initialize(const void *inData)
|
||||||
|
{
|
||||||
|
NSUInteger size = NSUInteger(mSize) * mStride;
|
||||||
|
if (inData != nullptr)
|
||||||
|
mBuffer = [mComputeSystem->GetDevice() newBufferWithBytes: inData length: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||||
|
else
|
||||||
|
mBuffer = [mComputeSystem->GetDevice() newBufferWithLength: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||||
|
return mBuffer != nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferMTL::~ComputeBufferMTL()
|
||||||
|
{
|
||||||
|
[mBuffer release];
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ComputeBufferMTL::MapInternal(EMode inMode)
|
||||||
|
{
|
||||||
|
return mBuffer.contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeBufferMTL::UnmapInternal()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferResult ComputeBufferMTL::CreateReadBackBuffer() const
|
||||||
|
{
|
||||||
|
ComputeBufferResult result;
|
||||||
|
result.Set(const_cast<ComputeBufferMTL *>(this));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_MTL
|
||||||
49
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeQueueMTL.h
vendored
Normal file
49
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeQueueMTL.h
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_MTL
|
||||||
|
|
||||||
|
#include <MetalKit/MetalKit.h>
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeQueue.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class ComputeShaderMTL;
|
||||||
|
|
||||||
|
/// A command queue for Metal for executing compute workloads on the GPU.
|
||||||
|
class JPH_EXPORT ComputeQueueMTL final : public ComputeQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Constructor / destructor
|
||||||
|
ComputeQueueMTL(id<MTLDevice> inDevice);
|
||||||
|
virtual ~ComputeQueueMTL() override;
|
||||||
|
|
||||||
|
// See: ComputeQueue
|
||||||
|
virtual void SetShader(const ComputeShader *inShader) override;
|
||||||
|
virtual void SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer) override;
|
||||||
|
virtual void SetBuffer(const char *inName, const ComputeBuffer *inBuffer) override;
|
||||||
|
virtual void SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier = EBarrier::Yes) override;
|
||||||
|
virtual void ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc) override;
|
||||||
|
virtual void Dispatch(uint inThreadGroupsX, uint inThreadGroupsY, uint inThreadGroupsZ) override;
|
||||||
|
virtual void Execute() override;
|
||||||
|
virtual void Wait() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void BeginCommandBuffer();
|
||||||
|
|
||||||
|
id<MTLCommandQueue> mCommandQueue;
|
||||||
|
id<MTLCommandBuffer> mCommandBuffer;
|
||||||
|
id<MTLComputeCommandEncoder> mComputeEncoder;
|
||||||
|
RefConst<ComputeShaderMTL> mShader;
|
||||||
|
bool mIsExecuting = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_MTL
|
||||||
123
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeQueueMTL.mm
vendored
Normal file
123
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeQueueMTL.mm
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_MTL
|
||||||
|
|
||||||
|
#include <Jolt/Compute/MTL/ComputeQueueMTL.h>
|
||||||
|
#include <Jolt/Compute/MTL/ComputeShaderMTL.h>
|
||||||
|
#include <Jolt/Compute/MTL/ComputeBufferMTL.h>
|
||||||
|
#include <Jolt/Compute/MTL/ComputeSystemMTL.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
ComputeQueueMTL::~ComputeQueueMTL()
|
||||||
|
{
|
||||||
|
Wait();
|
||||||
|
|
||||||
|
[mCommandQueue release];
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeQueueMTL::ComputeQueueMTL(id<MTLDevice> inDevice)
|
||||||
|
{
|
||||||
|
// Create the command queue
|
||||||
|
mCommandQueue = [inDevice newCommandQueue];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueMTL::BeginCommandBuffer()
|
||||||
|
{
|
||||||
|
if (mCommandBuffer == nil)
|
||||||
|
{
|
||||||
|
// Start a new command buffer
|
||||||
|
mCommandBuffer = [mCommandQueue commandBuffer];
|
||||||
|
mComputeEncoder = [mCommandBuffer computeCommandEncoder];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueMTL::SetShader(const ComputeShader *inShader)
|
||||||
|
{
|
||||||
|
BeginCommandBuffer();
|
||||||
|
|
||||||
|
mShader = static_cast<const ComputeShaderMTL *>(inShader);
|
||||||
|
|
||||||
|
[mComputeEncoder setComputePipelineState: mShader->GetPipelineState()];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueMTL::SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::ConstantBuffer);
|
||||||
|
|
||||||
|
BeginCommandBuffer();
|
||||||
|
|
||||||
|
const ComputeBufferMTL *buffer = static_cast<const ComputeBufferMTL *>(inBuffer);
|
||||||
|
[mComputeEncoder setBuffer: buffer->GetBuffer() offset: 0 atIndex: mShader->NameToBindingIndex(inName)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueMTL::SetBuffer(const char *inName, const ComputeBuffer *inBuffer)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::UploadBuffer || inBuffer->GetType() == ComputeBuffer::EType::Buffer || inBuffer->GetType() == ComputeBuffer::EType::RWBuffer);
|
||||||
|
|
||||||
|
BeginCommandBuffer();
|
||||||
|
|
||||||
|
const ComputeBufferMTL *buffer = static_cast<const ComputeBufferMTL *>(inBuffer);
|
||||||
|
[mComputeEncoder setBuffer: buffer->GetBuffer() offset: 0 atIndex: mShader->NameToBindingIndex(inName)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueMTL::SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::RWBuffer);
|
||||||
|
|
||||||
|
BeginCommandBuffer();
|
||||||
|
|
||||||
|
const ComputeBufferMTL *buffer = static_cast<const ComputeBufferMTL *>(inBuffer);
|
||||||
|
[mComputeEncoder setBuffer: buffer->GetBuffer() offset: 0 atIndex: mShader->NameToBindingIndex(inName)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueMTL::ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inDst == inSrc); // Since ComputeBuffer::CreateReadBackBuffer returns the same buffer, we don't need to copy
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueMTL::Dispatch(uint inThreadGroupsX, uint inThreadGroupsY, uint inThreadGroupsZ)
|
||||||
|
{
|
||||||
|
BeginCommandBuffer();
|
||||||
|
|
||||||
|
MTLSize thread_groups = MTLSizeMake(inThreadGroupsX, inThreadGroupsY, inThreadGroupsZ);
|
||||||
|
MTLSize group_size = MTLSizeMake(mShader->GetGroupSizeX(), mShader->GetGroupSizeY(), mShader->GetGroupSizeZ());
|
||||||
|
[mComputeEncoder dispatchThreadgroups: thread_groups threadsPerThreadgroup: group_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueMTL::Execute()
|
||||||
|
{
|
||||||
|
// End command buffer
|
||||||
|
if (mCommandBuffer == nil)
|
||||||
|
return;
|
||||||
|
|
||||||
|
[mComputeEncoder endEncoding];
|
||||||
|
[mCommandBuffer commit];
|
||||||
|
mShader = nullptr;
|
||||||
|
mIsExecuting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueMTL::Wait()
|
||||||
|
{
|
||||||
|
if (!mIsExecuting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
[mCommandBuffer waitUntilCompleted];
|
||||||
|
mComputeEncoder = nil;
|
||||||
|
mCommandBuffer = nil;
|
||||||
|
mIsExecuting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_MTL
|
||||||
39
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeShaderMTL.h
vendored
Normal file
39
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeShaderMTL.h
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_MTL
|
||||||
|
|
||||||
|
#include <MetalKit/MetalKit.h>
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeShader.h>
|
||||||
|
#include <Jolt/Core/UnorderedMap.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Compute shader handle for Metal
|
||||||
|
class JPH_EXPORT ComputeShaderMTL : public ComputeShader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
ComputeShaderMTL(id<MTLComputePipelineState> inPipelineState, MTLComputePipelineReflection *inReflection, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ);
|
||||||
|
virtual ~ComputeShaderMTL() override { [mPipelineState release]; }
|
||||||
|
|
||||||
|
/// Access to the function
|
||||||
|
id<MTLComputePipelineState> GetPipelineState() const { return mPipelineState; }
|
||||||
|
|
||||||
|
/// Get index of buffer name
|
||||||
|
uint NameToBindingIndex(const char *inName) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
id<MTLComputePipelineState> mPipelineState;
|
||||||
|
UnorderedMap<String, uint> mNameToBindingIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_MTL
|
||||||
34
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeShaderMTL.mm
vendored
Normal file
34
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeShaderMTL.mm
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_MTL
|
||||||
|
|
||||||
|
#include <Jolt/Compute/MTL/ComputeShaderMTL.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
ComputeShaderMTL::ComputeShaderMTL(id<MTLComputePipelineState> inPipelineState, MTLComputePipelineReflection *inReflection, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) :
|
||||||
|
ComputeShader(inGroupSizeX, inGroupSizeY, inGroupSizeZ),
|
||||||
|
mPipelineState(inPipelineState)
|
||||||
|
{
|
||||||
|
for (id<MTLBinding> binding in inReflection.bindings)
|
||||||
|
{
|
||||||
|
const char *name = [binding.name UTF8String];
|
||||||
|
uint index = uint(binding.index);
|
||||||
|
mNameToBindingIndex[name] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint ComputeShaderMTL::NameToBindingIndex(const char *inName) const
|
||||||
|
{
|
||||||
|
UnorderedMap<String, uint>::const_iterator it = mNameToBindingIndex.find(inName);
|
||||||
|
JPH_ASSERT(it != mNameToBindingIndex.end());
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_MTL
|
||||||
40
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeSystemMTL.h
vendored
Normal file
40
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeSystemMTL.h
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeSystem.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_MTL
|
||||||
|
|
||||||
|
#include <MetalKit/MetalKit.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Interface to run a workload on the GPU
|
||||||
|
class JPH_EXPORT ComputeSystemMTL : public ComputeSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_DECLARE_RTTI_VIRTUAL(JPH_EXPORT, ComputeSystemMTL)
|
||||||
|
|
||||||
|
// Initialize / shutdown the compute system
|
||||||
|
bool Initialize(id<MTLDevice> inDevice);
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
// See: ComputeSystem
|
||||||
|
virtual ComputeShaderResult CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) override;
|
||||||
|
virtual ComputeBufferResult CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData = nullptr) override;
|
||||||
|
virtual ComputeQueueResult CreateComputeQueue() override;
|
||||||
|
|
||||||
|
/// Get the metal device
|
||||||
|
id<MTLDevice> GetDevice() const { return mDevice; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
id<MTLDevice> mDevice;
|
||||||
|
id<MTLLibrary> mShaderLibrary;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_MTL
|
||||||
110
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeSystemMTL.mm
vendored
Normal file
110
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeSystemMTL.mm
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_MTL
|
||||||
|
|
||||||
|
#include <Jolt/Compute/MTL/ComputeSystemMTL.h>
|
||||||
|
#include <Jolt/Compute/MTL/ComputeBufferMTL.h>
|
||||||
|
#include <Jolt/Compute/MTL/ComputeShaderMTL.h>
|
||||||
|
#include <Jolt/Compute/MTL/ComputeQueueMTL.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
JPH_IMPLEMENT_RTTI_VIRTUAL(ComputeSystemMTL)
|
||||||
|
{
|
||||||
|
JPH_ADD_BASE_CLASS(ComputeSystemMTL, ComputeSystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeSystemMTL::Initialize(id<MTLDevice> inDevice)
|
||||||
|
{
|
||||||
|
mDevice = [inDevice retain];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeSystemMTL::Shutdown()
|
||||||
|
{
|
||||||
|
[mShaderLibrary release];
|
||||||
|
[mDevice release];
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeShaderResult ComputeSystemMTL::CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ)
|
||||||
|
{
|
||||||
|
ComputeShaderResult result;
|
||||||
|
|
||||||
|
if (mShaderLibrary == nil)
|
||||||
|
{
|
||||||
|
// Load the shader library containing all shaders
|
||||||
|
Array<uint8> *data = new Array<uint8>();
|
||||||
|
String error;
|
||||||
|
if (!mShaderLoader("Jolt.metallib", *data, error))
|
||||||
|
{
|
||||||
|
result.SetError(error);
|
||||||
|
delete data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to dispatch data
|
||||||
|
dispatch_data_t data_dispatch = dispatch_data_create(data->data(), data->size(), nullptr, ^{ delete data; });
|
||||||
|
|
||||||
|
// Create the library
|
||||||
|
NSError *ns_error = nullptr;
|
||||||
|
mShaderLibrary = [mDevice newLibraryWithData: data_dispatch error: &ns_error];
|
||||||
|
if (ns_error != nil)
|
||||||
|
{
|
||||||
|
result.SetError("Failed to laod shader library");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the shader function
|
||||||
|
id<MTLFunction> function = [mShaderLibrary newFunctionWithName: [NSString stringWithCString: inName encoding: NSUTF8StringEncoding]];
|
||||||
|
if (function == nil)
|
||||||
|
{
|
||||||
|
result.SetError("Failed to instantiate compute shader");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the pipeline
|
||||||
|
NSError *error = nil;
|
||||||
|
MTLComputePipelineReflection *reflection = nil;
|
||||||
|
id<MTLComputePipelineState> pipeline_state = [mDevice newComputePipelineStateWithFunction: function options: MTLPipelineOptionBindingInfo | MTLPipelineOptionBufferTypeInfo reflection: &reflection error: &error];
|
||||||
|
if (error != nil || pipeline_state == nil)
|
||||||
|
{
|
||||||
|
result.SetError("Failed to create compute pipeline");
|
||||||
|
[function release];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Set(new ComputeShaderMTL(pipeline_state, reflection, inGroupSizeX, inGroupSizeY, inGroupSizeZ));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferResult ComputeSystemMTL::CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData)
|
||||||
|
{
|
||||||
|
ComputeBufferResult result;
|
||||||
|
|
||||||
|
Ref<ComputeBufferMTL> buffer = new ComputeBufferMTL(this, inType, inSize, inStride);
|
||||||
|
if (!buffer->Initialize(inData))
|
||||||
|
{
|
||||||
|
result.SetError("Failed to create compute buffer");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Set(buffer.GetPtr());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeQueueResult ComputeSystemMTL::CreateComputeQueue()
|
||||||
|
{
|
||||||
|
ComputeQueueResult result;
|
||||||
|
result.Set(new ComputeQueueMTL(mDevice));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_MTL
|
||||||
28
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeSystemMTLImpl.h
vendored
Normal file
28
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeSystemMTLImpl.h
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_MTL
|
||||||
|
|
||||||
|
#include <Jolt/Compute/MTL/ComputeSystemMTL.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Interface to run a workload on the GPU that fully initializes Metal.
|
||||||
|
class JPH_EXPORT ComputeSystemMTLImpl : public ComputeSystemMTL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_DECLARE_RTTI_VIRTUAL(JPH_EXPORT, ComputeSystemMTLImpl)
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~ComputeSystemMTLImpl() override;
|
||||||
|
|
||||||
|
/// Initialize / shutdown the compute system
|
||||||
|
bool Initialize();
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_MTL
|
||||||
49
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeSystemMTLImpl.mm
vendored
Normal file
49
thirdparty/JoltPhysics/Jolt/Compute/MTL/ComputeSystemMTLImpl.mm
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_MTL
|
||||||
|
|
||||||
|
#include <Jolt/Compute/MTL/ComputeSystemMTLImpl.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
JPH_IMPLEMENT_RTTI_VIRTUAL(ComputeSystemMTLImpl)
|
||||||
|
{
|
||||||
|
JPH_ADD_BASE_CLASS(ComputeSystemMTLImpl, ComputeSystemMTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeSystemMTLImpl::~ComputeSystemMTLImpl()
|
||||||
|
{
|
||||||
|
Shutdown();
|
||||||
|
|
||||||
|
[GetDevice() release];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeSystemMTLImpl::Initialize()
|
||||||
|
{
|
||||||
|
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
|
||||||
|
|
||||||
|
return ComputeSystemMTL::Initialize(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeSystemResult CreateComputeSystemMTL()
|
||||||
|
{
|
||||||
|
ComputeSystemResult result;
|
||||||
|
|
||||||
|
Ref<ComputeSystemMTLImpl> compute = new ComputeSystemMTLImpl;
|
||||||
|
if (!compute->Initialize())
|
||||||
|
{
|
||||||
|
result.SetError("Failed to initialize compute system");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Set(compute.GetPtr());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_MTL
|
||||||
42
thirdparty/JoltPhysics/Jolt/Compute/VK/BufferVK.h
vendored
Normal file
42
thirdparty/JoltPhysics/Jolt/Compute/VK/BufferVK.h
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/IncludeVK.h>
|
||||||
|
#include <Jolt/Core/Reference.h>
|
||||||
|
#include <Jolt/Core/NonCopyable.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Simple wrapper class to manage a Vulkan memory block
|
||||||
|
class MemoryVK : public RefTarget<MemoryVK>, public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~MemoryVK()
|
||||||
|
{
|
||||||
|
// We should have unmapped and freed the block before destruction
|
||||||
|
JPH_ASSERT(mMappedCount == 0);
|
||||||
|
JPH_ASSERT(mMemory == VK_NULL_HANDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDeviceMemory mMemory = VK_NULL_HANDLE; ///< The Vulkan memory handle
|
||||||
|
VkDeviceSize mSize = 0; ///< Size of the memory block
|
||||||
|
VkDeviceSize mBufferSize = 0; ///< Size of each of the buffers that this memory block has been divided into
|
||||||
|
VkMemoryPropertyFlags mProperties = 0; ///< Vulkan memory properties used to allocate this block
|
||||||
|
int mMappedCount = 0; ///< How often buffers using this memory block were mapped
|
||||||
|
void * mMappedPtr = nullptr; ///< The CPU address of the memory block when mapped
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Simple wrapper class to manage a Vulkan buffer
|
||||||
|
class BufferVK
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Ref<MemoryVK> mMemory; ///< The memory block that contains the buffer (note that filling this in is optional if you do your own buffer allocation)
|
||||||
|
VkBuffer mBuffer = VK_NULL_HANDLE; ///< The Vulkan buffer handle
|
||||||
|
VkDeviceSize mOffset = 0; ///< Offset in the memory block where the buffer starts
|
||||||
|
VkDeviceSize mSize = 0; ///< Real size of the buffer
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
140
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeBufferVK.cpp
vendored
Normal file
140
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeBufferVK.cpp
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/ComputeBufferVK.h>
|
||||||
|
#include <Jolt/Compute/VK/ComputeSystemVK.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
ComputeBufferVK::ComputeBufferVK(ComputeSystemVK *inComputeSystem, EType inType, uint64 inSize, uint inStride) :
|
||||||
|
ComputeBuffer(inType, inSize, inStride),
|
||||||
|
mComputeSystem(inComputeSystem)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeBufferVK::Initialize(const void *inData)
|
||||||
|
{
|
||||||
|
VkDeviceSize buffer_size = VkDeviceSize(mSize * mStride);
|
||||||
|
|
||||||
|
switch (mType)
|
||||||
|
{
|
||||||
|
case EType::Buffer:
|
||||||
|
JPH_ASSERT(inData != nullptr);
|
||||||
|
[[fallthrough]];
|
||||||
|
|
||||||
|
case EType::UploadBuffer:
|
||||||
|
case EType::RWBuffer:
|
||||||
|
if (!mComputeSystem->CreateBuffer(buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, mBufferCPU))
|
||||||
|
return false;
|
||||||
|
if (!mComputeSystem->CreateBuffer(buffer_size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mBufferGPU))
|
||||||
|
return false;
|
||||||
|
if (inData != nullptr)
|
||||||
|
{
|
||||||
|
void *data = mComputeSystem->MapBuffer(mBufferCPU);
|
||||||
|
memcpy(data, inData, size_t(buffer_size));
|
||||||
|
mComputeSystem->UnmapBuffer(mBufferCPU);
|
||||||
|
mNeedsSync = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EType::ConstantBuffer:
|
||||||
|
if (!mComputeSystem->CreateBuffer(buffer_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, mBufferCPU))
|
||||||
|
return false;
|
||||||
|
if (inData != nullptr)
|
||||||
|
{
|
||||||
|
void* data = mComputeSystem->MapBuffer(mBufferCPU);
|
||||||
|
memcpy(data, inData, size_t(buffer_size));
|
||||||
|
mComputeSystem->UnmapBuffer(mBufferCPU);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EType::ReadbackBuffer:
|
||||||
|
JPH_ASSERT(inData == nullptr, "Can't upload data to a readback buffer");
|
||||||
|
if (!mComputeSystem->CreateBuffer(buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, mBufferCPU))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferVK::~ComputeBufferVK()
|
||||||
|
{
|
||||||
|
mComputeSystem->FreeBuffer(mBufferGPU);
|
||||||
|
mComputeSystem->FreeBuffer(mBufferCPU);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeBufferVK::Barrier(VkCommandBuffer inCommandBuffer, VkPipelineStageFlags inToStage, VkAccessFlagBits inToFlags, bool inForce) const
|
||||||
|
{
|
||||||
|
if (mAccessStage == inToStage && mAccessFlagBits == inToFlags && !inForce)
|
||||||
|
return;
|
||||||
|
|
||||||
|
VkBufferMemoryBarrier b = {};
|
||||||
|
b.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
||||||
|
b.srcAccessMask = mAccessFlagBits;
|
||||||
|
b.dstAccessMask = inToFlags;
|
||||||
|
b.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
b.buffer = mBufferGPU.mBuffer != VK_NULL_HANDLE? mBufferGPU.mBuffer : mBufferCPU.mBuffer;
|
||||||
|
b.offset = 0;
|
||||||
|
b.size = VK_WHOLE_SIZE;
|
||||||
|
mComputeSystem->mVkCmdPipelineBarrier(inCommandBuffer, mAccessStage, inToStage, 0, 0, nullptr, 1, &b, 0, nullptr);
|
||||||
|
|
||||||
|
mAccessStage = inToStage;
|
||||||
|
mAccessFlagBits = inToFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeBufferVK::SyncCPUToGPU(VkCommandBuffer inCommandBuffer) const
|
||||||
|
{
|
||||||
|
if (!mNeedsSync)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Barrier before write
|
||||||
|
Barrier(inCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, false);
|
||||||
|
|
||||||
|
// Copy from CPU to GPU
|
||||||
|
VkBufferCopy copy = {};
|
||||||
|
copy.srcOffset = 0;
|
||||||
|
copy.dstOffset = 0;
|
||||||
|
copy.size = GetSize() * GetStride();
|
||||||
|
mComputeSystem->mVkCmdCopyBuffer(inCommandBuffer, mBufferCPU.mBuffer, mBufferGPU.mBuffer, 1, ©);
|
||||||
|
|
||||||
|
mNeedsSync = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ComputeBufferVK::MapInternal(EMode inMode)
|
||||||
|
{
|
||||||
|
switch (inMode)
|
||||||
|
{
|
||||||
|
case EMode::Read:
|
||||||
|
JPH_ASSERT(mType == EType::ReadbackBuffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EMode::Write:
|
||||||
|
JPH_ASSERT(mType == EType::UploadBuffer || mType == EType::ConstantBuffer);
|
||||||
|
mNeedsSync = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mComputeSystem->MapBuffer(mBufferCPU);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeBufferVK::UnmapInternal()
|
||||||
|
{
|
||||||
|
mComputeSystem->UnmapBuffer(mBufferCPU);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferResult ComputeBufferVK::CreateReadBackBuffer() const
|
||||||
|
{
|
||||||
|
return mComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::ReadbackBuffer, mSize, mStride);
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
52
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeBufferVK.h
vendored
Normal file
52
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeBufferVK.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeBuffer.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/BufferVK.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class ComputeSystemVK;
|
||||||
|
|
||||||
|
/// Buffer that can be read from / written to by a compute shader
|
||||||
|
class JPH_EXPORT ComputeBufferVK final : public ComputeBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
ComputeBufferVK(ComputeSystemVK *inComputeSystem, EType inType, uint64 inSize, uint inStride);
|
||||||
|
virtual ~ComputeBufferVK() override;
|
||||||
|
|
||||||
|
bool Initialize(const void *inData);
|
||||||
|
|
||||||
|
virtual ComputeBufferResult CreateReadBackBuffer() const override;
|
||||||
|
|
||||||
|
VkBuffer GetBufferCPU() const { return mBufferCPU.mBuffer; }
|
||||||
|
VkBuffer GetBufferGPU() const { return mBufferGPU.mBuffer; }
|
||||||
|
BufferVK ReleaseBufferCPU() const { BufferVK tmp = mBufferCPU; mBufferCPU = BufferVK(); return tmp; }
|
||||||
|
|
||||||
|
void Barrier(VkCommandBuffer inCommandBuffer, VkPipelineStageFlags inToStage, VkAccessFlagBits inToFlags, bool inForce) const;
|
||||||
|
bool SyncCPUToGPU(VkCommandBuffer inCommandBuffer) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void * MapInternal(EMode inMode) override;
|
||||||
|
virtual void UnmapInternal() override;
|
||||||
|
|
||||||
|
ComputeSystemVK * mComputeSystem;
|
||||||
|
mutable BufferVK mBufferCPU;
|
||||||
|
BufferVK mBufferGPU;
|
||||||
|
mutable bool mNeedsSync = false; ///< If this buffer needs to be synced from CPU to GPU
|
||||||
|
mutable VkAccessFlagBits mAccessFlagBits = VK_ACCESS_SHADER_READ_BIT; ///< Access flags of the last usage, used for barriers
|
||||||
|
mutable VkPipelineStageFlags mAccessStage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; ///< Pipeline stage of the last usage, used for barriers
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
304
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeQueueVK.cpp
vendored
Normal file
304
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeQueueVK.cpp
vendored
Normal file
|
|
@ -0,0 +1,304 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/ComputeQueueVK.h>
|
||||||
|
#include <Jolt/Compute/VK/ComputeBufferVK.h>
|
||||||
|
#include <Jolt/Compute/VK/ComputeSystemVK.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
ComputeQueueVK::~ComputeQueueVK()
|
||||||
|
{
|
||||||
|
Wait();
|
||||||
|
|
||||||
|
VkDevice device = mComputeSystem->GetDevice();
|
||||||
|
|
||||||
|
if (mCommandBuffer != VK_NULL_HANDLE)
|
||||||
|
mComputeSystem->mVkFreeCommandBuffers(device, mCommandPool, 1, &mCommandBuffer);
|
||||||
|
|
||||||
|
if (mCommandPool != VK_NULL_HANDLE)
|
||||||
|
mComputeSystem->mVkDestroyCommandPool(device, mCommandPool, nullptr);
|
||||||
|
|
||||||
|
if (mDescriptorPool != VK_NULL_HANDLE)
|
||||||
|
mComputeSystem->mVkDestroyDescriptorPool(device, mDescriptorPool, nullptr);
|
||||||
|
|
||||||
|
if (mFence != VK_NULL_HANDLE)
|
||||||
|
mComputeSystem->mVkDestroyFence(device, mFence, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeQueueVK::Initialize(uint32 inComputeQueueIndex, ComputeQueueResult &outResult)
|
||||||
|
{
|
||||||
|
// Get the queue
|
||||||
|
VkDevice device = mComputeSystem->GetDevice();
|
||||||
|
mComputeSystem->mVkGetDeviceQueue(device, inComputeQueueIndex, 0, &mQueue);
|
||||||
|
|
||||||
|
// Create a command pool
|
||||||
|
VkCommandPoolCreateInfo pool_info = {};
|
||||||
|
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||||
|
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||||
|
pool_info.queueFamilyIndex = inComputeQueueIndex;
|
||||||
|
if (VKFailed(mComputeSystem->mVkCreateCommandPool(device, &pool_info, nullptr, &mCommandPool), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create descriptor pool
|
||||||
|
VkDescriptorPoolSize descriptor_pool_sizes[] = {
|
||||||
|
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1024 },
|
||||||
|
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 16 * 1024 },
|
||||||
|
};
|
||||||
|
VkDescriptorPoolCreateInfo descriptor_info = {};
|
||||||
|
descriptor_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||||
|
descriptor_info.poolSizeCount = (uint32)std::size(descriptor_pool_sizes);
|
||||||
|
descriptor_info.pPoolSizes = descriptor_pool_sizes;
|
||||||
|
descriptor_info.maxSets = 256;
|
||||||
|
if (VKFailed(mComputeSystem->mVkCreateDescriptorPool(device, &descriptor_info, nullptr, &mDescriptorPool), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create a command buffer
|
||||||
|
VkCommandBufferAllocateInfo alloc_info = {};
|
||||||
|
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
|
alloc_info.commandPool = mCommandPool;
|
||||||
|
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
|
alloc_info.commandBufferCount = 1;
|
||||||
|
if (VKFailed(mComputeSystem->mVkAllocateCommandBuffers(device, &alloc_info, &mCommandBuffer), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create a fence
|
||||||
|
VkFenceCreateInfo fence_info = {};
|
||||||
|
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||||
|
if (VKFailed(mComputeSystem->mVkCreateFence(device, &fence_info, nullptr, &mFence), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeQueueVK::BeginCommandBuffer()
|
||||||
|
{
|
||||||
|
if (!mCommandBufferRecording)
|
||||||
|
{
|
||||||
|
VkCommandBufferBeginInfo begin_info = {};
|
||||||
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
|
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||||
|
if (VKFailed(mComputeSystem->mVkBeginCommandBuffer(mCommandBuffer, &begin_info)))
|
||||||
|
return false;
|
||||||
|
mCommandBufferRecording = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueVK::SetShader(const ComputeShader *inShader)
|
||||||
|
{
|
||||||
|
mShader = static_cast<const ComputeShaderVK *>(inShader);
|
||||||
|
mBufferInfos = mShader->GetBufferInfos();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueVK::SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::ConstantBuffer);
|
||||||
|
|
||||||
|
if (!BeginCommandBuffer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ComputeBufferVK *buffer = static_cast<const ComputeBufferVK *>(inBuffer);
|
||||||
|
buffer->Barrier(mCommandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_UNIFORM_READ_BIT, false);
|
||||||
|
|
||||||
|
uint index = mShader->NameToBufferInfoIndex(inName);
|
||||||
|
JPH_ASSERT(mShader->GetLayoutBindings()[index].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||||
|
mBufferInfos[index].buffer = buffer->GetBufferCPU();
|
||||||
|
|
||||||
|
mUsedBuffers.insert(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueVK::SyncCPUToGPU(const ComputeBufferVK *inBuffer)
|
||||||
|
{
|
||||||
|
// Ensure that any CPU writes are visible to the GPU
|
||||||
|
if (inBuffer->SyncCPUToGPU(mCommandBuffer)
|
||||||
|
&& (inBuffer->GetType() == ComputeBuffer::EType::Buffer || inBuffer->GetType() == ComputeBuffer::EType::RWBuffer))
|
||||||
|
{
|
||||||
|
// After the first upload, the CPU buffer is no longer needed for Buffer and RWBuffer types
|
||||||
|
mDelayedFreedBuffers.push_back(inBuffer->ReleaseBufferCPU());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueVK::SetBuffer(const char *inName, const ComputeBuffer *inBuffer)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::UploadBuffer || inBuffer->GetType() == ComputeBuffer::EType::Buffer || inBuffer->GetType() == ComputeBuffer::EType::RWBuffer);
|
||||||
|
|
||||||
|
if (!BeginCommandBuffer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ComputeBufferVK *buffer = static_cast<const ComputeBufferVK *>(inBuffer);
|
||||||
|
SyncCPUToGPU(buffer);
|
||||||
|
buffer->Barrier(mCommandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT, false);
|
||||||
|
|
||||||
|
uint index = mShader->NameToBufferInfoIndex(inName);
|
||||||
|
JPH_ASSERT(mShader->GetLayoutBindings()[index].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||||
|
mBufferInfos[index].buffer = buffer->GetBufferGPU();
|
||||||
|
|
||||||
|
mUsedBuffers.insert(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueVK::SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier)
|
||||||
|
{
|
||||||
|
if (inBuffer == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::RWBuffer);
|
||||||
|
|
||||||
|
if (!BeginCommandBuffer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ComputeBufferVK *buffer = static_cast<const ComputeBufferVK *>(inBuffer);
|
||||||
|
SyncCPUToGPU(buffer);
|
||||||
|
buffer->Barrier(mCommandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VkAccessFlagBits(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT), inBarrier == EBarrier::Yes);
|
||||||
|
|
||||||
|
uint index = mShader->NameToBufferInfoIndex(inName);
|
||||||
|
JPH_ASSERT(mShader->GetLayoutBindings()[index].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||||
|
mBufferInfos[index].buffer = buffer->GetBufferGPU();
|
||||||
|
|
||||||
|
mUsedBuffers.insert(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueVK::ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc)
|
||||||
|
{
|
||||||
|
if (inDst == nullptr || inSrc == nullptr)
|
||||||
|
return;
|
||||||
|
JPH_ASSERT(inDst->GetType() == ComputeBuffer::EType::ReadbackBuffer);
|
||||||
|
|
||||||
|
if (!BeginCommandBuffer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ComputeBufferVK *src_vk = static_cast<const ComputeBufferVK *>(inSrc);
|
||||||
|
const ComputeBufferVK *dst_vk = static_cast<ComputeBufferVK *>(inDst);
|
||||||
|
|
||||||
|
// Barrier to start reading from GPU buffer and writing to CPU buffer
|
||||||
|
src_vk->Barrier(mCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, false);
|
||||||
|
dst_vk->Barrier(mCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, false);
|
||||||
|
|
||||||
|
// Copy
|
||||||
|
VkBufferCopy copy = {};
|
||||||
|
copy.srcOffset = 0;
|
||||||
|
copy.dstOffset = 0;
|
||||||
|
copy.size = src_vk->GetSize() * src_vk->GetStride();
|
||||||
|
mComputeSystem->mVkCmdCopyBuffer(mCommandBuffer, src_vk->GetBufferGPU(), dst_vk->GetBufferCPU(), 1, ©);
|
||||||
|
|
||||||
|
// Barrier to indicate that CPU can read from the buffer
|
||||||
|
dst_vk->Barrier(mCommandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_HOST_READ_BIT, false);
|
||||||
|
|
||||||
|
mUsedBuffers.insert(src_vk);
|
||||||
|
mUsedBuffers.insert(dst_vk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueVK::Dispatch(uint inThreadGroupsX, uint inThreadGroupsY, uint inThreadGroupsZ)
|
||||||
|
{
|
||||||
|
if (!BeginCommandBuffer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mComputeSystem->mVkCmdBindPipeline(mCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, mShader->GetPipeline());
|
||||||
|
|
||||||
|
VkDevice device = mComputeSystem->GetDevice();
|
||||||
|
const Array<VkDescriptorSetLayoutBinding> &ds_bindings = mShader->GetLayoutBindings();
|
||||||
|
if (!ds_bindings.empty())
|
||||||
|
{
|
||||||
|
// Create a descriptor set
|
||||||
|
VkDescriptorSetAllocateInfo alloc_info = {};
|
||||||
|
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||||
|
alloc_info.descriptorPool = mDescriptorPool;
|
||||||
|
alloc_info.descriptorSetCount = 1;
|
||||||
|
VkDescriptorSetLayout ds_layout = mShader->GetDescriptorSetLayout();
|
||||||
|
alloc_info.pSetLayouts = &ds_layout;
|
||||||
|
VkDescriptorSet descriptor_set;
|
||||||
|
if (VKFailed(mComputeSystem->mVkAllocateDescriptorSets(device, &alloc_info, &descriptor_set)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Write the values to the descriptor set
|
||||||
|
Array<VkWriteDescriptorSet> writes;
|
||||||
|
writes.reserve(ds_bindings.size());
|
||||||
|
for (uint32 i = 0; i < (uint32)ds_bindings.size(); ++i)
|
||||||
|
{
|
||||||
|
VkWriteDescriptorSet w = {};
|
||||||
|
w.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
w.dstSet = descriptor_set;
|
||||||
|
w.dstBinding = ds_bindings[i].binding;
|
||||||
|
w.dstArrayElement = 0;
|
||||||
|
w.descriptorCount = ds_bindings[i].descriptorCount;
|
||||||
|
w.descriptorType = ds_bindings[i].descriptorType;
|
||||||
|
w.pBufferInfo = &mBufferInfos[i];
|
||||||
|
writes.push_back(w);
|
||||||
|
}
|
||||||
|
mComputeSystem->mVkUpdateDescriptorSets(device, (uint32)writes.size(), writes.data(), 0, nullptr);
|
||||||
|
|
||||||
|
// Bind the descriptor set
|
||||||
|
mComputeSystem->mVkCmdBindDescriptorSets(mCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, mShader->GetPipelineLayout(), 0, 1, &descriptor_set, 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
mComputeSystem->mVkCmdDispatch(mCommandBuffer, inThreadGroupsX, inThreadGroupsY, inThreadGroupsZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueVK::Execute()
|
||||||
|
{
|
||||||
|
// End command buffer
|
||||||
|
if (!mCommandBufferRecording)
|
||||||
|
return;
|
||||||
|
if (VKFailed(mComputeSystem->mVkEndCommandBuffer(mCommandBuffer)))
|
||||||
|
return;
|
||||||
|
mCommandBufferRecording = false;
|
||||||
|
|
||||||
|
// Reset fence
|
||||||
|
VkDevice device = mComputeSystem->GetDevice();
|
||||||
|
if (VKFailed(mComputeSystem->mVkResetFences(device, 1, &mFence)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Submit
|
||||||
|
VkSubmitInfo submit = {};
|
||||||
|
submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
submit.commandBufferCount = 1;
|
||||||
|
submit.pCommandBuffers = &mCommandBuffer;
|
||||||
|
if (VKFailed(mComputeSystem->mVkQueueSubmit(mQueue, 1, &submit, mFence)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Clear the current shader
|
||||||
|
mShader = nullptr;
|
||||||
|
|
||||||
|
// Mark that we're executing
|
||||||
|
mIsExecuting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeQueueVK::Wait()
|
||||||
|
{
|
||||||
|
if (!mIsExecuting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Wait for the work to complete
|
||||||
|
VkDevice device = mComputeSystem->GetDevice();
|
||||||
|
if (VKFailed(mComputeSystem->mVkWaitForFences(device, 1, &mFence, VK_TRUE, UINT64_MAX)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Reset command buffer so it can be reused
|
||||||
|
if (mCommandBuffer != VK_NULL_HANDLE)
|
||||||
|
mComputeSystem->mVkResetCommandBuffer(mCommandBuffer, 0);
|
||||||
|
|
||||||
|
// Allow reusing the descriptors for next run
|
||||||
|
mComputeSystem->mVkResetDescriptorPool(device, mDescriptorPool, 0);
|
||||||
|
|
||||||
|
// Buffers can be freed now
|
||||||
|
mUsedBuffers.clear();
|
||||||
|
|
||||||
|
// Free delayed buffers
|
||||||
|
for (BufferVK &buffer : mDelayedFreedBuffers)
|
||||||
|
mComputeSystem->FreeBuffer(buffer);
|
||||||
|
mDelayedFreedBuffers.clear();
|
||||||
|
|
||||||
|
mIsExecuting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
66
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeQueueVK.h
vendored
Normal file
66
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeQueueVK.h
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeQueue.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/ComputeShaderVK.h>
|
||||||
|
#include <Jolt/Compute/VK/BufferVK.h>
|
||||||
|
#include <Jolt/Core/UnorderedMap.h>
|
||||||
|
#include <Jolt/Core/UnorderedSet.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class ComputeSystemVK;
|
||||||
|
class ComputeBufferVK;
|
||||||
|
|
||||||
|
/// A command queue for Vulkan for executing compute workloads on the GPU.
|
||||||
|
class JPH_EXPORT ComputeQueueVK final : public ComputeQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Constructor / Destructor
|
||||||
|
explicit ComputeQueueVK(ComputeSystemVK *inComputeSystem) : mComputeSystem(inComputeSystem) { }
|
||||||
|
virtual ~ComputeQueueVK() override;
|
||||||
|
|
||||||
|
/// Initialize the queue
|
||||||
|
bool Initialize(uint32 inComputeQueueIndex, ComputeQueueResult &outResult);
|
||||||
|
|
||||||
|
// See: ComputeQueue
|
||||||
|
virtual void SetShader(const ComputeShader *inShader) override;
|
||||||
|
virtual void SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer) override;
|
||||||
|
virtual void SetBuffer(const char *inName, const ComputeBuffer *inBuffer) override;
|
||||||
|
virtual void SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier = EBarrier::Yes) override;
|
||||||
|
virtual void ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc) override;
|
||||||
|
virtual void Dispatch(uint inThreadGroupsX, uint inThreadGroupsY, uint inThreadGroupsZ) override;
|
||||||
|
virtual void Execute() override;
|
||||||
|
virtual void Wait() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool BeginCommandBuffer();
|
||||||
|
|
||||||
|
// Copy the CPU buffer to the GPU buffer if needed
|
||||||
|
void SyncCPUToGPU(const ComputeBufferVK *inBuffer);
|
||||||
|
|
||||||
|
ComputeSystemVK * mComputeSystem;
|
||||||
|
VkQueue mQueue = VK_NULL_HANDLE;
|
||||||
|
VkCommandPool mCommandPool = VK_NULL_HANDLE;
|
||||||
|
VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE;
|
||||||
|
VkCommandBuffer mCommandBuffer = VK_NULL_HANDLE;
|
||||||
|
bool mCommandBufferRecording = false; ///< If we are currently recording commands into the command buffer
|
||||||
|
VkFence mFence = VK_NULL_HANDLE;
|
||||||
|
bool mIsExecuting = false; ///< If Execute has been called and we are waiting for it to finish
|
||||||
|
RefConst<ComputeShaderVK> mShader; ///< Shader that has been activated
|
||||||
|
Array<VkDescriptorBufferInfo> mBufferInfos; ///< List of parameters that will be sent to the current shader
|
||||||
|
UnorderedSet<RefConst<ComputeBuffer>> mUsedBuffers; ///< Buffers that are in use by the current execution, these will be retained until execution is finished so that we don't free buffers that are in use
|
||||||
|
Array<BufferVK> mDelayedFreedBuffers; ///< Hardware buffers that need to be freed after execution is done
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
237
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeShaderVK.cpp
vendored
Normal file
237
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeShaderVK.cpp
vendored
Normal file
|
|
@ -0,0 +1,237 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/ComputeShaderVK.h>
|
||||||
|
#include <Jolt/Compute/VK/ComputeSystemVK.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
ComputeShaderVK::~ComputeShaderVK()
|
||||||
|
{
|
||||||
|
VkDevice device = mComputeSystem->GetDevice();
|
||||||
|
|
||||||
|
if (mShaderModule != VK_NULL_HANDLE)
|
||||||
|
mComputeSystem->mVkDestroyShaderModule(device, mShaderModule, nullptr);
|
||||||
|
|
||||||
|
if (mDescriptorSetLayout != VK_NULL_HANDLE)
|
||||||
|
mComputeSystem->mVkDestroyDescriptorSetLayout(device, mDescriptorSetLayout, nullptr);
|
||||||
|
|
||||||
|
if (mPipelineLayout != VK_NULL_HANDLE)
|
||||||
|
mComputeSystem->mVkDestroyPipelineLayout(device, mPipelineLayout, nullptr);
|
||||||
|
|
||||||
|
if (mPipeline != VK_NULL_HANDLE)
|
||||||
|
mComputeSystem->mVkDestroyPipeline(device, mPipeline, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeShaderVK::Initialize(const Array<uint8> &inSPVCode, VkBuffer inDummyBuffer, ComputeShaderResult &outResult)
|
||||||
|
{
|
||||||
|
const uint32 *spv_words = reinterpret_cast<const uint32 *>(inSPVCode.data());
|
||||||
|
size_t spv_word_count = inSPVCode.size() / sizeof(uint32);
|
||||||
|
|
||||||
|
// Minimal SPIR-V parser to extract name to binding info
|
||||||
|
UnorderedMap<uint32, String> id_to_name;
|
||||||
|
UnorderedMap<uint32, uint32> id_to_binding;
|
||||||
|
UnorderedMap<uint32, VkDescriptorType> id_to_descriptor_type;
|
||||||
|
UnorderedMap<uint32, uint32> pointer_to_pointee;
|
||||||
|
UnorderedMap<uint32, uint32> var_to_ptr_type;
|
||||||
|
size_t i = 5; // Skip 5 word header
|
||||||
|
while (i < spv_word_count)
|
||||||
|
{
|
||||||
|
// Parse next word
|
||||||
|
uint32 word = spv_words[i];
|
||||||
|
uint16 opcode = uint16(word & 0xffff);
|
||||||
|
uint16 word_count = uint16(word >> 16);
|
||||||
|
if (word_count == 0 || i + word_count > spv_word_count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (opcode)
|
||||||
|
{
|
||||||
|
case 5: // OpName
|
||||||
|
if (word_count >= 2)
|
||||||
|
{
|
||||||
|
uint32 target_id = spv_words[i + 1];
|
||||||
|
const char* name = reinterpret_cast<const char*>(&spv_words[i + 2]);
|
||||||
|
if (*name != 0)
|
||||||
|
id_to_name.insert({ target_id, name });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 16: // OpExecutionMode
|
||||||
|
if (word_count >= 6)
|
||||||
|
{
|
||||||
|
uint32 execution_mode = spv_words[i + 2];
|
||||||
|
if (execution_mode == 17) // LocalSize
|
||||||
|
{
|
||||||
|
// Assert that the group size provided matches the one in the shader
|
||||||
|
JPH_ASSERT(GetGroupSizeX() == spv_words[i + 3], "Group size X mismatch");
|
||||||
|
JPH_ASSERT(GetGroupSizeY() == spv_words[i + 4], "Group size Y mismatch");
|
||||||
|
JPH_ASSERT(GetGroupSizeZ() == spv_words[i + 5], "Group size Z mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 32: // OpTypePointer
|
||||||
|
if (word_count >= 4)
|
||||||
|
{
|
||||||
|
uint32 result_id = spv_words[i + 1];
|
||||||
|
uint32 type_id = spv_words[i + 3];
|
||||||
|
pointer_to_pointee.insert({ result_id, type_id });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 59: // OpVariable
|
||||||
|
if (word_count >= 3)
|
||||||
|
{
|
||||||
|
uint32 ptr_type_id = spv_words[i + 1];
|
||||||
|
uint32 result_id = spv_words[i + 2];
|
||||||
|
var_to_ptr_type.insert({ result_id, ptr_type_id });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 71: // OpDecorate
|
||||||
|
if (word_count >= 3)
|
||||||
|
{
|
||||||
|
uint32 target_id = spv_words[i + 1];
|
||||||
|
uint32 decoration = spv_words[i + 2];
|
||||||
|
if (decoration == 2) // Block
|
||||||
|
{
|
||||||
|
id_to_descriptor_type.insert({ target_id, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER });
|
||||||
|
}
|
||||||
|
else if (decoration == 3) // BufferBlock
|
||||||
|
{
|
||||||
|
id_to_descriptor_type.insert({ target_id, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER });
|
||||||
|
}
|
||||||
|
else if (decoration == 33 && word_count >= 4) // Binding
|
||||||
|
{
|
||||||
|
uint32 binding = spv_words[i + 3];
|
||||||
|
id_to_binding.insert({ target_id, binding });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += word_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build name to binding map
|
||||||
|
UnorderedMap<String, std::pair<uint32, VkDescriptorType>> name_to_binding;
|
||||||
|
for (const UnorderedMap<uint32, uint32>::value_type &entry : id_to_binding)
|
||||||
|
{
|
||||||
|
uint32 target_id = entry.first;
|
||||||
|
uint32 binding = entry.second;
|
||||||
|
|
||||||
|
// Get the name of the variable
|
||||||
|
UnorderedMap<uint32, String>::const_iterator it_name = id_to_name.find(target_id);
|
||||||
|
if (it_name != id_to_name.end())
|
||||||
|
{
|
||||||
|
// Find variable that links to the target
|
||||||
|
UnorderedMap<uint32, uint32>::const_iterator it_var_ptr = var_to_ptr_type.find(target_id);
|
||||||
|
if (it_var_ptr != var_to_ptr_type.end())
|
||||||
|
{
|
||||||
|
// Find type pointed at
|
||||||
|
uint32 ptr_type = it_var_ptr->second;
|
||||||
|
UnorderedMap<uint32, uint32>::const_iterator it_pointee = pointer_to_pointee.find(ptr_type);
|
||||||
|
if (it_pointee != pointer_to_pointee.end())
|
||||||
|
{
|
||||||
|
uint32 pointee_type = it_pointee->second;
|
||||||
|
|
||||||
|
// Find descriptor type
|
||||||
|
UnorderedMap<uint32, VkDescriptorType>::iterator it_descriptor_type = id_to_descriptor_type.find(pointee_type);
|
||||||
|
VkDescriptorType descriptor_type = it_descriptor_type != id_to_descriptor_type.end() ? it_descriptor_type->second : VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||||
|
|
||||||
|
name_to_binding.insert({ it_name->second, { binding, descriptor_type } });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDevice device = mComputeSystem->GetDevice();
|
||||||
|
|
||||||
|
// Create layout bindings and buffer infos
|
||||||
|
if (!name_to_binding.empty())
|
||||||
|
{
|
||||||
|
mLayoutBindings.reserve(name_to_binding.size());
|
||||||
|
mBufferInfos.reserve(name_to_binding.size());
|
||||||
|
|
||||||
|
mBindingNames.reserve(name_to_binding.size());
|
||||||
|
for (const UnorderedMap<String, std::pair<uint32, VkDescriptorType>>::value_type &b : name_to_binding)
|
||||||
|
{
|
||||||
|
const String &name = b.first;
|
||||||
|
uint binding = b.second.first;
|
||||||
|
VkDescriptorType descriptor_type = b.second.second;
|
||||||
|
|
||||||
|
VkDescriptorSetLayoutBinding l = {};
|
||||||
|
l.binding = binding;
|
||||||
|
l.descriptorCount = 1;
|
||||||
|
l.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
|
l.descriptorType = descriptor_type;
|
||||||
|
mLayoutBindings.push_back(l);
|
||||||
|
|
||||||
|
mBindingNames.push_back(name); // Add all strings to a pool to keep them alive
|
||||||
|
mNameToBufferInfoIndex[string_view(mBindingNames.back())] = (uint32)mBufferInfos.size();
|
||||||
|
|
||||||
|
VkDescriptorBufferInfo bi = {};
|
||||||
|
bi.offset = 0;
|
||||||
|
bi.range = VK_WHOLE_SIZE;
|
||||||
|
bi.buffer = inDummyBuffer; // Avoid: The Vulkan spec states: If the nullDescriptor feature is not enabled, buffer must not be VK_NULL_HANDLE
|
||||||
|
mBufferInfos.push_back(bi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create descriptor set layout
|
||||||
|
VkDescriptorSetLayoutCreateInfo layout_info = {};
|
||||||
|
layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||||
|
layout_info.bindingCount = (uint32)mLayoutBindings.size();
|
||||||
|
layout_info.pBindings = mLayoutBindings.data();
|
||||||
|
if (VKFailed(mComputeSystem->mVkCreateDescriptorSetLayout(device, &layout_info, nullptr, &mDescriptorSetLayout), outResult))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create pipeline layout
|
||||||
|
VkPipelineLayoutCreateInfo pl_info = {};
|
||||||
|
pl_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||||
|
pl_info.setLayoutCount = mDescriptorSetLayout != VK_NULL_HANDLE ? 1 : 0;
|
||||||
|
pl_info.pSetLayouts = mDescriptorSetLayout != VK_NULL_HANDLE ? &mDescriptorSetLayout : nullptr;
|
||||||
|
if (VKFailed(mComputeSystem->mVkCreatePipelineLayout(device, &pl_info, nullptr, &mPipelineLayout), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create shader module
|
||||||
|
VkShaderModuleCreateInfo create_info = {};
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||||
|
create_info.codeSize = inSPVCode.size();
|
||||||
|
create_info.pCode = spv_words;
|
||||||
|
if (VKFailed(mComputeSystem->mVkCreateShaderModule(device, &create_info, nullptr, &mShaderModule), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create compute pipeline
|
||||||
|
VkComputePipelineCreateInfo pipe_info = {};
|
||||||
|
pipe_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||||
|
pipe_info.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
pipe_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
|
pipe_info.stage.module = mShaderModule;
|
||||||
|
pipe_info.stage.pName = "main";
|
||||||
|
pipe_info.layout = mPipelineLayout;
|
||||||
|
if (VKFailed(mComputeSystem->mVkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &pipe_info, nullptr, &mPipeline), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 ComputeShaderVK::NameToBufferInfoIndex(const char *inName) const
|
||||||
|
{
|
||||||
|
UnorderedMap<string_view, uint>::const_iterator it = mNameToBufferInfoIndex.find(inName);
|
||||||
|
JPH_ASSERT(it != mNameToBufferInfoIndex.end());
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
55
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeShaderVK.h
vendored
Normal file
55
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeShaderVK.h
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeShader.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/IncludeVK.h>
|
||||||
|
#include <Jolt/Core/UnorderedMap.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class ComputeSystemVK;
|
||||||
|
|
||||||
|
/// Compute shader handle for Vulkan
|
||||||
|
class JPH_EXPORT ComputeShaderVK : public ComputeShader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Constructor / destructor
|
||||||
|
ComputeShaderVK(ComputeSystemVK *inComputeSystem, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) : ComputeShader(inGroupSizeX, inGroupSizeY, inGroupSizeZ), mComputeSystem(inComputeSystem) { }
|
||||||
|
virtual ~ComputeShaderVK() override;
|
||||||
|
|
||||||
|
/// Initialize from SPIR-V code
|
||||||
|
bool Initialize(const Array<uint8> &inSPVCode, VkBuffer inDummyBuffer, ComputeShaderResult &outResult);
|
||||||
|
|
||||||
|
/// Get index of parameter in buffer infos
|
||||||
|
uint32 NameToBufferInfoIndex(const char *inName) const;
|
||||||
|
|
||||||
|
/// Getters
|
||||||
|
VkPipeline GetPipeline() const { return mPipeline; }
|
||||||
|
VkPipelineLayout GetPipelineLayout() const { return mPipelineLayout; }
|
||||||
|
VkDescriptorSetLayout GetDescriptorSetLayout() const { return mDescriptorSetLayout; }
|
||||||
|
const Array<VkDescriptorSetLayoutBinding> &GetLayoutBindings() const { return mLayoutBindings; }
|
||||||
|
const Array<VkDescriptorBufferInfo> &GetBufferInfos() const { return mBufferInfos; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComputeSystemVK * mComputeSystem;
|
||||||
|
VkShaderModule mShaderModule = VK_NULL_HANDLE;
|
||||||
|
VkPipelineLayout mPipelineLayout = VK_NULL_HANDLE;
|
||||||
|
VkPipeline mPipeline = VK_NULL_HANDLE;
|
||||||
|
VkDescriptorSetLayout mDescriptorSetLayout = VK_NULL_HANDLE;
|
||||||
|
Array<String> mBindingNames; ///< A list of binding names, mNameToBufferInfoIndex points to these strings
|
||||||
|
UnorderedMap<string_view, uint32> mNameToBufferInfoIndex; ///< Binding name to buffer index, using a string_view so we can do find() without an allocation
|
||||||
|
Array<VkDescriptorSetLayoutBinding> mLayoutBindings;
|
||||||
|
Array<VkDescriptorBufferInfo> mBufferInfos;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
163
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVK.cpp
vendored
Normal file
163
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVK.cpp
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/ComputeSystemVK.h>
|
||||||
|
#include <Jolt/Compute/VK/ComputeShaderVK.h>
|
||||||
|
#include <Jolt/Compute/VK/ComputeBufferVK.h>
|
||||||
|
#include <Jolt/Compute/VK/ComputeQueueVK.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
JPH_IMPLEMENT_RTTI_ABSTRACT(ComputeSystemVK)
|
||||||
|
{
|
||||||
|
JPH_ADD_BASE_CLASS(ComputeSystemVK, ComputeSystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeSystemVK::Initialize(VkPhysicalDevice inPhysicalDevice, PFN_vkGetDeviceProcAddr inVkGetDeviceProcAddr, VkDevice inDevice, uint32 inComputeQueueIndex, ComputeSystemResult &outResult)
|
||||||
|
{
|
||||||
|
mPhysicalDevice = inPhysicalDevice;
|
||||||
|
mDevice = inDevice;
|
||||||
|
mComputeQueueIndex = inComputeQueueIndex;
|
||||||
|
|
||||||
|
// Load Vulkan device functions
|
||||||
|
#define JPH_LOAD_VK(name) mVk##name = reinterpret_cast<PFN_vk##name>(reinterpret_cast<void *>(inVkGetDeviceProcAddr(mDevice, "vk" #name))); JPH_ASSERT(mVk##name != nullptr)
|
||||||
|
JPH_LOAD_VK(AllocateCommandBuffers);
|
||||||
|
JPH_LOAD_VK(AllocateDescriptorSets);
|
||||||
|
JPH_LOAD_VK(AllocateMemory);
|
||||||
|
JPH_LOAD_VK(BeginCommandBuffer);
|
||||||
|
JPH_LOAD_VK(BindBufferMemory);
|
||||||
|
JPH_LOAD_VK(CmdBindDescriptorSets);
|
||||||
|
JPH_LOAD_VK(CmdBindPipeline);
|
||||||
|
JPH_LOAD_VK(CmdCopyBuffer);
|
||||||
|
JPH_LOAD_VK(CmdDispatch);
|
||||||
|
JPH_LOAD_VK(CmdPipelineBarrier);
|
||||||
|
JPH_LOAD_VK(CreateBuffer);
|
||||||
|
JPH_LOAD_VK(CreateCommandPool);
|
||||||
|
JPH_LOAD_VK(CreateComputePipelines);
|
||||||
|
JPH_LOAD_VK(CreateDescriptorPool);
|
||||||
|
JPH_LOAD_VK(CreateDescriptorSetLayout);
|
||||||
|
JPH_LOAD_VK(CreateFence);
|
||||||
|
JPH_LOAD_VK(CreatePipelineLayout);
|
||||||
|
JPH_LOAD_VK(CreateShaderModule);
|
||||||
|
JPH_LOAD_VK(DestroyBuffer);
|
||||||
|
JPH_LOAD_VK(DestroyCommandPool);
|
||||||
|
JPH_LOAD_VK(DestroyDescriptorPool);
|
||||||
|
JPH_LOAD_VK(DestroyDescriptorSetLayout);
|
||||||
|
JPH_LOAD_VK(DestroyDevice);
|
||||||
|
JPH_LOAD_VK(DestroyFence);
|
||||||
|
JPH_LOAD_VK(DestroyPipeline);
|
||||||
|
JPH_LOAD_VK(DestroyPipelineLayout);
|
||||||
|
JPH_LOAD_VK(DestroyShaderModule);
|
||||||
|
JPH_LOAD_VK(DeviceWaitIdle);
|
||||||
|
JPH_LOAD_VK(EndCommandBuffer);
|
||||||
|
JPH_LOAD_VK(FreeCommandBuffers);
|
||||||
|
JPH_LOAD_VK(FreeMemory);
|
||||||
|
JPH_LOAD_VK(GetBufferMemoryRequirements);
|
||||||
|
JPH_LOAD_VK(GetDeviceQueue);
|
||||||
|
JPH_LOAD_VK(MapMemory);
|
||||||
|
JPH_LOAD_VK(QueueSubmit);
|
||||||
|
JPH_LOAD_VK(ResetCommandBuffer);
|
||||||
|
JPH_LOAD_VK(ResetDescriptorPool);
|
||||||
|
JPH_LOAD_VK(ResetFences);
|
||||||
|
JPH_LOAD_VK(UnmapMemory);
|
||||||
|
JPH_LOAD_VK(UpdateDescriptorSets);
|
||||||
|
JPH_LOAD_VK(WaitForFences);
|
||||||
|
#undef JPH_LOAD_VK
|
||||||
|
|
||||||
|
// Get function to set a debug name
|
||||||
|
mVkSetDebugUtilsObjectNameEXT = reinterpret_cast<PFN_vkSetDebugUtilsObjectNameEXT>(reinterpret_cast<void *>(inVkGetDeviceProcAddr(mDevice, "vkSetDebugUtilsObjectNameEXT")));
|
||||||
|
|
||||||
|
if (!InitializeMemory())
|
||||||
|
{
|
||||||
|
outResult.SetError("Failed to initialize memory subsystem");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the dummy buffer. This is used to bind to shaders for which we have no buffer. We can't rely on VK_EXT_robustness2 being available to set nullDescriptor = VK_TRUE (it is unavailable on macOS).
|
||||||
|
if (!CreateBuffer(1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mDummyBuffer))
|
||||||
|
{
|
||||||
|
outResult.SetError("Failed to create dummy buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeSystemVK::Shutdown()
|
||||||
|
{
|
||||||
|
if (mDevice != VK_NULL_HANDLE)
|
||||||
|
mVkDeviceWaitIdle(mDevice);
|
||||||
|
|
||||||
|
// Free the dummy buffer
|
||||||
|
FreeBuffer(mDummyBuffer);
|
||||||
|
|
||||||
|
ShutdownMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeShaderResult ComputeSystemVK::CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ)
|
||||||
|
{
|
||||||
|
ComputeShaderResult result;
|
||||||
|
|
||||||
|
// Read shader source file
|
||||||
|
Array<uint8> data;
|
||||||
|
String file_name = String(inName) + ".spv";
|
||||||
|
String error;
|
||||||
|
if (!mShaderLoader(file_name.c_str(), data, error))
|
||||||
|
{
|
||||||
|
result.SetError(error);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ComputeShaderVK> shader = new ComputeShaderVK(this, inGroupSizeX, inGroupSizeY, inGroupSizeZ);
|
||||||
|
if (!shader->Initialize(data, mDummyBuffer.mBuffer, result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Name the pipeline so we can easily find it in a profile
|
||||||
|
if (mVkSetDebugUtilsObjectNameEXT != nullptr)
|
||||||
|
{
|
||||||
|
VkDebugUtilsObjectNameInfoEXT info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
||||||
|
info.pNext = nullptr;
|
||||||
|
info.objectType = VK_OBJECT_TYPE_PIPELINE;
|
||||||
|
info.objectHandle = (uint64)shader->GetPipeline();
|
||||||
|
info.pObjectName = inName;
|
||||||
|
mVkSetDebugUtilsObjectNameEXT(mDevice, &info);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Set(shader.GetPtr());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeBufferResult ComputeSystemVK::CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData)
|
||||||
|
{
|
||||||
|
ComputeBufferResult result;
|
||||||
|
|
||||||
|
Ref<ComputeBufferVK> buffer = new ComputeBufferVK(this, inType, inSize, inStride);
|
||||||
|
if (!buffer->Initialize(inData))
|
||||||
|
{
|
||||||
|
result.SetError("Failed to create compute buffer");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Set(buffer.GetPtr());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeQueueResult ComputeSystemVK::CreateComputeQueue()
|
||||||
|
{
|
||||||
|
ComputeQueueResult result;
|
||||||
|
Ref<ComputeQueueVK> q = new ComputeQueueVK(this);
|
||||||
|
if (!q->Initialize(mComputeQueueIndex, result))
|
||||||
|
return result;
|
||||||
|
result.Set(q.GetPtr());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
100
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVK.h
vendored
Normal file
100
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVK.h
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Compute/ComputeSystem.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/ComputeQueueVK.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Interface to run a workload on the GPU using Vulkan.
|
||||||
|
/// Minimal implementation that can integrate with your own Vulkan setup.
|
||||||
|
class JPH_EXPORT ComputeSystemVK : public ComputeSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_DECLARE_RTTI_ABSTRACT(JPH_EXPORT, ComputeSystemVK)
|
||||||
|
|
||||||
|
// Initialize / shutdown the compute system
|
||||||
|
bool Initialize(VkPhysicalDevice inPhysicalDevice, PFN_vkGetDeviceProcAddr inVkGetDeviceProcAddr, VkDevice inDevice, uint32 inComputeQueueIndex, ComputeSystemResult &outResult);
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
// See: ComputeSystem
|
||||||
|
virtual ComputeShaderResult CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) override;
|
||||||
|
virtual ComputeBufferResult CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData = nullptr) override;
|
||||||
|
virtual ComputeQueueResult CreateComputeQueue() override;
|
||||||
|
|
||||||
|
/// Access to the Vulkan device
|
||||||
|
VkDevice GetDevice() const { return mDevice; }
|
||||||
|
|
||||||
|
/// Allow the application to override buffer creation and memory mapping in case it uses its own allocator
|
||||||
|
virtual bool CreateBuffer(VkDeviceSize inSize, VkBufferUsageFlags inUsage, VkMemoryPropertyFlags inProperties, BufferVK &outBuffer) = 0;
|
||||||
|
virtual void FreeBuffer(BufferVK &ioBuffer) = 0;
|
||||||
|
virtual void * MapBuffer(BufferVK &ioBuffer) = 0;
|
||||||
|
virtual void UnmapBuffer(BufferVK &ioBuffer) = 0;
|
||||||
|
|
||||||
|
// Vulkan device function pointers
|
||||||
|
PFN_vkAllocateCommandBuffers mVkAllocateCommandBuffers = nullptr;
|
||||||
|
PFN_vkAllocateDescriptorSets mVkAllocateDescriptorSets = nullptr;
|
||||||
|
PFN_vkAllocateMemory mVkAllocateMemory = nullptr;
|
||||||
|
PFN_vkBeginCommandBuffer mVkBeginCommandBuffer = nullptr;
|
||||||
|
PFN_vkBindBufferMemory mVkBindBufferMemory = nullptr;
|
||||||
|
PFN_vkCmdBindDescriptorSets mVkCmdBindDescriptorSets = nullptr;
|
||||||
|
PFN_vkCmdBindPipeline mVkCmdBindPipeline = nullptr;
|
||||||
|
PFN_vkCmdCopyBuffer mVkCmdCopyBuffer = nullptr;
|
||||||
|
PFN_vkCmdDispatch mVkCmdDispatch = nullptr;
|
||||||
|
PFN_vkCmdPipelineBarrier mVkCmdPipelineBarrier = nullptr;
|
||||||
|
PFN_vkCreateBuffer mVkCreateBuffer = nullptr;
|
||||||
|
PFN_vkCreateCommandPool mVkCreateCommandPool = nullptr;
|
||||||
|
PFN_vkCreateComputePipelines mVkCreateComputePipelines = nullptr;
|
||||||
|
PFN_vkCreateDescriptorPool mVkCreateDescriptorPool = nullptr;
|
||||||
|
PFN_vkCreateDescriptorSetLayout mVkCreateDescriptorSetLayout = nullptr;
|
||||||
|
PFN_vkCreateFence mVkCreateFence = nullptr;
|
||||||
|
PFN_vkCreatePipelineLayout mVkCreatePipelineLayout = nullptr;
|
||||||
|
PFN_vkCreateShaderModule mVkCreateShaderModule = nullptr;
|
||||||
|
PFN_vkDestroyBuffer mVkDestroyBuffer = nullptr;
|
||||||
|
PFN_vkDestroyCommandPool mVkDestroyCommandPool = nullptr;
|
||||||
|
PFN_vkDestroyDescriptorPool mVkDestroyDescriptorPool = nullptr;
|
||||||
|
PFN_vkDestroyDescriptorSetLayout mVkDestroyDescriptorSetLayout = nullptr;
|
||||||
|
PFN_vkDestroyDevice mVkDestroyDevice = nullptr;
|
||||||
|
PFN_vkDestroyFence mVkDestroyFence = nullptr;
|
||||||
|
PFN_vkDestroyPipeline mVkDestroyPipeline = nullptr;
|
||||||
|
PFN_vkDestroyPipelineLayout mVkDestroyPipelineLayout = nullptr;
|
||||||
|
PFN_vkDestroyShaderModule mVkDestroyShaderModule = nullptr;
|
||||||
|
PFN_vkDeviceWaitIdle mVkDeviceWaitIdle = nullptr;
|
||||||
|
PFN_vkEndCommandBuffer mVkEndCommandBuffer = nullptr;
|
||||||
|
PFN_vkFreeCommandBuffers mVkFreeCommandBuffers = nullptr;
|
||||||
|
PFN_vkFreeMemory mVkFreeMemory = nullptr;
|
||||||
|
PFN_vkGetBufferMemoryRequirements mVkGetBufferMemoryRequirements = nullptr;
|
||||||
|
PFN_vkGetDeviceQueue mVkGetDeviceQueue = nullptr;
|
||||||
|
PFN_vkMapMemory mVkMapMemory = nullptr;
|
||||||
|
PFN_vkQueueSubmit mVkQueueSubmit = nullptr;
|
||||||
|
PFN_vkResetCommandBuffer mVkResetCommandBuffer = nullptr;
|
||||||
|
PFN_vkResetDescriptorPool mVkResetDescriptorPool = nullptr;
|
||||||
|
PFN_vkResetFences mVkResetFences = nullptr;
|
||||||
|
PFN_vkSetDebugUtilsObjectNameEXT mVkSetDebugUtilsObjectNameEXT = nullptr;
|
||||||
|
PFN_vkUnmapMemory mVkUnmapMemory = nullptr;
|
||||||
|
PFN_vkUpdateDescriptorSets mVkUpdateDescriptorSets = nullptr;
|
||||||
|
PFN_vkWaitForFences mVkWaitForFences = nullptr;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Initialize / shutdown the memory subsystem
|
||||||
|
virtual bool InitializeMemory() = 0;
|
||||||
|
virtual void ShutdownMemory() = 0;
|
||||||
|
|
||||||
|
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
|
||||||
|
VkDevice mDevice = VK_NULL_HANDLE;
|
||||||
|
uint32 mComputeQueueIndex = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Buffer that can be bound when we have no buffer
|
||||||
|
BufferVK mDummyBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
398
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVKImpl.cpp
vendored
Normal file
398
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVKImpl.cpp
vendored
Normal file
|
|
@ -0,0 +1,398 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/ComputeSystemVKImpl.h>
|
||||||
|
#include <Jolt/Core/QuickSort.h>
|
||||||
|
#include <Jolt/Core/IncludeWindows.h>
|
||||||
|
#if defined(JPH_PLATFORM_LINUX) || defined(JPH_PLATFORM_ANDROID) ||defined(JPH_PLATFORM_MACOS)
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
JPH_IMPLEMENT_RTTI_VIRTUAL(ComputeSystemVKImpl)
|
||||||
|
{
|
||||||
|
JPH_ADD_BASE_CLASS(ComputeSystemVKImpl, ComputeSystemVKWithAllocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
|
||||||
|
static VKAPI_ATTR VkBool32 VKAPI_CALL sVulkanDebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT inSeverity, [[maybe_unused]] VkDebugUtilsMessageTypeFlagsEXT inType, const VkDebugUtilsMessengerCallbackDataEXT *inCallbackData, [[maybe_unused]] void *inUserData)
|
||||||
|
{
|
||||||
|
if (inSeverity & (VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT))
|
||||||
|
Trace("VK: %s", inCallbackData->pMessage);
|
||||||
|
JPH_ASSERT((inSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) == 0);
|
||||||
|
return VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // JPH_DEBUG
|
||||||
|
|
||||||
|
ComputeSystemVKImpl::~ComputeSystemVKImpl()
|
||||||
|
{
|
||||||
|
ComputeSystemVK::Shutdown();
|
||||||
|
|
||||||
|
if (mDevice != VK_NULL_HANDLE && mVkDestroyDevice != nullptr)
|
||||||
|
mVkDestroyDevice(mDevice, nullptr);
|
||||||
|
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
if (mInstance != VK_NULL_HANDLE && mDebugMessenger != VK_NULL_HANDLE && mVkDestroyDebugUtilsMessengerEXT != nullptr)
|
||||||
|
mVkDestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, nullptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (mInstance != VK_NULL_HANDLE && mVkDestroyInstance != nullptr)
|
||||||
|
mVkDestroyInstance(mInstance, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeSystemVKImpl::Initialize(ComputeSystemResult &outResult)
|
||||||
|
{
|
||||||
|
#ifdef JPH_PLATFORM_WINDOWS
|
||||||
|
HMODULE module = LoadLibraryA("vulkan-1.dll");
|
||||||
|
if (!module)
|
||||||
|
{
|
||||||
|
outResult.SetError("Failed to load vulkan-1.dll");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mVkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(reinterpret_cast<void *>(GetProcAddress(module, "vkGetInstanceProcAddr")));
|
||||||
|
#elif defined(JPH_PLATFORM_LINUX) || defined(JPH_PLATFORM_ANDROID)
|
||||||
|
void *library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL);
|
||||||
|
if (!library)
|
||||||
|
library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
|
||||||
|
if (!library)
|
||||||
|
{
|
||||||
|
outResult.SetError("Failed to load libvulkan.so.1 or libvulkan.so");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mVkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(dlsym(library, "vkGetInstanceProcAddr"));
|
||||||
|
#elif defined(JPH_PLATFORM_MACOS)
|
||||||
|
void *library = dlopen("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL);
|
||||||
|
if (!library)
|
||||||
|
library = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL);
|
||||||
|
if (!library)
|
||||||
|
{
|
||||||
|
outResult.SetError("Failed to load libvulkan.1.dylib or libvulkan.dylib");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mVkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(dlsym(library, "vkGetInstanceProcAddr"));
|
||||||
|
#else
|
||||||
|
#error "Unsupported platform"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check vkGetInstanceProcAddr
|
||||||
|
if (mVkGetInstanceProcAddr == nullptr)
|
||||||
|
{
|
||||||
|
outResult.SetError("Failed to get vkGetInstanceProcAddr");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required instance extensions
|
||||||
|
Array<const char *> required_instance_extensions;
|
||||||
|
required_instance_extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||||
|
required_instance_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
#ifdef JPH_PLATFORM_MACOS
|
||||||
|
required_instance_extensions.push_back("VK_KHR_portability_enumeration");
|
||||||
|
required_instance_extensions.push_back("VK_KHR_get_physical_device_properties2");
|
||||||
|
#endif
|
||||||
|
GetInstanceExtensions(required_instance_extensions);
|
||||||
|
|
||||||
|
// Required device extensions
|
||||||
|
Array<const char *> required_device_extensions;
|
||||||
|
required_device_extensions.push_back(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);
|
||||||
|
#ifdef JPH_PLATFORM_MACOS
|
||||||
|
required_device_extensions.push_back("VK_KHR_portability_subset"); // VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME
|
||||||
|
#endif
|
||||||
|
GetDeviceExtensions(required_device_extensions);
|
||||||
|
|
||||||
|
// Load pre-instance Vulkan functions
|
||||||
|
#define JPH_LOAD_VK_PRE_INST(name) mVk##name = reinterpret_cast<PFN_vk##name>(reinterpret_cast<void *>(mVkGetInstanceProcAddr(nullptr, "vk" #name))); JPH_ASSERT(mVk##name != nullptr)
|
||||||
|
JPH_LOAD_VK_PRE_INST(CreateInstance);
|
||||||
|
JPH_LOAD_VK_PRE_INST(EnumerateInstanceExtensionProperties);
|
||||||
|
JPH_LOAD_VK_PRE_INST(EnumerateInstanceLayerProperties);
|
||||||
|
#undef JPH_LOAD_VK_PRE_INST
|
||||||
|
|
||||||
|
// Query supported instance extensions
|
||||||
|
uint32 instance_extension_count = 0;
|
||||||
|
if (VKFailed(mVkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, nullptr), outResult))
|
||||||
|
return false;
|
||||||
|
Array<VkExtensionProperties> instance_extensions;
|
||||||
|
instance_extensions.resize(instance_extension_count);
|
||||||
|
if (VKFailed(mVkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, instance_extensions.data()), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Query supported validation layers
|
||||||
|
uint32 validation_layer_count;
|
||||||
|
mVkEnumerateInstanceLayerProperties(&validation_layer_count, nullptr);
|
||||||
|
Array<VkLayerProperties> validation_layers(validation_layer_count);
|
||||||
|
mVkEnumerateInstanceLayerProperties(&validation_layer_count, validation_layers.data());
|
||||||
|
|
||||||
|
VkApplicationInfo app_info = {};
|
||||||
|
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||||
|
app_info.apiVersion = VK_API_VERSION_1_1;
|
||||||
|
|
||||||
|
// Create Vulkan instance
|
||||||
|
VkInstanceCreateInfo instance_create_info = {};
|
||||||
|
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||||
|
#ifdef JPH_PLATFORM_MACOS
|
||||||
|
instance_create_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||||
|
#endif
|
||||||
|
instance_create_info.pApplicationInfo = &app_info;
|
||||||
|
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
// Enable validation layer if supported
|
||||||
|
const char *desired_validation_layers[] = { "VK_LAYER_KHRONOS_validation" };
|
||||||
|
for (const VkLayerProperties &p : validation_layers)
|
||||||
|
if (strcmp(desired_validation_layers[0], p.layerName) == 0)
|
||||||
|
{
|
||||||
|
instance_create_info.enabledLayerCount = 1;
|
||||||
|
instance_create_info.ppEnabledLayerNames = desired_validation_layers;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup debug messenger callback if the extension is supported
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT messenger_create_info = {};
|
||||||
|
for (const VkExtensionProperties &ext : instance_extensions)
|
||||||
|
if (strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, ext.extensionName) == 0)
|
||||||
|
{
|
||||||
|
messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||||
|
messenger_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||||
|
messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||||
|
messenger_create_info.pfnUserCallback = sVulkanDebugCallback;
|
||||||
|
instance_create_info.pNext = &messenger_create_info;
|
||||||
|
required_instance_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
instance_create_info.enabledExtensionCount = (uint32)required_instance_extensions.size();
|
||||||
|
instance_create_info.ppEnabledExtensionNames = required_instance_extensions.data();
|
||||||
|
if (VKFailed(mVkCreateInstance(&instance_create_info, nullptr, &mInstance), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Load instance-level Vulkan functions
|
||||||
|
#define JPH_LOAD_VK_INST(name) mVk##name = reinterpret_cast<PFN_vk##name>(reinterpret_cast<void *>(mVkGetInstanceProcAddr(mInstance, "vk" #name))); JPH_ASSERT(mVk##name != nullptr)
|
||||||
|
JPH_LOAD_VK_INST(CreateDevice);
|
||||||
|
JPH_LOAD_VK_INST(DestroyInstance);
|
||||||
|
JPH_LOAD_VK_INST(EnumerateDeviceExtensionProperties);
|
||||||
|
JPH_LOAD_VK_INST(EnumeratePhysicalDevices);
|
||||||
|
JPH_LOAD_VK_INST(GetPhysicalDeviceProperties);
|
||||||
|
JPH_LOAD_VK_INST(GetPhysicalDeviceQueueFamilyProperties);
|
||||||
|
#undef JPH_LOAD_VK_INST
|
||||||
|
|
||||||
|
// Get vkGetDeviceProcAddr
|
||||||
|
mVkGetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(reinterpret_cast<void *>(mVkGetInstanceProcAddr(mInstance, "vkGetDeviceProcAddr")));
|
||||||
|
if (mVkGetDeviceProcAddr == nullptr)
|
||||||
|
{
|
||||||
|
outResult.SetError("Failed to get vkGetDeviceProcAddr");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
// Finalize debug messenger callback
|
||||||
|
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)(std::uintptr_t)mVkGetInstanceProcAddr(mInstance, "vkCreateDebugUtilsMessengerEXT");
|
||||||
|
mVkDestroyDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(reinterpret_cast<void *>(mVkGetInstanceProcAddr(mInstance, "vkDestroyDebugUtilsMessengerEXT")));
|
||||||
|
if (vkCreateDebugUtilsMessengerEXT != nullptr && VKFailed(vkCreateDebugUtilsMessengerEXT(mInstance, &messenger_create_info, nullptr, &mDebugMessenger), outResult))
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Notify that instance has been created
|
||||||
|
OnInstanceCreated();
|
||||||
|
|
||||||
|
// Select device
|
||||||
|
uint32 device_count = 0;
|
||||||
|
if (VKFailed(mVkEnumeratePhysicalDevices(mInstance, &device_count, nullptr), outResult))
|
||||||
|
return false;
|
||||||
|
Array<VkPhysicalDevice> devices;
|
||||||
|
devices.resize(device_count);
|
||||||
|
if (VKFailed(mVkEnumeratePhysicalDevices(mInstance, &device_count, devices.data()), outResult))
|
||||||
|
return false;
|
||||||
|
struct Device
|
||||||
|
{
|
||||||
|
VkPhysicalDevice mPhysicalDevice;
|
||||||
|
String mName;
|
||||||
|
VkSurfaceFormatKHR mFormat;
|
||||||
|
uint32 mGraphicsQueueIndex;
|
||||||
|
uint32 mPresentQueueIndex;
|
||||||
|
uint32 mComputeQueueIndex;
|
||||||
|
int mScore;
|
||||||
|
};
|
||||||
|
Array<Device> available_devices;
|
||||||
|
for (VkPhysicalDevice device : devices)
|
||||||
|
{
|
||||||
|
// Get device properties
|
||||||
|
VkPhysicalDeviceProperties properties;
|
||||||
|
mVkGetPhysicalDeviceProperties(device, &properties);
|
||||||
|
|
||||||
|
// Test if it is an appropriate type
|
||||||
|
int score = 0;
|
||||||
|
switch (properties.deviceType)
|
||||||
|
{
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
|
||||||
|
score = 30;
|
||||||
|
break;
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
||||||
|
score = 20;
|
||||||
|
break;
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
|
||||||
|
score = 10;
|
||||||
|
break;
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_CPU:
|
||||||
|
score = 5;
|
||||||
|
break;
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the device supports all our required extensions
|
||||||
|
uint32 device_extension_count;
|
||||||
|
mVkEnumerateDeviceExtensionProperties(device, nullptr, &device_extension_count, nullptr);
|
||||||
|
Array<VkExtensionProperties> available_extensions;
|
||||||
|
available_extensions.resize(device_extension_count);
|
||||||
|
mVkEnumerateDeviceExtensionProperties(device, nullptr, &device_extension_count, available_extensions.data());
|
||||||
|
int found_extensions = 0;
|
||||||
|
for (const char *required_device_extension : required_device_extensions)
|
||||||
|
for (const VkExtensionProperties &ext : available_extensions)
|
||||||
|
if (strcmp(required_device_extension, ext.extensionName) == 0)
|
||||||
|
{
|
||||||
|
found_extensions++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (found_extensions != int(required_device_extensions.size()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Find the right queues
|
||||||
|
uint32 queue_family_count = 0;
|
||||||
|
mVkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, nullptr);
|
||||||
|
Array<VkQueueFamilyProperties> queue_families;
|
||||||
|
queue_families.resize(queue_family_count);
|
||||||
|
mVkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data());
|
||||||
|
uint32 graphics_queue = ~uint32(0);
|
||||||
|
uint32 present_queue = ~uint32(0);
|
||||||
|
uint32 compute_queue = ~uint32(0);
|
||||||
|
for (uint32 i = 0; i < uint32(queue_families.size()); ++i)
|
||||||
|
{
|
||||||
|
if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
||||||
|
{
|
||||||
|
graphics_queue = i;
|
||||||
|
|
||||||
|
if (queue_families[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
|
||||||
|
compute_queue = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasPresentSupport(device, i))
|
||||||
|
present_queue = i;
|
||||||
|
|
||||||
|
if (graphics_queue != ~uint32(0) && present_queue != ~uint32(0) && compute_queue != ~uint32(0))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (graphics_queue == ~uint32(0) || present_queue == ~uint32(0) || compute_queue == ~uint32(0))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Select surface format
|
||||||
|
VkSurfaceFormatKHR selected_format = SelectFormat(device);
|
||||||
|
if (selected_format.format == VK_FORMAT_UNDEFINED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Add the device
|
||||||
|
available_devices.push_back({ device, properties.deviceName, selected_format, graphics_queue, present_queue, compute_queue, score });
|
||||||
|
}
|
||||||
|
if (available_devices.empty())
|
||||||
|
{
|
||||||
|
outResult.SetError("No suitable Vulkan device found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the devices by score
|
||||||
|
QuickSort(available_devices.begin(), available_devices.end(), [](const Device &inLHS, const Device &inRHS) {
|
||||||
|
return inLHS.mScore > inRHS.mScore;
|
||||||
|
});
|
||||||
|
const Device &selected_device = available_devices[0];
|
||||||
|
|
||||||
|
// Create device
|
||||||
|
float queue_priority = 1.0f;
|
||||||
|
VkDeviceQueueCreateInfo queue_create_info[3] = {};
|
||||||
|
for (VkDeviceQueueCreateInfo &q : queue_create_info)
|
||||||
|
{
|
||||||
|
q.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
q.queueCount = 1;
|
||||||
|
q.pQueuePriorities = &queue_priority;
|
||||||
|
}
|
||||||
|
uint32 num_queues = 0;
|
||||||
|
queue_create_info[num_queues++].queueFamilyIndex = selected_device.mGraphicsQueueIndex;
|
||||||
|
bool found = false;
|
||||||
|
for (uint32 i = 0; i < num_queues; ++i)
|
||||||
|
if (queue_create_info[i].queueFamilyIndex == selected_device.mPresentQueueIndex)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
queue_create_info[num_queues++].queueFamilyIndex = selected_device.mPresentQueueIndex;
|
||||||
|
found = false;
|
||||||
|
for (uint32 i = 0; i < num_queues; ++i)
|
||||||
|
if (queue_create_info[i].queueFamilyIndex == selected_device.mComputeQueueIndex)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
queue_create_info[num_queues++].queueFamilyIndex = selected_device.mComputeQueueIndex;
|
||||||
|
|
||||||
|
VkPhysicalDeviceScalarBlockLayoutFeatures enable_scalar_block = {};
|
||||||
|
enable_scalar_block.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES;
|
||||||
|
enable_scalar_block.scalarBlockLayout = VK_TRUE;
|
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures2 enabled_features2 = {};
|
||||||
|
enabled_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||||
|
GetEnabledFeatures(enabled_features2);
|
||||||
|
enable_scalar_block.pNext = enabled_features2.pNext;
|
||||||
|
enabled_features2.pNext = &enable_scalar_block;
|
||||||
|
|
||||||
|
VkDeviceCreateInfo device_create_info = {};
|
||||||
|
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||||
|
device_create_info.queueCreateInfoCount = num_queues;
|
||||||
|
device_create_info.pQueueCreateInfos = queue_create_info;
|
||||||
|
device_create_info.enabledExtensionCount = uint32(required_device_extensions.size());
|
||||||
|
device_create_info.ppEnabledExtensionNames = required_device_extensions.data();
|
||||||
|
device_create_info.pNext = &enabled_features2;
|
||||||
|
device_create_info.pEnabledFeatures = nullptr;
|
||||||
|
|
||||||
|
VkDevice device = VK_NULL_HANDLE;
|
||||||
|
if (VKFailed(mVkCreateDevice(selected_device.mPhysicalDevice, &device_create_info, nullptr, &device), outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Store selected format
|
||||||
|
mSelectedFormat = selected_device.mFormat;
|
||||||
|
|
||||||
|
// Initialize the compute system (loads device-level functions)
|
||||||
|
if (!ComputeSystemVKWithAllocator::Initialize(mInstance, selected_device.mPhysicalDevice, mVkGetInstanceProcAddr, mVkGetDeviceProcAddr, device, selected_device.mComputeQueueIndex, outResult))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Get the queues
|
||||||
|
mGraphicsQueueIndex = selected_device.mGraphicsQueueIndex;
|
||||||
|
mPresentQueueIndex = selected_device.mPresentQueueIndex;
|
||||||
|
mVkGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
|
||||||
|
mVkGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeSystemResult CreateComputeSystemVK()
|
||||||
|
{
|
||||||
|
ComputeSystemResult result;
|
||||||
|
|
||||||
|
Ref<ComputeSystemVKImpl> compute = new ComputeSystemVKImpl;
|
||||||
|
if (!compute->Initialize(result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result.Set(compute.GetPtr());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
74
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVKImpl.h
vendored
Normal file
74
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVKImpl.h
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/ComputeSystemVKWithAllocator.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Implementation of ComputeSystemVK that fully initializes Vulkan
|
||||||
|
class JPH_EXPORT ComputeSystemVKImpl : public ComputeSystemVKWithAllocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_DECLARE_RTTI_VIRTUAL(JPH_EXPORT, ComputeSystemVKImpl)
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~ComputeSystemVKImpl() override;
|
||||||
|
|
||||||
|
/// Initialize the compute system
|
||||||
|
bool Initialize(ComputeSystemResult &outResult);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Override to perform actions once the instance has been created
|
||||||
|
virtual void OnInstanceCreated() { /* Do nothing */ }
|
||||||
|
|
||||||
|
/// Override to add platform specific instance extensions
|
||||||
|
virtual void GetInstanceExtensions(Array<const char *> &outExtensions) { /* Add nothing */ }
|
||||||
|
|
||||||
|
/// Override to add platform specific device extensions
|
||||||
|
virtual void GetDeviceExtensions(Array<const char *> &outExtensions) { /* Add nothing */ }
|
||||||
|
|
||||||
|
/// Override to enable specific features
|
||||||
|
virtual void GetEnabledFeatures(VkPhysicalDeviceFeatures2 &ioFeatures) { /* Add nothing */ }
|
||||||
|
|
||||||
|
/// Override to check for present support on a given device and queue family
|
||||||
|
virtual bool HasPresentSupport(VkPhysicalDevice inDevice, uint32 inQueueFamilyIndex) { return true; }
|
||||||
|
|
||||||
|
/// Override to select the surface format
|
||||||
|
virtual VkSurfaceFormatKHR SelectFormat(VkPhysicalDevice inDevice) { return { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; }
|
||||||
|
|
||||||
|
VkInstance mInstance = VK_NULL_HANDLE;
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
|
||||||
|
PFN_vkDestroyDebugUtilsMessengerEXT mVkDestroyDebugUtilsMessengerEXT = nullptr;
|
||||||
|
#endif
|
||||||
|
uint32 mGraphicsQueueIndex = 0;
|
||||||
|
uint32 mPresentQueueIndex = 0;
|
||||||
|
VkQueue mGraphicsQueue = VK_NULL_HANDLE;
|
||||||
|
VkQueue mPresentQueue = VK_NULL_HANDLE;
|
||||||
|
VkSurfaceFormatKHR mSelectedFormat;
|
||||||
|
|
||||||
|
PFN_vkGetInstanceProcAddr mVkGetInstanceProcAddr = nullptr;
|
||||||
|
PFN_vkGetDeviceProcAddr mVkGetDeviceProcAddr = nullptr;
|
||||||
|
|
||||||
|
// Pre instance functions
|
||||||
|
PFN_vkCreateInstance mVkCreateInstance = nullptr;
|
||||||
|
PFN_vkEnumerateInstanceExtensionProperties mVkEnumerateInstanceExtensionProperties = nullptr;
|
||||||
|
PFN_vkEnumerateInstanceLayerProperties mVkEnumerateInstanceLayerProperties = nullptr;
|
||||||
|
|
||||||
|
// Post instance functions
|
||||||
|
PFN_vkCreateDevice mVkCreateDevice = nullptr;
|
||||||
|
PFN_vkDestroyInstance mVkDestroyInstance = nullptr;
|
||||||
|
PFN_vkEnumerateDeviceExtensionProperties mVkEnumerateDeviceExtensionProperties = nullptr;
|
||||||
|
PFN_vkEnumeratePhysicalDevices mVkEnumeratePhysicalDevices = nullptr;
|
||||||
|
PFN_vkGetPhysicalDeviceProperties mVkGetPhysicalDeviceProperties = nullptr;
|
||||||
|
PFN_vkGetPhysicalDeviceQueueFamilyProperties mVkGetPhysicalDeviceQueueFamilyProperties = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
181
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVKWithAllocator.cpp
vendored
Normal file
181
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVKWithAllocator.cpp
vendored
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/ComputeSystemVKWithAllocator.h>
|
||||||
|
#include <Jolt/Compute/VK/ComputeShaderVK.h>
|
||||||
|
#include <Jolt/Compute/VK/ComputeBufferVK.h>
|
||||||
|
#include <Jolt/Compute/VK/ComputeQueueVK.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
JPH_IMPLEMENT_RTTI_VIRTUAL(ComputeSystemVKWithAllocator)
|
||||||
|
{
|
||||||
|
JPH_ADD_BASE_CLASS(ComputeSystemVKWithAllocator, ComputeSystemVK)
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeSystemVKWithAllocator::Initialize(VkInstance inInstance, VkPhysicalDevice inPhysicalDevice, PFN_vkGetInstanceProcAddr inGetInstanceProcAddr, PFN_vkGetDeviceProcAddr inVkGetDeviceProcAddr, VkDevice inDevice, uint32 inComputeQueueIndex, ComputeSystemResult &outResult)
|
||||||
|
{
|
||||||
|
#define JPH_LOAD_VK_INST(name) mVk##name = reinterpret_cast<PFN_vk##name>(reinterpret_cast<void *>(inGetInstanceProcAddr(inInstance, "vk" #name))); JPH_ASSERT(mVk##name != nullptr)
|
||||||
|
JPH_LOAD_VK_INST(GetPhysicalDeviceMemoryProperties);
|
||||||
|
#undef JPH_LOAD_VK_INST
|
||||||
|
|
||||||
|
return ComputeSystemVK::Initialize(inPhysicalDevice, inVkGetDeviceProcAddr, inDevice, inComputeQueueIndex, outResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeSystemVKWithAllocator::InitializeMemory()
|
||||||
|
{
|
||||||
|
// Get memory properties
|
||||||
|
mVkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &mMemoryProperties);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeSystemVKWithAllocator::ShutdownMemory()
|
||||||
|
{
|
||||||
|
// Free all memory
|
||||||
|
for (const MemoryCache::value_type &mc : mMemoryCache)
|
||||||
|
for (const Memory &m : mc.second)
|
||||||
|
if (m.mOffset == 0)
|
||||||
|
FreeMemory(*m.mMemory);
|
||||||
|
mMemoryCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 ComputeSystemVKWithAllocator::FindMemoryType(uint32 inTypeFilter, VkMemoryPropertyFlags inProperties) const
|
||||||
|
{
|
||||||
|
for (uint32 i = 0; i < mMemoryProperties.memoryTypeCount; i++)
|
||||||
|
if ((inTypeFilter & (1 << i))
|
||||||
|
&& (mMemoryProperties.memoryTypes[i].propertyFlags & inProperties) == inProperties)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
JPH_ASSERT(false, "Failed to find memory type!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeSystemVKWithAllocator::AllocateMemory(VkDeviceSize inSize, uint32 inMemoryTypeBits, VkMemoryPropertyFlags inProperties, MemoryVK &ioMemory)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(ioMemory.mMemory == VK_NULL_HANDLE);
|
||||||
|
|
||||||
|
ioMemory.mSize = inSize;
|
||||||
|
ioMemory.mProperties = inProperties;
|
||||||
|
|
||||||
|
VkMemoryAllocateInfo alloc_info = {};
|
||||||
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||||
|
alloc_info.allocationSize = inSize;
|
||||||
|
alloc_info.memoryTypeIndex = FindMemoryType(inMemoryTypeBits, inProperties);
|
||||||
|
mVkAllocateMemory(mDevice, &alloc_info, nullptr, &ioMemory.mMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeSystemVKWithAllocator::FreeMemory(MemoryVK &ioMemory)
|
||||||
|
{
|
||||||
|
mVkFreeMemory(mDevice, ioMemory.mMemory, nullptr);
|
||||||
|
ioMemory.mMemory = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComputeSystemVKWithAllocator::CreateBuffer(VkDeviceSize inSize, VkBufferUsageFlags inUsage, VkMemoryPropertyFlags inProperties, BufferVK &outBuffer)
|
||||||
|
{
|
||||||
|
// Create a new buffer
|
||||||
|
outBuffer.mSize = inSize;
|
||||||
|
|
||||||
|
VkBufferCreateInfo create_info = {};
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||||
|
create_info.size = inSize;
|
||||||
|
create_info.usage = inUsage;
|
||||||
|
create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
if (VKFailed(mVkCreateBuffer(mDevice, &create_info, nullptr, &outBuffer.mBuffer)))
|
||||||
|
{
|
||||||
|
outBuffer.mBuffer = VK_NULL_HANDLE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkMemoryRequirements mem_requirements;
|
||||||
|
mVkGetBufferMemoryRequirements(mDevice, outBuffer.mBuffer, &mem_requirements);
|
||||||
|
|
||||||
|
if (mem_requirements.size > cMaxAllocSize)
|
||||||
|
{
|
||||||
|
// Allocate block directly
|
||||||
|
Ref<MemoryVK> memory_vk = new MemoryVK();
|
||||||
|
memory_vk->mBufferSize = mem_requirements.size;
|
||||||
|
AllocateMemory(mem_requirements.size, mem_requirements.memoryTypeBits, inProperties, *memory_vk);
|
||||||
|
outBuffer.mMemory = memory_vk;
|
||||||
|
outBuffer.mOffset = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Round allocation to the next power of 2 so that we can use a simple block based allocator
|
||||||
|
VkDeviceSize buffer_size = max(VkDeviceSize(GetNextPowerOf2(uint32(mem_requirements.size))), cMinAllocSize);
|
||||||
|
|
||||||
|
// Ensure that we have memory available from the right pool
|
||||||
|
Array<Memory> &mem_array = mMemoryCache[{ buffer_size, inProperties }];
|
||||||
|
if (mem_array.empty())
|
||||||
|
{
|
||||||
|
// Allocate a bigger block
|
||||||
|
Ref<MemoryVK> memory_vk = new MemoryVK();
|
||||||
|
memory_vk->mBufferSize = buffer_size;
|
||||||
|
AllocateMemory(cBlockSize, mem_requirements.memoryTypeBits, inProperties, *memory_vk);
|
||||||
|
|
||||||
|
// Divide into sub blocks
|
||||||
|
for (VkDeviceSize offset = 0; offset < cBlockSize; offset += buffer_size)
|
||||||
|
mem_array.push_back({ memory_vk, offset });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Claim memory from the pool
|
||||||
|
Memory &memory = mem_array.back();
|
||||||
|
outBuffer.mMemory = memory.mMemory;
|
||||||
|
outBuffer.mOffset = memory.mOffset;
|
||||||
|
mem_array.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind the memory to the buffer
|
||||||
|
mVkBindBufferMemory(mDevice, outBuffer.mBuffer, outBuffer.mMemory->mMemory, outBuffer.mOffset);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeSystemVKWithAllocator::FreeBuffer(BufferVK &ioBuffer)
|
||||||
|
{
|
||||||
|
if (ioBuffer.mBuffer != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
// Destroy the buffer
|
||||||
|
mVkDestroyBuffer(mDevice, ioBuffer.mBuffer, nullptr);
|
||||||
|
ioBuffer.mBuffer = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
// Hand the memory back to the cache
|
||||||
|
VkDeviceSize buffer_size = ioBuffer.mMemory->mBufferSize;
|
||||||
|
if (buffer_size > cMaxAllocSize)
|
||||||
|
FreeMemory(*ioBuffer.mMemory);
|
||||||
|
else
|
||||||
|
mMemoryCache[{ buffer_size, ioBuffer.mMemory->mProperties }].push_back({ ioBuffer.mMemory, ioBuffer.mOffset });
|
||||||
|
|
||||||
|
ioBuffer = BufferVK();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ComputeSystemVKWithAllocator::MapBuffer(BufferVK& ioBuffer)
|
||||||
|
{
|
||||||
|
if (++ioBuffer.mMemory->mMappedCount == 1
|
||||||
|
&& VKFailed(mVkMapMemory(mDevice, ioBuffer.mMemory->mMemory, 0, VK_WHOLE_SIZE, 0, &ioBuffer.mMemory->mMappedPtr)))
|
||||||
|
{
|
||||||
|
ioBuffer.mMemory->mMappedCount = 0;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<uint8 *>(ioBuffer.mMemory->mMappedPtr) + ioBuffer.mOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeSystemVKWithAllocator::UnmapBuffer(BufferVK& ioBuffer)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(ioBuffer.mMemory->mMappedCount > 0);
|
||||||
|
if (--ioBuffer.mMemory->mMappedCount == 0)
|
||||||
|
{
|
||||||
|
mVkUnmapMemory(mDevice, ioBuffer.mMemory->mMemory);
|
||||||
|
ioBuffer.mMemory->mMappedPtr = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
75
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVKWithAllocator.h
vendored
Normal file
75
thirdparty/JoltPhysics/Jolt/Compute/VK/ComputeSystemVKWithAllocator.h
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
#include <Jolt/Compute/VK/ComputeSystemVK.h>
|
||||||
|
#include <Jolt/Core/UnorderedMap.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// This extends ComputeSystemVK to provide a default implementation for memory allocation and mapping.
|
||||||
|
/// It uses a simple block based allocator to reduce the number of allocations done to Vulkan.
|
||||||
|
class JPH_EXPORT ComputeSystemVKWithAllocator : public ComputeSystemVK
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_DECLARE_RTTI_VIRTUAL(JPH_EXPORT, ComputeSystemVKWithAllocator)
|
||||||
|
|
||||||
|
// Initialize the compute system
|
||||||
|
bool Initialize(VkInstance inInstance, VkPhysicalDevice inPhysicalDevice, PFN_vkGetInstanceProcAddr inGetInstanceProcAddr, PFN_vkGetDeviceProcAddr inVkGetDeviceProcAddr, VkDevice inDevice, uint32 inComputeQueueIndex, ComputeSystemResult &outResult);
|
||||||
|
|
||||||
|
/// Allow the application to override buffer creation and memory mapping in case it uses its own allocator
|
||||||
|
virtual bool CreateBuffer(VkDeviceSize inSize, VkBufferUsageFlags inUsage, VkMemoryPropertyFlags inProperties, BufferVK &outBuffer) override;
|
||||||
|
virtual void FreeBuffer(BufferVK &ioBuffer) override;
|
||||||
|
virtual void * MapBuffer(BufferVK &ioBuffer) override;
|
||||||
|
virtual void UnmapBuffer(BufferVK &ioBuffer) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool InitializeMemory() override;
|
||||||
|
virtual void ShutdownMemory() override;
|
||||||
|
|
||||||
|
uint32 FindMemoryType(uint32 inTypeFilter, VkMemoryPropertyFlags inProperties) const;
|
||||||
|
void AllocateMemory(VkDeviceSize inSize, uint32 inMemoryTypeBits, VkMemoryPropertyFlags inProperties, MemoryVK &ioMemory);
|
||||||
|
void FreeMemory(MemoryVK &ioMemory);
|
||||||
|
|
||||||
|
VkPhysicalDeviceMemoryProperties mMemoryProperties;
|
||||||
|
|
||||||
|
PFN_vkGetPhysicalDeviceMemoryProperties mVkGetPhysicalDeviceMemoryProperties = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Smaller allocations (from cMinAllocSize to cMaxAllocSize) will be done in blocks of cBlockSize bytes.
|
||||||
|
// We do this because there is a limit to the number of allocations that we can make in Vulkan.
|
||||||
|
static constexpr VkDeviceSize cMinAllocSize = 512;
|
||||||
|
static constexpr VkDeviceSize cMaxAllocSize = 65536;
|
||||||
|
static constexpr VkDeviceSize cBlockSize = 524288;
|
||||||
|
|
||||||
|
struct MemoryKey
|
||||||
|
{
|
||||||
|
bool operator == (const MemoryKey &inRHS) const
|
||||||
|
{
|
||||||
|
return mSize == inRHS.mSize && mProperties == inRHS.mProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDeviceSize mSize;
|
||||||
|
VkMemoryPropertyFlags mProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_MAKE_HASH_STRUCT(MemoryKey, MemoryKeyHasher, t.mProperties, t.mSize)
|
||||||
|
|
||||||
|
struct Memory
|
||||||
|
{
|
||||||
|
Ref<MemoryVK> mMemory;
|
||||||
|
VkDeviceSize mOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
using MemoryCache = UnorderedMap<MemoryKey, Array<Memory>, MemoryKeyHasher>;
|
||||||
|
|
||||||
|
MemoryCache mMemoryCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
43
thirdparty/JoltPhysics/Jolt/Compute/VK/IncludeVK.h
vendored
Normal file
43
thirdparty/JoltPhysics/Jolt/Compute/VK/IncludeVK.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/StringTools.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_VK
|
||||||
|
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_END
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
inline bool VKFailed(VkResult inResult)
|
||||||
|
{
|
||||||
|
if (inResult == VK_SUCCESS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Trace("Vulkan call failed with error code: %d", (int)inResult);
|
||||||
|
JPH_ASSERT(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Result>
|
||||||
|
inline bool VKFailed(VkResult inResult, Result &outResult)
|
||||||
|
{
|
||||||
|
if (inResult == VK_SUCCESS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
String error = StringFormat("Vulkan call failed with error code: %d", (int)inResult);
|
||||||
|
outResult.SetError(error);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // JPH_USE_VK
|
||||||
118
thirdparty/JoltPhysics/Jolt/ConfigurationString.h
vendored
Normal file
118
thirdparty/JoltPhysics/Jolt/ConfigurationString.h
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Construct a string that lists the most important configuration settings
|
||||||
|
inline const char *GetConfigurationString()
|
||||||
|
{
|
||||||
|
return JPH_IF_SINGLE_PRECISION_ELSE("Single", "Double") " precision "
|
||||||
|
#if defined(JPH_CPU_X86)
|
||||||
|
"x86 "
|
||||||
|
#elif defined(JPH_CPU_ARM)
|
||||||
|
"ARM "
|
||||||
|
#elif defined(JPH_CPU_RISCV)
|
||||||
|
"RISC-V "
|
||||||
|
#elif defined(JPH_CPU_PPC)
|
||||||
|
"PowerPC "
|
||||||
|
#ifdef JPH_CPU_BIG_ENDIAN
|
||||||
|
"(Big Endian) "
|
||||||
|
#else
|
||||||
|
"(Little Endian) "
|
||||||
|
#endif
|
||||||
|
#elif defined(JPH_CPU_LOONGARCH)
|
||||||
|
"LoongArch "
|
||||||
|
#elif defined(JPH_CPU_E2K)
|
||||||
|
"E2K "
|
||||||
|
#elif defined(JPH_CPU_WASM)
|
||||||
|
"WASM "
|
||||||
|
#else
|
||||||
|
#error Unknown CPU architecture
|
||||||
|
#endif
|
||||||
|
#if JPH_CPU_ARCH_BITS == 64
|
||||||
|
"64-bit "
|
||||||
|
#elif JPH_CPU_ARCH_BITS == 32
|
||||||
|
"32-bit "
|
||||||
|
#endif
|
||||||
|
"with instructions: "
|
||||||
|
#ifdef JPH_USE_RVV
|
||||||
|
"RVV "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_NEON
|
||||||
|
"NEON "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_SSE
|
||||||
|
"SSE2 "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_SSE4_1
|
||||||
|
"SSE4.1 "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_SSE4_2
|
||||||
|
"SSE4.2 "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_AVX
|
||||||
|
"AVX "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_AVX2
|
||||||
|
"AVX2 "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_AVX512
|
||||||
|
"AVX512 "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_F16C
|
||||||
|
"F16C "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_LZCNT
|
||||||
|
"LZCNT "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_TZCNT
|
||||||
|
"TZCNT "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_USE_FMADD
|
||||||
|
"FMADD "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_CROSS_PLATFORM_DETERMINISTIC
|
||||||
|
"(Cross Platform Deterministic) "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED
|
||||||
|
"(FP Exceptions) "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_DEBUG_RENDERER
|
||||||
|
"(Debug Renderer) "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_PROFILE_ENABLED
|
||||||
|
"(Profile) "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_EXTERNAL_PROFILE
|
||||||
|
"(External Profile) "
|
||||||
|
#endif
|
||||||
|
#if defined(JPH_OBJECT_LAYER_BITS) && JPH_OBJECT_LAYER_BITS == 32
|
||||||
|
"(32-bit ObjectLayer) "
|
||||||
|
#else
|
||||||
|
"(16-bit ObjectLayer) "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_ENABLE_ASSERTS
|
||||||
|
"(Assertions) "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_OBJECT_STREAM
|
||||||
|
"(ObjectStream) "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
"(Debug) "
|
||||||
|
#endif
|
||||||
|
#if defined(__cpp_rtti) && __cpp_rtti
|
||||||
|
"(C++ RTTI) "
|
||||||
|
#endif
|
||||||
|
#if defined(__cpp_exceptions) && __cpp_exceptions
|
||||||
|
"(C++ Exceptions) "
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_SHARED_LIBRARY
|
||||||
|
"(Shared Library) "
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
340
thirdparty/JoltPhysics/Jolt/Core/ARMNeon.h
vendored
Normal file
340
thirdparty/JoltPhysics/Jolt/Core/ARMNeon.h
vendored
Normal file
|
|
@ -0,0 +1,340 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2022 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef JPH_USE_NEON
|
||||||
|
|
||||||
|
// Constructing NEON values
|
||||||
|
#ifdef JPH_COMPILER_MSVC
|
||||||
|
#define JPH_NEON_INT32x4(v1, v2, v3, v4) { int64_t(v1) + (int64_t(v2) << 32), int64_t(v3) + (int64_t(v4) << 32) }
|
||||||
|
#define JPH_NEON_UINT32x4(v1, v2, v3, v4) { uint64_t(v1) + (uint64_t(v2) << 32), uint64_t(v3) + (uint64_t(v4) << 32) }
|
||||||
|
#define JPH_NEON_INT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { int64_t(v1) + (int64_t(v2) << 8) + (int64_t(v3) << 16) + (int64_t(v4) << 24) + (int64_t(v5) << 32) + (int64_t(v6) << 40) + (int64_t(v7) << 48) + (int64_t(v8) << 56), int64_t(v9) + (int64_t(v10) << 8) + (int64_t(v11) << 16) + (int64_t(v12) << 24) + (int64_t(v13) << 32) + (int64_t(v14) << 40) + (int64_t(v15) << 48) + (int64_t(v16) << 56) }
|
||||||
|
#define JPH_NEON_UINT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { uint64_t(v1) + (uint64_t(v2) << 8) + (uint64_t(v3) << 16) + (uint64_t(v4) << 24) + (uint64_t(v5) << 32) + (uint64_t(v6) << 40) + (uint64_t(v7) << 48) + (uint64_t(v8) << 56), uint64_t(v9) + (uint64_t(v10) << 8) + (uint64_t(v11) << 16) + (uint64_t(v12) << 24) + (uint64_t(v13) << 32) + (uint64_t(v14) << 40) + (uint64_t(v15) << 48) + (uint64_t(v16) << 56) }
|
||||||
|
#else
|
||||||
|
#define JPH_NEON_INT32x4(v1, v2, v3, v4) { v1, v2, v3, v4 }
|
||||||
|
#define JPH_NEON_UINT32x4(v1, v2, v3, v4) { v1, v2, v3, v4 }
|
||||||
|
#define JPH_NEON_INT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 }
|
||||||
|
#define JPH_NEON_UINT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// MSVC and GCC prior to version 12 don't define __builtin_shufflevector
|
||||||
|
#if defined(JPH_COMPILER_MSVC) || (defined(JPH_COMPILER_GCC) && __GNUC__ < 12)
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Generic shuffle vector template
|
||||||
|
template <unsigned I1, unsigned I2, unsigned I3, unsigned I4>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
float32x2_t lo = vcopy_laneq_f32(vdup_n_f32(0), 0, I1 >= 4? inV2 : inV1, I1 & 0b11);
|
||||||
|
lo = vcopy_laneq_f32(lo, 1, I2 >= 4? inV2 : inV1, I2 & 0b11);
|
||||||
|
|
||||||
|
float32x2_t hi = vcopy_laneq_f32(vdup_n_f32(0), 0, I3 >= 4? inV2 : inV1, I3 & 0b11);
|
||||||
|
hi = vcopy_laneq_f32(hi, 1, I4 >= 4? inV2 : inV1, I4 & 0b11);
|
||||||
|
|
||||||
|
return vcombine_f32(lo, hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specializations
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 0, 0, 0>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vdupq_laneq_f32(inV1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 0, 0>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcombine_f32(vget_low_f32(inV1), vdup_lane_f32(vget_low_f32(inV1), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 2, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcopyq_laneq_f32(inV1, 3, inV1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 2, 3>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return inV1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 3, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcombine_f32(vget_low_f32(inV1), vrev64_f32(vget_high_f32(inV1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 3, 3>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcopyq_laneq_f32(inV1, 2, inV1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 4, 5>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vreinterpretq_f32_f64(vzip1q_f64(vreinterpretq_f64_f32(inV1), vreinterpretq_f64_f32(inV2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 2, 1, 1>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vuzp1q_f32(inV1, vdupq_laneq_f32(inV1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 2, 1, 3>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vuzp1q_f32(inV1, vrev64q_f32(inV1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 2, 2, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vuzp1q_f32(inV1, vdupq_laneq_f32(inV1, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 2, 2, 3>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcopyq_laneq_f32(inV1, 1, inV1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 2, 3, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcopyq_laneq_f32(vuzp1q_f32(inV1, inV1), 2, inV1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 2, 3, 3>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vuzp1q_f32(inV1, vdupq_laneq_f32(inV1, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 2, 4, 6>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vuzp1q_f32(inV1, inV2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 3, 1, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vzip1q_f32(inV1, vextq_f32(inV1, vdupq_laneq_f32(inV1, 2), 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 0, 0, 0>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcombine_f32(vrev64_f32(vget_low_f32(inV1)), vdup_lane_f32(vget_low_f32(inV1), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 0, 0, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcombine_f32(vrev64_f32(vget_low_f32(inV1)), vzip1_f32(vget_low_f32(inV1), vget_high_f32(inV1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 0, 3, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vrev64q_f32(inV1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 1, 1, 1>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vdupq_laneq_f32(inV1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 1, 2, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
float32x4_t t = vextq_f32(inV1, inV1, 1);
|
||||||
|
return vzip1q_f32(t, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 1, 3, 3>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vtrn2q_f32(inV1, inV1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used extensively by cross product
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 2, 0, 0>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcopyq_laneq_f32(vextq_f32(inV1, inV1, 1), 2, inV1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 2, 0, 1>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vextq_f32(vextq_f32(inV1, inV1, 3), inV1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 2, 0, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcopyq_laneq_f32(vuzp1q_f32(inV1, inV1), 0, inV1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 2, 2, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcombine_f32(vext_f32(vget_low_f32(inV1), vget_high_f32(inV1), 1), vdup_lane_f32(vget_high_f32(inV1), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 2, 3, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vextq_f32(inV1, vdupq_laneq_f32(inV1, 2), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 2, 3, 3>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vextq_f32(inV1, vdupq_laneq_f32(inV1, 3), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 3, 0, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vuzp2q_f32(inV1, vrev64q_f32(inV1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 3, 5, 7>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vuzp2q_f32(inV1, inV2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 0, 1, 1>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcopyq_laneq_f32(vzip1q_f32(inV1, inV1), 0, inV1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 0, 1, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vextq_f32(vuzp1q_f32(inV1, inV1), inV1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 1, 0, 0>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
float32x4_t t = vextq_f32(vuzp1q_f32(inV1, inV1), inV1, 3);
|
||||||
|
return vuzp1q_f32(t, vuzp1q_f32(inV1, inV1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 1, 0, 3>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
float32x4_t t = vrev64q_f32(inV1);
|
||||||
|
return vextq_f32(t, t, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 2, 1, 0>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vextq_f32(vtrn1q_f32(inV1, inV1), vrev64q_f32(inV1), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 2, 1, 1>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
float32x4_t t = vcopyq_laneq_f32(inV1, 3, inV1, 1);
|
||||||
|
return vzip2q_f32(t, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 2, 1, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcopyq_laneq_f32(vdupq_laneq_f32(inV1, 2), 2, inV1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 2, 2, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vdupq_laneq_f32(inV1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 3, 0, 1>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vcombine_f32(vget_high_f32(inV1), vget_low_f32(inV1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 3, 1, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vextq_f32(inV1, vextq_f32(inV1, inV1, 1), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 3, 2, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vextq_f32(inV1, vdupq_laneq_f32(inV1, 2), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 3, 2, 3>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vreinterpretq_f32_f64(vdupq_laneq_f64(vreinterpretq_f64_f32(inV1), 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 3, 6, 7>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vreinterpretq_f32_f64(vzip2q_f64(vreinterpretq_f64_f32(inV1), vreinterpretq_f64_f32(inV2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<3, 0, 1, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vextq_f32(inV1, inV1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<3, 0, 3, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vtrn1q_f32(vdupq_laneq_f32(inV1, 3), inV1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<3, 2, 1, 0>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
float32x4_t t = vrev64q_f32(inV1);
|
||||||
|
return vextq_f32(t, t, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<3, 2, 3, 2>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
float32x2_t zy = vrev64_f32(vget_high_f32(inV1));
|
||||||
|
return vcombine_f32(zy, zy);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
JPH_INLINE float32x4_t NeonShuffleFloat32x4<3, 3, 3, 3>(float32x4_t inV1, float32x4_t inV2)
|
||||||
|
{
|
||||||
|
return vdupq_laneq_f32(inV1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shuffle a vector
|
||||||
|
#define JPH_NEON_SHUFFLE_F32x4(vec1, vec2, index1, index2, index3, index4) NeonShuffleFloat32x4<index1, index2, index3, index4>(vec1, vec2)
|
||||||
|
#define JPH_NEON_SHUFFLE_U32x4(vec1, vec2, index1, index2, index3, index4) vreinterpretq_u32_f32((NeonShuffleFloat32x4<index1, index2, index3, index4>(vreinterpretq_f32_u32(vec1), vreinterpretq_f32_u32(vec2))))
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
#else
|
||||||
|
// Shuffle a vector
|
||||||
|
#define JPH_NEON_SHUFFLE_F32x4(vec1, vec2, index1, index2, index3, index4) __builtin_shufflevector(vec1, vec2, index1, index2, index3, index4)
|
||||||
|
#define JPH_NEON_SHUFFLE_U32x4(vec1, vec2, index1, index2, index3, index4) __builtin_shufflevector(vec1, vec2, index1, index2, index3, index4)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // JPH_USE_NEON
|
||||||
713
thirdparty/JoltPhysics/Jolt/Core/Array.h
vendored
Normal file
713
thirdparty/JoltPhysics/Jolt/Core/Array.h
vendored
Normal file
|
|
@ -0,0 +1,713 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/STLAllocator.h>
|
||||||
|
#include <Jolt/Core/HashCombine.h>
|
||||||
|
|
||||||
|
#ifdef JPH_USE_STD_VECTOR
|
||||||
|
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||||
|
#include <vector>
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_END
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
template <class T, class Allocator = STLAllocator<T>> using Array = std::vector<T, Allocator>;
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Simple replacement for std::vector
|
||||||
|
///
|
||||||
|
/// Major differences:
|
||||||
|
/// - Memory is not initialized to zero (this was causing a lot of page faults when deserializing large MeshShapes / HeightFieldShapes)
|
||||||
|
/// - Iterators are simple pointers (for now)
|
||||||
|
/// - No exception safety
|
||||||
|
/// - No specialization like std::vector<bool> has
|
||||||
|
/// - Not all functions have been implemented
|
||||||
|
template <class T, class Allocator = STLAllocator<T>>
|
||||||
|
class [[nodiscard]] Array : private Allocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using allocator_type = Allocator;
|
||||||
|
using size_type = size_t;
|
||||||
|
using difference_type = typename Allocator::difference_type;
|
||||||
|
using pointer = T *;
|
||||||
|
using const_pointer = const T *;
|
||||||
|
using reference = T &;
|
||||||
|
using const_reference = const T &;
|
||||||
|
|
||||||
|
using const_iterator = const T *;
|
||||||
|
using iterator = T *;
|
||||||
|
|
||||||
|
/// An iterator that traverses the array in reverse order
|
||||||
|
class rev_it
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
rev_it() = default;
|
||||||
|
explicit rev_it(T *inValue) : mValue(inValue) { }
|
||||||
|
|
||||||
|
/// Copying
|
||||||
|
rev_it(const rev_it &) = default;
|
||||||
|
rev_it & operator = (const rev_it &) = default;
|
||||||
|
|
||||||
|
/// Comparison
|
||||||
|
bool operator == (const rev_it &inRHS) const { return mValue == inRHS.mValue; }
|
||||||
|
bool operator != (const rev_it &inRHS) const { return mValue != inRHS.mValue; }
|
||||||
|
|
||||||
|
/// Arithmetics
|
||||||
|
rev_it & operator ++ () { --mValue; return *this; }
|
||||||
|
rev_it operator ++ (int) { return rev_it(mValue--); }
|
||||||
|
rev_it & operator -- () { ++mValue; return *this; }
|
||||||
|
rev_it operator -- (int) { return rev_it(mValue++); }
|
||||||
|
|
||||||
|
rev_it operator + (int inValue) const { return rev_it(mValue - inValue); }
|
||||||
|
rev_it operator - (int inValue) const { return rev_it(mValue + inValue); }
|
||||||
|
|
||||||
|
rev_it & operator += (int inValue) { mValue -= inValue; return *this; }
|
||||||
|
rev_it & operator -= (int inValue) { mValue += inValue; return *this; }
|
||||||
|
|
||||||
|
/// Access
|
||||||
|
T & operator * () const { return *mValue; }
|
||||||
|
T & operator -> () const { return *mValue; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T * mValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A const iterator that traverses the array in reverse order
|
||||||
|
class crev_it
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
crev_it() = default;
|
||||||
|
explicit crev_it(const T *inValue) : mValue(inValue) { }
|
||||||
|
|
||||||
|
/// Copying
|
||||||
|
crev_it(const crev_it &) = default;
|
||||||
|
explicit crev_it(const rev_it &inValue) : mValue(inValue.mValue) { }
|
||||||
|
crev_it & operator = (const crev_it &) = default;
|
||||||
|
crev_it & operator = (const rev_it &inRHS) { mValue = inRHS.mValue; return *this; }
|
||||||
|
|
||||||
|
/// Comparison
|
||||||
|
bool operator == (const crev_it &inRHS) const { return mValue == inRHS.mValue; }
|
||||||
|
bool operator != (const crev_it &inRHS) const { return mValue != inRHS.mValue; }
|
||||||
|
|
||||||
|
/// Arithmetics
|
||||||
|
crev_it & operator ++ () { --mValue; return *this; }
|
||||||
|
crev_it operator ++ (int) { return crev_it(mValue--); }
|
||||||
|
crev_it & operator -- () { ++mValue; return *this; }
|
||||||
|
crev_it operator -- (int) { return crev_it(mValue++); }
|
||||||
|
|
||||||
|
crev_it operator + (int inValue) { return crev_it(mValue - inValue); }
|
||||||
|
crev_it operator - (int inValue) { return crev_it(mValue + inValue); }
|
||||||
|
|
||||||
|
crev_it & operator += (int inValue) { mValue -= inValue; return *this; }
|
||||||
|
crev_it & operator -= (int inValue) { mValue += inValue; return *this; }
|
||||||
|
|
||||||
|
/// Access
|
||||||
|
const T & operator * () const { return *mValue; }
|
||||||
|
const T & operator -> () const { return *mValue; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const T * mValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
using reverse_iterator = rev_it;
|
||||||
|
using const_reverse_iterator = crev_it;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Move elements from one location to another
|
||||||
|
inline void move(pointer inDestination, pointer inSource, size_type inCount)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_trivially_copyable<T>())
|
||||||
|
memmove(inDestination, inSource, inCount * sizeof(T));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (inDestination < inSource)
|
||||||
|
{
|
||||||
|
for (T *destination_end = inDestination + inCount; inDestination < destination_end; ++inDestination, ++inSource)
|
||||||
|
{
|
||||||
|
new (inDestination) T(std::move(*inSource));
|
||||||
|
inSource->~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (T *destination = inDestination + inCount - 1, *source = inSource + inCount - 1; destination >= inDestination; --destination, --source)
|
||||||
|
{
|
||||||
|
new (destination) T(std::move(*source));
|
||||||
|
source->~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reallocate the data block to inNewCapacity
|
||||||
|
inline void reallocate(size_type inNewCapacity)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inNewCapacity > 0 && inNewCapacity >= mSize);
|
||||||
|
|
||||||
|
pointer ptr;
|
||||||
|
if constexpr (AllocatorHasReallocate<Allocator>::sValue)
|
||||||
|
{
|
||||||
|
// Reallocate data block
|
||||||
|
ptr = get_allocator().reallocate(mElements, mCapacity, inNewCapacity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Copy data to a new location
|
||||||
|
ptr = get_allocator().allocate(inNewCapacity);
|
||||||
|
if (mElements != nullptr)
|
||||||
|
{
|
||||||
|
move(ptr, mElements, mSize);
|
||||||
|
get_allocator().deallocate(mElements, mCapacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mElements = ptr;
|
||||||
|
mCapacity = inNewCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destruct elements [inStart, inEnd - 1]
|
||||||
|
inline void destruct(size_type inStart, size_type inEnd)
|
||||||
|
{
|
||||||
|
if constexpr (!std::is_trivially_destructible<T>())
|
||||||
|
if (inStart < inEnd)
|
||||||
|
for (T *element = mElements + inStart, *element_end = mElements + inEnd; element < element_end; ++element)
|
||||||
|
element->~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Reserve array space
|
||||||
|
inline void reserve(size_type inNewSize)
|
||||||
|
{
|
||||||
|
if (mCapacity < inNewSize)
|
||||||
|
reallocate(inNewSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resize array to new length
|
||||||
|
inline void resize(size_type inNewSize)
|
||||||
|
{
|
||||||
|
destruct(inNewSize, mSize);
|
||||||
|
reserve(inNewSize);
|
||||||
|
|
||||||
|
if constexpr (!std::is_trivially_constructible<T>())
|
||||||
|
for (T *element = mElements + mSize, *element_end = mElements + inNewSize; element < element_end; ++element)
|
||||||
|
new (element) T;
|
||||||
|
mSize = inNewSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resize array to new length and initialize all elements with inValue
|
||||||
|
inline void resize(size_type inNewSize, const T &inValue)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(&inValue < mElements || &inValue >= mElements + mSize, "Can't pass an element from the array to resize");
|
||||||
|
|
||||||
|
destruct(inNewSize, mSize);
|
||||||
|
reserve(inNewSize);
|
||||||
|
|
||||||
|
for (T *element = mElements + mSize, *element_end = mElements + inNewSize; element < element_end; ++element)
|
||||||
|
new (element) T(inValue);
|
||||||
|
mSize = inNewSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destruct all elements and set length to zero
|
||||||
|
inline void clear()
|
||||||
|
{
|
||||||
|
destruct(0, mSize);
|
||||||
|
mSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Grow the array by at least inAmount elements
|
||||||
|
inline void grow(size_type inAmount = 1)
|
||||||
|
{
|
||||||
|
size_type min_size = mSize + inAmount;
|
||||||
|
if (min_size > mCapacity)
|
||||||
|
{
|
||||||
|
size_type new_capacity = max(min_size, mCapacity * 2);
|
||||||
|
reserve(new_capacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Free memory
|
||||||
|
inline void deallocate()
|
||||||
|
{
|
||||||
|
get_allocator().deallocate(mElements, mCapacity);
|
||||||
|
mElements = nullptr;
|
||||||
|
mCapacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy all elements and free memory
|
||||||
|
inline void destroy()
|
||||||
|
{
|
||||||
|
if (mElements != nullptr)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
deallocate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Replace the contents of this array with inBegin .. inEnd
|
||||||
|
template <class Iterator>
|
||||||
|
inline void assign(Iterator inBegin, Iterator inEnd)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
reserve(size_type(std::distance(inBegin, inEnd)));
|
||||||
|
|
||||||
|
for (Iterator element = inBegin; element != inEnd; ++element)
|
||||||
|
new (&mElements[mSize++]) T(*element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace the contents of this array with inList
|
||||||
|
inline void assign(std::initializer_list<T> inList)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
reserve(size_type(inList.size()));
|
||||||
|
|
||||||
|
for (const T &v : inList)
|
||||||
|
new (&mElements[mSize++]) T(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default constructor
|
||||||
|
Array() = default;
|
||||||
|
|
||||||
|
/// Constructor with allocator
|
||||||
|
explicit inline Array(const Allocator &inAllocator) :
|
||||||
|
Allocator(inAllocator)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor with length
|
||||||
|
explicit inline Array(size_type inLength, const Allocator &inAllocator = { }) :
|
||||||
|
Allocator(inAllocator)
|
||||||
|
{
|
||||||
|
resize(inLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor with length and value
|
||||||
|
inline Array(size_type inLength, const T &inValue, const Allocator &inAllocator = { }) :
|
||||||
|
Allocator(inAllocator)
|
||||||
|
{
|
||||||
|
resize(inLength, inValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor from initializer list
|
||||||
|
inline Array(std::initializer_list<T> inList, const Allocator &inAllocator = { }) :
|
||||||
|
Allocator(inAllocator)
|
||||||
|
{
|
||||||
|
assign(inList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor from iterator
|
||||||
|
inline Array(const_iterator inBegin, const_iterator inEnd, const Allocator &inAllocator = { }) :
|
||||||
|
Allocator(inAllocator)
|
||||||
|
{
|
||||||
|
assign(inBegin, inEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy constructor
|
||||||
|
inline Array(const Array<T, Allocator> &inRHS) :
|
||||||
|
Allocator(inRHS.get_allocator())
|
||||||
|
{
|
||||||
|
assign(inRHS.begin(), inRHS.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move constructor
|
||||||
|
inline Array(Array<T, Allocator> &&inRHS) noexcept :
|
||||||
|
Allocator(std::move(inRHS.get_allocator())),
|
||||||
|
mSize(inRHS.mSize),
|
||||||
|
mCapacity(inRHS.mCapacity),
|
||||||
|
mElements(inRHS.mElements)
|
||||||
|
{
|
||||||
|
inRHS.mSize = 0;
|
||||||
|
inRHS.mCapacity = 0;
|
||||||
|
inRHS.mElements = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destruct all elements
|
||||||
|
inline ~Array()
|
||||||
|
{
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the allocator
|
||||||
|
inline Allocator & get_allocator()
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const Allocator &get_allocator() const
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add element to the back of the array
|
||||||
|
inline void push_back(const T &inValue)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(&inValue < mElements || &inValue >= mElements + mSize, "Can't pass an element from the array to push_back");
|
||||||
|
|
||||||
|
grow();
|
||||||
|
|
||||||
|
T *element = mElements + mSize++;
|
||||||
|
new (element) T(inValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void push_back(T &&inValue)
|
||||||
|
{
|
||||||
|
grow();
|
||||||
|
|
||||||
|
T *element = mElements + mSize++;
|
||||||
|
new (element) T(std::move(inValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct element at the back of the array
|
||||||
|
template <class... A>
|
||||||
|
inline T & emplace_back(A &&... inValue)
|
||||||
|
{
|
||||||
|
grow();
|
||||||
|
|
||||||
|
T *element = mElements + mSize++;
|
||||||
|
new (element) T(std::forward<A>(inValue)...);
|
||||||
|
return *element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove element from the back of the array
|
||||||
|
inline void pop_back()
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mSize > 0);
|
||||||
|
mElements[--mSize].~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if there are no elements in the array
|
||||||
|
inline bool empty() const
|
||||||
|
{
|
||||||
|
return mSize == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns amount of elements in the array
|
||||||
|
inline size_type size() const
|
||||||
|
{
|
||||||
|
return mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns maximum amount of elements the array can hold
|
||||||
|
inline size_type capacity() const
|
||||||
|
{
|
||||||
|
return mCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reduce the capacity of the array to match its size
|
||||||
|
void shrink_to_fit()
|
||||||
|
{
|
||||||
|
if (mElements != nullptr)
|
||||||
|
{
|
||||||
|
if (mSize == 0)
|
||||||
|
deallocate();
|
||||||
|
else if (mCapacity > mSize)
|
||||||
|
reallocate(mSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Swap the contents of two arrays
|
||||||
|
void swap(Array<T, Allocator> &inRHS) noexcept
|
||||||
|
{
|
||||||
|
std::swap(get_allocator(), inRHS.get_allocator());
|
||||||
|
std::swap(mSize, inRHS.mSize);
|
||||||
|
std::swap(mCapacity, inRHS.mCapacity);
|
||||||
|
std::swap(mElements, inRHS.mElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Iterator>
|
||||||
|
void insert(const_iterator inPos, Iterator inBegin, Iterator inEnd)
|
||||||
|
{
|
||||||
|
size_type num_elements = size_type(std::distance(inBegin, inEnd));
|
||||||
|
if (num_elements > 0)
|
||||||
|
{
|
||||||
|
// After grow() inPos may be invalid
|
||||||
|
size_type first_element = inPos - mElements;
|
||||||
|
|
||||||
|
grow(num_elements);
|
||||||
|
|
||||||
|
T *element_begin = mElements + first_element;
|
||||||
|
T *element_end = element_begin + num_elements;
|
||||||
|
move(element_end, element_begin, mSize - first_element);
|
||||||
|
|
||||||
|
for (T *element = element_begin; element < element_end; ++element, ++inBegin)
|
||||||
|
new (element) T(*inBegin);
|
||||||
|
|
||||||
|
mSize += num_elements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(const_iterator inPos, const T &inValue)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(&inValue < mElements || &inValue >= mElements + mSize, "Can't pass an element from the array to insert");
|
||||||
|
|
||||||
|
// After grow() inPos may be invalid
|
||||||
|
size_type first_element = inPos - mElements;
|
||||||
|
|
||||||
|
grow();
|
||||||
|
|
||||||
|
T *element = mElements + first_element;
|
||||||
|
move(element + 1, element, mSize - first_element);
|
||||||
|
|
||||||
|
new (element) T(inValue);
|
||||||
|
mSize++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove one element from the array
|
||||||
|
iterator erase(const_iterator inIter)
|
||||||
|
{
|
||||||
|
size_type p = size_type(inIter - begin());
|
||||||
|
JPH_ASSERT(p < mSize);
|
||||||
|
mElements[p].~T();
|
||||||
|
if (p + 1 < mSize)
|
||||||
|
move(mElements + p, mElements + p + 1, mSize - p - 1);
|
||||||
|
--mSize;
|
||||||
|
return const_cast<iterator>(inIter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove multiple element from the array
|
||||||
|
iterator erase(const_iterator inBegin, const_iterator inEnd)
|
||||||
|
{
|
||||||
|
size_type p = size_type(inBegin - begin());
|
||||||
|
size_type n = size_type(inEnd - inBegin);
|
||||||
|
JPH_ASSERT(inEnd <= end());
|
||||||
|
destruct(p, p + n);
|
||||||
|
if (p + n < mSize)
|
||||||
|
move(mElements + p, mElements + p + n, mSize - p - n);
|
||||||
|
mSize -= n;
|
||||||
|
return const_cast<iterator>(inBegin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterators
|
||||||
|
inline const_iterator begin() const
|
||||||
|
{
|
||||||
|
return mElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const_iterator end() const
|
||||||
|
{
|
||||||
|
return mElements + mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline crev_it rbegin() const
|
||||||
|
{
|
||||||
|
return crev_it(mElements + mSize - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline crev_it rend() const
|
||||||
|
{
|
||||||
|
return crev_it(mElements - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const_iterator cbegin() const
|
||||||
|
{
|
||||||
|
return begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const_iterator cend() const
|
||||||
|
{
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline crev_it crbegin() const
|
||||||
|
{
|
||||||
|
return rbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline crev_it crend() const
|
||||||
|
{
|
||||||
|
return rend();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline iterator begin()
|
||||||
|
{
|
||||||
|
return mElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline iterator end()
|
||||||
|
{
|
||||||
|
return mElements + mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline rev_it rbegin()
|
||||||
|
{
|
||||||
|
return rev_it(mElements + mSize - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline rev_it rend()
|
||||||
|
{
|
||||||
|
return rev_it(mElements - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const T * data() const
|
||||||
|
{
|
||||||
|
return mElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T * data()
|
||||||
|
{
|
||||||
|
return mElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access element
|
||||||
|
inline T & operator [] (size_type inIdx)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inIdx < mSize);
|
||||||
|
return mElements[inIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const T & operator [] (size_type inIdx) const
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inIdx < mSize);
|
||||||
|
return mElements[inIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access element
|
||||||
|
inline T & at(size_type inIdx)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inIdx < mSize);
|
||||||
|
return mElements[inIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const T & at(size_type inIdx) const
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inIdx < mSize);
|
||||||
|
return mElements[inIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// First element in the array
|
||||||
|
inline const T & front() const
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mSize > 0);
|
||||||
|
return mElements[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T & front()
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mSize > 0);
|
||||||
|
return mElements[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Last element in the array
|
||||||
|
inline const T & back() const
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mSize > 0);
|
||||||
|
return mElements[mSize - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T & back()
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mSize > 0);
|
||||||
|
return mElements[mSize - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assignment operator
|
||||||
|
Array<T, Allocator> & operator = (const Array<T, Allocator> &inRHS)
|
||||||
|
{
|
||||||
|
if (static_cast<const void *>(this) != static_cast<const void *>(&inRHS))
|
||||||
|
assign(inRHS.begin(), inRHS.end());
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assignment move operator
|
||||||
|
Array<T, Allocator> & operator = (Array<T, Allocator> &&inRHS) noexcept
|
||||||
|
{
|
||||||
|
if (static_cast<const void *>(this) != static_cast<const void *>(&inRHS))
|
||||||
|
{
|
||||||
|
destroy();
|
||||||
|
|
||||||
|
get_allocator() = std::move(inRHS.get_allocator());
|
||||||
|
|
||||||
|
mSize = inRHS.mSize;
|
||||||
|
mCapacity = inRHS.mCapacity;
|
||||||
|
mElements = inRHS.mElements;
|
||||||
|
|
||||||
|
inRHS.mSize = 0;
|
||||||
|
inRHS.mCapacity = 0;
|
||||||
|
inRHS.mElements = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assignment operator
|
||||||
|
Array<T, Allocator> & operator = (std::initializer_list<T> inRHS)
|
||||||
|
{
|
||||||
|
assign(inRHS);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Comparing arrays
|
||||||
|
bool operator == (const Array<T, Allocator> &inRHS) const
|
||||||
|
{
|
||||||
|
if (mSize != inRHS.mSize)
|
||||||
|
return false;
|
||||||
|
for (size_type i = 0; i < mSize; ++i)
|
||||||
|
if (!(mElements[i] == inRHS.mElements[i]))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (const Array<T, Allocator> &inRHS) const
|
||||||
|
{
|
||||||
|
if (mSize != inRHS.mSize)
|
||||||
|
return true;
|
||||||
|
for (size_type i = 0; i < mSize; ++i)
|
||||||
|
if (mElements[i] != inRHS.mElements[i])
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get hash for this array
|
||||||
|
uint64 GetHash() const
|
||||||
|
{
|
||||||
|
// Hash length first
|
||||||
|
uint64 ret = Hash<uint32> { } (uint32(size()));
|
||||||
|
|
||||||
|
// Then hash elements
|
||||||
|
for (const T *element = mElements, *element_end = mElements + mSize; element < element_end; ++element)
|
||||||
|
HashCombine(ret, *element);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_type mSize = 0;
|
||||||
|
size_type mCapacity = 0;
|
||||||
|
T * mElements = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
JPH_SUPPRESS_WARNING_PUSH
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
/// Declare std::hash for Array
|
||||||
|
template <class T, class Allocator>
|
||||||
|
struct hash<JPH::Array<T, Allocator>>
|
||||||
|
{
|
||||||
|
size_t operator () (const JPH::Array<T, Allocator> &inRHS) const
|
||||||
|
{
|
||||||
|
return std::size_t(inRHS.GetHash());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_SUPPRESS_WARNING_POP
|
||||||
|
|
||||||
|
#endif // JPH_USE_STD_VECTOR
|
||||||
44
thirdparty/JoltPhysics/Jolt/Core/Atomics.h
vendored
Normal file
44
thirdparty/JoltPhysics/Jolt/Core/Atomics.h
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||||
|
#include <atomic>
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_END
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Things we're using from STL
|
||||||
|
using std::atomic;
|
||||||
|
using std::memory_order;
|
||||||
|
using std::memory_order_relaxed;
|
||||||
|
using std::memory_order_acquire;
|
||||||
|
using std::memory_order_release;
|
||||||
|
using std::memory_order_acq_rel;
|
||||||
|
using std::memory_order_seq_cst;
|
||||||
|
|
||||||
|
/// Atomically compute the min(ioAtomic, inValue) and store it in ioAtomic, returns true if value was updated
|
||||||
|
template <class T>
|
||||||
|
bool AtomicMin(atomic<T> &ioAtomic, const T inValue, const memory_order inMemoryOrder = memory_order_seq_cst)
|
||||||
|
{
|
||||||
|
T cur_value = ioAtomic.load(memory_order_relaxed);
|
||||||
|
while (cur_value > inValue)
|
||||||
|
if (ioAtomic.compare_exchange_weak(cur_value, inValue, inMemoryOrder))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Atomically compute the max(ioAtomic, inValue) and store it in ioAtomic, returns true if value was updated
|
||||||
|
template <class T>
|
||||||
|
bool AtomicMax(atomic<T> &ioAtomic, const T inValue, const memory_order inMemoryOrder = memory_order_seq_cst)
|
||||||
|
{
|
||||||
|
T cur_value = ioAtomic.load(memory_order_relaxed);
|
||||||
|
while (cur_value < inValue)
|
||||||
|
if (ioAtomic.compare_exchange_weak(cur_value, inValue, inMemoryOrder))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
96
thirdparty/JoltPhysics/Jolt/Core/BinaryHeap.h
vendored
Normal file
96
thirdparty/JoltPhysics/Jolt/Core/BinaryHeap.h
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Push a new element into a binary max-heap.
|
||||||
|
/// [inBegin, inEnd - 1) must be a a valid heap. Element inEnd - 1 will be inserted into the heap. The heap will be [inBegin, inEnd) after this call.
|
||||||
|
/// inPred is a function that returns true if the first element is less or equal than the second element.
|
||||||
|
/// See: https://en.wikipedia.org/wiki/Binary_heap
|
||||||
|
template <typename Iterator, typename Pred>
|
||||||
|
void BinaryHeapPush(Iterator inBegin, Iterator inEnd, Pred inPred)
|
||||||
|
{
|
||||||
|
using diff_t = typename std::iterator_traits<Iterator>::difference_type;
|
||||||
|
using elem_t = typename std::iterator_traits<Iterator>::value_type;
|
||||||
|
|
||||||
|
// New heap size
|
||||||
|
diff_t count = std::distance(inBegin, inEnd);
|
||||||
|
|
||||||
|
// Start from the last element
|
||||||
|
diff_t current = count - 1;
|
||||||
|
while (current > 0)
|
||||||
|
{
|
||||||
|
// Get current element
|
||||||
|
elem_t ¤t_elem = *(inBegin + current);
|
||||||
|
|
||||||
|
// Get parent element
|
||||||
|
diff_t parent = (current - 1) >> 1;
|
||||||
|
elem_t &parent_elem = *(inBegin + parent);
|
||||||
|
|
||||||
|
// Sort them so that the parent is larger than the child
|
||||||
|
if (inPred(parent_elem, current_elem))
|
||||||
|
{
|
||||||
|
std::swap(parent_elem, current_elem);
|
||||||
|
current = parent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// When there's no change, we're done
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pop an element from a binary max-heap.
|
||||||
|
/// [inBegin, inEnd) must be a valid heap. The largest element will be removed from the heap. The heap will be [inBegin, inEnd - 1) after this call.
|
||||||
|
/// inPred is a function that returns true if the first element is less or equal than the second element.
|
||||||
|
/// See: https://en.wikipedia.org/wiki/Binary_heap
|
||||||
|
template <typename Iterator, typename Pred>
|
||||||
|
void BinaryHeapPop(Iterator inBegin, Iterator inEnd, Pred inPred)
|
||||||
|
{
|
||||||
|
using diff_t = typename std::iterator_traits<Iterator>::difference_type;
|
||||||
|
|
||||||
|
// Begin by moving the highest element to the end, this is the popped element
|
||||||
|
std::swap(*(inEnd - 1), *inBegin);
|
||||||
|
|
||||||
|
// New heap size
|
||||||
|
diff_t count = std::distance(inBegin, inEnd) - 1;
|
||||||
|
|
||||||
|
// Start from the root
|
||||||
|
diff_t largest = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Get first child
|
||||||
|
diff_t child = (largest << 1) + 1;
|
||||||
|
|
||||||
|
// Check if we're beyond the end of the heap, if so the 2nd child is also beyond the end
|
||||||
|
if (child >= count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Remember the largest element from the previous iteration
|
||||||
|
diff_t prev_largest = largest;
|
||||||
|
|
||||||
|
// Check if first child is bigger, if so select it
|
||||||
|
if (inPred(*(inBegin + largest), *(inBegin + child)))
|
||||||
|
largest = child;
|
||||||
|
|
||||||
|
// Switch to the second child
|
||||||
|
++child;
|
||||||
|
|
||||||
|
// Check if second child is bigger, if so select it
|
||||||
|
if (child < count && inPred(*(inBegin + largest), *(inBegin + child)))
|
||||||
|
largest = child;
|
||||||
|
|
||||||
|
// If there was no change, we're done
|
||||||
|
if (prev_largest == largest)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Swap element
|
||||||
|
std::swap(*(inBegin + prev_largest), *(inBegin + largest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
74
thirdparty/JoltPhysics/Jolt/Core/ByteBuffer.h
vendored
Normal file
74
thirdparty/JoltPhysics/Jolt/Core/ByteBuffer.h
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/STLAlignedAllocator.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Underlying data type for ByteBuffer
|
||||||
|
using ByteBufferVector = Array<uint8, STLAlignedAllocator<uint8, JPH_CACHE_LINE_SIZE>>;
|
||||||
|
|
||||||
|
/// Simple byte buffer, aligned to a cache line
|
||||||
|
class ByteBuffer : public ByteBufferVector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Align the size to a multiple of inSize, returns the length after alignment
|
||||||
|
size_t Align(size_t inSize)
|
||||||
|
{
|
||||||
|
// Assert power of 2
|
||||||
|
JPH_ASSERT(IsPowerOf2(inSize));
|
||||||
|
|
||||||
|
// Calculate new size and resize buffer
|
||||||
|
size_t s = AlignUp(size(), inSize);
|
||||||
|
resize(s, 0);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate block of data of inSize elements and return the pointer
|
||||||
|
template <class Type>
|
||||||
|
Type * Allocate(size_t inSize = 1)
|
||||||
|
{
|
||||||
|
// Reserve space
|
||||||
|
size_t s = size();
|
||||||
|
resize(s + inSize * sizeof(Type));
|
||||||
|
|
||||||
|
// Get data pointer
|
||||||
|
Type *data = reinterpret_cast<Type *>(&at(s));
|
||||||
|
|
||||||
|
// Construct elements
|
||||||
|
for (Type *d = data, *d_end = data + inSize; d < d_end; ++d)
|
||||||
|
new (d) Type;
|
||||||
|
|
||||||
|
// Return pointer
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append inData to the buffer
|
||||||
|
template <class Type>
|
||||||
|
void AppendVector(const Array<Type> &inData)
|
||||||
|
{
|
||||||
|
size_t size = inData.size() * sizeof(Type);
|
||||||
|
uint8 *data = Allocate<uint8>(size);
|
||||||
|
memcpy(data, &inData[0], size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get object at inPosition (an offset in bytes)
|
||||||
|
template <class Type>
|
||||||
|
const Type * Get(size_t inPosition) const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<const Type *>(&at(inPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get object at inPosition (an offset in bytes)
|
||||||
|
template <class Type>
|
||||||
|
Type * Get(size_t inPosition)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<Type *>(&at(inPosition));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
38
thirdparty/JoltPhysics/Jolt/Core/Color.cpp
vendored
Normal file
38
thirdparty/JoltPhysics/Jolt/Core/Color.cpp
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#include <Jolt/Core/Color.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Predefined colors
|
||||||
|
const Color Color::sBlack(0, 0, 0);
|
||||||
|
const Color Color::sDarkRed(128, 0, 0);
|
||||||
|
const Color Color::sRed(255, 0, 0);
|
||||||
|
const Color Color::sDarkGreen(0, 128, 0);
|
||||||
|
const Color Color::sGreen(0, 255, 0);
|
||||||
|
const Color Color::sDarkBlue(0, 0, 128);
|
||||||
|
const Color Color::sBlue(0, 0, 255);
|
||||||
|
const Color Color::sYellow(255, 255, 0);
|
||||||
|
const Color Color::sPurple(255, 0, 255);
|
||||||
|
const Color Color::sCyan(0, 255, 255);
|
||||||
|
const Color Color::sOrange(255, 128, 0);
|
||||||
|
const Color Color::sDarkOrange(128, 64, 0);
|
||||||
|
const Color Color::sGrey(128, 128, 128);
|
||||||
|
const Color Color::sLightGrey(192, 192, 192);
|
||||||
|
const Color Color::sWhite(255, 255, 255);
|
||||||
|
|
||||||
|
// Generated by: http://phrogz.net/css/distinct-colors.html (this algo: https://en.wikipedia.org/wiki/Color_difference#CMC_l:c_.281984.29)
|
||||||
|
static constexpr Color sColors[] = { Color(255, 0, 0), Color(204, 143, 102), Color(226, 242, 0), Color(41, 166, 124), Color(0, 170, 255), Color(69, 38, 153), Color(153, 38, 130), Color(229, 57, 80), Color(204, 0, 0), Color(255, 170, 0), Color(85, 128, 0), Color(64, 255, 217), Color(0, 75, 140), Color(161, 115, 230), Color(242, 61, 157), Color(178, 101, 89), Color(140, 94, 0), Color(181, 217, 108), Color(64, 242, 255), Color(77, 117, 153), Color(157, 61, 242), Color(140, 0, 56), Color(127, 57, 32), Color(204, 173, 51), Color(64, 255, 64), Color(38, 145, 153), Color(0, 102, 255), Color(242, 0, 226), Color(153, 77, 107), Color(229, 92, 0), Color(140, 126, 70), Color(0, 179, 71), Color(0, 194, 242), Color(27, 0, 204), Color(230, 115, 222), Color(127, 0, 17) };
|
||||||
|
|
||||||
|
Color Color::sGetDistinctColor(int inIndex)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inIndex >= 0);
|
||||||
|
|
||||||
|
return sColors[inIndex % (sizeof(sColors) / sizeof(uint32))];
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
98
thirdparty/JoltPhysics/Jolt/Core/Color.h
vendored
Normal file
98
thirdparty/JoltPhysics/Jolt/Core/Color.h
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class Color;
|
||||||
|
|
||||||
|
/// Type to use for passing arguments to a function
|
||||||
|
using ColorArg = Color;
|
||||||
|
|
||||||
|
/// Class that holds an RGBA color with 8-bits per component
|
||||||
|
class JPH_EXPORT_GCC_BUG_WORKAROUND [[nodiscard]] Color
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructors
|
||||||
|
Color() = default; ///< Intentionally not initialized for performance reasons
|
||||||
|
Color(const Color &inRHS) = default;
|
||||||
|
Color & operator = (const Color &inRHS) = default;
|
||||||
|
explicit constexpr Color(uint32 inColor) : mU32(inColor) { }
|
||||||
|
constexpr Color(uint8 inRed, uint8 inGreen, uint8 inBlue, uint8 inAlpha = 255) : r(inRed), g(inGreen), b(inBlue), a(inAlpha) { }
|
||||||
|
constexpr Color(ColorArg inRHS, uint8 inAlpha) : r(inRHS.r), g(inRHS.g), b(inRHS.b), a(inAlpha) { }
|
||||||
|
|
||||||
|
/// Comparison
|
||||||
|
inline bool operator == (ColorArg inRHS) const { return mU32 == inRHS.mU32; }
|
||||||
|
inline bool operator != (ColorArg inRHS) const { return mU32 != inRHS.mU32; }
|
||||||
|
|
||||||
|
/// Convert to uint32
|
||||||
|
uint32 GetUInt32() const { return mU32; }
|
||||||
|
|
||||||
|
/// Element access, 0 = red, 1 = green, 2 = blue, 3 = alpha
|
||||||
|
inline uint8 operator () (uint inIdx) const { JPH_ASSERT(inIdx < 4); return (&r)[inIdx]; }
|
||||||
|
inline uint8 & operator () (uint inIdx) { JPH_ASSERT(inIdx < 4); return (&r)[inIdx]; }
|
||||||
|
|
||||||
|
/// Multiply two colors
|
||||||
|
inline Color operator * (const Color &inRHS) const { return Color(uint8((uint32(r) * inRHS.r) >> 8), uint8((uint32(g) * inRHS.g) >> 8), uint8((uint32(b) * inRHS.b) >> 8), uint8((uint32(a) * inRHS.a) >> 8)); }
|
||||||
|
|
||||||
|
/// Multiply color with intensity in the range [0, 1]
|
||||||
|
inline Color operator * (float inIntensity) const { return Color(uint8(r * inIntensity), uint8(g * inIntensity), uint8(b * inIntensity), a); }
|
||||||
|
|
||||||
|
/// Convert to Vec4 with range [0, 1]
|
||||||
|
inline Vec4 ToVec4() const { return Vec4(r, g, b, a) / 255.0f; }
|
||||||
|
|
||||||
|
/// Get grayscale intensity of color
|
||||||
|
inline uint8 GetIntensity() const { return uint8((uint32(r) * 54 + g * 183 + b * 19) >> 8); }
|
||||||
|
|
||||||
|
/// Get a visually distinct color
|
||||||
|
static Color sGetDistinctColor(int inIndex);
|
||||||
|
|
||||||
|
/// Get a color value on the gradient from green through yellow to red
|
||||||
|
/// @param inValue Value in the range [0, 1], 0 = green, 0.5 = yellow, 1 = red
|
||||||
|
static Color sGreenRedGradient(float inValue)
|
||||||
|
{
|
||||||
|
if (inValue < 0.0f)
|
||||||
|
return Color::sGreen;
|
||||||
|
else if (inValue < 0.5f)
|
||||||
|
return Color(uint8(510.0f * inValue), 255, 0);
|
||||||
|
else if (inValue < 1.0f)
|
||||||
|
return Color(255, uint8(510.0f * (1.0f - inValue)), 0);
|
||||||
|
else
|
||||||
|
return Color::sRed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Predefined colors
|
||||||
|
static const Color sBlack;
|
||||||
|
static const Color sDarkRed;
|
||||||
|
static const Color sRed;
|
||||||
|
static const Color sDarkGreen;
|
||||||
|
static const Color sGreen;
|
||||||
|
static const Color sDarkBlue;
|
||||||
|
static const Color sBlue;
|
||||||
|
static const Color sYellow;
|
||||||
|
static const Color sPurple;
|
||||||
|
static const Color sCyan;
|
||||||
|
static const Color sOrange;
|
||||||
|
static const Color sDarkOrange;
|
||||||
|
static const Color sGrey;
|
||||||
|
static const Color sLightGrey;
|
||||||
|
static const Color sWhite;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint32 mU32; ///< Combined value for red, green, blue and alpha
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8 r; ///< Red channel
|
||||||
|
uint8 g; ///< Green channel
|
||||||
|
uint8 b; ///< Blue channel
|
||||||
|
uint8 a; ///< Alpha channel
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(std::is_trivially_default_constructible<Color>() && std::is_trivially_copyable<Color>(), "Is supposed to be a trivial type!");
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
708
thirdparty/JoltPhysics/Jolt/Core/Core.h
vendored
Normal file
708
thirdparty/JoltPhysics/Jolt/Core/Core.h
vendored
Normal file
|
|
@ -0,0 +1,708 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Jolt library version
|
||||||
|
#define JPH_VERSION_MAJOR 5
|
||||||
|
#define JPH_VERSION_MINOR 6
|
||||||
|
#define JPH_VERSION_PATCH 0
|
||||||
|
|
||||||
|
// Determine which features the library was compiled with
|
||||||
|
#ifdef JPH_DOUBLE_PRECISION
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_1 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_1 0
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_CROSS_PLATFORM_DETERMINISTIC
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_2 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_2 0
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_3 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_3 0
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_PROFILE_ENABLED
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_4 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_4 0
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_EXTERNAL_PROFILE
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_5 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_5 0
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_DEBUG_RENDERER
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_6 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_6 0
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_DISABLE_TEMP_ALLOCATOR
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_7 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_7 0
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_DISABLE_CUSTOM_ALLOCATOR
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_8 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_8 0
|
||||||
|
#endif
|
||||||
|
#if defined(JPH_OBJECT_LAYER_BITS) && JPH_OBJECT_LAYER_BITS == 32
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_9 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_9 0
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_ENABLE_ASSERTS
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_10 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_10 0
|
||||||
|
#endif
|
||||||
|
#ifdef JPH_OBJECT_STREAM
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_11 1
|
||||||
|
#else
|
||||||
|
#define JPH_VERSION_FEATURE_BIT_11 0
|
||||||
|
#endif
|
||||||
|
#define JPH_VERSION_FEATURES (uint64(JPH_VERSION_FEATURE_BIT_1) | (JPH_VERSION_FEATURE_BIT_2 << 1) | (JPH_VERSION_FEATURE_BIT_3 << 2) | (JPH_VERSION_FEATURE_BIT_4 << 3) | (JPH_VERSION_FEATURE_BIT_5 << 4) | (JPH_VERSION_FEATURE_BIT_6 << 5) | (JPH_VERSION_FEATURE_BIT_7 << 6) | (JPH_VERSION_FEATURE_BIT_8 << 7) | (JPH_VERSION_FEATURE_BIT_9 << 8) | (JPH_VERSION_FEATURE_BIT_10 << 9) | (JPH_VERSION_FEATURE_BIT_11 << 10))
|
||||||
|
|
||||||
|
// Combine the version and features in a single ID
|
||||||
|
#define JPH_VERSION_ID ((JPH_VERSION_FEATURES << 24) | (JPH_VERSION_MAJOR << 16) | (JPH_VERSION_MINOR << 8) | JPH_VERSION_PATCH)
|
||||||
|
|
||||||
|
// Determine platform
|
||||||
|
#if defined(JPH_PLATFORM_BLUE)
|
||||||
|
// Correct define already defined, this overrides everything else
|
||||||
|
#elif defined(_WIN32) || defined(_WIN64)
|
||||||
|
#include <winapifamily.h>
|
||||||
|
#if WINAPI_FAMILY == WINAPI_FAMILY_APP
|
||||||
|
#define JPH_PLATFORM_WINDOWS_UWP // Building for Universal Windows Platform
|
||||||
|
#endif
|
||||||
|
#define JPH_PLATFORM_WINDOWS
|
||||||
|
#elif defined(__ANDROID__) // Android is linux too, so that's why we check it first
|
||||||
|
#define JPH_PLATFORM_ANDROID
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#define JPH_PLATFORM_LINUX
|
||||||
|
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||||
|
#define JPH_PLATFORM_BSD
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
|
||||||
|
#define JPH_PLATFORM_MACOS
|
||||||
|
#else
|
||||||
|
#define JPH_PLATFORM_IOS
|
||||||
|
#endif
|
||||||
|
#elif defined(__EMSCRIPTEN__)
|
||||||
|
#define JPH_PLATFORM_WASM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Platform helper macros
|
||||||
|
#ifdef JPH_PLATFORM_ANDROID
|
||||||
|
#define JPH_IF_NOT_ANDROID(x)
|
||||||
|
#else
|
||||||
|
#define JPH_IF_NOT_ANDROID(x) x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Determine compiler
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define JPH_COMPILER_CLANG
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define JPH_COMPILER_GCC
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define JPH_COMPILER_MSVC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__MINGW64__) || defined (__MINGW32__)
|
||||||
|
#define JPH_COMPILER_MINGW
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Detect CPU architecture
|
||||||
|
#if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) || defined(_M_ARM64EC)
|
||||||
|
// ARM CPU architecture
|
||||||
|
#define JPH_CPU_ARM
|
||||||
|
#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
|
||||||
|
#define JPH_CPU_ARCH_BITS 64
|
||||||
|
#define JPH_USE_NEON
|
||||||
|
#define JPH_VECTOR_ALIGNMENT 16
|
||||||
|
#define JPH_DVECTOR_ALIGNMENT 32
|
||||||
|
#else
|
||||||
|
#define JPH_CPU_ARCH_BITS 32
|
||||||
|
#define JPH_VECTOR_ALIGNMENT 8 // 32-bit ARM does not support aligning on the stack on 16 byte boundaries
|
||||||
|
#define JPH_DVECTOR_ALIGNMENT 8
|
||||||
|
#endif
|
||||||
|
#ifndef JPH_CROSS_PLATFORM_DETERMINISTIC // FMA is not compatible with cross platform determinism
|
||||||
|
#if defined(__ARM_FEATURE_FMA) && !defined(JPH_USE_FMADD)
|
||||||
|
#define JPH_USE_FMADD
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
|
||||||
|
// X86 CPU architecture
|
||||||
|
#define JPH_CPU_X86
|
||||||
|
#if defined(__x86_64__) || defined(_M_X64)
|
||||||
|
#define JPH_CPU_ARCH_BITS 64
|
||||||
|
#else
|
||||||
|
#define JPH_CPU_ARCH_BITS 32
|
||||||
|
#endif
|
||||||
|
#define JPH_USE_SSE
|
||||||
|
#define JPH_VECTOR_ALIGNMENT 16
|
||||||
|
#define JPH_DVECTOR_ALIGNMENT 32
|
||||||
|
|
||||||
|
// Detect enabled instruction sets
|
||||||
|
#if defined(__AVX512F__) && defined(__AVX512VL__) && defined(__AVX512DQ__) && !defined(JPH_USE_AVX512)
|
||||||
|
#define JPH_USE_AVX512
|
||||||
|
#endif
|
||||||
|
#if (defined(__AVX2__) || defined(JPH_USE_AVX512)) && !defined(JPH_USE_AVX2)
|
||||||
|
#define JPH_USE_AVX2
|
||||||
|
#endif
|
||||||
|
#if (defined(__AVX__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_AVX)
|
||||||
|
#define JPH_USE_AVX
|
||||||
|
#endif
|
||||||
|
#if (defined(__SSE4_2__) || defined(JPH_USE_AVX)) && !defined(JPH_USE_SSE4_2)
|
||||||
|
#define JPH_USE_SSE4_2
|
||||||
|
#endif
|
||||||
|
#if (defined(__SSE4_1__) || defined(JPH_USE_SSE4_2)) && !defined(JPH_USE_SSE4_1)
|
||||||
|
#define JPH_USE_SSE4_1
|
||||||
|
#endif
|
||||||
|
#if (defined(__F16C__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_F16C)
|
||||||
|
#define JPH_USE_F16C
|
||||||
|
#endif
|
||||||
|
#if (defined(__LZCNT__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_LZCNT)
|
||||||
|
#define JPH_USE_LZCNT
|
||||||
|
#endif
|
||||||
|
#if (defined(__BMI__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_TZCNT)
|
||||||
|
#define JPH_USE_TZCNT
|
||||||
|
#endif
|
||||||
|
#ifndef JPH_CROSS_PLATFORM_DETERMINISTIC // FMA is not compatible with cross platform determinism
|
||||||
|
#if defined(JPH_COMPILER_CLANG) || defined(JPH_COMPILER_GCC)
|
||||||
|
#if defined(__FMA__) && !defined(JPH_USE_FMADD)
|
||||||
|
#define JPH_USE_FMADD
|
||||||
|
#endif
|
||||||
|
#elif defined(JPH_COMPILER_MSVC)
|
||||||
|
#if defined(__AVX2__) && !defined(JPH_USE_FMADD) // AVX2 also enables fused multiply add
|
||||||
|
#define JPH_USE_FMADD
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#error Undefined compiler
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#elif defined(__riscv)
|
||||||
|
// RISC-V CPU architecture
|
||||||
|
#define JPH_CPU_RISCV
|
||||||
|
#if __riscv_xlen == 64
|
||||||
|
#define JPH_CPU_ARCH_BITS 64
|
||||||
|
#define JPH_VECTOR_ALIGNMENT 16
|
||||||
|
#define JPH_DVECTOR_ALIGNMENT 32
|
||||||
|
#else
|
||||||
|
#define JPH_CPU_ARCH_BITS 32
|
||||||
|
#define JPH_VECTOR_ALIGNMENT 16
|
||||||
|
#define JPH_DVECTOR_ALIGNMENT 8
|
||||||
|
#endif
|
||||||
|
#if defined(__riscv_vector)
|
||||||
|
#define JPH_USE_RVV
|
||||||
|
#endif
|
||||||
|
#elif defined(JPH_PLATFORM_WASM)
|
||||||
|
// WebAssembly CPU architecture
|
||||||
|
#define JPH_CPU_WASM
|
||||||
|
#if defined(__wasm64__)
|
||||||
|
#define JPH_CPU_ARCH_BITS 64
|
||||||
|
#else
|
||||||
|
#define JPH_CPU_ARCH_BITS 32
|
||||||
|
#endif
|
||||||
|
#define JPH_VECTOR_ALIGNMENT 16
|
||||||
|
#define JPH_DVECTOR_ALIGNMENT 32
|
||||||
|
#ifdef __wasm_simd128__
|
||||||
|
#define JPH_USE_SSE
|
||||||
|
#define JPH_USE_SSE4_1
|
||||||
|
#define JPH_USE_SSE4_2
|
||||||
|
#endif
|
||||||
|
#elif defined(__powerpc__) || defined(__powerpc64__)
|
||||||
|
// PowerPC CPU architecture
|
||||||
|
#define JPH_CPU_PPC
|
||||||
|
#if defined(__powerpc64__)
|
||||||
|
#define JPH_CPU_ARCH_BITS 64
|
||||||
|
#else
|
||||||
|
#define JPH_CPU_ARCH_BITS 32
|
||||||
|
#endif
|
||||||
|
#ifdef _BIG_ENDIAN
|
||||||
|
#define JPH_CPU_BIG_ENDIAN
|
||||||
|
#endif
|
||||||
|
#define JPH_VECTOR_ALIGNMENT 16
|
||||||
|
#define JPH_DVECTOR_ALIGNMENT 8
|
||||||
|
#elif defined(__loongarch__)
|
||||||
|
// LoongArch CPU architecture
|
||||||
|
#define JPH_CPU_LOONGARCH
|
||||||
|
#if defined(__loongarch64)
|
||||||
|
#define JPH_CPU_ARCH_BITS 64
|
||||||
|
#else
|
||||||
|
#define JPH_CPU_ARCH_BITS 32
|
||||||
|
#endif
|
||||||
|
#define JPH_VECTOR_ALIGNMENT 16
|
||||||
|
#define JPH_DVECTOR_ALIGNMENT 8
|
||||||
|
#elif defined(__e2k__)
|
||||||
|
// E2K CPU architecture (MCST Elbrus 2000)
|
||||||
|
#define JPH_CPU_E2K
|
||||||
|
#define JPH_CPU_ARCH_BITS 64
|
||||||
|
#define JPH_VECTOR_ALIGNMENT 16
|
||||||
|
#define JPH_DVECTOR_ALIGNMENT 32
|
||||||
|
|
||||||
|
// Compiler flags on e2k arch determine CPU features
|
||||||
|
#if defined(__SSE__) && !defined(JPH_USE_SSE)
|
||||||
|
#define JPH_USE_SSE
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#error Unsupported CPU architecture
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// If this define is set, Jolt is compiled as a shared library
|
||||||
|
#ifdef JPH_SHARED_LIBRARY
|
||||||
|
#ifdef JPH_BUILD_SHARED_LIBRARY
|
||||||
|
// While building the shared library, we must export these symbols
|
||||||
|
#if defined(JPH_PLATFORM_WINDOWS) && !defined(JPH_COMPILER_MINGW)
|
||||||
|
#define JPH_EXPORT __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define JPH_EXPORT __attribute__ ((visibility ("default")))
|
||||||
|
#if defined(JPH_COMPILER_GCC)
|
||||||
|
// Prevents an issue with GCC attribute parsing (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69585)
|
||||||
|
#define JPH_EXPORT_GCC_BUG_WORKAROUND [[gnu::visibility("default")]]
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
// When linking against Jolt, we must import these symbols
|
||||||
|
#if defined(JPH_PLATFORM_WINDOWS) && !defined(JPH_COMPILER_MINGW)
|
||||||
|
#define JPH_EXPORT __declspec(dllimport)
|
||||||
|
#else
|
||||||
|
#define JPH_EXPORT __attribute__ ((visibility ("default")))
|
||||||
|
#if defined(JPH_COMPILER_GCC)
|
||||||
|
// Prevents an issue with GCC attribute parsing (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69585)
|
||||||
|
#define JPH_EXPORT_GCC_BUG_WORKAROUND [[gnu::visibility("default")]]
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define JPH_IF_SHARED_LIBRARY(x) x
|
||||||
|
#define JPH_IF_NOT_SHARED_LIBRARY(x)
|
||||||
|
#else
|
||||||
|
// If the define is not set, we use static linking and symbols don't need to be imported or exported
|
||||||
|
#define JPH_EXPORT
|
||||||
|
|
||||||
|
#define JPH_IF_SHARED_LIBRARY(x)
|
||||||
|
#define JPH_IF_NOT_SHARED_LIBRARY(x) x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef JPH_EXPORT_GCC_BUG_WORKAROUND
|
||||||
|
#define JPH_EXPORT_GCC_BUG_WORKAROUND JPH_EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Macro used by the RTTI macros to not export a function
|
||||||
|
#define JPH_NO_EXPORT
|
||||||
|
|
||||||
|
// Pragmas to store / restore the warning state and to disable individual warnings
|
||||||
|
#ifdef JPH_COMPILER_CLANG
|
||||||
|
#define JPH_PRAGMA(x) _Pragma(#x)
|
||||||
|
#define JPH_SUPPRESS_WARNING_PUSH JPH_PRAGMA(clang diagnostic push)
|
||||||
|
#define JPH_SUPPRESS_WARNING_POP JPH_PRAGMA(clang diagnostic pop)
|
||||||
|
#define JPH_CLANG_SUPPRESS_WARNING(w) JPH_PRAGMA(clang diagnostic ignored w)
|
||||||
|
#if __has_warning("-Wdeprecated-copy")
|
||||||
|
#define JPH_CLANG_SUPPRESS_DEPRECATED_COPY_WARNING JPH_CLANG_SUPPRESS_WARNING("-Wdeprecated-copy")
|
||||||
|
#endif
|
||||||
|
#if __has_warning("-Wdeprecated-copy-with-dtor")
|
||||||
|
#define JPH_CLANG_SUPPRESS_DEPRECATED_COPY_WITH_DTOR_WARNING JPH_CLANG_SUPPRESS_WARNING("-Wdeprecated-copy-with-dtor")
|
||||||
|
#endif
|
||||||
|
#if __has_warning("-Wunsafe-buffer-usage")
|
||||||
|
#define JPH_CLANG_SUPPRESS_UNSAFE_BUFFER_USAGE_WARNING JPH_CLANG_SUPPRESS_WARNING("-Wunsafe-buffer-usage")
|
||||||
|
#endif
|
||||||
|
#if __has_warning("-Wimplicit-int-float-conversion")
|
||||||
|
#define JPH_CLANG_SUPPRESS_IMPLICIT_INT_FLOAT_CONVERSION_WARNING JPH_CLANG_SUPPRESS_WARNING("-Wimplicit-int-float-conversion")
|
||||||
|
#endif
|
||||||
|
#if __has_warning("-Wunique-object-duplication")
|
||||||
|
#define JPH_CLANG_SUPPRESS_UNIQUE_OBJECT_DUPLICATION_WARNING JPH_CLANG_SUPPRESS_WARNING("-Wunique-object-duplication")
|
||||||
|
#endif
|
||||||
|
#if __has_warning("-Wnrvo")
|
||||||
|
#define JPH_CLANG_SUPPRESS_NRVO_WARNING JPH_CLANG_SUPPRESS_WARNING("-Wnrvo")
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define JPH_CLANG_SUPPRESS_WARNING(w)
|
||||||
|
#endif
|
||||||
|
#ifndef JPH_CLANG_SUPPRESS_DEPRECATED_COPY_WARNING
|
||||||
|
#define JPH_CLANG_SUPPRESS_DEPRECATED_COPY_WARNING
|
||||||
|
#endif
|
||||||
|
#ifndef JPH_CLANG_SUPPRESS_DEPRECATED_COPY_WITH_DTOR_WARNING
|
||||||
|
#define JPH_CLANG_SUPPRESS_DEPRECATED_COPY_WITH_DTOR_WARNING
|
||||||
|
#endif
|
||||||
|
#ifndef JPH_CLANG_SUPPRESS_UNSAFE_BUFFER_USAGE_WARNING
|
||||||
|
#define JPH_CLANG_SUPPRESS_UNSAFE_BUFFER_USAGE_WARNING
|
||||||
|
#endif
|
||||||
|
#ifndef JPH_CLANG_SUPPRESS_IMPLICIT_INT_FLOAT_CONVERSION_WARNING
|
||||||
|
#define JPH_CLANG_SUPPRESS_IMPLICIT_INT_FLOAT_CONVERSION_WARNING
|
||||||
|
#endif
|
||||||
|
#ifndef JPH_CLANG_SUPPRESS_UNIQUE_OBJECT_DUPLICATION_WARNING
|
||||||
|
#define JPH_CLANG_SUPPRESS_UNIQUE_OBJECT_DUPLICATION_WARNING
|
||||||
|
#endif
|
||||||
|
#ifndef JPH_CLANG_SUPPRESS_NRVO_WARNING
|
||||||
|
#define JPH_CLANG_SUPPRESS_NRVO_WARNING
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef JPH_COMPILER_GCC
|
||||||
|
#define JPH_PRAGMA(x) _Pragma(#x)
|
||||||
|
#define JPH_SUPPRESS_WARNING_PUSH JPH_PRAGMA(GCC diagnostic push)
|
||||||
|
#define JPH_SUPPRESS_WARNING_POP JPH_PRAGMA(GCC diagnostic pop)
|
||||||
|
#define JPH_GCC_SUPPRESS_WARNING(w) JPH_PRAGMA(GCC diagnostic ignored w)
|
||||||
|
#else
|
||||||
|
#define JPH_GCC_SUPPRESS_WARNING(w)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef JPH_COMPILER_MSVC
|
||||||
|
#define JPH_PRAGMA(x) __pragma(x)
|
||||||
|
#define JPH_SUPPRESS_WARNING_PUSH JPH_PRAGMA(warning (push))
|
||||||
|
#define JPH_SUPPRESS_WARNING_POP JPH_PRAGMA(warning (pop))
|
||||||
|
#define JPH_MSVC_SUPPRESS_WARNING(w) JPH_PRAGMA(warning (disable : w))
|
||||||
|
#if _MSC_VER >= 1920 && _MSC_VER < 1930
|
||||||
|
#define JPH_MSVC2019_SUPPRESS_WARNING(w) JPH_MSVC_SUPPRESS_WARNING(w)
|
||||||
|
#else
|
||||||
|
#define JPH_MSVC2019_SUPPRESS_WARNING(w)
|
||||||
|
#endif
|
||||||
|
#if _MSC_VER >= 1950
|
||||||
|
#define JPH_MSVC2026_PLUS_SUPPRESS_WARNING(w) JPH_MSVC_SUPPRESS_WARNING(w)
|
||||||
|
#else
|
||||||
|
#define JPH_MSVC2026_PLUS_SUPPRESS_WARNING(w)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define JPH_MSVC_SUPPRESS_WARNING(w)
|
||||||
|
#define JPH_MSVC2019_SUPPRESS_WARNING(w)
|
||||||
|
#define JPH_MSVC2026_PLUS_SUPPRESS_WARNING(w)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Disable common warnings triggered by Jolt when compiling with -Wall
|
||||||
|
#define JPH_SUPPRESS_WARNINGS \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wfloat-equal") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wsign-conversion") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wold-style-cast") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wgnu-anonymous-struct") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wnested-anon-types") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wlanguage-extension-token") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wunused-parameter") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wformat-nonliteral") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wcast-align") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Winvalid-offsetof") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wgnu-zero-variadic-macro-arguments") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wdocumentation-unknown-command") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wctad-maybe-unsupported") \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wswitch-default") \
|
||||||
|
JPH_CLANG_SUPPRESS_DEPRECATED_COPY_WARNING \
|
||||||
|
JPH_CLANG_SUPPRESS_DEPRECATED_COPY_WITH_DTOR_WARNING \
|
||||||
|
JPH_CLANG_SUPPRESS_UNSAFE_BUFFER_USAGE_WARNING \
|
||||||
|
JPH_CLANG_SUPPRESS_IMPLICIT_INT_FLOAT_CONVERSION_WARNING \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wpadded") \
|
||||||
|
JPH_IF_NOT_SHARED_LIBRARY(JPH_CLANG_SUPPRESS_UNIQUE_OBJECT_DUPLICATION_WARNING) \
|
||||||
|
JPH_CLANG_SUPPRESS_NRVO_WARNING \
|
||||||
|
\
|
||||||
|
JPH_GCC_SUPPRESS_WARNING("-Wcomment") \
|
||||||
|
JPH_GCC_SUPPRESS_WARNING("-Winvalid-offsetof") \
|
||||||
|
JPH_GCC_SUPPRESS_WARNING("-Wclass-memaccess") \
|
||||||
|
JPH_GCC_SUPPRESS_WARNING("-Wpedantic") \
|
||||||
|
JPH_GCC_SUPPRESS_WARNING("-Wunused-parameter") \
|
||||||
|
JPH_GCC_SUPPRESS_WARNING("-Wmaybe-uninitialized") \
|
||||||
|
\
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4619) /* #pragma warning: there is no warning number 'XXXX' */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4514) /* 'X' : unreferenced inline function has been removed */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4710) /* 'X' : function not inlined */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4711) /* function 'X' selected for automatic inline expansion */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4714) /* function 'X' marked as __forceinline not inlined */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4820) /* 'X': 'Y' bytes padding added after data member 'Z' */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4100) /* 'X' : unreferenced formal parameter */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4626) /* 'X' : assignment operator was implicitly defined as deleted because a base class assignment operator is inaccessible or deleted */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5027) /* 'X' : move assignment operator was implicitly defined as deleted because a base class move assignment operator is inaccessible or deleted */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4365) /* 'argument' : conversion from 'X' to 'Y', signed / unsigned mismatch */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4324) /* 'X' : structure was padded due to alignment specifier */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4625) /* 'X' : copy constructor was implicitly defined as deleted because a base class copy constructor is inaccessible or deleted */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5026) /* 'X': move constructor was implicitly defined as deleted because a base class move constructor is inaccessible or deleted */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4623) /* 'X' : default constructor was implicitly defined as deleted */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4201) /* nonstandard extension used: nameless struct/union */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4371) /* 'X': layout of class may have changed from a previous version of the compiler due to better packing of member 'Y' */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5045) /* Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4583) /* 'X': destructor is not implicitly called */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4582) /* 'X': constructor is not implicitly called */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5219) /* implicit conversion from 'X' to 'Y', possible loss of data */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4826) /* Conversion from 'X *' to 'JPH::uint64' is sign-extended. This may cause unexpected runtime behavior. (32-bit) */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5264) /* 'X': 'const' variable is not used */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4251) /* class 'X' needs to have DLL-interface to be used by clients of class 'Y' */ \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4738) /* storing 32-bit float result in memory, possible loss of performance */ \
|
||||||
|
JPH_MSVC2019_SUPPRESS_WARNING(5246) /* the initialization of a subobject should be wrapped in braces */ \
|
||||||
|
JPH_MSVC2026_PLUS_SUPPRESS_WARNING(5291) /* 'X': deriving from the base class 'Y' can cause potential runtime issues due to an ABI bug. Recommend adding a 4-byte data member to the base class for the padding at the end of it to work around this bug. */
|
||||||
|
|
||||||
|
// OS-specific includes
|
||||||
|
#if defined(JPH_PLATFORM_WINDOWS)
|
||||||
|
#define JPH_BREAKPOINT __debugbreak()
|
||||||
|
#elif defined(JPH_PLATFORM_BLUE)
|
||||||
|
// Configuration for a popular game console.
|
||||||
|
// This file is not distributed because it would violate an NDA.
|
||||||
|
// Creating one should only be a couple of minutes of work if you have the documentation for the platform
|
||||||
|
// (you only need to define JPH_BREAKPOINT, JPH_PLATFORM_BLUE_GET_TICKS, JPH_PLATFORM_BLUE_MUTEX*, JPH_PLATFORM_BLUE_RWLOCK*, JPH_PLATFORM_BLUE_SEMAPHORE* and include the right header).
|
||||||
|
#include <Jolt/Core/PlatformBlue.h>
|
||||||
|
#elif defined(JPH_PLATFORM_LINUX) || defined(JPH_PLATFORM_ANDROID) || defined(JPH_PLATFORM_MACOS) || defined(JPH_PLATFORM_IOS) || defined(JPH_PLATFORM_BSD)
|
||||||
|
#if defined(JPH_CPU_X86)
|
||||||
|
#define JPH_BREAKPOINT __asm volatile ("int $0x3")
|
||||||
|
#elif defined(JPH_CPU_ARM) || defined(JPH_CPU_RISCV) || defined(JPH_CPU_E2K) || defined(JPH_CPU_PPC) || defined(JPH_CPU_LOONGARCH)
|
||||||
|
#define JPH_BREAKPOINT __builtin_trap()
|
||||||
|
#else
|
||||||
|
#error Unknown CPU architecture
|
||||||
|
#endif
|
||||||
|
#elif defined(JPH_PLATFORM_WASM)
|
||||||
|
#define JPH_BREAKPOINT do { } while (false) // Not supported
|
||||||
|
#else
|
||||||
|
#error Unknown platform
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Begin the JPH namespace
|
||||||
|
#define JPH_NAMESPACE_BEGIN \
|
||||||
|
JPH_SUPPRESS_WARNING_PUSH \
|
||||||
|
JPH_SUPPRESS_WARNINGS \
|
||||||
|
namespace JPH {
|
||||||
|
|
||||||
|
// End the JPH namespace
|
||||||
|
#define JPH_NAMESPACE_END \
|
||||||
|
} \
|
||||||
|
JPH_SUPPRESS_WARNING_POP
|
||||||
|
|
||||||
|
// Suppress warnings generated by the standard template library
|
||||||
|
#define JPH_SUPPRESS_WARNINGS_STD_BEGIN \
|
||||||
|
JPH_SUPPRESS_WARNING_PUSH \
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wpadded") \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4365) \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4619) \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4710) \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4711) \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4820) \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4514) \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5262) \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5264) \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(4738) \
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5045)
|
||||||
|
|
||||||
|
#define JPH_SUPPRESS_WARNINGS_STD_END \
|
||||||
|
JPH_SUPPRESS_WARNING_POP
|
||||||
|
|
||||||
|
// MSVC STL requires _HAS_EXCEPTIONS=0 if exceptions are turned off
|
||||||
|
#if defined(JPH_COMPILER_MSVC) && (!defined(__cpp_exceptions) || !__cpp_exceptions) && !defined(_HAS_EXCEPTIONS)
|
||||||
|
#define _HAS_EXCEPTIONS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Standard C++ includes
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||||
|
#include <float.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <new>
|
||||||
|
#include <utility>
|
||||||
|
#include <cmath>
|
||||||
|
#include <sstream>
|
||||||
|
#include <functional>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
#if defined(JPH_COMPILER_MSVC) || (defined(JPH_COMPILER_CLANG) && defined(_MSC_VER)) // MSVC or clang-cl
|
||||||
|
#include <malloc.h> // for alloca
|
||||||
|
#endif
|
||||||
|
#if defined(JPH_USE_SSE)
|
||||||
|
#include <immintrin.h>
|
||||||
|
#elif defined(JPH_USE_NEON)
|
||||||
|
#ifdef JPH_COMPILER_MSVC
|
||||||
|
#include <intrin.h>
|
||||||
|
#include <arm64_neon.h>
|
||||||
|
#else
|
||||||
|
#include <arm_neon.h>
|
||||||
|
#endif
|
||||||
|
#elif defined(JPH_USE_RVV)
|
||||||
|
#include <riscv_vector.h>
|
||||||
|
#endif
|
||||||
|
JPH_SUPPRESS_WARNINGS_STD_END
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Commonly used STL types
|
||||||
|
using std::min;
|
||||||
|
using std::max;
|
||||||
|
using std::abs;
|
||||||
|
using std::ceil;
|
||||||
|
using std::floor;
|
||||||
|
using std::trunc;
|
||||||
|
using std::round;
|
||||||
|
using std::fmod;
|
||||||
|
using std::string_view;
|
||||||
|
using std::function;
|
||||||
|
using std::numeric_limits;
|
||||||
|
using std::isfinite;
|
||||||
|
using std::isnan;
|
||||||
|
using std::ostream;
|
||||||
|
using std::istream;
|
||||||
|
|
||||||
|
// Standard types
|
||||||
|
using uint = unsigned int;
|
||||||
|
using uint8 = std::uint8_t;
|
||||||
|
using uint16 = std::uint16_t;
|
||||||
|
using uint32 = std::uint32_t;
|
||||||
|
using int32 = std::int32_t;
|
||||||
|
using uint64 = std::uint64_t;
|
||||||
|
using int64 = std::int64_t;
|
||||||
|
|
||||||
|
// Assert sizes of types
|
||||||
|
static_assert(sizeof(uint) >= 4, "Invalid size of uint");
|
||||||
|
static_assert(sizeof(uint8) == 1, "Invalid size of uint8");
|
||||||
|
static_assert(sizeof(uint16) == 2, "Invalid size of uint16");
|
||||||
|
static_assert(sizeof(uint32) == 4, "Invalid size of uint32");
|
||||||
|
static_assert(sizeof(uint64) == 8, "Invalid size of uint64");
|
||||||
|
|
||||||
|
// Determine if we want extra debugging code to be active
|
||||||
|
#if !defined(NDEBUG) && !defined(JPH_NO_DEBUG)
|
||||||
|
#define JPH_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Define inline macro
|
||||||
|
#if defined(JPH_NO_FORCE_INLINE)
|
||||||
|
#define JPH_INLINE inline
|
||||||
|
#elif defined(JPH_COMPILER_CLANG)
|
||||||
|
#define JPH_INLINE __inline__ __attribute__((always_inline))
|
||||||
|
#elif defined(JPH_COMPILER_GCC)
|
||||||
|
// On gcc 14 using always_inline in debug mode causes error: "inlining failed in call to 'always_inline' 'XXX': function not considered for inlining"
|
||||||
|
// See: https://github.com/jrouwe/JoltPhysics/issues/1096
|
||||||
|
#if __GNUC__ >= 14 && defined(JPH_DEBUG)
|
||||||
|
#define JPH_INLINE inline
|
||||||
|
#else
|
||||||
|
#define JPH_INLINE __inline__ __attribute__((always_inline))
|
||||||
|
#endif
|
||||||
|
#elif defined(JPH_COMPILER_MSVC)
|
||||||
|
#define JPH_INLINE __forceinline
|
||||||
|
#else
|
||||||
|
#error Undefined
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Default memory allocation alignment.
|
||||||
|
// This define can be overridden in case the user provides an Allocate function that has a different alignment than the platform default.
|
||||||
|
#ifndef JPH_DEFAULT_ALLOCATE_ALIGNMENT
|
||||||
|
#define JPH_DEFAULT_ALLOCATE_ALIGNMENT __STDCPP_DEFAULT_NEW_ALIGNMENT__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Cache line size (used for aligning to cache line)
|
||||||
|
#ifndef JPH_CACHE_LINE_SIZE
|
||||||
|
#define JPH_CACHE_LINE_SIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Define macro to get current function name
|
||||||
|
#if defined(JPH_COMPILER_CLANG) || defined(JPH_COMPILER_GCC)
|
||||||
|
#define JPH_FUNCTION_NAME __PRETTY_FUNCTION__
|
||||||
|
#elif defined(JPH_COMPILER_MSVC)
|
||||||
|
#define JPH_FUNCTION_NAME __FUNCTION__
|
||||||
|
#else
|
||||||
|
#error Undefined
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Stack allocation
|
||||||
|
#define JPH_STACK_ALLOC(n) alloca(n)
|
||||||
|
|
||||||
|
// Shorthand for #ifdef JPH_DEBUG / #endif
|
||||||
|
#ifdef JPH_DEBUG
|
||||||
|
#define JPH_IF_DEBUG(...) __VA_ARGS__
|
||||||
|
#define JPH_IF_NOT_DEBUG(...)
|
||||||
|
#else
|
||||||
|
#define JPH_IF_DEBUG(...)
|
||||||
|
#define JPH_IF_NOT_DEBUG(...) __VA_ARGS__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Shorthand for #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED / #endif
|
||||||
|
#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED
|
||||||
|
#define JPH_IF_FLOATING_POINT_EXCEPTIONS_ENABLED(...) __VA_ARGS__
|
||||||
|
#else
|
||||||
|
#define JPH_IF_FLOATING_POINT_EXCEPTIONS_ENABLED(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Helper macros to detect if we're running in single or double precision mode
|
||||||
|
#ifdef JPH_DOUBLE_PRECISION
|
||||||
|
#define JPH_IF_SINGLE_PRECISION(...)
|
||||||
|
#define JPH_IF_SINGLE_PRECISION_ELSE(s, d) d
|
||||||
|
#define JPH_IF_DOUBLE_PRECISION(...) __VA_ARGS__
|
||||||
|
#else
|
||||||
|
#define JPH_IF_SINGLE_PRECISION(...) __VA_ARGS__
|
||||||
|
#define JPH_IF_SINGLE_PRECISION_ELSE(s, d) s
|
||||||
|
#define JPH_IF_DOUBLE_PRECISION(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Helper macro to detect if the debug renderer is active
|
||||||
|
#ifdef JPH_DEBUG_RENDERER
|
||||||
|
#define JPH_IF_DEBUG_RENDERER(...) __VA_ARGS__
|
||||||
|
#define JPH_IF_NOT_DEBUG_RENDERER(...)
|
||||||
|
#else
|
||||||
|
#define JPH_IF_DEBUG_RENDERER(...)
|
||||||
|
#define JPH_IF_NOT_DEBUG_RENDERER(...) __VA_ARGS__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Macro to indicate that a parameter / variable is unused
|
||||||
|
#define JPH_UNUSED(x) (void)x
|
||||||
|
|
||||||
|
// Macro to enable floating point precise mode
|
||||||
|
#if defined(JPH_COMPILER_CLANG)
|
||||||
|
#define JPH_PRECISE_MATH_ON \
|
||||||
|
_Pragma("clang diagnostic push") \
|
||||||
|
_Pragma("clang diagnostic ignored \"-Wignored-pragmas\"") \
|
||||||
|
_Pragma("float_control(precise, on, push)") \
|
||||||
|
_Pragma("clang diagnostic pop")
|
||||||
|
#define JPH_PRECISE_MATH_OFF \
|
||||||
|
_Pragma("clang diagnostic push") \
|
||||||
|
_Pragma("clang diagnostic ignored \"-Wignored-pragmas\"") \
|
||||||
|
_Pragma("float_control(pop)") \
|
||||||
|
_Pragma("clang diagnostic pop")
|
||||||
|
#elif defined(JPH_COMPILER_MSVC)
|
||||||
|
#define JPH_PRECISE_MATH_ON \
|
||||||
|
__pragma(float_control(precise, on, push))
|
||||||
|
#define JPH_PRECISE_MATH_OFF \
|
||||||
|
__pragma(float_control(pop))
|
||||||
|
#else
|
||||||
|
#define JPH_PRECISE_MATH_ON
|
||||||
|
#define JPH_PRECISE_MATH_OFF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check if Thread Sanitizer is enabled
|
||||||
|
#ifdef __has_feature
|
||||||
|
#if __has_feature(thread_sanitizer)
|
||||||
|
#define JPH_TSAN_ENABLED
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifdef __SANITIZE_THREAD__
|
||||||
|
#define JPH_TSAN_ENABLED
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Attribute to disable Thread Sanitizer for a particular function
|
||||||
|
#ifdef JPH_TSAN_ENABLED
|
||||||
|
#define JPH_TSAN_NO_SANITIZE __attribute__((no_sanitize("thread")))
|
||||||
|
#else
|
||||||
|
#define JPH_TSAN_NO_SANITIZE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check if Address Sanitizer is enabled
|
||||||
|
#ifdef __has_feature
|
||||||
|
#if __has_feature(address_sanitizer)
|
||||||
|
#define JPH_ASAN_ENABLED
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
|
#define JPH_ASAN_ENABLED
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// DirectX 12 is only supported on Windows
|
||||||
|
#if defined(JPH_USE_DX12) && !defined(JPH_PLATFORM_WINDOWS)
|
||||||
|
#undef JPH_USE_DX12
|
||||||
|
#endif // JPH_PLATFORM_WINDOWS
|
||||||
|
|
||||||
|
// Metal is only supported on Apple platforms
|
||||||
|
#if defined(JPH_USE_MTL) && !defined(JPH_PLATFORM_MACOS) && !defined(JPH_PLATFORM_IOS)
|
||||||
|
#undef JPH_USE_MTL
|
||||||
|
#endif // !JPH_PLATFORM_MACOS && !JPH_PLATFORM_IOS
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
143
thirdparty/JoltPhysics/Jolt/Core/FPControlWord.h
vendored
Normal file
143
thirdparty/JoltPhysics/Jolt/Core/FPControlWord.h
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/NonCopyable.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#if defined(JPH_CPU_WASM)
|
||||||
|
|
||||||
|
// Not supported
|
||||||
|
|
||||||
|
#elif defined(JPH_USE_SSE)
|
||||||
|
|
||||||
|
/// Helper class that needs to be put on the stack to update the state of the floating point control word.
|
||||||
|
/// This state is kept per thread.
|
||||||
|
template <uint Value, uint Mask>
|
||||||
|
class FPControlWord : public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FPControlWord()
|
||||||
|
{
|
||||||
|
mPrevState = _mm_getcsr();
|
||||||
|
_mm_setcsr((mPrevState & ~Mask) | Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
~FPControlWord()
|
||||||
|
{
|
||||||
|
_mm_setcsr((_mm_getcsr() & ~Mask) | (mPrevState & Mask));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint mPrevState;
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_ARM) && defined(JPH_COMPILER_MSVC)
|
||||||
|
|
||||||
|
/// Helper class that needs to be put on the stack to update the state of the floating point control word.
|
||||||
|
/// This state is kept per thread.
|
||||||
|
template <unsigned int Value, unsigned int Mask>
|
||||||
|
class FPControlWord : public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FPControlWord()
|
||||||
|
{
|
||||||
|
// Read state before change
|
||||||
|
_controlfp_s(&mPrevState, 0, 0);
|
||||||
|
|
||||||
|
// Update the state
|
||||||
|
unsigned int dummy;
|
||||||
|
_controlfp_s(&dummy, Value, Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
~FPControlWord()
|
||||||
|
{
|
||||||
|
// Restore state
|
||||||
|
unsigned int dummy;
|
||||||
|
_controlfp_s(&dummy, mPrevState, Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int mPrevState;
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_ARM) && defined(JPH_USE_NEON)
|
||||||
|
|
||||||
|
/// Helper class that needs to be put on the stack to update the state of the floating point control word.
|
||||||
|
/// This state is kept per thread.
|
||||||
|
template <uint64 Value, uint64 Mask>
|
||||||
|
class FPControlWord : public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FPControlWord()
|
||||||
|
{
|
||||||
|
uint64 val;
|
||||||
|
asm volatile("mrs %0, fpcr" : "=r" (val));
|
||||||
|
mPrevState = val;
|
||||||
|
val &= ~Mask;
|
||||||
|
val |= Value;
|
||||||
|
asm volatile("msr fpcr, %0" : /* no output */ : "r" (val));
|
||||||
|
}
|
||||||
|
|
||||||
|
~FPControlWord()
|
||||||
|
{
|
||||||
|
uint64 val;
|
||||||
|
asm volatile("mrs %0, fpcr" : "=r" (val));
|
||||||
|
val &= ~Mask;
|
||||||
|
val |= mPrevState & Mask;
|
||||||
|
asm volatile("msr fpcr, %0" : /* no output */ : "r" (val));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64 mPrevState;
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_ARM)
|
||||||
|
|
||||||
|
/// Helper class that needs to be put on the stack to update the state of the floating point control word.
|
||||||
|
/// This state is kept per thread.
|
||||||
|
template <uint32 Value, uint32 Mask>
|
||||||
|
class FPControlWord : public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FPControlWord()
|
||||||
|
{
|
||||||
|
uint32 val;
|
||||||
|
asm volatile("vmrs %0, fpscr" : "=r" (val));
|
||||||
|
mPrevState = val;
|
||||||
|
val &= ~Mask;
|
||||||
|
val |= Value;
|
||||||
|
asm volatile("vmsr fpscr, %0" : /* no output */ : "r" (val));
|
||||||
|
}
|
||||||
|
|
||||||
|
~FPControlWord()
|
||||||
|
{
|
||||||
|
uint32 val;
|
||||||
|
asm volatile("vmrs %0, fpscr" : "=r" (val));
|
||||||
|
val &= ~Mask;
|
||||||
|
val |= mPrevState & Mask;
|
||||||
|
asm volatile("vmsr fpscr, %0" : /* no output */ : "r" (val));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32 mPrevState;
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_RISCV)
|
||||||
|
|
||||||
|
// RISC-V only implements manually checking if exceptions occurred by reading the fcsr register. It doesn't generate exceptions.
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_PPC) || defined(JPH_CPU_LOONGARCH)
|
||||||
|
|
||||||
|
// Not implemented right now
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error Unsupported CPU architecture
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
96
thirdparty/JoltPhysics/Jolt/Core/FPException.h
vendored
Normal file
96
thirdparty/JoltPhysics/Jolt/Core/FPException.h
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/FPControlWord.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED
|
||||||
|
|
||||||
|
#if defined(JPH_CPU_WASM)
|
||||||
|
|
||||||
|
// Not supported
|
||||||
|
class FPExceptionsEnable { };
|
||||||
|
class FPExceptionDisableInvalid { };
|
||||||
|
class FPExceptionDisableDivByZero { };
|
||||||
|
class FPExceptionDisableOverflow { };
|
||||||
|
|
||||||
|
#elif defined(JPH_USE_SSE)
|
||||||
|
|
||||||
|
/// Enable floating point divide by zero exception, overflow exceptions and exceptions on invalid numbers
|
||||||
|
class FPExceptionsEnable : public FPControlWord<0, _MM_MASK_DIV_ZERO | _MM_MASK_INVALID | _MM_MASK_OVERFLOW> { };
|
||||||
|
|
||||||
|
/// Disable invalid floating point value exceptions
|
||||||
|
class FPExceptionDisableInvalid : public FPControlWord<_MM_MASK_INVALID, _MM_MASK_INVALID> { };
|
||||||
|
|
||||||
|
/// Disable division by zero floating point exceptions
|
||||||
|
class FPExceptionDisableDivByZero : public FPControlWord<_MM_MASK_DIV_ZERO, _MM_MASK_DIV_ZERO> { };
|
||||||
|
|
||||||
|
/// Disable floating point overflow exceptions
|
||||||
|
class FPExceptionDisableOverflow : public FPControlWord<_MM_MASK_OVERFLOW, _MM_MASK_OVERFLOW> { };
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_ARM) && defined(JPH_COMPILER_MSVC)
|
||||||
|
|
||||||
|
/// Enable floating point divide by zero exception, overflow exceptions and exceptions on invalid numbers
|
||||||
|
class FPExceptionsEnable : public FPControlWord<0, _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW> { };
|
||||||
|
|
||||||
|
/// Disable invalid floating point value exceptions
|
||||||
|
class FPExceptionDisableInvalid : public FPControlWord<_EM_INVALID, _EM_INVALID> { };
|
||||||
|
|
||||||
|
/// Disable division by zero floating point exceptions
|
||||||
|
class FPExceptionDisableDivByZero : public FPControlWord<_EM_ZERODIVIDE, _EM_ZERODIVIDE> { };
|
||||||
|
|
||||||
|
/// Disable floating point overflow exceptions
|
||||||
|
class FPExceptionDisableOverflow : public FPControlWord<_EM_OVERFLOW, _EM_OVERFLOW> { };
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_ARM)
|
||||||
|
|
||||||
|
/// Invalid operation exception bit
|
||||||
|
static constexpr uint64 FP_IOE = 1 << 8;
|
||||||
|
|
||||||
|
/// Enable divide by zero exception bit
|
||||||
|
static constexpr uint64 FP_DZE = 1 << 9;
|
||||||
|
|
||||||
|
/// Enable floating point overflow bit
|
||||||
|
static constexpr uint64 FP_OFE = 1 << 10;
|
||||||
|
|
||||||
|
/// Enable floating point divide by zero exception, overflow exceptions and exceptions on invalid numbers
|
||||||
|
class FPExceptionsEnable : public FPControlWord<FP_IOE | FP_DZE | FP_OFE, FP_IOE | FP_DZE | FP_OFE> { };
|
||||||
|
|
||||||
|
/// Disable invalid floating point value exceptions
|
||||||
|
class FPExceptionDisableInvalid : public FPControlWord<0, FP_IOE> { };
|
||||||
|
|
||||||
|
/// Disable division by zero floating point exceptions
|
||||||
|
class FPExceptionDisableDivByZero : public FPControlWord<0, FP_DZE> { };
|
||||||
|
|
||||||
|
/// Disable floating point overflow exceptions
|
||||||
|
class FPExceptionDisableOverflow : public FPControlWord<0, FP_OFE> { };
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_RISCV)
|
||||||
|
|
||||||
|
#error "RISC-V only implements manually checking if exceptions occurred by reading the fcsr register. It doesn't generate exceptions. JPH_FLOATING_POINT_EXCEPTIONS_ENABLED must be disabled."
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_PPC)
|
||||||
|
|
||||||
|
#error PowerPC floating point exception handling to be implemented. JPH_FLOATING_POINT_EXCEPTIONS_ENABLED must be disabled.
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error Unsupported CPU architecture
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/// Dummy implementations
|
||||||
|
class FPExceptionsEnable { };
|
||||||
|
class FPExceptionDisableInvalid { };
|
||||||
|
class FPExceptionDisableDivByZero { };
|
||||||
|
class FPExceptionDisableOverflow { };
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
43
thirdparty/JoltPhysics/Jolt/Core/FPFlushDenormals.h
vendored
Normal file
43
thirdparty/JoltPhysics/Jolt/Core/FPFlushDenormals.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/FPControlWord.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#if defined(JPH_CPU_WASM) || defined(JPH_CPU_RISCV) || defined(JPH_CPU_PPC) || defined(JPH_CPU_LOONGARCH)
|
||||||
|
|
||||||
|
// Not supported
|
||||||
|
class FPFlushDenormals { };
|
||||||
|
|
||||||
|
#elif defined(JPH_USE_SSE)
|
||||||
|
|
||||||
|
/// Helper class that needs to be put on the stack to enable flushing denormals to zero
|
||||||
|
/// This can make floating point operations much faster when working with very small numbers
|
||||||
|
class FPFlushDenormals : public FPControlWord<_MM_FLUSH_ZERO_ON, _MM_FLUSH_ZERO_MASK> { };
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_ARM) && defined(JPH_COMPILER_MSVC)
|
||||||
|
|
||||||
|
/// Helper class that needs to be put on the stack to enable flushing denormals to zero
|
||||||
|
/// This can make floating point operations much faster when working with very small numbers
|
||||||
|
class FPFlushDenormals : public FPControlWord<_DN_FLUSH, _MCW_DN> { };
|
||||||
|
|
||||||
|
#elif defined(JPH_CPU_ARM)
|
||||||
|
|
||||||
|
/// Flush denormals to zero bit
|
||||||
|
static constexpr uint64 FP_FZ = 1 << 24;
|
||||||
|
|
||||||
|
/// Helper class that needs to be put on the stack to enable flushing denormals to zero
|
||||||
|
/// This can make floating point operations much faster when working with very small numbers
|
||||||
|
class FPFlushDenormals : public FPControlWord<FP_FZ, FP_FZ> { };
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error Unsupported CPU architecture
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
92
thirdparty/JoltPhysics/Jolt/Core/Factory.cpp
vendored
Normal file
92
thirdparty/JoltPhysics/Jolt/Core/Factory.cpp
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Jolt/Jolt.h>
|
||||||
|
|
||||||
|
#include <Jolt/Core/Factory.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
Factory *Factory::sInstance = nullptr;
|
||||||
|
|
||||||
|
void *Factory::CreateObject(const char *inName)
|
||||||
|
{
|
||||||
|
const RTTI *ci = Find(inName);
|
||||||
|
return ci != nullptr? ci->CreateObject() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RTTI *Factory::Find(const char *inName)
|
||||||
|
{
|
||||||
|
ClassNameMap::iterator c = mClassNameMap.find(inName);
|
||||||
|
return c != mClassNameMap.end()? c->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RTTI *Factory::Find(uint32 inHash)
|
||||||
|
{
|
||||||
|
ClassHashMap::iterator c = mClassHashMap.find(inHash);
|
||||||
|
return c != mClassHashMap.end()? c->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Factory::Register(const RTTI *inRTTI)
|
||||||
|
{
|
||||||
|
// Check if we already know the type
|
||||||
|
if (Find(inRTTI->GetName()) != nullptr)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Insert this class by name
|
||||||
|
mClassNameMap.try_emplace(inRTTI->GetName(), inRTTI);
|
||||||
|
|
||||||
|
// Insert this class by hash
|
||||||
|
if (!mClassHashMap.try_emplace(inRTTI->GetHash(), inRTTI).second)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(false, "Hash collision registering type!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register base classes
|
||||||
|
for (int i = 0; i < inRTTI->GetBaseClassCount(); ++i)
|
||||||
|
if (!Register(inRTTI->GetBaseClass(i)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef JPH_OBJECT_STREAM
|
||||||
|
// Register attribute classes
|
||||||
|
for (int i = 0; i < inRTTI->GetAttributeCount(); ++i)
|
||||||
|
{
|
||||||
|
const RTTI *rtti = inRTTI->GetAttribute(i).GetMemberPrimitiveType();
|
||||||
|
if (rtti != nullptr && !Register(rtti))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // JPH_OBJECT_STREAM
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Factory::Register(const RTTI **inRTTIs, uint inNumber)
|
||||||
|
{
|
||||||
|
mClassHashMap.reserve(mClassHashMap.size() + inNumber);
|
||||||
|
mClassNameMap.reserve(mClassNameMap.size() + inNumber);
|
||||||
|
|
||||||
|
for (const RTTI **rtti = inRTTIs; rtti < inRTTIs + inNumber; ++rtti)
|
||||||
|
if (!Register(*rtti))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Factory::Clear()
|
||||||
|
{
|
||||||
|
mClassNameMap.clear();
|
||||||
|
mClassHashMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<const RTTI *> Factory::GetAllClasses() const
|
||||||
|
{
|
||||||
|
Array<const RTTI *> all_classes;
|
||||||
|
all_classes.reserve(mClassNameMap.size());
|
||||||
|
for (const ClassNameMap::value_type &c : mClassNameMap)
|
||||||
|
all_classes.push_back(c.second);
|
||||||
|
return all_classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
54
thirdparty/JoltPhysics/Jolt/Core/Factory.h
vendored
Normal file
54
thirdparty/JoltPhysics/Jolt/Core/Factory.h
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/RTTI.h>
|
||||||
|
#include <Jolt/Core/UnorderedMap.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// This class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data.
|
||||||
|
class JPH_EXPORT Factory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JPH_OVERRIDE_NEW_DELETE
|
||||||
|
|
||||||
|
/// Create an object
|
||||||
|
void * CreateObject(const char *inName);
|
||||||
|
|
||||||
|
/// Find type info for a specific class by name
|
||||||
|
const RTTI * Find(const char *inName);
|
||||||
|
|
||||||
|
/// Find type info for a specific class by hash
|
||||||
|
const RTTI * Find(uint32 inHash);
|
||||||
|
|
||||||
|
/// Register an object with the factory. Returns false on failure.
|
||||||
|
bool Register(const RTTI *inRTTI);
|
||||||
|
|
||||||
|
/// Register a list of objects with the factory. Returns false on failure.
|
||||||
|
bool Register(const RTTI **inRTTIs, uint inNumber);
|
||||||
|
|
||||||
|
/// Unregisters all types
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
/// Get all registered classes
|
||||||
|
Array<const RTTI *> GetAllClasses() const;
|
||||||
|
|
||||||
|
/// Singleton factory instance
|
||||||
|
static Factory * sInstance;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using ClassNameMap = UnorderedMap<string_view, const RTTI *>;
|
||||||
|
|
||||||
|
using ClassHashMap = UnorderedMap<uint32, const RTTI *>;
|
||||||
|
|
||||||
|
/// Map of class names to type info
|
||||||
|
ClassNameMap mClassNameMap;
|
||||||
|
|
||||||
|
// Map of class hash to type info
|
||||||
|
ClassHashMap mClassHashMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
122
thirdparty/JoltPhysics/Jolt/Core/FixedSizeFreeList.h
vendored
Normal file
122
thirdparty/JoltPhysics/Jolt/Core/FixedSizeFreeList.h
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Core/NonCopyable.h>
|
||||||
|
#include <Jolt/Core/Mutex.h>
|
||||||
|
#include <Jolt/Core/Atomics.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Class that allows lock free creation / destruction of objects (unless a new page of objects needs to be allocated)
|
||||||
|
/// It contains a fixed pool of objects and also allows batching up a lot of objects to be destroyed
|
||||||
|
/// and doing the actual free in a single atomic operation
|
||||||
|
template <typename Object>
|
||||||
|
class FixedSizeFreeList : public NonCopyable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/// Storage type for an Object
|
||||||
|
struct ObjectStorage
|
||||||
|
{
|
||||||
|
/// The object we're storing
|
||||||
|
Object mObject;
|
||||||
|
|
||||||
|
/// When the object is freed (or in the process of being freed as a batch) this will contain the next free object
|
||||||
|
/// When an object is in use it will contain the object's index in the free list
|
||||||
|
atomic<uint32> mNextFreeObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(alignof(ObjectStorage) == alignof(Object), "Object not properly aligned");
|
||||||
|
|
||||||
|
/// Access the object storage given the object index
|
||||||
|
const ObjectStorage & GetStorage(uint32 inObjectIndex) const { return mPages[inObjectIndex >> mPageShift][inObjectIndex & mObjectMask]; }
|
||||||
|
ObjectStorage & GetStorage(uint32 inObjectIndex) { return mPages[inObjectIndex >> mPageShift][inObjectIndex & mObjectMask]; }
|
||||||
|
|
||||||
|
/// Size (in objects) of a single page
|
||||||
|
uint32 mPageSize;
|
||||||
|
|
||||||
|
/// Number of bits to shift an object index to the right to get the page number
|
||||||
|
uint32 mPageShift;
|
||||||
|
|
||||||
|
/// Mask to and an object index with to get the page number
|
||||||
|
uint32 mObjectMask;
|
||||||
|
|
||||||
|
/// Total number of pages that are usable
|
||||||
|
uint32 mNumPages;
|
||||||
|
|
||||||
|
/// Total number of objects that have been allocated
|
||||||
|
uint32 mNumObjectsAllocated;
|
||||||
|
|
||||||
|
/// Array of pages of objects
|
||||||
|
ObjectStorage ** mPages = nullptr;
|
||||||
|
|
||||||
|
/// Mutex that is used to allocate a new page if the storage runs out
|
||||||
|
/// This variable is aligned to the cache line to prevent false sharing with
|
||||||
|
/// the constants used to index into the list via `Get()`.
|
||||||
|
alignas(JPH_CACHE_LINE_SIZE) Mutex mPageMutex;
|
||||||
|
|
||||||
|
/// Number of objects that we currently have in the free list / new pages
|
||||||
|
#ifdef JPH_ENABLE_ASSERTS
|
||||||
|
atomic<uint32> mNumFreeObjects;
|
||||||
|
#endif // JPH_ENABLE_ASSERTS
|
||||||
|
|
||||||
|
/// Simple counter that makes the first free object pointer update with every CAS so that we don't suffer from the ABA problem
|
||||||
|
atomic<uint32> mAllocationTag;
|
||||||
|
|
||||||
|
/// Index of first free object, the first 32 bits of an object are used to point to the next free object
|
||||||
|
atomic<uint64> mFirstFreeObjectAndTag;
|
||||||
|
|
||||||
|
/// The first free object to use when the free list is empty (may need to allocate a new page)
|
||||||
|
atomic<uint32> mFirstFreeObjectInNewPage;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Invalid index
|
||||||
|
static const uint32 cInvalidObjectIndex = 0xffffffff;
|
||||||
|
|
||||||
|
/// Size of an object + bookkeeping for the freelist
|
||||||
|
static const int ObjectStorageSize = sizeof(ObjectStorage);
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
inline ~FixedSizeFreeList();
|
||||||
|
|
||||||
|
/// Initialize the free list, up to inMaxObjects can be allocated
|
||||||
|
inline void Init(uint inMaxObjects, uint inPageSize);
|
||||||
|
|
||||||
|
/// Lockless construct a new object, inParameters are passed on to the constructor
|
||||||
|
template <typename... Parameters>
|
||||||
|
inline uint32 ConstructObject(Parameters &&... inParameters);
|
||||||
|
|
||||||
|
/// Lockless destruct an object and return it to the free pool
|
||||||
|
inline void DestructObject(uint32 inObjectIndex);
|
||||||
|
|
||||||
|
/// Lockless destruct an object and return it to the free pool
|
||||||
|
inline void DestructObject(Object *inObject);
|
||||||
|
|
||||||
|
/// A batch of objects that can be destructed
|
||||||
|
struct Batch
|
||||||
|
{
|
||||||
|
uint32 mFirstObjectIndex = cInvalidObjectIndex;
|
||||||
|
uint32 mLastObjectIndex = cInvalidObjectIndex;
|
||||||
|
uint32 mNumObjects = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Add a object to an existing batch to be destructed.
|
||||||
|
/// Adding objects to a batch does not destroy or modify the objects, this will merely link them
|
||||||
|
/// so that the entire batch can be returned to the free list in a single atomic operation
|
||||||
|
inline void AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex);
|
||||||
|
|
||||||
|
/// Lockless destruct batch of objects
|
||||||
|
inline void DestructObjectBatch(Batch &ioBatch);
|
||||||
|
|
||||||
|
/// Access an object by index.
|
||||||
|
inline Object & Get(uint32 inObjectIndex) { return GetStorage(inObjectIndex).mObject; }
|
||||||
|
|
||||||
|
/// Access an object by index.
|
||||||
|
inline const Object & Get(uint32 inObjectIndex) const { return GetStorage(inObjectIndex).mObject; }
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
|
|
||||||
|
#include "FixedSizeFreeList.inl"
|
||||||
215
thirdparty/JoltPhysics/Jolt/Core/FixedSizeFreeList.inl
vendored
Normal file
215
thirdparty/JoltPhysics/Jolt/Core/FixedSizeFreeList.inl
vendored
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
template <typename Object>
|
||||||
|
FixedSizeFreeList<Object>::~FixedSizeFreeList()
|
||||||
|
{
|
||||||
|
// Check if we got our Init call
|
||||||
|
if (mPages != nullptr)
|
||||||
|
{
|
||||||
|
// Ensure everything is freed before the freelist is destructed
|
||||||
|
JPH_ASSERT(mNumFreeObjects.load(memory_order_relaxed) == mNumPages * mPageSize);
|
||||||
|
|
||||||
|
// Free memory for pages
|
||||||
|
uint32 num_pages = mNumObjectsAllocated / mPageSize;
|
||||||
|
for (uint32 page = 0; page < num_pages; ++page)
|
||||||
|
AlignedFree(mPages[page]);
|
||||||
|
Free(mPages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Object>
|
||||||
|
void FixedSizeFreeList<Object>::Init(uint inMaxObjects, uint inPageSize)
|
||||||
|
{
|
||||||
|
// Check sanity
|
||||||
|
JPH_ASSERT(inPageSize > 0 && IsPowerOf2(inPageSize));
|
||||||
|
JPH_ASSERT(mPages == nullptr);
|
||||||
|
|
||||||
|
// Store configuration parameters
|
||||||
|
mNumPages = (inMaxObjects + inPageSize - 1) / inPageSize;
|
||||||
|
mPageSize = inPageSize;
|
||||||
|
mPageShift = CountTrailingZeros(inPageSize);
|
||||||
|
mObjectMask = inPageSize - 1;
|
||||||
|
JPH_IF_ENABLE_ASSERTS(mNumFreeObjects = mNumPages * inPageSize;)
|
||||||
|
|
||||||
|
// Allocate page table
|
||||||
|
mPages = reinterpret_cast<ObjectStorage **>(Allocate(mNumPages * sizeof(ObjectStorage *)));
|
||||||
|
|
||||||
|
// We didn't yet use any objects of any page
|
||||||
|
mNumObjectsAllocated = 0;
|
||||||
|
mFirstFreeObjectInNewPage = 0;
|
||||||
|
|
||||||
|
// Start with 1 as the first tag
|
||||||
|
mAllocationTag = 1;
|
||||||
|
|
||||||
|
// Set first free object (with tag 0)
|
||||||
|
mFirstFreeObjectAndTag = cInvalidObjectIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Object>
|
||||||
|
template <typename... Parameters>
|
||||||
|
uint32 FixedSizeFreeList<Object>::ConstructObject(Parameters &&... inParameters)
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Get first object from the linked list
|
||||||
|
uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
|
||||||
|
uint32 first_free = uint32(first_free_object_and_tag);
|
||||||
|
if (first_free == cInvalidObjectIndex)
|
||||||
|
{
|
||||||
|
// The free list is empty, we take an object from the page that has never been used before
|
||||||
|
first_free = mFirstFreeObjectInNewPage.fetch_add(1, memory_order_relaxed);
|
||||||
|
if (first_free >= mNumObjectsAllocated)
|
||||||
|
{
|
||||||
|
// Allocate new page
|
||||||
|
lock_guard lock(mPageMutex);
|
||||||
|
while (first_free >= mNumObjectsAllocated)
|
||||||
|
{
|
||||||
|
uint32 next_page = mNumObjectsAllocated / mPageSize;
|
||||||
|
if (next_page == mNumPages)
|
||||||
|
return cInvalidObjectIndex; // Out of space!
|
||||||
|
mPages[next_page] = reinterpret_cast<ObjectStorage *>(AlignedAllocate(mPageSize * sizeof(ObjectStorage), max<size_t>(alignof(ObjectStorage), JPH_CACHE_LINE_SIZE)));
|
||||||
|
mNumObjectsAllocated += mPageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocation successful
|
||||||
|
JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_sub(1, memory_order_relaxed);)
|
||||||
|
ObjectStorage &storage = GetStorage(first_free);
|
||||||
|
new (&storage.mObject) Object(std::forward<Parameters>(inParameters)...);
|
||||||
|
storage.mNextFreeObject.store(first_free, memory_order_release);
|
||||||
|
return first_free;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Load next pointer
|
||||||
|
uint32 new_first_free = GetStorage(first_free).mNextFreeObject.load(memory_order_acquire);
|
||||||
|
|
||||||
|
// Construct a new first free object tag
|
||||||
|
uint64 new_first_free_object_and_tag = uint64(new_first_free) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
|
||||||
|
|
||||||
|
// Compare and swap
|
||||||
|
if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
|
||||||
|
{
|
||||||
|
// Allocation successful
|
||||||
|
JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_sub(1, memory_order_relaxed);)
|
||||||
|
ObjectStorage &storage = GetStorage(first_free);
|
||||||
|
new (&storage.mObject) Object(std::forward<Parameters>(inParameters)...);
|
||||||
|
storage.mNextFreeObject.store(first_free, memory_order_release);
|
||||||
|
return first_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Object>
|
||||||
|
void FixedSizeFreeList<Object>::AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(ioBatch.mNumObjects != uint32(-1), "Trying to reuse a batch that has already been freed");
|
||||||
|
|
||||||
|
// Reset next index
|
||||||
|
atomic<uint32> &next_free_object = GetStorage(inObjectIndex).mNextFreeObject;
|
||||||
|
JPH_ASSERT(next_free_object.load(memory_order_relaxed) == inObjectIndex, "Trying to add a object to the batch that is already in a free list");
|
||||||
|
next_free_object.store(cInvalidObjectIndex, memory_order_release);
|
||||||
|
|
||||||
|
// Link object in batch to free
|
||||||
|
if (ioBatch.mFirstObjectIndex == cInvalidObjectIndex)
|
||||||
|
ioBatch.mFirstObjectIndex = inObjectIndex;
|
||||||
|
else
|
||||||
|
GetStorage(ioBatch.mLastObjectIndex).mNextFreeObject.store(inObjectIndex, memory_order_release);
|
||||||
|
ioBatch.mLastObjectIndex = inObjectIndex;
|
||||||
|
ioBatch.mNumObjects++;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Object>
|
||||||
|
void FixedSizeFreeList<Object>::DestructObjectBatch(Batch &ioBatch)
|
||||||
|
{
|
||||||
|
if (ioBatch.mFirstObjectIndex != cInvalidObjectIndex)
|
||||||
|
{
|
||||||
|
// Call destructors
|
||||||
|
if constexpr (!std::is_trivially_destructible<Object>())
|
||||||
|
{
|
||||||
|
uint32 object_idx = ioBatch.mFirstObjectIndex;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ObjectStorage &storage = GetStorage(object_idx);
|
||||||
|
storage.mObject.~Object();
|
||||||
|
object_idx = storage.mNextFreeObject.load(memory_order_relaxed);
|
||||||
|
}
|
||||||
|
while (object_idx != cInvalidObjectIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to objects free list
|
||||||
|
ObjectStorage &storage = GetStorage(ioBatch.mLastObjectIndex);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Get first object from the list
|
||||||
|
uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
|
||||||
|
uint32 first_free = uint32(first_free_object_and_tag);
|
||||||
|
|
||||||
|
// Make it the next pointer of the last object in the batch that is to be freed
|
||||||
|
storage.mNextFreeObject.store(first_free, memory_order_release);
|
||||||
|
|
||||||
|
// Construct a new first free object tag
|
||||||
|
uint64 new_first_free_object_and_tag = uint64(ioBatch.mFirstObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
|
||||||
|
|
||||||
|
// Compare and swap
|
||||||
|
if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
|
||||||
|
{
|
||||||
|
// Free successful
|
||||||
|
JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(ioBatch.mNumObjects, memory_order_relaxed);)
|
||||||
|
|
||||||
|
// Mark the batch as freed
|
||||||
|
#ifdef JPH_ENABLE_ASSERTS
|
||||||
|
ioBatch.mNumObjects = uint32(-1);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Object>
|
||||||
|
void FixedSizeFreeList<Object>::DestructObject(uint32 inObjectIndex)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inObjectIndex != cInvalidObjectIndex);
|
||||||
|
|
||||||
|
// Call destructor
|
||||||
|
ObjectStorage &storage = GetStorage(inObjectIndex);
|
||||||
|
storage.mObject.~Object();
|
||||||
|
|
||||||
|
// Add to object free list
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Get first object from the list
|
||||||
|
uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
|
||||||
|
uint32 first_free = uint32(first_free_object_and_tag);
|
||||||
|
|
||||||
|
// Make it the next pointer of the last object in the batch that is to be freed
|
||||||
|
storage.mNextFreeObject.store(first_free, memory_order_release);
|
||||||
|
|
||||||
|
// Construct a new first free object tag
|
||||||
|
uint64 new_first_free_object_and_tag = uint64(inObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
|
||||||
|
|
||||||
|
// Compare and swap
|
||||||
|
if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
|
||||||
|
{
|
||||||
|
// Free successful
|
||||||
|
JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(1, memory_order_relaxed);)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Object>
|
||||||
|
inline void FixedSizeFreeList<Object>::DestructObject(Object *inObject)
|
||||||
|
{
|
||||||
|
uint32 index = reinterpret_cast<ObjectStorage *>(inObject)->mNextFreeObject.load(memory_order_relaxed);
|
||||||
|
JPH_ASSERT(index < mNumObjectsAllocated);
|
||||||
|
DestructObject(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
234
thirdparty/JoltPhysics/Jolt/Core/HashCombine.h
vendored
Normal file
234
thirdparty/JoltPhysics/Jolt/Core/HashCombine.h
vendored
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Implements the FNV-1a hash algorithm
|
||||||
|
/// @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||||
|
/// @param inData Data block of bytes
|
||||||
|
/// @param inSize Number of bytes
|
||||||
|
/// @param inSeed Seed of the hash (can be used to pass in the hash of a previous operation, otherwise leave default)
|
||||||
|
/// @return Hash
|
||||||
|
inline uint64 HashBytes(const void *inData, uint inSize, uint64 inSeed = 0xcbf29ce484222325UL)
|
||||||
|
{
|
||||||
|
uint64 hash = inSeed;
|
||||||
|
for (const uint8 *data = reinterpret_cast<const uint8 *>(inData); data < reinterpret_cast<const uint8 *>(inData) + inSize; ++data)
|
||||||
|
{
|
||||||
|
hash ^= uint64(*data);
|
||||||
|
hash *= 0x100000001b3UL;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the FNV-1a hash of inString.
|
||||||
|
/// @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||||
|
constexpr uint64 HashString(const char *inString, uint64 inSeed = 0xcbf29ce484222325UL)
|
||||||
|
{
|
||||||
|
uint64 hash = inSeed;
|
||||||
|
for (const char *c = inString; *c != 0; ++c)
|
||||||
|
{
|
||||||
|
hash ^= uint64(*c);
|
||||||
|
hash *= 0x100000001b3UL;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A 64 bit hash function by Thomas Wang, Jan 1997
|
||||||
|
/// See: http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm
|
||||||
|
/// @param inValue Value to hash
|
||||||
|
/// @return Hash
|
||||||
|
inline uint64 Hash64(uint64 inValue)
|
||||||
|
{
|
||||||
|
uint64 hash = inValue;
|
||||||
|
hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1;
|
||||||
|
hash = hash ^ (hash >> 24);
|
||||||
|
hash = (hash + (hash << 3)) + (hash << 8); // hash * 265
|
||||||
|
hash = hash ^ (hash >> 14);
|
||||||
|
hash = (hash + (hash << 2)) + (hash << 4); // hash * 21
|
||||||
|
hash = hash ^ (hash >> 28);
|
||||||
|
hash = hash + (hash << 31);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fallback hash function that calls T::GetHash()
|
||||||
|
template <class T>
|
||||||
|
struct Hash
|
||||||
|
{
|
||||||
|
uint64 operator () (const T &inValue) const
|
||||||
|
{
|
||||||
|
return inValue.GetHash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A hash function for floats
|
||||||
|
template <>
|
||||||
|
struct Hash<float>
|
||||||
|
{
|
||||||
|
uint64 operator () (float inValue) const
|
||||||
|
{
|
||||||
|
float value = inValue == 0.0f? 0.0f : inValue; // Convert -0.0f to 0.0f
|
||||||
|
return HashBytes(&value, sizeof(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A hash function for doubles
|
||||||
|
template <>
|
||||||
|
struct Hash<double>
|
||||||
|
{
|
||||||
|
uint64 operator () (double inValue) const
|
||||||
|
{
|
||||||
|
double value = inValue == 0.0? 0.0 : inValue; // Convert -0.0 to 0.0
|
||||||
|
return HashBytes(&value, sizeof(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A hash function for character pointers
|
||||||
|
template <>
|
||||||
|
struct Hash<const char *>
|
||||||
|
{
|
||||||
|
uint64 operator () (const char *inValue) const
|
||||||
|
{
|
||||||
|
return HashString(inValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A hash function for std::string_view
|
||||||
|
template <>
|
||||||
|
struct Hash<std::string_view>
|
||||||
|
{
|
||||||
|
uint64 operator () (const std::string_view &inValue) const
|
||||||
|
{
|
||||||
|
return HashBytes(inValue.data(), uint(inValue.size()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A hash function for String
|
||||||
|
template <>
|
||||||
|
struct Hash<String>
|
||||||
|
{
|
||||||
|
uint64 operator () (const String &inValue) const
|
||||||
|
{
|
||||||
|
return HashBytes(inValue.data(), uint(inValue.size()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A fallback function for generic pointers
|
||||||
|
template <class T>
|
||||||
|
struct Hash<T *>
|
||||||
|
{
|
||||||
|
uint64 operator () (T *inValue) const
|
||||||
|
{
|
||||||
|
return HashBytes(&inValue, sizeof(inValue));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Helper macro to define a hash function for trivial types
|
||||||
|
#define JPH_DEFINE_TRIVIAL_HASH(type) \
|
||||||
|
template <> \
|
||||||
|
struct Hash<type> \
|
||||||
|
{ \
|
||||||
|
uint64 operator () (const type &inValue) const \
|
||||||
|
{ \
|
||||||
|
return HashBytes(&inValue, sizeof(inValue)); \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Commonly used types
|
||||||
|
JPH_DEFINE_TRIVIAL_HASH(char)
|
||||||
|
JPH_DEFINE_TRIVIAL_HASH(int)
|
||||||
|
JPH_DEFINE_TRIVIAL_HASH(uint32)
|
||||||
|
JPH_DEFINE_TRIVIAL_HASH(uint64)
|
||||||
|
|
||||||
|
/// Helper function that hashes a single value into ioSeed
|
||||||
|
/// Based on https://github.com/jonmaiga/mx3 by Jon Maiga
|
||||||
|
template <typename T>
|
||||||
|
inline void HashCombine(uint64 &ioSeed, const T &inValue)
|
||||||
|
{
|
||||||
|
constexpr uint64 c = 0xbea225f9eb34556dUL;
|
||||||
|
|
||||||
|
uint64 h = ioSeed;
|
||||||
|
uint64 x = Hash<T> { } (inValue);
|
||||||
|
|
||||||
|
// See: https://github.com/jonmaiga/mx3/blob/master/mx3.h
|
||||||
|
// mix_stream(h, x)
|
||||||
|
x *= c;
|
||||||
|
x ^= x >> 39;
|
||||||
|
h += x * c;
|
||||||
|
h *= c;
|
||||||
|
|
||||||
|
// mix(h)
|
||||||
|
h ^= h >> 32;
|
||||||
|
h *= c;
|
||||||
|
h ^= h >> 29;
|
||||||
|
h *= c;
|
||||||
|
h ^= h >> 32;
|
||||||
|
h *= c;
|
||||||
|
h ^= h >> 29;
|
||||||
|
|
||||||
|
ioSeed = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hash combiner to use a custom struct in an unordered map or set
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
///
|
||||||
|
/// struct SomeHashKey
|
||||||
|
/// {
|
||||||
|
/// std::string key1;
|
||||||
|
/// std::string key2;
|
||||||
|
/// bool key3;
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// JPH_MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3)
|
||||||
|
template <typename FirstValue, typename... Values>
|
||||||
|
inline uint64 HashCombineArgs(const FirstValue &inFirstValue, Values... inValues)
|
||||||
|
{
|
||||||
|
// Prime the seed by hashing the first value
|
||||||
|
uint64 seed = Hash<FirstValue> { } (inFirstValue);
|
||||||
|
|
||||||
|
// Hash all remaining values together using a fold expression
|
||||||
|
(HashCombine(seed, inValues), ...);
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define JPH_MAKE_HASH_STRUCT(type, name, ...) \
|
||||||
|
struct [[nodiscard]] name \
|
||||||
|
{ \
|
||||||
|
::JPH::uint64 operator()(const type &t) const \
|
||||||
|
{ \
|
||||||
|
return ::JPH::HashCombineArgs(__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
|
||||||
|
#define JPH_MAKE_STD_HASH(type) \
|
||||||
|
JPH_SUPPRESS_WARNING_PUSH \
|
||||||
|
JPH_SUPPRESS_WARNINGS \
|
||||||
|
namespace std \
|
||||||
|
{ \
|
||||||
|
template<> \
|
||||||
|
struct [[nodiscard]] hash<type> \
|
||||||
|
{ \
|
||||||
|
size_t operator()(const type &t) const \
|
||||||
|
{ \
|
||||||
|
return size_t(::JPH::Hash<type>{ }(t)); \
|
||||||
|
} \
|
||||||
|
}; \
|
||||||
|
} \
|
||||||
|
JPH_SUPPRESS_WARNING_POP
|
||||||
|
|
||||||
|
#define JPH_MAKE_HASHABLE(type, ...) \
|
||||||
|
JPH_SUPPRESS_WARNING_PUSH \
|
||||||
|
JPH_SUPPRESS_WARNINGS \
|
||||||
|
namespace JPH \
|
||||||
|
{ \
|
||||||
|
template<> \
|
||||||
|
JPH_MAKE_HASH_STRUCT(type, Hash<type>, __VA_ARGS__) \
|
||||||
|
} \
|
||||||
|
JPH_SUPPRESS_WARNING_POP \
|
||||||
|
JPH_MAKE_STD_HASH(type)
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
876
thirdparty/JoltPhysics/Jolt/Core/HashTable.h
vendored
Normal file
876
thirdparty/JoltPhysics/Jolt/Core/HashTable.h
vendored
Normal file
|
|
@ -0,0 +1,876 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Jolt/Math/BVec16.h>
|
||||||
|
|
||||||
|
JPH_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/// Helper class for implementing an UnorderedSet or UnorderedMap
|
||||||
|
/// Based on CppCon 2017: Matt Kulukundis "Designing a Fast, Efficient, Cache-friendly Hash Table, Step by Step"
|
||||||
|
/// See: https://www.youtube.com/watch?v=ncHmEUmJZf4
|
||||||
|
template <class Key, class KeyValue, class HashTableDetail, class Hash, class KeyEqual>
|
||||||
|
class HashTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Properties
|
||||||
|
using value_type = KeyValue;
|
||||||
|
using size_type = uint32;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Base class for iterators
|
||||||
|
template <class Table, class Iterator>
|
||||||
|
class IteratorBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Properties
|
||||||
|
using difference_type = typename Table::difference_type;
|
||||||
|
using value_type = typename Table::value_type;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
|
/// Copy constructor
|
||||||
|
IteratorBase(const IteratorBase &inRHS) = default;
|
||||||
|
|
||||||
|
/// Assignment operator
|
||||||
|
IteratorBase & operator = (const IteratorBase &inRHS) = default;
|
||||||
|
|
||||||
|
/// Iterator at start of table
|
||||||
|
explicit IteratorBase(Table *inTable) :
|
||||||
|
mTable(inTable),
|
||||||
|
mIndex(0)
|
||||||
|
{
|
||||||
|
while (mIndex < mTable->mMaxSize && (mTable->mControl[mIndex] & cBucketUsed) == 0)
|
||||||
|
++mIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator at specific index
|
||||||
|
IteratorBase(Table *inTable, size_type inIndex) :
|
||||||
|
mTable(inTable),
|
||||||
|
mIndex(inIndex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prefix increment
|
||||||
|
Iterator & operator ++ ()
|
||||||
|
{
|
||||||
|
JPH_ASSERT(IsValid());
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
++mIndex;
|
||||||
|
}
|
||||||
|
while (mIndex < mTable->mMaxSize && (mTable->mControl[mIndex] & cBucketUsed) == 0);
|
||||||
|
|
||||||
|
return static_cast<Iterator &>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Postfix increment
|
||||||
|
Iterator operator ++ (int)
|
||||||
|
{
|
||||||
|
Iterator result(mTable, mIndex);
|
||||||
|
++(*this);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access to key value pair
|
||||||
|
const KeyValue & operator * () const
|
||||||
|
{
|
||||||
|
JPH_ASSERT(IsValid());
|
||||||
|
return mTable->mData[mIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access to key value pair
|
||||||
|
const KeyValue * operator -> () const
|
||||||
|
{
|
||||||
|
JPH_ASSERT(IsValid());
|
||||||
|
return mTable->mData + mIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Equality operator
|
||||||
|
bool operator == (const Iterator &inRHS) const
|
||||||
|
{
|
||||||
|
return mIndex == inRHS.mIndex && mTable == inRHS.mTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inequality operator
|
||||||
|
bool operator != (const Iterator &inRHS) const
|
||||||
|
{
|
||||||
|
return !(*this == inRHS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check that the iterator is valid
|
||||||
|
bool IsValid() const
|
||||||
|
{
|
||||||
|
return mIndex < mTable->mMaxSize
|
||||||
|
&& (mTable->mControl[mIndex] & cBucketUsed) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Table * mTable;
|
||||||
|
size_type mIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Get the maximum number of elements that we can support given a number of buckets
|
||||||
|
static constexpr size_type sGetMaxLoad(size_type inBucketCount)
|
||||||
|
{
|
||||||
|
return uint32((cMaxLoadFactorNumerator * inBucketCount) / cMaxLoadFactorDenominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the control value for a bucket
|
||||||
|
JPH_INLINE void SetControlValue(size_type inIndex, uint8 inValue)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inIndex < mMaxSize);
|
||||||
|
mControl[inIndex] = inValue;
|
||||||
|
|
||||||
|
// Mirror the first 15 bytes to the 15 bytes beyond mMaxSize
|
||||||
|
// Note that this is equivalent to:
|
||||||
|
// if (inIndex < 15)
|
||||||
|
// mControl[inIndex + mMaxSize] = inValue
|
||||||
|
// else
|
||||||
|
// mControl[inIndex] = inValue
|
||||||
|
// Which performs a needless write if inIndex >= 15 but at least it is branch-less
|
||||||
|
mControl[((inIndex - 15) & (mMaxSize - 1)) + 15] = inValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the index and control value for a particular key
|
||||||
|
JPH_INLINE void GetIndexAndControlValue(const Key &inKey, size_type &outIndex, uint8 &outControl) const
|
||||||
|
{
|
||||||
|
// Calculate hash
|
||||||
|
uint64 hash_value = Hash { } (inKey);
|
||||||
|
|
||||||
|
// Split hash into index and control value
|
||||||
|
outIndex = size_type(hash_value >> 7) & (mMaxSize - 1);
|
||||||
|
outControl = cBucketUsed | uint8(hash_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate space for the hash table
|
||||||
|
void AllocateTable(size_type inMaxSize)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(mData == nullptr);
|
||||||
|
|
||||||
|
mMaxSize = inMaxSize;
|
||||||
|
mLoadLeft = sGetMaxLoad(inMaxSize);
|
||||||
|
size_t required_size = size_t(mMaxSize) * (sizeof(KeyValue) + 1) + 15; // Add 15 bytes to mirror the first 15 bytes of the control values
|
||||||
|
if constexpr (cNeedsAlignedAllocate)
|
||||||
|
mData = reinterpret_cast<KeyValue *>(AlignedAllocate(required_size, alignof(KeyValue)));
|
||||||
|
else
|
||||||
|
mData = reinterpret_cast<KeyValue *>(Allocate(required_size));
|
||||||
|
mControl = reinterpret_cast<uint8 *>(mData + mMaxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy the contents of another hash table
|
||||||
|
void CopyTable(const HashTable &inRHS)
|
||||||
|
{
|
||||||
|
if (inRHS.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
AllocateTable(inRHS.mMaxSize);
|
||||||
|
|
||||||
|
// Copy control bytes
|
||||||
|
memcpy(mControl, inRHS.mControl, mMaxSize + 15);
|
||||||
|
|
||||||
|
// Copy elements
|
||||||
|
uint index = 0;
|
||||||
|
for (const uint8 *control = mControl, *control_end = mControl + mMaxSize; control != control_end; ++control, ++index)
|
||||||
|
if (*control & cBucketUsed)
|
||||||
|
new (mData + index) KeyValue(inRHS.mData[index]);
|
||||||
|
mSize = inRHS.mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Grow the table to a new size
|
||||||
|
void GrowTable(size_type inNewMaxSize)
|
||||||
|
{
|
||||||
|
// Move the old table to a temporary structure
|
||||||
|
size_type old_max_size = mMaxSize;
|
||||||
|
KeyValue *old_data = mData;
|
||||||
|
const uint8 *old_control = mControl;
|
||||||
|
mData = nullptr;
|
||||||
|
mControl = nullptr;
|
||||||
|
mSize = 0;
|
||||||
|
mMaxSize = 0;
|
||||||
|
mLoadLeft = 0;
|
||||||
|
|
||||||
|
// Allocate new table
|
||||||
|
AllocateTable(inNewMaxSize);
|
||||||
|
|
||||||
|
// Reset all control bytes
|
||||||
|
memset(mControl, cBucketEmpty, mMaxSize + 15);
|
||||||
|
|
||||||
|
if (old_data != nullptr)
|
||||||
|
{
|
||||||
|
// Copy all elements from the old table
|
||||||
|
for (size_type i = 0; i < old_max_size; ++i)
|
||||||
|
if (old_control[i] & cBucketUsed)
|
||||||
|
{
|
||||||
|
size_type index;
|
||||||
|
KeyValue *element = old_data + i;
|
||||||
|
JPH_IF_ENABLE_ASSERTS(bool inserted =) InsertKey</* InsertAfterGrow= */ true>(HashTableDetail::sGetKey(*element), index);
|
||||||
|
JPH_ASSERT(inserted);
|
||||||
|
new (mData + index) KeyValue(std::move(*element));
|
||||||
|
element->~KeyValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory
|
||||||
|
if constexpr (cNeedsAlignedAllocate)
|
||||||
|
AlignedFree(old_data);
|
||||||
|
else
|
||||||
|
Free(old_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Get an element by index
|
||||||
|
KeyValue & GetElement(size_type inIndex) const
|
||||||
|
{
|
||||||
|
return mData[inIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a key into the map, returns true if the element was inserted, false if it already existed.
|
||||||
|
/// outIndex is the index at which the element should be constructed / where it is located.
|
||||||
|
template <bool InsertAfterGrow = false>
|
||||||
|
bool InsertKey(const Key &inKey, size_type &outIndex)
|
||||||
|
{
|
||||||
|
// Ensure we have enough space
|
||||||
|
if (mLoadLeft == 0)
|
||||||
|
{
|
||||||
|
// Should not be growing if we're already growing!
|
||||||
|
if constexpr (InsertAfterGrow)
|
||||||
|
JPH_ASSERT(false);
|
||||||
|
|
||||||
|
// Decide if we need to clean up all tombstones or if we need to grow the map
|
||||||
|
size_type num_deleted = sGetMaxLoad(mMaxSize) - mSize;
|
||||||
|
if (num_deleted * cMaxDeletedElementsDenominator > mMaxSize * cMaxDeletedElementsNumerator)
|
||||||
|
rehash(0);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Grow by a power of 2
|
||||||
|
size_type new_max_size = max<size_type>(mMaxSize << 1, 16);
|
||||||
|
if (new_max_size < mMaxSize)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(false, "Overflow in hash table size, can't grow!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GrowTable(new_max_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split hash into index and control value
|
||||||
|
size_type index;
|
||||||
|
uint8 control;
|
||||||
|
GetIndexAndControlValue(inKey, index, control);
|
||||||
|
|
||||||
|
// Keeps track of the index of the first deleted bucket we found
|
||||||
|
constexpr size_type cNoDeleted = ~size_type(0);
|
||||||
|
size_type first_deleted_index = cNoDeleted;
|
||||||
|
|
||||||
|
// Linear probing
|
||||||
|
KeyEqual equal;
|
||||||
|
size_type bucket_mask = mMaxSize - 1;
|
||||||
|
BVec16 control16 = BVec16::sReplicate(control);
|
||||||
|
BVec16 bucket_empty = BVec16::sZero();
|
||||||
|
BVec16 bucket_deleted = BVec16::sReplicate(cBucketDeleted);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Read 16 control values (note that we added 15 bytes at the end of the control values that mirror the first 15 bytes)
|
||||||
|
BVec16 control_bytes = BVec16::sLoadByte16(mControl + index);
|
||||||
|
|
||||||
|
// Check if we must find the element before we can insert
|
||||||
|
if constexpr (!InsertAfterGrow)
|
||||||
|
{
|
||||||
|
// Check for the control value we're looking for
|
||||||
|
// Note that when deleting we can create empty buckets instead of deleted buckets.
|
||||||
|
// This means we must unconditionally check all buckets in this batch for equality
|
||||||
|
// (also beyond the first empty bucket).
|
||||||
|
uint32 control_equal = uint32(BVec16::sEquals(control_bytes, control16).GetTrues());
|
||||||
|
|
||||||
|
// Index within the 16 buckets
|
||||||
|
size_type local_index = index;
|
||||||
|
|
||||||
|
// Loop while there's still buckets to process
|
||||||
|
while (control_equal != 0)
|
||||||
|
{
|
||||||
|
// Get the first equal bucket
|
||||||
|
uint first_equal = CountTrailingZeros(control_equal);
|
||||||
|
|
||||||
|
// Skip to the bucket
|
||||||
|
local_index += first_equal;
|
||||||
|
|
||||||
|
// Make sure that our index is not beyond the end of the table
|
||||||
|
local_index &= bucket_mask;
|
||||||
|
|
||||||
|
// We found a bucket with same control value
|
||||||
|
if (equal(HashTableDetail::sGetKey(mData[local_index]), inKey))
|
||||||
|
{
|
||||||
|
// Element already exists
|
||||||
|
outIndex = local_index;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip past this bucket
|
||||||
|
control_equal >>= first_equal + 1;
|
||||||
|
local_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we're still scanning for deleted buckets
|
||||||
|
if (first_deleted_index == cNoDeleted)
|
||||||
|
{
|
||||||
|
// Check if any buckets have been deleted, if so store the first one
|
||||||
|
uint32 control_deleted = uint32(BVec16::sEquals(control_bytes, bucket_deleted).GetTrues());
|
||||||
|
if (control_deleted != 0)
|
||||||
|
first_deleted_index = index + CountTrailingZeros(control_deleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for empty buckets
|
||||||
|
uint32 control_empty = uint32(BVec16::sEquals(control_bytes, bucket_empty).GetTrues());
|
||||||
|
if (control_empty != 0)
|
||||||
|
{
|
||||||
|
// If we found a deleted bucket, use it.
|
||||||
|
// It doesn't matter if it is before or after the first empty bucket we found
|
||||||
|
// since we will always be scanning in batches of 16 buckets.
|
||||||
|
if (first_deleted_index == cNoDeleted || InsertAfterGrow)
|
||||||
|
{
|
||||||
|
index += CountTrailingZeros(control_empty);
|
||||||
|
--mLoadLeft; // Using an empty bucket decreases the load left
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = first_deleted_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that our index is not beyond the end of the table
|
||||||
|
index &= bucket_mask;
|
||||||
|
|
||||||
|
// Update control byte
|
||||||
|
SetControlValue(index, control);
|
||||||
|
++mSize;
|
||||||
|
|
||||||
|
// Return index to newly allocated bucket
|
||||||
|
outIndex = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to next batch of 16 buckets
|
||||||
|
index = (index + 16) & bucket_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Non-const iterator
|
||||||
|
class iterator : public IteratorBase<HashTable, iterator>
|
||||||
|
{
|
||||||
|
using Base = IteratorBase<HashTable, iterator>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using IteratorBase<HashTable, iterator>::operator ==;
|
||||||
|
|
||||||
|
/// Properties
|
||||||
|
using reference = typename Base::value_type &;
|
||||||
|
using pointer = typename Base::value_type *;
|
||||||
|
|
||||||
|
/// Constructors
|
||||||
|
explicit iterator(HashTable *inTable) : Base(inTable) { }
|
||||||
|
iterator(HashTable *inTable, size_type inIndex) : Base(inTable, inIndex) { }
|
||||||
|
iterator(const iterator &inIterator) : Base(inIterator) { }
|
||||||
|
|
||||||
|
/// Assignment
|
||||||
|
iterator & operator = (const iterator &inRHS) { Base::operator = (inRHS); return *this; }
|
||||||
|
|
||||||
|
using Base::operator *;
|
||||||
|
|
||||||
|
/// Non-const access to key value pair
|
||||||
|
KeyValue & operator * ()
|
||||||
|
{
|
||||||
|
JPH_ASSERT(this->IsValid());
|
||||||
|
return this->mTable->mData[this->mIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
using Base::operator ->;
|
||||||
|
|
||||||
|
/// Non-const access to key value pair
|
||||||
|
KeyValue * operator -> ()
|
||||||
|
{
|
||||||
|
JPH_ASSERT(this->IsValid());
|
||||||
|
return this->mTable->mData + this->mIndex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Const iterator
|
||||||
|
class const_iterator : public IteratorBase<const HashTable, const_iterator>
|
||||||
|
{
|
||||||
|
using Base = IteratorBase<const HashTable, const_iterator>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using IteratorBase<const HashTable, const_iterator>::operator ==;
|
||||||
|
|
||||||
|
/// Properties
|
||||||
|
using reference = const typename Base::value_type &;
|
||||||
|
using pointer = const typename Base::value_type *;
|
||||||
|
|
||||||
|
/// Constructors
|
||||||
|
explicit const_iterator(const HashTable *inTable) : Base(inTable) { }
|
||||||
|
const_iterator(const HashTable *inTable, size_type inIndex) : Base(inTable, inIndex) { }
|
||||||
|
const_iterator(const const_iterator &inRHS) : Base(inRHS) { }
|
||||||
|
const_iterator(const iterator &inIterator) : Base(inIterator.mTable, inIterator.mIndex) { }
|
||||||
|
|
||||||
|
/// Assignment
|
||||||
|
const_iterator & operator = (const iterator &inRHS) { this->mTable = inRHS.mTable; this->mIndex = inRHS.mIndex; return *this; }
|
||||||
|
const_iterator & operator = (const const_iterator &inRHS) { Base::operator = (inRHS); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Default constructor
|
||||||
|
HashTable() = default;
|
||||||
|
|
||||||
|
/// Copy constructor
|
||||||
|
HashTable(const HashTable &inRHS)
|
||||||
|
{
|
||||||
|
CopyTable(inRHS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move constructor
|
||||||
|
HashTable(HashTable &&ioRHS) noexcept :
|
||||||
|
mData(ioRHS.mData),
|
||||||
|
mControl(ioRHS.mControl),
|
||||||
|
mSize(ioRHS.mSize),
|
||||||
|
mMaxSize(ioRHS.mMaxSize),
|
||||||
|
mLoadLeft(ioRHS.mLoadLeft)
|
||||||
|
{
|
||||||
|
ioRHS.mData = nullptr;
|
||||||
|
ioRHS.mControl = nullptr;
|
||||||
|
ioRHS.mSize = 0;
|
||||||
|
ioRHS.mMaxSize = 0;
|
||||||
|
ioRHS.mLoadLeft = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assignment operator
|
||||||
|
HashTable & operator = (const HashTable &inRHS)
|
||||||
|
{
|
||||||
|
if (this != &inRHS)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
|
CopyTable(inRHS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move assignment operator
|
||||||
|
HashTable & operator = (HashTable &&ioRHS) noexcept
|
||||||
|
{
|
||||||
|
if (this != &ioRHS)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
|
mData = ioRHS.mData;
|
||||||
|
mControl = ioRHS.mControl;
|
||||||
|
mSize = ioRHS.mSize;
|
||||||
|
mMaxSize = ioRHS.mMaxSize;
|
||||||
|
mLoadLeft = ioRHS.mLoadLeft;
|
||||||
|
|
||||||
|
ioRHS.mData = nullptr;
|
||||||
|
ioRHS.mControl = nullptr;
|
||||||
|
ioRHS.mSize = 0;
|
||||||
|
ioRHS.mMaxSize = 0;
|
||||||
|
ioRHS.mLoadLeft = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
~HashTable()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reserve memory for a certain number of elements
|
||||||
|
void reserve(size_type inMaxSize)
|
||||||
|
{
|
||||||
|
// Calculate max size based on load factor
|
||||||
|
size_type max_size = GetNextPowerOf2(max<uint32>((cMaxLoadFactorDenominator * inMaxSize) / cMaxLoadFactorNumerator, 16));
|
||||||
|
if (max_size <= mMaxSize)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GrowTable(max_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy the entire hash table
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
// Delete all elements
|
||||||
|
if constexpr (!std::is_trivially_destructible<KeyValue>())
|
||||||
|
if (!empty())
|
||||||
|
for (size_type i = 0; i < mMaxSize; ++i)
|
||||||
|
if (mControl[i] & cBucketUsed)
|
||||||
|
mData[i].~KeyValue();
|
||||||
|
|
||||||
|
if (mData != nullptr)
|
||||||
|
{
|
||||||
|
// Free memory
|
||||||
|
if constexpr (cNeedsAlignedAllocate)
|
||||||
|
AlignedFree(mData);
|
||||||
|
else
|
||||||
|
Free(mData);
|
||||||
|
|
||||||
|
// Reset members
|
||||||
|
mData = nullptr;
|
||||||
|
mControl = nullptr;
|
||||||
|
mSize = 0;
|
||||||
|
mMaxSize = 0;
|
||||||
|
mLoadLeft = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy the entire hash table but keeps the memory allocated
|
||||||
|
void ClearAndKeepMemory()
|
||||||
|
{
|
||||||
|
// Destruct elements
|
||||||
|
if constexpr (!std::is_trivially_destructible<KeyValue>())
|
||||||
|
if (!empty())
|
||||||
|
for (size_type i = 0; i < mMaxSize; ++i)
|
||||||
|
if (mControl[i] & cBucketUsed)
|
||||||
|
mData[i].~KeyValue();
|
||||||
|
mSize = 0;
|
||||||
|
|
||||||
|
// If there are elements that are not marked cBucketEmpty, we reset them
|
||||||
|
size_type max_load = sGetMaxLoad(mMaxSize);
|
||||||
|
if (mLoadLeft != max_load)
|
||||||
|
{
|
||||||
|
// Reset all control bytes
|
||||||
|
memset(mControl, cBucketEmpty, mMaxSize + 15);
|
||||||
|
mLoadLeft = max_load;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator to first element
|
||||||
|
iterator begin()
|
||||||
|
{
|
||||||
|
return iterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator to one beyond last element
|
||||||
|
iterator end()
|
||||||
|
{
|
||||||
|
return iterator(this, mMaxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator to first element
|
||||||
|
const_iterator begin() const
|
||||||
|
{
|
||||||
|
return const_iterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator to one beyond last element
|
||||||
|
const_iterator end() const
|
||||||
|
{
|
||||||
|
return const_iterator(this, mMaxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator to first element
|
||||||
|
const_iterator cbegin() const
|
||||||
|
{
|
||||||
|
return const_iterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator to one beyond last element
|
||||||
|
const_iterator cend() const
|
||||||
|
{
|
||||||
|
return const_iterator(this, mMaxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number of buckets in the table
|
||||||
|
size_type bucket_count() const
|
||||||
|
{
|
||||||
|
return mMaxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Max number of buckets that the table can have
|
||||||
|
constexpr size_type max_bucket_count() const
|
||||||
|
{
|
||||||
|
return size_type(1) << (sizeof(size_type) * 8 - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if there are no elements in the table
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return mSize == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number of elements in the table
|
||||||
|
size_type size() const
|
||||||
|
{
|
||||||
|
return mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Max number of elements that the table can hold
|
||||||
|
constexpr size_type max_size() const
|
||||||
|
{
|
||||||
|
return size_type((uint64(max_bucket_count()) * cMaxLoadFactorNumerator) / cMaxLoadFactorDenominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the max load factor for this table (max number of elements / number of buckets)
|
||||||
|
constexpr float max_load_factor() const
|
||||||
|
{
|
||||||
|
return float(cMaxLoadFactorNumerator) / float(cMaxLoadFactorDenominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a new element, returns iterator and if the element was inserted
|
||||||
|
std::pair<iterator, bool> insert(const value_type &inValue)
|
||||||
|
{
|
||||||
|
size_type index;
|
||||||
|
bool inserted = InsertKey(HashTableDetail::sGetKey(inValue), index);
|
||||||
|
if (inserted)
|
||||||
|
new (mData + index) KeyValue(inValue);
|
||||||
|
return std::make_pair(iterator(this, index), inserted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find an element, returns iterator to element or end() if not found
|
||||||
|
const_iterator find(const Key &inKey) const
|
||||||
|
{
|
||||||
|
// Check if we have any data
|
||||||
|
if (empty())
|
||||||
|
return cend();
|
||||||
|
|
||||||
|
// Split hash into index and control value
|
||||||
|
size_type index;
|
||||||
|
uint8 control;
|
||||||
|
GetIndexAndControlValue(inKey, index, control);
|
||||||
|
|
||||||
|
// Linear probing
|
||||||
|
KeyEqual equal;
|
||||||
|
size_type bucket_mask = mMaxSize - 1;
|
||||||
|
BVec16 control16 = BVec16::sReplicate(control);
|
||||||
|
BVec16 bucket_empty = BVec16::sZero();
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Read 16 control values
|
||||||
|
// (note that we added 15 bytes at the end of the control values that mirror the first 15 bytes)
|
||||||
|
BVec16 control_bytes = BVec16::sLoadByte16(mControl + index);
|
||||||
|
|
||||||
|
// Check for the control value we're looking for
|
||||||
|
// Note that when deleting we can create empty buckets instead of deleted buckets.
|
||||||
|
// This means we must unconditionally check all buckets in this batch for equality
|
||||||
|
// (also beyond the first empty bucket).
|
||||||
|
uint32 control_equal = uint32(BVec16::sEquals(control_bytes, control16).GetTrues());
|
||||||
|
|
||||||
|
// Index within the 16 buckets
|
||||||
|
size_type local_index = index;
|
||||||
|
|
||||||
|
// Loop while there's still buckets to process
|
||||||
|
while (control_equal != 0)
|
||||||
|
{
|
||||||
|
// Get the first equal bucket
|
||||||
|
uint first_equal = CountTrailingZeros(control_equal);
|
||||||
|
|
||||||
|
// Skip to the bucket
|
||||||
|
local_index += first_equal;
|
||||||
|
|
||||||
|
// Make sure that our index is not beyond the end of the table
|
||||||
|
local_index &= bucket_mask;
|
||||||
|
|
||||||
|
// We found a bucket with same control value
|
||||||
|
if (equal(HashTableDetail::sGetKey(mData[local_index]), inKey))
|
||||||
|
{
|
||||||
|
// Element found
|
||||||
|
return const_iterator(this, local_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip past this bucket
|
||||||
|
control_equal >>= first_equal + 1;
|
||||||
|
local_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for empty buckets
|
||||||
|
uint32 control_empty = uint32(BVec16::sEquals(control_bytes, bucket_empty).GetTrues());
|
||||||
|
if (control_empty != 0)
|
||||||
|
{
|
||||||
|
// An empty bucket was found, we didn't find the element
|
||||||
|
return cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to next batch of 16 buckets
|
||||||
|
index = (index + 16) & bucket_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Erase an element by iterator
|
||||||
|
void erase(const const_iterator &inIterator)
|
||||||
|
{
|
||||||
|
JPH_ASSERT(inIterator.IsValid());
|
||||||
|
|
||||||
|
// Read 16 control values before and after the current index
|
||||||
|
// (note that we added 15 bytes at the end of the control values that mirror the first 15 bytes)
|
||||||
|
BVec16 control_bytes_before = BVec16::sLoadByte16(mControl + ((inIterator.mIndex - 16) & (mMaxSize - 1)));
|
||||||
|
BVec16 control_bytes_after = BVec16::sLoadByte16(mControl + inIterator.mIndex);
|
||||||
|
BVec16 bucket_empty = BVec16::sZero();
|
||||||
|
uint32 control_empty_before = uint32(BVec16::sEquals(control_bytes_before, bucket_empty).GetTrues());
|
||||||
|
uint32 control_empty_after = uint32(BVec16::sEquals(control_bytes_after, bucket_empty).GetTrues());
|
||||||
|
|
||||||
|
// If (this index including) there exist 16 consecutive non-empty slots (represented by a bit being 0) then
|
||||||
|
// a probe looking for some element needs to continue probing so we cannot mark the bucket as empty
|
||||||
|
// but must mark it as deleted instead.
|
||||||
|
// Note that we use: CountLeadingZeros(uint16) = CountLeadingZeros(uint32) - 16.
|
||||||
|
uint8 control_value = CountLeadingZeros(control_empty_before) - 16 + CountTrailingZeros(control_empty_after) < 16? cBucketEmpty : cBucketDeleted;
|
||||||
|
|
||||||
|
// Mark the bucket as empty/deleted
|
||||||
|
SetControlValue(inIterator.mIndex, control_value);
|
||||||
|
|
||||||
|
// Destruct the element
|
||||||
|
mData[inIterator.mIndex].~KeyValue();
|
||||||
|
|
||||||
|
// If we marked the bucket as empty we can increase the load left
|
||||||
|
if (control_value == cBucketEmpty)
|
||||||
|
++mLoadLeft;
|
||||||
|
|
||||||
|
// Decrease size
|
||||||
|
--mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Erase an element by key
|
||||||
|
size_type erase(const Key &inKey)
|
||||||
|
{
|
||||||
|
const_iterator it = find(inKey);
|
||||||
|
if (it == cend())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
erase(it);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Swap the contents of two hash tables
|
||||||
|
void swap(HashTable &ioRHS) noexcept
|
||||||
|
{
|
||||||
|
std::swap(mData, ioRHS.mData);
|
||||||
|
std::swap(mControl, ioRHS.mControl);
|
||||||
|
std::swap(mSize, ioRHS.mSize);
|
||||||
|
std::swap(mMaxSize, ioRHS.mMaxSize);
|
||||||
|
std::swap(mLoadLeft, ioRHS.mLoadLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In place re-hashing of all elements in the table. Removes all cBucketDeleted elements
|
||||||
|
/// The std version takes a bucket count, but we just re-hash to the same size.
|
||||||
|
void rehash(size_type)
|
||||||
|
{
|
||||||
|
// Update the control value for all buckets
|
||||||
|
for (size_type i = 0; i < mMaxSize; ++i)
|
||||||
|
{
|
||||||
|
uint8 &control = mControl[i];
|
||||||
|
switch (control)
|
||||||
|
{
|
||||||
|
case cBucketDeleted:
|
||||||
|
// Deleted buckets become empty
|
||||||
|
control = cBucketEmpty;
|
||||||
|
break;
|
||||||
|
case cBucketEmpty:
|
||||||
|
// Remains empty
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Mark all occupied as deleted, to indicate it needs to move to the correct place
|
||||||
|
control = cBucketDeleted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replicate control values to the last 15 entries
|
||||||
|
for (size_type i = 0; i < 15; ++i)
|
||||||
|
mControl[mMaxSize + i] = mControl[i];
|
||||||
|
|
||||||
|
// Loop over all elements that have been 'deleted' and move them to their new spot
|
||||||
|
BVec16 bucket_used = BVec16::sReplicate(cBucketUsed);
|
||||||
|
size_type bucket_mask = mMaxSize - 1;
|
||||||
|
uint32 probe_mask = bucket_mask & ~uint32(0b1111); // Mask out lower 4 bits because we test 16 buckets at a time
|
||||||
|
for (size_type src = 0; src < mMaxSize; ++src)
|
||||||
|
if (mControl[src] == cBucketDeleted)
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Split hash into index and control value
|
||||||
|
size_type src_index;
|
||||||
|
uint8 src_control;
|
||||||
|
GetIndexAndControlValue(HashTableDetail::sGetKey(mData[src]), src_index, src_control);
|
||||||
|
|
||||||
|
// Linear probing
|
||||||
|
size_type dst = src_index;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Check if any buckets are free
|
||||||
|
BVec16 control_bytes = BVec16::sLoadByte16(mControl + dst);
|
||||||
|
uint32 control_free = uint32(BVec16::sAnd(control_bytes, bucket_used).GetTrues()) ^ 0xffff;
|
||||||
|
if (control_free != 0)
|
||||||
|
{
|
||||||
|
// Select this bucket as destination
|
||||||
|
dst += CountTrailingZeros(control_free);
|
||||||
|
dst &= bucket_mask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to next batch of 16 buckets
|
||||||
|
dst = (dst + 16) & bucket_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we stay in the same probe group
|
||||||
|
if (((dst - src_index) & probe_mask) == ((src - src_index) & probe_mask))
|
||||||
|
{
|
||||||
|
// We stay in the same group, we can stay where we are
|
||||||
|
SetControlValue(src, src_control);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (mControl[dst] == cBucketEmpty)
|
||||||
|
{
|
||||||
|
// There's an empty bucket, move us there
|
||||||
|
SetControlValue(dst, src_control);
|
||||||
|
SetControlValue(src, cBucketEmpty);
|
||||||
|
new (mData + dst) KeyValue(std::move(mData[src]));
|
||||||
|
mData[src].~KeyValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// There's an element in the bucket we want to move to, swap them
|
||||||
|
JPH_ASSERT(mControl[dst] == cBucketDeleted);
|
||||||
|
SetControlValue(dst, src_control);
|
||||||
|
std::swap(mData[src], mData[dst]);
|
||||||
|
// Iterate again with the same source bucket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinitialize load left
|
||||||
|
mLoadLeft = sGetMaxLoad(mMaxSize) - mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// If this allocator needs to fall back to aligned allocations because the type requires it
|
||||||
|
static constexpr bool cNeedsAlignedAllocate = alignof(KeyValue) > JPH_DEFAULT_ALLOCATE_ALIGNMENT;
|
||||||
|
|
||||||
|
/// Max load factor is cMaxLoadFactorNumerator / cMaxLoadFactorDenominator
|
||||||
|
static constexpr uint64 cMaxLoadFactorNumerator = 7;
|
||||||
|
static constexpr uint64 cMaxLoadFactorDenominator = 8;
|
||||||
|
|
||||||
|
/// If we can recover this fraction of deleted elements, we'll reshuffle the buckets in place rather than growing the table
|
||||||
|
static constexpr uint64 cMaxDeletedElementsNumerator = 1;
|
||||||
|
static constexpr uint64 cMaxDeletedElementsDenominator = 8;
|
||||||
|
|
||||||
|
/// Values that the control bytes can have
|
||||||
|
static constexpr uint8 cBucketEmpty = 0;
|
||||||
|
static constexpr uint8 cBucketDeleted = 0x7f;
|
||||||
|
static constexpr uint8 cBucketUsed = 0x80; // Lowest 7 bits are lowest 7 bits of the hash value
|
||||||
|
|
||||||
|
/// The buckets, an array of size mMaxSize
|
||||||
|
KeyValue * mData = nullptr;
|
||||||
|
|
||||||
|
/// Control bytes, an array of size mMaxSize + 15
|
||||||
|
uint8 * mControl = nullptr;
|
||||||
|
|
||||||
|
/// Number of elements in the table
|
||||||
|
size_type mSize = 0;
|
||||||
|
|
||||||
|
/// Max number of elements that can be stored in the table
|
||||||
|
size_type mMaxSize = 0;
|
||||||
|
|
||||||
|
/// Number of elements we can add to the table before we need to grow
|
||||||
|
size_type mLoadLeft = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
JPH_NAMESPACE_END
|
||||||
36
thirdparty/JoltPhysics/Jolt/Core/IncludeWindows.h
vendored
Normal file
36
thirdparty/JoltPhysics/Jolt/Core/IncludeWindows.h
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||||
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifdef JPH_PLATFORM_WINDOWS
|
||||||
|
|
||||||
|
JPH_SUPPRESS_WARNING_PUSH
|
||||||
|
JPH_MSVC_SUPPRESS_WARNING(5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception.
|
||||||
|
JPH_MSVC2026_PLUS_SUPPRESS_WARNING(4865) // wingdi.h(2806,1): '<unnamed-enum-DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER>': the underlying type will change from 'int' to '__int64' when '/Zc:enumTypes' is specified on the command line
|
||||||
|
JPH_CLANG_SUPPRESS_WARNING("-Wreserved-macro-identifier") // Complains about _WIN32_WINNT being reserved
|
||||||
|
|
||||||
|
#ifndef WINVER
|
||||||
|
#define WINVER 0x0A00 // Targeting Windows 10 and above
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32_WINNT
|
||||||
|
#define _WIN32_WINNT 0x0A00
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef JPH_COMPILER_MINGW
|
||||||
|
#include <Windows.h>
|
||||||
|
#else
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JPH_SUPPRESS_WARNING_POP
|
||||||
|
|
||||||
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue