rewrote meshopt (again) to actually perform vertex optimizations, and fixed LOD generation), fixed LOD level calculation (because Vulkan's -Y ruined things), some tweaks and fixes to the memory pool / allocator (it will still segfault on termination because of how things are ordered)
This commit is contained in:
parent
b37eb0ed62
commit
89ca3efb3e
@ -352,7 +352,7 @@
|
||||
"enabled": true, // needs to be kept on for GC
|
||||
"subPools": true,
|
||||
"alignment": 64,
|
||||
"override": false,
|
||||
"override": true,
|
||||
"size": "512 MiB",
|
||||
"pools": {
|
||||
"entity": "128 MiB",
|
||||
@ -363,11 +363,11 @@
|
||||
"render modes": { "gui": true, "deferred": true },
|
||||
"limiters": {
|
||||
"deltaTime": 5,
|
||||
"framerate": 300 // "auto" // for some reason drops to 60
|
||||
"framerate": "auto" // "auto" // for some reason drops to 60
|
||||
},
|
||||
"threads": {
|
||||
"workers" : "auto",
|
||||
"frame limiter": 0 // "auto"
|
||||
"frame limiter": "auto"
|
||||
},
|
||||
"debug": {
|
||||
"framerate": {
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"metadata": {
|
||||
"holdable": true,
|
||||
"physics": {
|
||||
"mass": 100,
|
||||
"mass": 0,
|
||||
"inertia": false,
|
||||
"type": "bounding box"
|
||||
// "type": "mesh"
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"graph": {
|
||||
"renderer": { "separate": false },
|
||||
"exporter": {
|
||||
"optimize": "tagged"
|
||||
"optimize": { "simplify": 0, "lods": true, "print": true }
|
||||
},
|
||||
"baking": { "enabled": true },
|
||||
"tags": {
|
||||
@ -12,13 +12,11 @@
|
||||
"worldspawn": {
|
||||
"physics": { "type": "mesh", "static": true, "mass": 0 },
|
||||
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
|
||||
"optimize mesh": { "simplify": 0, "lods": true, "print": true },
|
||||
"unwrap mesh": true
|
||||
},
|
||||
"worldspawn_skybox": {
|
||||
"physics": { "type": "aabb", "static": true, "mass": 0 },
|
||||
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
|
||||
"optimize mesh": { "simplify": 0, "lods": true, "print": true },
|
||||
"unwrap mesh": true
|
||||
},
|
||||
"info_player_spawn": { "action": "attach", "filename": "./player.json", "transform": { "orientation": [ 0, 1, 0, 0 ] } },
|
||||
@ -42,8 +40,8 @@
|
||||
"/^prop_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } },
|
||||
"/^prop_static/": { /*"action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } }*/ },
|
||||
"/^prop_dynamic/": { /*"action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } }*/ },
|
||||
"/^func_physbox/": { "action": "load", "payload": { "import": "/prop.json" }, "optimize mesh": { "simplify": 0, "lods": true, "print": true } },
|
||||
"/^prop_physics/": { "action": "load", "payload": { "import": "/prop.json" }, "optimize mesh": { "simplify": 0, "lods": true, "print": true } },
|
||||
"/^func_physbox/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
"/^prop_physics/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
|
||||
"/^tools\\/toolsnodraw/": { "material": { "base": [ 1.0, 1.0, 1.0, 0.0 ] } }
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
"import": "./base_sourceengine.json",
|
||||
"assets": [
|
||||
// { "filename": "./models/mds_mcdonalds.glb" }
|
||||
{ "filename": "./models/mds_mcdonalds/graph.json" },
|
||||
{ "filename": "/burger.json", "delay": 1 }
|
||||
{ "filename": "./models/mds_mcdonalds/graph.json" }
|
||||
// { "filename": "/burger.json", "delay": 1 }
|
||||
],
|
||||
"metadata": {
|
||||
"graph": {
|
||||
@ -15,7 +15,7 @@
|
||||
"func_door_rotating_5568": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
"func_door_rotating_5584": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
|
||||
"prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
|
||||
// "prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
|
||||
|
||||
// regex matches
|
||||
"/^prop_physics_[^o]/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
|
||||
@ -84,7 +84,7 @@ struct DrawCommand {
|
||||
uint indices; // triangle count
|
||||
uint instances; // instance count
|
||||
uint indexID; // starting triangle position
|
||||
int vertexID; // starting vertex position
|
||||
uint vertexID; // starting vertex position
|
||||
|
||||
uint instanceID; // starting instance position
|
||||
float padding1; //
|
||||
@ -101,7 +101,9 @@ struct Bounds {
|
||||
|
||||
struct LOD {
|
||||
uint indices;
|
||||
uint vertexID;
|
||||
uint indexID;
|
||||
uint vertices;
|
||||
};
|
||||
|
||||
struct LODMetadata {
|
||||
|
||||
@ -182,23 +182,25 @@ void main() {
|
||||
#endif
|
||||
#if LODS
|
||||
if ( isVisible ) {
|
||||
vec3 viewCenter = (camera.viewport[0].view * vec4(worldCenter, 1.0)).xyz;
|
||||
vec3 viewCenter = (camera.viewport[0].view * vec4(worldCenter, 1.0)).xyz;
|
||||
float dist = length(viewCenter);
|
||||
float P11 = abs(camera.viewport[0].projection[1][1]);
|
||||
float projectedSize = (worldRadius * P11) / max(dist, 0.001);
|
||||
|
||||
float P11 = camera.viewport[0].projection[1][1];
|
||||
|
||||
float screenRadius = (worldRadius * P11) / max(abs(viewCenter.z), 0.001);
|
||||
|
||||
uint lodLevel = 0;
|
||||
if ( screenRadius < 0.5 ) lodLevel = 1;
|
||||
if ( screenRadius < 0.2 ) lodLevel = 2;
|
||||
if ( screenRadius < 0.05 ) lodLevel = 3;
|
||||
lodLevel = min(lodLevel, MAX_LODS - 1);
|
||||
uint lodLevel = 0;
|
||||
if ( projectedSize < 0.20 ) lodLevel = 1;
|
||||
if ( projectedSize < 0.08 ) lodLevel = 2;
|
||||
if ( projectedSize < 0.02 ) lodLevel = 3;
|
||||
lodLevel = min(lodLevel, MAX_LODS - 1);
|
||||
lodLevel = 3;
|
||||
|
||||
LOD lod = lodMetadata[drawCommand.instanceID].levels[lodLevel];
|
||||
|
||||
if ( lod.indices > 0 ) {
|
||||
drawCommands[gID].indices = lod.indices;
|
||||
drawCommands[gID].indexID = lod.indexID;
|
||||
drawCommands[gID].vertexID = lod.vertexID;
|
||||
drawCommands[gID].vertices = lod.vertices;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -143,6 +143,12 @@
|
||||
#define UF_MSG(...) {}
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#define STATIC_THREAD_LOCAL(T, name) T name;
|
||||
#else
|
||||
#define STATIC_THREAD_LOCAL(T, name) static thread_local T name; name.clear();
|
||||
#endif
|
||||
|
||||
#ifndef UF_DEBUG
|
||||
#define UF_DEBUG 1
|
||||
#endif
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <uf/config.h>
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
// #include <limits>
|
||||
// #include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
#define UF_MEMORYPOOL_USE_STL_ALLOCATOR 1
|
||||
|
||||
@ -11,178 +11,59 @@ namespace uf {
|
||||
namespace allocator {
|
||||
extern UF_API bool override;
|
||||
|
||||
void* UF_API allocate( size_t n );
|
||||
void UF_API deallocate( void* p, size_t = 0 );
|
||||
|
||||
void* UF_API allocate( size_t n);
|
||||
void UF_API deallocate( void* p, size_t n = 0 );
|
||||
|
||||
void* UF_API malloc_m( size_t n );
|
||||
void UF_API free_m( void* p, size_t = 0 );
|
||||
|
||||
/*
|
||||
template<typename T>
|
||||
struct Use : std::true_type {};
|
||||
|
||||
template<typename ... Ts>
|
||||
struct Use<std::function<Ts...>> : std::false_type {};
|
||||
*/
|
||||
void UF_API free_m( void* p, size_t n = 0 );
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
struct Allocator {
|
||||
typedef T value_type;
|
||||
|
||||
Allocator () = default;
|
||||
template <class U> constexpr Allocator (const Allocator <U>&) noexcept {}
|
||||
Allocator() = default;
|
||||
template <class U> constexpr Allocator( const Allocator<U>& ) noexcept {}
|
||||
|
||||
T* allocate(size_t n) noexcept {
|
||||
// n *= sizeof(T);
|
||||
// if ( !uf::allocator::Use<T>::value ) return static_cast<T*>( uf::allocator::malloc_m( n ) );
|
||||
// return static_cast<T*>( uf::allocator::allocate( n ) );
|
||||
return static_cast<T*>( uf::allocator::allocate( n * sizeof(T) ) );
|
||||
T* allocate( size_t n ) {
|
||||
void* p = uf::allocator::allocate( n * sizeof(T) );
|
||||
if ( !p ) throw std::bad_alloc();
|
||||
return static_cast<T*>(p);
|
||||
}
|
||||
void deallocate(T* p, size_t n) noexcept {
|
||||
// if ( !uf::allocator::Use<T>::value ) return uf::allocator::free_m(p);
|
||||
uf::allocator::deallocate( p, n );
|
||||
|
||||
void deallocate( T* p, size_t n ) noexcept {
|
||||
uf::allocator::deallocate( p, n * sizeof(T) );
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
bool operator==(const uf::Allocator <T>&, const uf::Allocator <U>&) { return true; }
|
||||
bool operator==( const uf::Allocator<T>&, const uf::Allocator<U>& ) { return true; }
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!=(const uf::Allocator <T>&, const uf::Allocator <U>&) { return false; }
|
||||
bool operator!=( const uf::Allocator<T>&, const uf::Allocator<U>& ) { return false; }
|
||||
|
||||
// will never ever use the pool
|
||||
template <class T>
|
||||
struct Mallocator {
|
||||
typedef T value_type;
|
||||
|
||||
Mallocator () = default;
|
||||
template <class U> constexpr Mallocator (const Mallocator <U>&) noexcept {}
|
||||
Mallocator() = default;
|
||||
template <class U> constexpr Mallocator( const Mallocator<U>& ) noexcept {}
|
||||
|
||||
T* allocate(size_t n) noexcept {
|
||||
// n *= sizeof(T);
|
||||
return static_cast<T*>( uf::allocator::malloc_m( n * sizeof(T) ) );
|
||||
T* allocate( size_t n ) {
|
||||
void* p = uf::allocator::malloc_m( n * sizeof(T) );
|
||||
if ( !p ) throw std::bad_alloc();
|
||||
return static_cast<T*>( p );
|
||||
}
|
||||
|
||||
void deallocate(T* p, size_t n) noexcept {
|
||||
uf::allocator::free_m( p, n );
|
||||
uf::allocator::free_m( p, n * sizeof(T) );
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
bool operator==(const uf::Mallocator <T>&, const uf::Mallocator <U>&) { return true; }
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!=(const uf::Mallocator <T>&, const uf::Mallocator <U>&) { return false; }
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
template <class T>
|
||||
class Allocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
// template <class U> struct rebind {typedef Allocator<U> other;};
|
||||
// using pointer = value_type*;
|
||||
// using const_pointer = typename std::pointer_traits<pointer>::template rebind<value_type const>;
|
||||
// using void_pointer = typename std::pointer_traits<pointer>::template rebind<void>;
|
||||
// using const_void_pointer = typename std::pointer_traits<pointer>::template rebind<const void>;
|
||||
// using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
||||
// using size_type = std::make_unsigned_t<difference_type>;
|
||||
|
||||
Allocator() noexcept {} // not required, unless used
|
||||
template <class U> Allocator(Allocator<U> const&) noexcept {}
|
||||
|
||||
value_type* allocate( size_t n );
|
||||
void deallocate( value_type* p, size_t = 0 ) noexcept;
|
||||
|
||||
// value_type*
|
||||
// allocate(size_t n, const_void_pointer) {
|
||||
// return allocate(n);
|
||||
// }
|
||||
|
||||
// template <class U, class ...Args> void construct(U* p, Args&& ...args) {
|
||||
// ::new(p) U(std::forward<Args>(args)...);
|
||||
// }
|
||||
|
||||
// template <class U> void destroy(U* p) noexcept {
|
||||
// p->~U();
|
||||
// }
|
||||
|
||||
// size_t max_size() const noexcept { return std::numeric_limits<size_type>::max(); }
|
||||
// Allocator select_on_container_copy_construction() const { return *this; }
|
||||
|
||||
// using propagate_on_container_copy_assignment = std::false_type;
|
||||
// using propagate_on_container_move_assignment = std::false_type;
|
||||
// using propagate_on_container_swap = std::false_type;
|
||||
// using is_always_equal = std::is_empty<Allocator>;
|
||||
|
||||
};
|
||||
bool operator==(const uf::Mallocator<T>&, const uf::Mallocator<U>&) { return true; }
|
||||
|
||||
template <class T, class U>
|
||||
bool operator==(uf::Allocator<T> const&, uf::Allocator<U> const&) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!=(uf::Allocator<T> const& x, uf::Allocator<U> const& y) noexcept {
|
||||
return !(x == y);
|
||||
}
|
||||
}
|
||||
|
||||
namespace uf {
|
||||
template <class T>
|
||||
class Mallocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
// template <class U> struct rebind {typedef Mallocator<U> other;};
|
||||
// using pointer = value_type*;
|
||||
// using const_pointer = typename std::pointer_traits<pointer>::template rebind<value_type const>;
|
||||
// using void_pointer = typename std::pointer_traits<pointer>::template rebind<void>;
|
||||
// using const_void_pointer = typename std::pointer_traits<pointer>::template rebind<const void>;
|
||||
// using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
||||
// using size_type = std::make_unsigned_t<difference_type>;
|
||||
|
||||
Mallocator() noexcept {} // not required, unless used
|
||||
template <class U> Mallocator(Mallocator<U> const&) noexcept {}
|
||||
|
||||
value_type* allocate( size_t n );
|
||||
void deallocate( value_type* p, size_t = 0 ) noexcept;
|
||||
|
||||
// value_type*
|
||||
// allocate(size_t n, const_void_pointer) {
|
||||
// return allocate(n);
|
||||
// }
|
||||
|
||||
// template <class U, class ...Args> void construct(U* p, Args&& ...args) {
|
||||
// ::new(p) U(std::forward<Args>(args)...);
|
||||
// }
|
||||
|
||||
// template <class U> void destroy(U* p) noexcept {
|
||||
// p->~U();
|
||||
// }
|
||||
|
||||
// size_t max_size() const noexcept { return std::numeric_limits<size_type>::max(); }
|
||||
// Mallocator select_on_container_copy_construction() const { return *this; }
|
||||
|
||||
// using propagate_on_container_copy_assignment = std::false_type;
|
||||
// using propagate_on_container_move_assignment = std::false_type;
|
||||
// using propagate_on_container_swap = std::false_type;
|
||||
// using is_always_equal = std::is_empty<Mallocator>;
|
||||
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
bool operator==(uf::Mallocator<T> const&, uf::Mallocator<U> const&) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!=(uf::Mallocator<T> const& x, uf::Mallocator<U> const& y) noexcept {
|
||||
return !(x == y);
|
||||
}
|
||||
}
|
||||
|
||||
#include "allocator.inl"
|
||||
*/
|
||||
bool operator!=(const uf::Mallocator<T>&, const uf::Mallocator<U>&) { return false; }
|
||||
}
|
||||
@ -60,11 +60,11 @@ namespace pod {
|
||||
} buddy;
|
||||
} state;
|
||||
|
||||
typedef std::vector<pod::Allocation, uf::Mallocator<pod::Allocation>> allocations_t;
|
||||
#if UF_MEMORYPOOL_MUTEX
|
||||
std::mutex mutex;
|
||||
#endif
|
||||
#if UF_MEMORYPOOL_STORE_ORPHANS
|
||||
typedef std::vector<pod::Allocation, uf::Mallocator<pod::Allocation>> allocations_t;
|
||||
allocations_t orphaned;
|
||||
#endif
|
||||
};
|
||||
@ -88,11 +88,11 @@ namespace uf {
|
||||
pod::Allocation UF_API allocate( pod::MemoryPool&, size_t, size_t alignment = uf::memoryPool::alignment );
|
||||
void* UF_API alloc( pod::MemoryPool&, size_t, size_t alignment = uf::memoryPool::alignment );
|
||||
|
||||
pod::Allocation& UF_API fetch( pod::MemoryPool&, void*, size_t = 0 );
|
||||
// pod::Allocation& UF_API fetch( pod::MemoryPool&, void*, size_t = 0 );
|
||||
bool UF_API exists( pod::MemoryPool&, void*, size_t = 0 );
|
||||
bool UF_API free( pod::MemoryPool&, void*, size_t = 0 );
|
||||
|
||||
const pod::MemoryPool::allocations_t& UF_API allocations( const pod::MemoryPool& );
|
||||
// const pod::MemoryPool::allocations_t& UF_API allocations( const pod::MemoryPool& );
|
||||
|
||||
template<typename T> T& alloc( pod::MemoryPool&, const T& = T()/*, size_t alignment = uf::memoryPool::alignment*/ );
|
||||
template<typename T> pod::Allocation allocate( pod::MemoryPool&, const T& = T()/*, size_t alignment = uf::memoryPool::alignment*/ );
|
||||
@ -120,11 +120,11 @@ namespace uf {
|
||||
// inline void* alloc( size_t size, void* data = NULL/*, size_t alignment = uf::memoryPool::alignment*/ );
|
||||
inline pod::Allocation allocate( size_t size/*, size_t alignment = uf::memoryPool::alignment*/ );
|
||||
inline void* alloc( size_t size/*, size_t alignment = uf::memoryPool::alignment*/ );
|
||||
inline pod::Allocation& fetch( void* data, size_t size = 0 );
|
||||
// inline pod::Allocation& fetch( void* data, size_t size = 0 );
|
||||
inline bool exists( void* data, size_t size = 0 );
|
||||
inline bool free( void* data, size_t size );
|
||||
|
||||
inline const pod::MemoryPool::allocations_t& allocations() const;
|
||||
// inline const pod::MemoryPool::allocations_t& allocations() const;
|
||||
inline pod::MemoryPool& data();
|
||||
inline const pod::MemoryPool& data() const;
|
||||
|
||||
|
||||
@ -51,11 +51,11 @@ void uf::MemoryPool::destroy() { return uf::memoryPool::destroy( m_pod ); }
|
||||
//void* uf::MemoryPool::alloc( size_t size, void* data/*, size_t alignment*/ ) { return uf::memoryPool::alloc( m_pod, data, size/*, alignment*/ ); }
|
||||
pod::Allocation uf::MemoryPool::allocate( size_t size/*, size_t alignment*/ ) { return uf::memoryPool::allocate( m_pod, size/*, alignment*/ ); }
|
||||
void* uf::MemoryPool::alloc( size_t size/*, size_t alignment*/ ) { return uf::memoryPool::alloc( m_pod, size/*, alignment*/ ); }
|
||||
pod::Allocation& uf::MemoryPool::fetch( void* data, size_t size ) { return uf::memoryPool::fetch( m_pod, data, size ); }
|
||||
//pod::Allocation& uf::MemoryPool::fetch( void* data, size_t size ) { return uf::memoryPool::fetch( m_pod, data, size ); }
|
||||
bool uf::MemoryPool::exists( void* data, size_t size ) { return uf::memoryPool::exists( m_pod, data, size ); }
|
||||
bool uf::MemoryPool::free( void* data, size_t size ) { return uf::memoryPool::free( m_pod, data, size ); }
|
||||
|
||||
const pod::MemoryPool::allocations_t& uf::MemoryPool::allocations() const { return uf::memoryPool::allocations( m_pod ); }
|
||||
//const pod::MemoryPool::allocations_t& uf::MemoryPool::allocations() const { return uf::memoryPool::allocations( m_pod ); }
|
||||
inline pod::MemoryPool& uf::MemoryPool::data() { return m_pod; }
|
||||
inline const pod::MemoryPool& uf::MemoryPool::data() const { return m_pod; }
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@ namespace pod {
|
||||
alignas(4) uint32_t indices = 0; // triangle count
|
||||
alignas(4) uint32_t instances = 0; // instance count
|
||||
alignas(4) uint32_t indexID = 0; // starting triangle position
|
||||
alignas(4) int32_t vertexID = 0; // starting vertex position
|
||||
alignas(4) uint32_t vertexID = 0; // starting vertex position
|
||||
alignas(4) uint32_t instanceID = 0; // starting instance position
|
||||
// extra data for padding
|
||||
alignas(4) uint32_t auxID = 0; // used for storing which grid this belongs to when slicing, otherwise unused
|
||||
@ -77,7 +77,9 @@ namespace pod {
|
||||
struct UF_API LODMetadata {
|
||||
struct Level {
|
||||
alignas(4) uint32_t indices = 0;
|
||||
alignas(4) uint32_t vertexID = 0;
|
||||
alignas(4) uint32_t indexID = 0;
|
||||
alignas(4) uint32_t vertices = 0;
|
||||
} levels[4];
|
||||
};
|
||||
|
||||
|
||||
@ -390,18 +390,18 @@ void UF_API uf::initialize() {
|
||||
if ( size <= 0 || uf::memoryPool::subPool ) {
|
||||
{
|
||||
size_t size = deduceSize( configMemoryPoolJson["pools"]["component"] );
|
||||
UF_MSG_DEBUG("Requesting {} bytes for component memory pool: {}", (int) size, (void*) &uf::component::memoryPool);
|
||||
uf::component::memoryPool.initialize( size );
|
||||
UF_MSG_DEBUG("Requested {} bytes for component memory pool: {}", (int) size, uf::component::memoryPool.data().memory);
|
||||
}
|
||||
{
|
||||
size_t size = deduceSize( configMemoryPoolJson["pools"]["userdata"] );
|
||||
UF_MSG_DEBUG("Requesting {} bytes for userdata memory pool: {}", (int) size, (void*) &uf::userdata::memoryPool);
|
||||
uf::userdata::memoryPool.initialize( size );
|
||||
UF_MSG_DEBUG("Requested {} bytes for userdata memory pool: {}", (int) size, uf::userdata::memoryPool.data().memory);
|
||||
}
|
||||
{
|
||||
size_t size = deduceSize( configMemoryPoolJson["pools"]["entity"] );
|
||||
UF_MSG_DEBUG("Requesting {} bytes for entity memory pool: {}", (int) size, (void*) &uf::Entity::memoryPool);
|
||||
uf::Entity::memoryPool.initialize( size, pod::MemoryPool::Strategy::POOL, sizeof(uf::Entity) );
|
||||
UF_MSG_DEBUG("Requested {} bytes for entity memory pool: {}", (int) size, uf::Entity::memoryPool.data().memory);
|
||||
}
|
||||
}
|
||||
uf::allocator::override = configMemoryPoolJson["override"].as( uf::allocator::override );
|
||||
@ -873,7 +873,7 @@ void UF_API uf::tick() {
|
||||
#if UF_ENV_DREAMCAST
|
||||
DC_STATS();
|
||||
#endif
|
||||
#if UF_THREAD_METRICS
|
||||
#if 0 && UF_THREAD_METRICS
|
||||
auto metrics = uf::thread::collectStats();
|
||||
for ( auto& [ name, stats ] : metrics ) UF_MSG_DEBUG("Thread {}: active={}, idle={}, total={}, tasks={}", name, std::get<0>(stats), std::get<1>(stats), std::get<2>(stats), std::get<3>(stats) );
|
||||
#endif
|
||||
@ -993,9 +993,6 @@ void UF_API uf::terminate() {
|
||||
{
|
||||
uf::scene::destroy();
|
||||
}
|
||||
/* Kill physics */ {
|
||||
// uf::physics::terminate();
|
||||
}
|
||||
/* Garbage collection */ if ( /*global*/::config.engine.gc.enabled ) {
|
||||
size_t collected = uf::instantiator::collect( /*global*/::config.engine.gc.mode );
|
||||
if ( collected > 0 ) {
|
||||
@ -1018,6 +1015,14 @@ void UF_API uf::terminate() {
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Destroy memory pools */ {
|
||||
uf::component::memoryPool.destroy();
|
||||
uf::userdata::memoryPool.destroy();
|
||||
uf::Entity::memoryPool.destroy();
|
||||
|
||||
uf::memoryPool::global.destroy(); // should probably leave this to be statically destructed
|
||||
}
|
||||
|
||||
/* Print system stats */ {
|
||||
/*global*/::times.total.time = /*global*/::times.sys.elapsed().asDouble();
|
||||
UF_MSG_DEBUG("System: Total Time: {} | Total Frames: {} | Average FPS: {}", /*global*/::times.total.time, /*global*/::times.total.frames, /*global*/::times.total.frames / /*global*/::times.total.time);
|
||||
|
||||
@ -447,7 +447,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
|
||||
if ( stats.walking ) {
|
||||
float factor = stats.floored ? 1.0f : speed.air;
|
||||
if ( stats.noclipped ) {
|
||||
physicsBody.velocity += target * speed.move * ONE_OVER_SIXTY;
|
||||
physicsBody.velocity += target * speed.move * 50 * ONE_OVER_SIXTY;
|
||||
} else {
|
||||
physicsBody.velocity += target * std::clamp( speed.move * factor - uf::vector::dot( physicsBody.velocity, target ), 0.0f, speed.move * 10 * ONE_OVER_SIXTY /*uf::physics::time::delta*/ );
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
#if UF_USE_OPENGL
|
||||
#define UF_GRAPH_LOAD_MULTITHREAD 0
|
||||
#else
|
||||
#define UF_GRAPH_LOAD_MULTITHREAD 0
|
||||
#define UF_GRAPH_LOAD_MULTITHREAD 1
|
||||
#endif
|
||||
|
||||
#define UF_GRAPH_EXTENDED 1
|
||||
@ -190,6 +190,8 @@ namespace {
|
||||
ext::json::forEach( json, [&]( size_t i, ext::json::Value& value ){
|
||||
lodMetadata.levels[i].indices = value["indices"].as( lodMetadata.levels[i].indices );
|
||||
lodMetadata.levels[i].indexID = value["indexID"].as( lodMetadata.levels[i].indexID );
|
||||
lodMetadata.levels[i].vertices = value["vertices"].as( lodMetadata.levels[i].vertices );
|
||||
lodMetadata.levels[i].vertexID = value["vertexID"].as( lodMetadata.levels[i].vertexID );
|
||||
});
|
||||
return lodMetadata;
|
||||
}
|
||||
|
||||
@ -142,6 +142,8 @@ namespace {
|
||||
auto& value = json.emplace_back();
|
||||
value["indices"] = lodMetadata.levels[i].indices;
|
||||
value["indexID"] = lodMetadata.levels[i].indexID;
|
||||
value["vertexID"] = lodMetadata.levels[i].vertexID;
|
||||
value["vertices"] = lodMetadata.levels[i].vertices;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
@ -23,11 +23,7 @@
|
||||
#define UF_DEBUG_TIMER_MULTITRACE_END(...)
|
||||
#endif
|
||||
|
||||
#if UF_USE_OPENGL
|
||||
#define UF_GRAPH_SPARSE_READ_MESH 1
|
||||
#else
|
||||
#define UF_GRAPH_SPARSE_READ_MESH 1
|
||||
#endif
|
||||
#define UF_GRAPH_SPARSE_READ_MESH 1
|
||||
#define UF_GRAPH_EXTENDED 1
|
||||
|
||||
namespace {
|
||||
@ -1401,21 +1397,19 @@ void uf::graph::tick( uf::Object& object ) {
|
||||
bool uf::graph::tick( pod::Graph::Storage& storage ) {
|
||||
bool rebuild = false;
|
||||
|
||||
static thread_local uf::stl::vector<pod::Instance> instances;
|
||||
static thread_local uf::stl::vector<pod::Instance::Addresses> instanceAddresses;
|
||||
static thread_local uf::stl::vector<pod::LODMetadata> lodMetadata;
|
||||
static thread_local uf::stl::vector<pod::Matrix4f> joints;
|
||||
static thread_local uf::stl::vector<pod::Instance::Object> objects;
|
||||
static thread_local uf::stl::vector<pod::Material> materials;
|
||||
static thread_local uf::stl::vector<pod::Texture> textures;
|
||||
static thread_local uf::stl::vector<pod::DrawCommand> drawCommands;
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Instance>, instances);
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Instance::Addresses>, instanceAddresses);
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::LODMetadata>, lodMetadata);
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Matrix4f>, joints);
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Instance::Object>, objects);
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Material>, materials);
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Texture>, textures);
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::DrawCommand>, drawCommands);
|
||||
|
||||
joints.clear();
|
||||
for ( auto& key : storage.joints.keys ) {
|
||||
joints.insert( joints.end(), storage.joints.map[key].begin(), storage.joints.map[key].end() );
|
||||
}
|
||||
|
||||
objects.clear();
|
||||
for ( auto& key : storage.objects.keys ) {
|
||||
auto& entity = *storage.entities.map[key];
|
||||
auto& object = storage.objects.map[key];
|
||||
@ -1437,10 +1431,6 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) {
|
||||
rebuild = storage.buffers.object.update( (const void*) objects.data(), objects.size() * sizeof(pod::Instance::Object) ) || rebuild;
|
||||
|
||||
if ( ::newGraphAdded ) {
|
||||
drawCommands.clear();
|
||||
instances.clear();
|
||||
lodMetadata.clear();
|
||||
|
||||
for ( auto& key : storage.primitives.keys ) {
|
||||
for ( auto& primitive : storage.primitives[key] ) {
|
||||
drawCommands.emplace_back( primitive.drawCommand );
|
||||
@ -1449,15 +1439,12 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) {
|
||||
}
|
||||
}
|
||||
|
||||
instanceAddresses.clear();
|
||||
for ( auto& key : storage.instanceAddresses.keys ) {
|
||||
instanceAddresses.insert( instanceAddresses.end(), storage.instanceAddresses.map[key].begin(), storage.instanceAddresses.map[key].end() );
|
||||
}
|
||||
|
||||
textures.clear();
|
||||
for ( auto& key : storage.textures.keys ) textures.emplace_back( storage.textures.map[key] );
|
||||
|
||||
materials.clear();
|
||||
for ( auto& key : storage.materials.keys ) materials.emplace_back( storage.materials.map[key] );
|
||||
|
||||
rebuild = storage.buffers.instance.update( (const void*) instances.data(), instances.size() * sizeof(pod::Instance) ) || rebuild;
|
||||
|
||||
@ -208,15 +208,14 @@ void uf::scene::unloadScene() {
|
||||
uf::Scene* current = uf::scene::scenes.back();
|
||||
current->queueDeletion();
|
||||
|
||||
// destroy phyiscs state
|
||||
// destroy graph
|
||||
if ( current->hasComponent<pod::Graph::Storage>() ) {
|
||||
uf::graph::destroy( current->getComponent<pod::Graph::Storage>() );
|
||||
}
|
||||
#if 0
|
||||
if ( current->hasComponent<uf::physics::WorldState>() ) {
|
||||
// destroy physics state
|
||||
if ( current->hasComponent<pod::World>() ) {
|
||||
uf::physics::destroy( *current );
|
||||
}
|
||||
#endif
|
||||
|
||||
// mark rendermodes as disabled immediately
|
||||
auto graph = current->getGraph(true);
|
||||
|
||||
@ -296,7 +296,7 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
|
||||
meshgrid.metadata = value["grid"];
|
||||
});
|
||||
|
||||
#if UF_USE_MESHOPT
|
||||
#if 0 && UF_USE_MESHOPT
|
||||
// cleanup if blender's exporter is poopy
|
||||
if ( graph.metadata["exporter"]["optimize"].as<bool>(false) || graph.metadata["exporter"]["optimize"].as<uf::stl::string>("") == "tagged" ) {
|
||||
if ( graph.metadata["exporter"]["optimize"].as<uf::stl::string>("") == "tagged" ) {
|
||||
@ -518,7 +518,7 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
|
||||
#endif
|
||||
#if UF_USE_MESHOPT
|
||||
// cleanup if blender's exporter is poopy
|
||||
if ( graph.metadata["exporter"]["optimize"].as<bool>(false) || graph.metadata["exporter"]["optimize"].as<uf::stl::string>("") == "tagged" ) {
|
||||
if ( graph.metadata["exporter"]["optimize"].as<bool>(false) || graph.metadata["exporter"]["optimize"].as<uf::stl::string>("") == "tagged" || ext::json::isObject( graph.metadata["exporter"]["optimize"] ) ) {
|
||||
UF_MSG_DEBUG( "Optimizing meshes..." );
|
||||
for ( auto& keyName : graph.meshes ) {
|
||||
size_t level = SIZE_MAX;
|
||||
@ -544,6 +544,11 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
|
||||
});
|
||||
|
||||
if ( !should ) continue;
|
||||
} else if ( ext::json::isObject( graph.metadata["exporter"]["optimize"] ) ) {
|
||||
level = graph.metadata["exporter"]["optimize"]["level"].as( level );
|
||||
simplify = graph.metadata["exporter"]["optimize"]["simplify"].as( simplify );
|
||||
print = graph.metadata["exporter"]["optimize"]["print"].as( print );
|
||||
lods = graph.metadata["exporter"]["optimize"]["lods"].as( lods );
|
||||
}
|
||||
|
||||
auto& mesh = storage.meshes[keyName];
|
||||
@ -559,7 +564,7 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
|
||||
} else {
|
||||
UF_MSG_DEBUG("Generated {} LODs: {}", factors.size() - 1, keyName);
|
||||
auto& primitives = storage.primitives[keyName];
|
||||
UF_ASSERT( primitives.size() == lodMetadata.size() );
|
||||
UF_ASSERT( primitives.size() == lodMetadata.size() );
|
||||
for ( auto i = 0; i < primitives.size(); ++i ) primitives[i].lod = lodMetadata[i];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,102 +1,162 @@
|
||||
#include <uf/ext/meshopt/meshopt.h>
|
||||
|
||||
#if UF_USE_MESHOPT
|
||||
#include <meshoptimizer.h>
|
||||
#include <cfloat>
|
||||
|
||||
namespace {
|
||||
uint32_t readIndex(const uint8_t* ptr, size_t index, size_t size) {
|
||||
switch (size) {
|
||||
case 1: return ((const uint8_t*)ptr)[index];
|
||||
case 2: return ((const uint16_t*)ptr)[index];
|
||||
case 4: return ((const uint32_t*)ptr)[index];
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
void writeIndex(uint8_t* ptr, size_t index, size_t size, uint32_t value) {
|
||||
switch (size) {
|
||||
case 1: ((uint8_t*)ptr)[index] = (uint8_t)value; break;
|
||||
case 2: ((uint16_t*)ptr)[index] = (uint16_t)value; break;
|
||||
case 4: ((uint32_t*)ptr)[index] = (uint32_t)value; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ext::meshopt::optimize( uf::Mesh& mesh, float simplify, size_t o, bool verbose ) {
|
||||
if ( mesh.isInterleaved() ) {
|
||||
UF_MSG_ERROR("Optimization of interleaved meshes is currently not supported. Consider optimizing on meshlets.");
|
||||
UF_MSG_ERROR("Optimization of interleaved meshes is currently not supported.");
|
||||
return false;
|
||||
}
|
||||
mesh.updateDescriptor();
|
||||
|
||||
const auto& views = mesh.buffer_views;
|
||||
if ( views.empty() ) {
|
||||
UF_MSG_ERROR("No buffer views found. Cannot optimize per-submesh.");
|
||||
return false;
|
||||
}
|
||||
if ( views.empty() ) return false;
|
||||
|
||||
uf::stl::vector<uint32_t> optIndices;
|
||||
pod::DrawCommand* drawCommands = mesh.indirect.count > 0 ? (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data() : nullptr;
|
||||
const uint8_t* srcIndexData = mesh.index.count > 0 ? mesh.getBuffer(mesh.index).data() : nullptr;
|
||||
|
||||
uf::stl::vector<uint32_t> outIndices;
|
||||
uf::stl::vector<uf::stl::vector<uint8_t>> outVertices(mesh.vertex.attributes.size());
|
||||
|
||||
uf::Mesh::Attribute positionAttribute;
|
||||
for ( auto& attr : mesh.vertex.attributes ) if ( attr.descriptor.name == "position" ) positionAttribute = attr;
|
||||
|
||||
for ( size_t viewIdx = 0; viewIdx < views.size(); ++viewIdx ) {
|
||||
const auto& view = views[viewIdx];
|
||||
auto& indicesView = view["index"];
|
||||
auto& positionsView = view["position"];
|
||||
uint32_t cmdIdx = view.indirectIndex;
|
||||
|
||||
if ( !indicesView.valid() || !positionsView.valid() ) continue;
|
||||
uint32_t srcVertexOffset = view.vertex.first;
|
||||
uint32_t srcVertexCount = view.vertex.count;
|
||||
uint32_t srcIndexOffset = view.index.first;
|
||||
uint32_t srcIndexCount = view.index.count;
|
||||
|
||||
size_t indicesCount = view.index.count;
|
||||
size_t vertexCount = view.vertex.count;
|
||||
if ( srcIndexCount == 0 ) continue;
|
||||
|
||||
uf::stl::vector<uint32_t> submeshIndices(indicesCount);
|
||||
for ( size_t i = 0; i < indicesCount; ++i ) {
|
||||
size_t global_i = view.index.first + i;
|
||||
switch ( indicesView.attribute.descriptor.size ) {
|
||||
case 1: submeshIndices[i] = indicesView.get<uint8_t>(global_i)[0]; break;
|
||||
case 2: submeshIndices[i] = indicesView.get<uint16_t>(global_i)[0]; break;
|
||||
case 4: submeshIndices[i] = indicesView.get<uint32_t>(global_i)[0]; break;
|
||||
// retrieve indices
|
||||
uf::stl::vector<uint32_t> localIndices(srcIndexCount);
|
||||
if ( srcIndexData ) {
|
||||
for ( size_t i = 0; i < srcIndexCount; ++i ) {
|
||||
localIndices[i] = readIndex(srcIndexData, srcIndexOffset + i, mesh.index.size);
|
||||
}
|
||||
} else {
|
||||
for ( size_t i = 0; i < srcIndexCount; ++i ) localIndices[i] = i;
|
||||
}
|
||||
|
||||
meshopt_optimizeVertexCache(&submeshIndices[0], &submeshIndices[0], indicesCount, mesh.vertex.count);
|
||||
// setup streams
|
||||
uf::stl::vector<meshopt_Stream> streams;
|
||||
for ( auto& attr : mesh.vertex.attributes ) {
|
||||
const uint8_t* basePtr = (const uint8_t*)attr.pointer + srcVertexOffset * attr.stride;
|
||||
streams.emplace_back({ basePtr, attr.descriptor.size, attr.stride });
|
||||
}
|
||||
|
||||
meshopt_optimizeOverdraw(
|
||||
&submeshIndices[0],
|
||||
&submeshIndices[0],
|
||||
indicesCount,
|
||||
(const float*) positionsView.data(),
|
||||
mesh.vertex.count,
|
||||
positionsView.stride(),
|
||||
1.05f
|
||||
// deduplicate vertices
|
||||
uf::stl::vector<uint32_t> remap(srcVertexCount);
|
||||
size_t uniqueVertices = meshopt_generateVertexRemapMulti(
|
||||
remap.data(), localIndices.data(), srcIndexCount,
|
||||
srcVertexCount, streams.data(), streams.size()
|
||||
);
|
||||
meshopt_remapIndexBuffer(localIndices.data(), localIndices.data(), srcIndexCount, remap.data());
|
||||
|
||||
// copy position data
|
||||
uf::stl::vector<uint8_t> tempPositions(uniqueVertices * positionAttribute.stride);
|
||||
const uint8_t* srcPositions = (const uint8_t*)positionAttribute.pointer + srcVertexOffset * positionAttribute.stride;
|
||||
meshopt_remapVertexBuffer(tempPositions.data(), srcPositions, srcVertexCount, positionAttribute.stride, remap.data());
|
||||
|
||||
// optimize cache + overdray
|
||||
meshopt_optimizeVertexCache(localIndices.data(), localIndices.data(), srcIndexCount, uniqueVertices);
|
||||
meshopt_optimizeOverdraw(localIndices.data(), localIndices.data(), srcIndexCount, (const float*)tempPositions.data(), uniqueVertices, positionAttribute.stride, 1.05f);
|
||||
|
||||
// simplify
|
||||
size_t optimizedIndexCount = srcIndexCount;
|
||||
if ( 0.0f < simplify && simplify < 1.0f ) {
|
||||
uf::stl::vector<uint32_t> indicesSimplified(indicesCount);
|
||||
|
||||
float targetError = FLT_MAX; // 1e-2f / simplify;
|
||||
uf::stl::vector<uint32_t> simplified(srcIndexCount);
|
||||
float targetError = 1e-2f / simplify;
|
||||
float realError = 0.0f;
|
||||
|
||||
size_t realIndices = meshopt_simplify(
|
||||
&indicesSimplified[0],
|
||||
&submeshIndices[0],
|
||||
indicesCount,
|
||||
(const float*) positionsView.data(),
|
||||
mesh.vertex.count,
|
||||
positionsView.stride(),
|
||||
indicesCount * simplify,
|
||||
targetError,
|
||||
0, &realError
|
||||
optimizedIndexCount = meshopt_simplify(
|
||||
simplified.data(), localIndices.data(), srcIndexCount,
|
||||
(const float*)tempPositions.data(), uniqueVertices, positionAttribute.stride,
|
||||
srcIndexCount * simplify, targetError, meshopt_SimplifyLockBorder, &realError
|
||||
);
|
||||
|
||||
if ( verbose ) {
|
||||
UF_MSG_DEBUG("[View {} Simplified] indices: {} -> {} | error: {} -> {}", viewIdx, indicesCount, realIndices, targetError, realError);
|
||||
}
|
||||
|
||||
indicesCount = realIndices;
|
||||
submeshIndices.swap(indicesSimplified);
|
||||
submeshIndices.resize(indicesCount);
|
||||
if ( verbose ) UF_MSG_DEBUG("[View {}] Simplified: {} -> {}", viewIdx, srcIndexCount, optimizedIndexCount);
|
||||
localIndices.swap(simplified);
|
||||
localIndices.resize(optimizedIndexCount);
|
||||
}
|
||||
|
||||
size_t newIndexStart = optIndices.size();
|
||||
optIndices.insert(optIndices.end(), submeshIndices.begin(), submeshIndices.end());
|
||||
// optimize for vertex fetch
|
||||
uf::stl::vector<uint32_t> fetchRemap(uniqueVertices);
|
||||
size_t finalVertices = meshopt_optimizeVertexFetchRemap(fetchRemap.data(), localIndices.data(), optimizedIndexCount, uniqueVertices);
|
||||
meshopt_remapIndexBuffer(localIndices.data(), localIndices.data(), optimizedIndexCount, fetchRemap.data());
|
||||
|
||||
// store to output buffer
|
||||
uint32_t outVertexOffset = outVertices[0].size() / mesh.vertex.attributes[0].stride;
|
||||
uint32_t outIndexOffset = outIndices.size();
|
||||
|
||||
for ( size_t i = 0; i < localIndices.size(); ++i ) outIndices.emplace_back( localIndices[i] );
|
||||
|
||||
// remap buffers
|
||||
for ( size_t a = 0; a < mesh.vertex.attributes.size(); ++a ) {
|
||||
auto& attr = mesh.vertex.attributes[a];
|
||||
const uint8_t* basePtr = (const uint8_t*) attr.pointer + srcVertexOffset * attr.stride;
|
||||
|
||||
// double remap: source -> unique -> final
|
||||
uf::stl::vector<uint8_t> tempBuf(uniqueVertices * attr.stride);
|
||||
meshopt_remapVertexBuffer(tempBuf.data(), basePtr, srcVertexCount, attr.stride, remap.data());
|
||||
|
||||
uf::stl::vector<uint8_t> finalBuf(finalVertices * attr.stride);
|
||||
meshopt_remapVertexBuffer(finalBuf.data(), tempBuf.data(), uniqueVertices, attr.stride, fetchRemap.data());
|
||||
|
||||
outVertices[a].insert(outVertices[a].end(), finalBuf.begin(), finalBuf.end());
|
||||
}
|
||||
|
||||
// update indirect buffer
|
||||
if ( drawCommands ) {
|
||||
drawCommands[view.indirectIndex].indexID = newIndexStart;
|
||||
drawCommands[view.indirectIndex].indices = indicesCount;
|
||||
drawCommands[cmdIdx].indexID = outIndexOffset;
|
||||
drawCommands[cmdIdx].indices = optimizedIndexCount;
|
||||
drawCommands[cmdIdx].vertexID = outVertexOffset;
|
||||
drawCommands[cmdIdx].vertices = finalVertices;
|
||||
}
|
||||
}
|
||||
|
||||
mesh.index.count = optIndices.size();
|
||||
mesh.resizeIndices( mesh.index.count );
|
||||
// apply index buffer (if missing)
|
||||
if ( mesh.index.attributes.empty() ) {
|
||||
mesh.bindIndex<uint32_t>();
|
||||
mesh.bind(mesh, mesh.isInterleaved());
|
||||
}
|
||||
|
||||
uint8_t* dstPointer = (uint8_t*) mesh.getBuffer(mesh.index).data();
|
||||
for ( size_t i = 0; i < optIndices.size(); ++i ) {
|
||||
switch ( mesh.index.size ) {
|
||||
case 1: (( uint8_t*) dstPointer)[i] = (uint8_t) optIndices[i]; break;
|
||||
case 2: ((uint16_t*) dstPointer)[i] = (uint16_t) optIndices[i]; break;
|
||||
case 4: ((uint32_t*) dstPointer)[i] = (uint32_t) optIndices[i]; break;
|
||||
}
|
||||
// write indices to buffer
|
||||
mesh.index.count = outIndices.size();
|
||||
mesh.resizeIndices(mesh.index.count);
|
||||
uint8_t* dstIdx = mesh.getBuffer(mesh.index).data();
|
||||
for ( size_t i = 0; i < outIndices.size(); ++i ) writeIndex(dstIdx, i, mesh.index.size, outIndices[i]);
|
||||
|
||||
// write vertices to buffer
|
||||
mesh.vertex.count = outVertices[0].size() / mesh.vertex.attributes[0].stride;
|
||||
for ( size_t a = 0; a < mesh.vertex.attributes.size(); ++a ) {
|
||||
auto& attr = mesh.vertex.attributes[a];
|
||||
mesh.buffers[attr.buffer].swap(outVertices[a]);
|
||||
attr.pointer = mesh.buffers[attr.buffer].data();
|
||||
}
|
||||
|
||||
mesh.updateDescriptor();
|
||||
@ -128,103 +188,128 @@ uf::stl::vector<pod::LODMetadata> ext::meshopt::generateLODs( uf::Mesh& mesh, co
|
||||
mesh.updateDescriptor();
|
||||
|
||||
const auto& views = mesh.buffer_views;
|
||||
if ( views.empty() ) return lodMetadata;
|
||||
if ( views.empty() || lodFactors.empty() ) return lodMetadata;
|
||||
|
||||
size_t numLODs = std::min(lodFactors.size(), (size_t)4);
|
||||
lodMetadata.resize(mesh.indirect.count);
|
||||
|
||||
uf::stl::vector<uf::stl::vector<uint32_t>> lodBlocks(numLODs);
|
||||
pod::DrawCommand* drawCommands = mesh.indirect.count > 0 ? (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data() : nullptr;
|
||||
if ( !drawCommands ) return lodMetadata;
|
||||
|
||||
// store LOD0 as-is
|
||||
uf::stl::vector<uint32_t> outIndices(mesh.index.count);
|
||||
const uint8_t* srcIndexData = mesh.getBuffer(mesh.index).data();
|
||||
for ( size_t i = 0; i < mesh.index.count; ++i ) {
|
||||
outIndices[i] = readIndex(srcIndexData, i, mesh.index.size);
|
||||
}
|
||||
// write LOD0 data
|
||||
for ( size_t viewIdx = 0; viewIdx < views.size(); ++viewIdx ) {
|
||||
const auto& view = views[viewIdx];
|
||||
uint32_t cmdIdx = view.indirectIndex;
|
||||
|
||||
auto& indicesView = view["index"];
|
||||
auto& positionsView = view["position"];
|
||||
|
||||
size_t baseIndicesCount = view.index.count;
|
||||
uf::stl::vector<uint32_t> baseIndices(baseIndicesCount);
|
||||
|
||||
for ( size_t i = 0; i < baseIndicesCount; ++i ) {
|
||||
size_t global_i = view.index.first + i;
|
||||
switch ( indicesView.attribute.descriptor.size ) {
|
||||
case 1: baseIndices[i] = indicesView.get<uint8_t>(global_i)[0]; break;
|
||||
case 2: baseIndices[i] = indicesView.get<uint16_t>(global_i)[0]; break;
|
||||
case 4: baseIndices[i] = indicesView.get<uint32_t>(global_i)[0]; break;
|
||||
}
|
||||
}
|
||||
|
||||
meshopt_optimizeVertexCache(&baseIndices[0], &baseIndices[0], baseIndicesCount, mesh.vertex.count);
|
||||
|
||||
size_t previousIndicesCount = baseIndicesCount;
|
||||
for ( size_t lodIdx = 0; lodIdx < numLODs; ++lodIdx ) {
|
||||
float simplify = lodFactors[lodIdx];
|
||||
uf::stl::vector<uint32_t> lodIndices = baseIndices;
|
||||
size_t currentIndicesCount = baseIndicesCount;
|
||||
|
||||
if ( simplify < 1.0f ) {
|
||||
float targetError = FLT_MAX; // 1e-2f / simplify;
|
||||
float realError = 0.0f;
|
||||
currentIndicesCount = meshopt_simplify(
|
||||
&lodIndices[0], &baseIndices[0], baseIndicesCount,
|
||||
(const float*)positionsView.data(0), mesh.vertex.count, positionsView.stride(),
|
||||
baseIndicesCount * simplify, targetError,
|
||||
0, &realError
|
||||
);
|
||||
|
||||
if ( previousIndicesCount == currentIndicesCount ) {
|
||||
continue;
|
||||
}
|
||||
previousIndicesCount = currentIndicesCount;
|
||||
|
||||
if ( verbose ) {
|
||||
UF_MSG_DEBUG("[View {} Simplified LOD {}] indices: {} -> {} | error: {} -> {}", viewIdx, lodIdx, baseIndicesCount, currentIndicesCount, targetError, realError);
|
||||
}
|
||||
|
||||
|
||||
lodIndices.resize(currentIndicesCount);
|
||||
}
|
||||
|
||||
lodMetadata[cmdIdx].levels[lodIdx].indexID = lodBlocks[lodIdx].size();
|
||||
lodMetadata[cmdIdx].levels[lodIdx].indices = currentIndicesCount;
|
||||
|
||||
lodBlocks[lodIdx].insert(lodBlocks[lodIdx].end(), lodIndices.begin(), lodIndices.end());
|
||||
}
|
||||
uint32_t cmdIdx = views[viewIdx].indirectIndex;
|
||||
auto& cmd = drawCommands[cmdIdx];
|
||||
lodMetadata[cmdIdx].levels[0].indexID = cmd.indexID;
|
||||
lodMetadata[cmdIdx].levels[0].indices = cmd.indices;
|
||||
lodMetadata[cmdIdx].levels[0].vertexID = cmd.vertexID;
|
||||
lodMetadata[cmdIdx].levels[0].vertices = cmd.vertices;
|
||||
}
|
||||
|
||||
uf::stl::vector<uint32_t> unifiedIndices;
|
||||
size_t currentGlobalOffset = 0;
|
||||
// copy position attribute
|
||||
int posAttrIdx = -1;
|
||||
uf::stl::vector<uf::stl::vector<uint8_t>> outVertices(mesh.vertex.attributes.size());
|
||||
for ( size_t a = 0; a < mesh.vertex.attributes.size(); ++a ) {
|
||||
auto& attr = mesh.vertex.attributes[a];
|
||||
if ( attr.descriptor.name == "position" ) posAttrIdx = a;
|
||||
|
||||
auto& buf = mesh.buffers[attr.buffer];
|
||||
outVertices[a].assign(buf.begin(), buf.end());
|
||||
}
|
||||
|
||||
|
||||
// generate LOD1=>N
|
||||
for ( size_t lodIdx = 1; lodIdx < numLODs; ++lodIdx ) {
|
||||
float simplify = lodFactors[lodIdx];
|
||||
|
||||
for ( size_t lodIdx = 0; lodIdx < numLODs; ++lodIdx ) {
|
||||
for ( size_t viewIdx = 0; viewIdx < views.size(); ++viewIdx ) {
|
||||
uint32_t cmdIdx = views[viewIdx].indirectIndex;
|
||||
lodMetadata[cmdIdx].levels[lodIdx].indexID += currentGlobalOffset;
|
||||
|
||||
if ( lodIdx == 0 && drawCommands ) {
|
||||
drawCommands[cmdIdx].indexID = lodMetadata[cmdIdx].levels[0].indexID;
|
||||
drawCommands[cmdIdx].indices = lodMetadata[cmdIdx].levels[0].indices;
|
||||
// source from LOD0
|
||||
auto& cmd0 = lodMetadata[cmdIdx].levels[0];
|
||||
size_t previousIndicesCount = lodMetadata[cmdIdx].levels[lodIdx - 1].indices;
|
||||
|
||||
uf::stl::vector<uint32_t> baseIndices(cmd0.indices);
|
||||
for ( size_t i = 0; i < cmd0.indices; ++i ) baseIndices[i] = outIndices[cmd0.indexID + i];
|
||||
|
||||
// generate LOD
|
||||
if ( 0.0f < simplify && simplify < 1.0f ) {
|
||||
float targetError = 1e-2f / simplify;
|
||||
float realError = 0.0f;
|
||||
size_t currentIndicesCount = cmd0.indices;
|
||||
uf::stl::vector<uint32_t> lodIndices = baseIndices;
|
||||
|
||||
const float* basePositions = (const float*) (outVertices[posAttrIdx].data() + cmd0.vertexID * mesh.vertex.attributes[posAttrIdx].stride);
|
||||
|
||||
currentIndicesCount = meshopt_simplify(
|
||||
lodIndices.data(), baseIndices.data(), cmd0.indices,
|
||||
basePositions, cmd0.vertices, mesh.vertex.attributes[posAttrIdx].stride,
|
||||
cmd0.indices * simplify, targetError, meshopt_SimplifyLockBorder, &realError
|
||||
);
|
||||
|
||||
// couldn't simplify further, use previous LOD
|
||||
if ( currentIndicesCount == previousIndicesCount ) {
|
||||
lodMetadata[cmdIdx].levels[lodIdx] = lodMetadata[cmdIdx].levels[lodIdx - 1];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( verbose ) UF_MSG_DEBUG("[View {}] LOD {}: {} -> {}", viewIdx, lodIdx, cmd0.indices, currentIndicesCount);
|
||||
|
||||
lodIndices.resize(currentIndicesCount);
|
||||
|
||||
// optimize and pack vertices for this specific LOD
|
||||
uf::stl::vector<uint32_t> fetchRemap(cmd0.vertices);
|
||||
size_t uniqueVertices = meshopt_optimizeVertexFetchRemap(fetchRemap.data(), lodIndices.data(), currentIndicesCount, cmd0.vertices);
|
||||
meshopt_remapIndexBuffer(lodIndices.data(), lodIndices.data(), currentIndicesCount, fetchRemap.data());
|
||||
|
||||
// record the new offsets appended at the end of the global buffers
|
||||
uint32_t lodVertexOffset = outVertices[0].size() / mesh.vertex.attributes[0].stride;
|
||||
uint32_t lodIndexOffset = outIndices.size();
|
||||
|
||||
lodMetadata[cmdIdx].levels[lodIdx].indexID = lodIndexOffset;
|
||||
lodMetadata[cmdIdx].levels[lodIdx].indices = currentIndicesCount;
|
||||
lodMetadata[cmdIdx].levels[lodIdx].vertexID = lodVertexOffset;
|
||||
lodMetadata[cmdIdx].levels[lodIdx].vertices = uniqueVertices;
|
||||
|
||||
// append indices
|
||||
for ( size_t i = 0; i < currentIndicesCount; ++i ) outIndices.emplace_back(lodIndices[i]);
|
||||
// append vertices
|
||||
for ( size_t a = 0; a < mesh.vertex.attributes.size(); ++a ) {
|
||||
auto& attr = mesh.vertex.attributes[a];
|
||||
const uint8_t* srcPtr = outVertices[a].data() + cmd0.vertexID * attr.stride;
|
||||
|
||||
uf::stl::vector<uint8_t> packed(uniqueVertices * attr.stride);
|
||||
meshopt_remapVertexBuffer(packed.data(), srcPtr, cmd0.vertices, attr.stride, fetchRemap.data());
|
||||
outVertices[a].insert(outVertices[a].end(), packed.begin(), packed.end());
|
||||
}
|
||||
} else {
|
||||
// no simplification, just use LOD0 (shouldn't happen)
|
||||
lodMetadata[cmdIdx].levels[lodIdx] = lodMetadata[cmdIdx].levels[0];
|
||||
}
|
||||
}
|
||||
|
||||
unifiedIndices.insert(unifiedIndices.end(), lodBlocks[lodIdx].begin(), lodBlocks[lodIdx].end());
|
||||
currentGlobalOffset = unifiedIndices.size();
|
||||
}
|
||||
|
||||
mesh.index.count = unifiedIndices.size();
|
||||
mesh.resizeIndices( mesh.index.count );
|
||||
uint8_t* dstPointer = (uint8_t*) mesh.getBuffer(mesh.index).data();
|
||||
for ( size_t i = 0; i < unifiedIndices.size(); ++i ) {
|
||||
switch ( mesh.index.size ) {
|
||||
case 1: (( uint8_t*) dstPointer)[i] = (uint8_t) unifiedIndices[i]; break;
|
||||
case 2: ((uint16_t*) dstPointer)[i] = (uint16_t) unifiedIndices[i]; break;
|
||||
case 4: ((uint32_t*) dstPointer)[i] = (uint32_t) unifiedIndices[i]; break;
|
||||
}
|
||||
// write indices to mesh
|
||||
mesh.index.count = outIndices.size();
|
||||
mesh.resizeIndices(mesh.index.count);
|
||||
uint8_t* dstIdx = mesh.getBuffer(mesh.index).data();
|
||||
for ( size_t i = 0; i < outIndices.size(); ++i ) writeIndex(dstIdx, i, mesh.index.size, outIndices[i]);
|
||||
|
||||
// write vertices to mesh
|
||||
mesh.vertex.count = outVertices[0].size() / mesh.vertex.attributes[0].stride;
|
||||
for ( size_t a = 0; a < mesh.vertex.attributes.size(); ++a ) {
|
||||
auto& attr = mesh.vertex.attributes[a];
|
||||
mesh.buffers[attr.buffer].swap(outVertices[a]);
|
||||
attr.pointer = mesh.buffers[attr.buffer].data();
|
||||
}
|
||||
|
||||
mesh.updateDescriptor();
|
||||
|
||||
return lodMetadata;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -399,8 +399,7 @@ void ext::vulkan::Pipeline::record( const Graphic& graphic, const GraphicDescrip
|
||||
auto shaders = getShaders( graphic.material.shaders, descriptor.pipeline );
|
||||
|
||||
// create dynamic offset ranges
|
||||
static thread_local uf::stl::vector<uint32_t> dynamicOffsets;
|
||||
dynamicOffsets.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<uint32_t>, dynamicOffsets);
|
||||
|
||||
RenderMode& renderMode = ext::vulkan::getRenderMode(descriptor.renderMode, true);
|
||||
|
||||
|
||||
@ -221,8 +221,7 @@ ext::vulkan::GraphicDescriptor ext::vulkan::RenderMode::bindGraphicDescriptor( c
|
||||
}
|
||||
|
||||
void ext::vulkan::RenderMode::createCommandBuffers() {
|
||||
static thread_local uf::stl::vector<ext::vulkan::Graphic*> graphics;
|
||||
graphics.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<ext::vulkan::Graphic*>, graphics);
|
||||
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
auto/*&*/ graph = scene.getGraph();
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#if UF_USE_XATLAS
|
||||
#include <xatlas/xatlas.h>
|
||||
|
||||
#define UF_XATLAS_UNWRAP_MULTITHREAD 0 // prone to crashing
|
||||
#define UF_XATLAS_UNWRAP_MULTITHREAD 1 // prone to crashing
|
||||
|
||||
size_t ext::xatlas::unwrap( pod::Graph& graph ) {
|
||||
struct Entry {
|
||||
|
||||
@ -68,7 +68,7 @@ pod::BVH::index_t impl::buildBVHNode_SAH( pod::BVH& bvh, const uf::stl::vector<p
|
||||
}
|
||||
|
||||
constexpr auto numBins = 16;
|
||||
static thread_local Bin bins[numBins];
|
||||
static thread_local Bin bins[numBins]; // do I even need to make this a static buffer......
|
||||
|
||||
auto extent = bound.max - bound.min;
|
||||
auto bestAxis = -1, bestSplit = -1;
|
||||
@ -813,8 +813,7 @@ void impl::queryFlatOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, pod::B
|
||||
if ( nodesA.empty() || nodesB.empty() ) return;
|
||||
outPairs.reserve(uf::physics::settings.reserveCount);
|
||||
|
||||
static thread_local uf::stl::vector<std::pair<pod::BVH::index_t, pod::BVH::index_t>> stack;
|
||||
stack.clear();
|
||||
STATIC_THREAD_LOCAL(pod::BVH::pairs_t, stack);
|
||||
stack.emplace_back(0, 0);
|
||||
|
||||
while ( !stack.empty() ) {
|
||||
@ -879,8 +878,7 @@ void impl::queryFlatOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, const
|
||||
if ( nodesA.empty() || nodesB.empty() ) return;
|
||||
outPairs.reserve(uf::physics::settings.reserveCount);
|
||||
|
||||
static thread_local uf::stl::vector<std::pair<pod::BVH::index_t, pod::BVH::index_t>> stack;
|
||||
stack.clear();
|
||||
STATIC_THREAD_LOCAL(pod::BVH::pairs_t, stack);
|
||||
stack.emplace_back(0, 0);
|
||||
|
||||
while ( !stack.empty() ) {
|
||||
|
||||
@ -48,8 +48,8 @@ void impl::buildIslands( const pod::BVH::pairs_t& pairs, const uf::stl::vector<p
|
||||
}
|
||||
|
||||
// map root to island index
|
||||
static thread_local uf::stl::unordered_map<pod::BVH::index_t, pod::BVH::index_t> rootToIsland;
|
||||
rootToIsland.clear();
|
||||
typedef uf::stl::unordered_map<pod::BVH::index_t, pod::BVH::index_t> map_t;
|
||||
STATIC_THREAD_LOCAL(map_t, rootToIsland);
|
||||
|
||||
islands.clear();
|
||||
islands.reserve(bodies.size());
|
||||
|
||||
@ -133,8 +133,7 @@ void uf::physics::step( pod::World& world, float dt ) {
|
||||
// iterate islands
|
||||
#pragma omp parallel for schedule(dynamic)
|
||||
for ( auto& island : islands ) {
|
||||
static thread_local uf::stl::vector<pod::Manifold> manifolds;
|
||||
manifolds.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Manifold>, manifolds);
|
||||
manifolds.reserve(uf::physics::settings.reserveCount);
|
||||
|
||||
// sleeping island, skip (asleep islands shouldn't ever be in here)
|
||||
@ -533,8 +532,7 @@ pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world
|
||||
auto& staticBvh = world.staticBvh;
|
||||
auto& bodies = world.bodies;
|
||||
|
||||
static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
|
||||
candidates.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::BVH::index_t>, candidates);
|
||||
impl::queryBVH( dynamicBvh, ray, candidates );
|
||||
if ( uf::physics::settings.useSplitBvhs ) impl::queryBVH( staticBvh, ray, candidates );
|
||||
|
||||
|
||||
@ -152,8 +152,7 @@ bool impl::similarContact( const pod::Contact& a, const pod::Contact& b, float d
|
||||
void impl::reduceContacts( pod::Manifold& manifold ) {
|
||||
if ( manifold.points.size() <= 4 ) return;
|
||||
|
||||
static thread_local uf::stl::vector<pod::Contact> result;
|
||||
result.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Contact>, result);
|
||||
result.reserve(4);
|
||||
|
||||
for ( auto& c : manifold.points ) {
|
||||
@ -184,8 +183,7 @@ void impl::reduceContacts( pod::Manifold& manifold ) {
|
||||
}
|
||||
|
||||
void impl::mergeContacts( pod::Manifold& manifold ) {
|
||||
static thread_local uf::stl::vector<pod::Contact> result;
|
||||
result.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Contact>, result);
|
||||
result.reserve(4);
|
||||
|
||||
for ( auto& c : manifold.points ) {
|
||||
|
||||
@ -12,8 +12,7 @@ namespace impl {
|
||||
|
||||
// transform to local space for BVH query
|
||||
auto bounds = impl::transformAabbToLocal( body.bounds, impl::getTransform( hull ) );
|
||||
static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
|
||||
candidates.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::BVH::index_t>, candidates);
|
||||
impl::queryBVH( bvh, bounds, candidates );
|
||||
|
||||
bool hit = false;
|
||||
@ -60,8 +59,7 @@ bool impl::hullHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
|
||||
auto tB = impl::getTransform( b );
|
||||
auto relTransform = uf::transform::relative( tA, tB );
|
||||
|
||||
static thread_local pod::BVH::pairs_t pairs;
|
||||
pairs.clear();
|
||||
STATIC_THREAD_LOCAL(pod::BVH::pairs_t, pairs);
|
||||
impl::queryOverlaps( bvhA, bvhB, relTransform, pairs );
|
||||
|
||||
bool hit = false;
|
||||
|
||||
@ -13,8 +13,7 @@ bool impl::meshAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
|
||||
|
||||
// transform to local space for BVH query
|
||||
auto bounds = impl::transformAabbToLocal( aabb.bounds, impl::getTransform( mesh ) );
|
||||
static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
|
||||
candidates.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::BVH::index_t>, candidates);
|
||||
impl::queryBVH( bvh, bounds, candidates );
|
||||
|
||||
bool hit = false;
|
||||
@ -37,8 +36,7 @@ bool impl::meshSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod
|
||||
|
||||
// transform to local space for BVH query
|
||||
auto bounds = impl::transformAabbToLocal( sphere.bounds, impl::getTransform( mesh ) );
|
||||
static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
|
||||
candidates.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::BVH::index_t>, candidates);
|
||||
impl::queryBVH( bvh, bounds, candidates );
|
||||
|
||||
bool hit = false;
|
||||
@ -63,8 +61,7 @@ bool impl::meshPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:
|
||||
|
||||
// transform to local space for BVH query
|
||||
auto bounds = impl::transformAabbToLocal( plane.bounds, impl::getTransform( mesh ) );
|
||||
static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
|
||||
candidates.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::BVH::index_t>, candidates);
|
||||
impl::queryBVH( bvh, bounds, candidates );
|
||||
|
||||
bool hit = false;
|
||||
@ -88,8 +85,7 @@ bool impl::meshCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, po
|
||||
|
||||
// transform to local space for BVH query
|
||||
auto bounds = impl::transformAabbToLocal( capsule.bounds, impl::getTransform( mesh ) );
|
||||
static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
|
||||
candidates.clear();
|
||||
STATIC_THREAD_LOCAL(uf::stl::vector<pod::BVH::index_t>, candidates);
|
||||
impl::queryBVH( bvh, bounds, candidates );
|
||||
|
||||
bool hit = false;
|
||||
@ -117,8 +113,7 @@ bool impl::meshMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
|
||||
auto relTransform = uf::transform::relative( tA, tB );
|
||||
|
||||
// compute overlaps between one BVH and another BVH
|
||||
static thread_local pod::BVH::pairs_t pairs;
|
||||
pairs.clear();
|
||||
STATIC_THREAD_LOCAL(pod::BVH::pairs_t, pairs);
|
||||
|
||||
impl::queryOverlaps( bvhA, bvhB, relTransform, pairs );
|
||||
|
||||
@ -152,8 +147,7 @@ bool impl::meshHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
|
||||
auto relTransform = uf::transform::relative( tA, tB );
|
||||
|
||||
// compute overlaps between one BVH and another BVH
|
||||
static thread_local pod::BVH::pairs_t pairs;
|
||||
pairs.clear();
|
||||
STATIC_THREAD_LOCAL(pod::BVH::pairs_t, pairs);
|
||||
impl::queryOverlaps( bvhA, bvhB, relTransform, pairs );
|
||||
|
||||
bool hit = false;
|
||||
|
||||
@ -1,31 +1,54 @@
|
||||
#include <uf/utils/memory/allocator.h>
|
||||
#include <uf/utils/memory/pool.h>
|
||||
|
||||
#define UF_MEMORYPOOL_OVERRIDE_NEW_DELETE 0
|
||||
bool uf::allocator::override = false;
|
||||
|
||||
void* uf::allocator::allocate( size_t n ) {
|
||||
return uf::memoryPool::global.size() > 0 && uf::allocator::override ? uf::memoryPool::global.alloc( n ) : malloc( n );
|
||||
if ( override && uf::memoryPool::global.size() > 0 ) return uf::memoryPool::global.alloc( n );
|
||||
return std::malloc( n );
|
||||
}
|
||||
|
||||
void uf::allocator::deallocate( void* p, size_t n ) {
|
||||
if ( uf::memoryPool::global.size() > 0 && uf::allocator::override ) uf::memoryPool::global.free( p );
|
||||
else free( p );
|
||||
}
|
||||
if ( !p ) return;
|
||||
|
||||
if ( override && uf::memoryPool::global.size() > 0 ) uf::memoryPool::global.free( p, n );
|
||||
else std::free( p );
|
||||
}
|
||||
|
||||
void* uf::allocator::malloc_m( size_t n ) {
|
||||
return std::malloc( n );
|
||||
}
|
||||
void uf::allocator::free_m( void* p, size_t n ) {
|
||||
|
||||
void uf::allocator::free_m( void* p, size_t /*n*/ ) {
|
||||
std::free( p );
|
||||
}
|
||||
|
||||
//
|
||||
#if UF_MEMORYPOOL_OVERRIDE_NEW_DELETE
|
||||
void* operator new( size_t n ) {
|
||||
return uf::allocator::allocate( n );
|
||||
void* p = uf::allocator::allocate( n );
|
||||
if ( !p ) throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
void operator delete( void* p ) {
|
||||
uf::allocator::deallocate( p );
|
||||
|
||||
void operator delete( void* p ) noexcept {
|
||||
uf::allocator::deallocate( p, 0 );
|
||||
}
|
||||
|
||||
void* operator new[]( size_t n ) {
|
||||
void* p = uf::allocator::allocate( n );
|
||||
if ( !p ) throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
|
||||
void operator delete[]( void* p ) noexcept {
|
||||
uf::allocator::deallocate( p, 0 );
|
||||
}
|
||||
|
||||
void operator delete( void* p, size_t n ) noexcept {
|
||||
uf::allocator::deallocate( p, n );
|
||||
}
|
||||
|
||||
void operator delete[]( void* p, size_t n ) noexcept {
|
||||
uf::allocator::deallocate( p, n );
|
||||
}
|
||||
#endif
|
||||
@ -63,7 +63,11 @@ void uf::memoryPool::initialize( pod::MemoryPool& pool, size_t size, pod::Memory
|
||||
|
||||
pool.size = size;
|
||||
pool.strategy = strategy;
|
||||
pool.memory = uf::allocator::malloc_m(size);
|
||||
if ( uf::memoryPool::subPool && &pool != &uf::memoryPool::global.data() ) {
|
||||
pool.memory = uf::memoryPool::global.alloc( size );
|
||||
} else {
|
||||
pool.memory = uf::allocator::malloc_m( size );
|
||||
}
|
||||
UF_ASSERT( pool.memory );
|
||||
|
||||
switch ( pool.strategy ) {
|
||||
@ -130,11 +134,11 @@ void uf::memoryPool::initialize( pod::MemoryPool& pool, size_t size, pod::Memory
|
||||
}
|
||||
}
|
||||
void uf::memoryPool::destroy( pod::MemoryPool& pool ) {
|
||||
if ( uf::memoryPool::size( pool ) <= 0 ) goto CLEAR;
|
||||
if ( uf::memoryPool::size( pool ) <= 0 || !pool.memory ) goto CLEAR;
|
||||
if ( uf::memoryPool::subPool && &pool != &uf::memoryPool::global.data() ) {
|
||||
uf::memoryPool::global.free( pool.memory );
|
||||
uf::memoryPool::global.free( pool.memory, pool.size );
|
||||
} else {
|
||||
uf::allocator::free_m(pool.memory);
|
||||
uf::allocator::free_m( pool.memory, pool.size );
|
||||
}
|
||||
|
||||
// per-pool destruction
|
||||
@ -293,14 +297,14 @@ RETURN:
|
||||
}
|
||||
|
||||
bool uf::memoryPool::free( pod::MemoryPool& pool, void* pointer, size_t size ) {
|
||||
if (!pointer) return false;
|
||||
if ( !pointer ) return false;
|
||||
#if UF_MEMORYPOOL_MUTEX
|
||||
std::lock_guard<std::mutex> lock(pool.mutex);
|
||||
#endif
|
||||
bool oob = !exists( pool, pointer, size );
|
||||
if ( oob ) goto MANUAL_FREE;
|
||||
|
||||
switch (pool.strategy) {
|
||||
switch ( pool.strategy ) {
|
||||
case pod::MemoryPool::Strategy::LINEAR: {
|
||||
UF_EXCEPTION("cannot free individual allocation");
|
||||
return false;
|
||||
@ -322,13 +326,13 @@ bool uf::memoryPool::free( pod::MemoryPool& pool, void* pointer, size_t size ) {
|
||||
goto RETURN;
|
||||
}
|
||||
case pod::MemoryPool::Strategy::BUDDY: {
|
||||
UF_ASSERT(size > 0);
|
||||
UF_ASSERT( size > 0 );
|
||||
void* block = pointer;
|
||||
|
||||
// attempt to merge with buddies
|
||||
size_t currentSize = pool.state.buddy.minBlockSize;
|
||||
size_t level = getTargetLevel(size, currentSize, pool.state.buddy.maxLevel);
|
||||
while (level < pool.state.buddy.maxLevel) {
|
||||
while ( level < pool.state.buddy.maxLevel ) {
|
||||
void* buddy = getBuddy(block, currentSize, pool.memory);
|
||||
|
||||
// search for buddy in the current level's free list
|
||||
@ -382,11 +386,11 @@ MANUAL_FREE:
|
||||
#endif
|
||||
|
||||
#if UF_MEMORYPOOL_INVALID_FREE
|
||||
UF_MSG_DEBUG("manually freeing {}", pointer);
|
||||
UF_MSG_DEBUG("memory pool {}: manually freeing {}", (void*) &pool, pointer );
|
||||
uf::allocator::free_m(pointer);
|
||||
return true;
|
||||
#else
|
||||
UF_EXCEPTION("cannot free: {}", pointer);
|
||||
UF_ASSERT("cannot free: {}", pointer);
|
||||
return false;
|
||||
#endif
|
||||
|
||||
@ -420,12 +424,14 @@ uf::stl::string uf::memoryPool::stats( const pod::MemoryPool& pool ) {
|
||||
metadata["pool"] = ss.str();
|
||||
return metadata;
|
||||
}
|
||||
/*
|
||||
pod::Allocation& uf::memoryPool::fetch( pod::MemoryPool& pool, void* pointer, size_t size ) {
|
||||
UF_EXCEPTION("unimplemented");
|
||||
}
|
||||
const pod::MemoryPool::allocations_t& uf::memoryPool::allocations( const pod::MemoryPool& pool ) {
|
||||
UF_EXCEPTION("unimplemented")
|
||||
}
|
||||
*/
|
||||
//
|
||||
uf::MemoryPool::MemoryPool( size_t size ) {
|
||||
if ( size > 0 ) this->initialize( size );
|
||||
|
||||
@ -71,10 +71,18 @@ uf::stl::vector<pod::Thread*> uf::thread::execute( pod::Thread::Tasks& tasks ) {
|
||||
if ( tasks.container.empty() ) return workers;
|
||||
|
||||
if ( tasks.name == uf::thread::mainThreadName ) {
|
||||
#if UF_THREAD_METRICS
|
||||
auto& thread = uf::thread::get( uf::thread::mainThreadName );
|
||||
uint32_t tasksThisFrame = 0;
|
||||
for ( auto& task : tasks.container ) {
|
||||
task();
|
||||
++tasksThisFrame;
|
||||
}
|
||||
tasks.container.clear();
|
||||
thread.metrics.tasksProcessed.store(tasksThisFrame, std::memory_order_relaxed);
|
||||
#else
|
||||
for ( auto& task : tasks.container ) task();
|
||||
#endif
|
||||
} else {
|
||||
for ( auto& task : tasks.container ) {
|
||||
auto& worker = uf::thread::fetchWorker( tasks.name );
|
||||
@ -114,10 +122,8 @@ void uf::thread::queue( pod::Thread& thread, const pod::Thread::function_t& func
|
||||
thread.conditions.queued.notify_one();
|
||||
}
|
||||
void uf::thread::process( pod::Thread& thread ) { if ( !uf::thread::has(thread.name) ) return; // ops
|
||||
static thread_local pod::Thread::container_t local_queue;
|
||||
static thread_local pod::Thread::container_t local_container;
|
||||
local_queue.clear();
|
||||
local_container.clear();
|
||||
STATIC_THREAD_LOCAL(pod::Thread::container_t, local_queue);
|
||||
STATIC_THREAD_LOCAL(pod::Thread::container_t, local_container);
|
||||
|
||||
#if UF_THREAD_METRICS
|
||||
uint32_t tasksThisFrame = 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user