AGX Dynamics 2.41.3.2
Loading...
Searching...
No Matches
Thread.h
Go to the documentation of this file.
1/*
2Copyright 2007-2025. Algoryx Simulation AB.
3
4All AGX source code, intellectual property, documentation, sample code,
5tutorials, scene files and technical white papers, are copyrighted, proprietary
6and confidential material of Algoryx Simulation AB. You may not download, read,
7store, distribute, publish, copy or otherwise disseminate, use or expose this
8material unless having a written signed agreement with Algoryx Simulation AB, or having been
9advised so by Algoryx Simulation AB for a time limited evaluation, or having purchased a
10valid commercial license from Algoryx Simulation AB.
11
12Algoryx Simulation AB disclaims all responsibilities for loss or damage caused
13from using this software, unless otherwise stated in written agreements with
14Algoryx Simulation AB.
15*/
16
17#pragma once
18
19#ifdef _MSC_VER
20# pragma warning(push)
21# pragma warning( disable: 4275 ) // warning C4275: non dll-interface class
22# pragma warning(disable: 4251) // warning C4251: class X needs to have dll-interface to be used by clients of class Y
23#endif
24
25
26#include <agx/agx.h>
27#include <agx/Vector.h>
28#include <agx/HashTable.h>
29#include <agx/Timer.h>
30#include <agx/Task.h>
32
35
36#include <agx/AtomicValue.h>
37#include <agx/Job.h>
38#include <agx/Uuid.h>
39#include <agx/Notify.h>
40
41#include <queue>
42#include <thread>
43
44
45// Set to 1 to enable thread timeline reporting of internal steps
46#define ENABLE_VERBOSE_THREAD_TIMELINE 0
47
48
49#define AGX_MAX_NUM_THREADS 1024
50
51
52extern "C"
53{
55}
56
57namespace agxData
58{
59 class EntityStorage;
60 class EntityModel;
61}
62
63
64
65
66
67namespace agx
68{
69 class TiXmlElement;
70 class Task;
71
72
73 // AGX_DECLARE_POINTER_TYPES(Thread);
74 // AGX_DECLARE_VECTOR_TYPES(Thread);
75
76
77
84 {
85 public:
86
91 THREAD_NEW = 0, // When a BasicThread is created
92 THREAD_RUNNING = 1, // start has been called
93 THREAD_DETACHED = 2, // detach has been called
94 THREAD_DONE = 3, // After run() is done executing
95 THREAD_JOINED = 4, // After join() has been called
96 THREAD_CANCELLED = 5 // After cancel but before join
97 };
98
103
104 BasicThread( const BasicThread& other ) = delete;
105
106 BasicThread& operator=(const BasicThread& rhs ) = delete;
107
111 virtual ~BasicThread() = default;
112
117 virtual void run() { }
118
126 bool start();
127
135 void cancel();
136
140 bool join();
141
142
146 void detach();
147
151 inline bool joinable();
152
156 inline unsigned int getThreadState();
157
158
159
160
175
176
180 static std::thread::native_handle_type getCurrentThreadHandle();
181
182
183 private:
184 void launchThread();
185
186 protected:
187 std::thread m_handle;
188 std::atomic< unsigned int> m_state;
189 private:
190 std::mutex m_handleMutex;
191 };
192
193
194
200 {
201 public:
202
203
204 // The types used when communicating with the thread local storage facilities.
205 // The key type is an integer type and the data type is a pointer type.
206 #ifdef _WIN32
207 typedef unsigned long ThreadStorageKey; // Should be DWORD
208 typedef void* ThreadStorageData; // Should be LPVOID
209 #else
210 typedef pthread_key_t ThreadStorageKey;
211 typedef void* ThreadStorageData;
212 #endif
213
214 typedef std::mt19937 RandomGenerator;
215
219 bool start();
220
224 void stop();
225
232 Index getId() const;
233
245
250
256 static std::string getCurrentThreadDescription();
257
261 static Thread *getThread(size_t id);
262
266 static Thread *getMainThread();
267
273
279
285
291 RandomGenerator& getRandomGenerator();
292
293
297 static bool isMainThread();
298
299
303 static void shutdown();
304
305 static bool isShuttingDown();
306
307
309 static void setEnableJobTimeline(bool flag);
310 static bool getEnableJobTimeline();
311
321 UInt64 startTick, UInt64 endTick,
322 const char* description,
323 const char* extraDataTitle = nullptr,
324 agx::Real64 extraData = 0.0);
325
329 Real getOverheadTime() const;
330
335
336 // Allocations
337 // void *allocateBytes(size_t numBytes);
338 // void *deallocateBytes(void *ptr);
339
345
346
347 // Main thread is normally allocated automatically, special usage before main is entered
348 static void initThreadSystem();
349
350 // Dump profiling logs, called by Simulation
351 static void exportAllTimelines();
352 static void resetStartTick();
353 static void flushTimelineLogs();
354
355
356 /*
357 * Methods used for manipulating the thread local storage.
358 */
359
368
369 static bool immediateLogging;
370 static int log(const char *format, ...);
371 static void flushLogs();
373
375
376
377 static void addTask(Task *task);
378
379 // Threads that aren't "real" AGX threads, i.e., registered threads, get
380 // a pseudo-ID in this table. These IDs are unrelated from the IDs of the
381 // "real" AGX threads.
384
385 protected:
387 Thread(const Thread&) = delete;
388 virtual ~Thread();
389
390 private:
391 friend void AGXCORE_EXPORT setNumThreads(size_t numThreads);
393
394 class MainThreadSingleton;
395 static Thread *initMainThread();
396
397 static void performNumThreadsChange();
398
399 virtual void run();
400
401 void freeDefaultStorages();
402 void spawn(Job *job);
403 void sortInsertJob(Job *job);
404
405 void activate();
406 void completeFrame(Task *task);
407
408 bool isActive() const;
409
410
411 template <bool THREAD_TIMELINE_STATISTICS>
412 void doWork();
413
414 void doWork();
415 void blockingDoWork();
416
417 void stealWork();
418 void wakeupThreads();
419 void pushTargetJob(Thread *target, Job *job, bool activateTarget = true);
420 static void taskCompleted(Task *task);
421
422 void initialize();
423 void sleep();
424
425 Job *getExecutionJob();
426
427 Index getRandomOtherThreadId(Index excludeIndex = InvalidIndex);
428
429 friend class Block;
430
431 friend class Job;
432 friend class Notify;
433
434 private:
435 Notify::ThreadData *getNotifyData();
437
438 friend class Task;
439
440 // Simple linear congruential generator
441 class FastRandom
442 {
443 public:
444 FastRandom(UInt32 seed);
445 FastRandom();
446
447 UInt32 operator() ();
448
449 private:
450 static const UInt32 mod = ((1ULL << 32) - 5);
451 static const UInt32 mul = 69070U;
452
453 void init(UInt32 seed);
454
455 private:
456 UInt32 m_state;
457 };
458
459 FastRandom m_fastRandom;
460
461 struct Frame
462 {
463 inline Frame(Task *t = nullptr) : task(t), done(false) {}
464 inline Frame& operator=(const Frame& other) {task = other.task; done = other.done.load(); return *this;}
465 Task *task;
466 std::atomic<bool> done;
467 };
468
469 #define AGX_THREAD_FRAME_STACK_MAX_DEPTH 64
470 Frame m_frames[AGX_THREAD_FRAME_STACK_MAX_DEPTH];
471 size_t m_numFrames;
472 size_t m_activationDepth;
473 std::atomic<Int32> m_activationCount;
474
475
476 struct JobCompare
477 {
478 AGX_FORCE_INLINE bool operator() (const Job *lhs, const Job *rhs) const
479 {
480 return lhs->getCostEstimate() < rhs->getCostEstimate();
481 }
482 };
483
484 Block m_startBlock;
485 Block m_runBlock;
486
487 Thread *m_listNodeNext;
488
489 using JobQueue = std::priority_queue<Job *, agx::VectorPOD<Job *>, JobCompare>;
490
491 JobQueue m_sharedJobs;
492 JobPtrVector m_localJobs;
493 JobPtrVector m_pushedJobs;
494
495 std::atomic<Int32> m_pushCounter;
496 std::atomic<bool> m_running;
497
498 agx::SpinMutex m_timelineMutex;
499 char m_padding_1[64-sizeof(agx::SpinMutex)];
500 agx::SpinMutex m_jobMutex;
501 char m_padding_2[64-sizeof(agx::SpinMutex)];
502 agx::SpinMutex m_pushMutex;
503 AGX_STATIC_ASSERT(sizeof(agx::SpinMutex) <= 64);
504
505 Index m_id;
506
507 Timer m_overheadTimer;
508 Timer m_wakeupTimer;
509 Timer m_sleepTimer;
510
511 UInt64 m_savedRegisterState;
512
513 bool m_isSleeping;
514
515 agx::UuidGenerator m_uuidGenerator;
516 std::random_device m_randomDevice;
517 RandomGenerator m_mersienneTwister;
518
519 private:
520
522 void registerContainerAllocation(Container *container);
523 void unregisterContainerAllocation(Container *container);
524 void *allocateScratchPadBuffer(size_t numBytes);
525 void deallocateScratchPadBuffer(void *buffer, size_t numBytes);
526
527 struct ScratchPadArea
528 {
529 ScratchPadArea() : buffer(nullptr), end(nullptr), head(nullptr), m_allocator("ScratchPad")
530 {}
531
532 ~ScratchPadArea() { m_allocator.deallocateBytes(buffer); }
533
534 char *buffer;
535 char *end;
536 char *head;
537 ByteAllocator m_allocator;
538 VectorPOD<Container *> m_activeAllocations;
539 };
540
541 ScratchPadArea m_scratchPad;
542
543 agxData::EntityStorageRef m_timelineEntries;
544
545
546 static Thread *s_threads[AGX_MAX_NUM_THREADS];
547 static Thread *s_mainThread;
548 static Callback1<Task *> s_taskCompletionCallback;
549
550 static bool s_enableJobTimeline;
551
552 static std::exception_ptr s_unhandledException;
553
554
555 struct LocalTimelineEntry
556 {
557 LocalTimelineEntry()
558 : jobType(UNKNOWN)
559 , startTick(0)
560 , endTick(0)
561 , poolSize(0)
562 , costEstimate(0)
563 , message(nullptr)
564 , job(nullptr)
565 , extraDataTitle(nullptr)
566 , extraData(0.0)
567 {}
568
569 enum JobType { PRE, DISPATCH, POST, UNKNOWN };
570
571 agx::TaskRef task;
572 JobType jobType;
573 agx::UInt64 startTick;
574 agx::UInt64 endTick;
575 agx::UInt32 poolSize;
576 agx::UInt32 costEstimate;
577 const char* message;
578 void* job;
579
580 const char* extraDataTitle;
581 agx::Real64 extraData;
582 };
583
584 Vector<LocalTimelineEntry> m_localTimelineEntries;
585 AGX_DECLARE_POINTER_TYPES(TimelineExportKernel);
586
587
588 void exportTimelineEntries();
589 void reportTimelineJob(Job *job);
590 void pushTimelineEntry(const LocalTimelineEntry& entry);
591
593 class LogChunk : public Referenced
594 {
595 public:
596 LogChunk(size_t numBytes);
597 char* buffer;
598 char* head;
599 char* end;
600 LogChunkRef next;
601 size_t numAvailable();
602
603 protected:
604 virtual ~LogChunk();
605 };
606
607 struct LogEntry
608 {
609 LogEntry();
610 LogEntry(agx::UInt64 timestamp_, const char* message_);
611 agx::UInt64 timestamp;
612 const char* message;
613 };
614
615 int logImplementation(const char* format, va_list ap);
616 static int cmpLogEntry(const LogEntry& entry1, const LogEntry& entry2);
617
618 LogChunkRef m_logChunk;
619 VectorPOD<LogEntry> m_logEntries;
620
621 typedef HashTable<agxData::EntityModel*, ref_ptr<Referenced>> DefaultStorageTable;
622 DefaultStorageTable m_defaultStorageTable;
623 };
624
626
627
628
629
630
631
632
633 /* Implementation */
635
637 {
638 return m_handle.joinable();
639 }
640
641
642
643 inline unsigned int BasicThread::getThreadState()
644 {
645 return m_state.load();
646 }
647
648
649
651 {
652 return m_id;
653 }
654
656 {
657 agxAssert(s_threads[id]);
658 return s_threads[id];
659 }
660
662 {
663 agxAssert(s_mainThread);
664 return s_mainThread;
665 }
666
668 AGX_FORCE_INLINE bool Thread::isActive() const { return m_activationCount.load() > 0; }
669
671 {
672 return m_mersienneTwister;
673 }
674
675 AGX_FORCE_INLINE Real Thread::getOverheadTime() const { return Real(m_overheadTimer.getTime()); }
676
677
678 inline int Thread::log(const char *format, ...)
679 {
680 va_list arguments;
681 va_start(arguments, format);
682 int result = Thread::getCurrentThread()->logImplementation(format, arguments);
683 va_end(arguments);
684 return result;
685 }
686
687
688 #if 0
689 AGX_FORCE_INLINE void *Thread::allocateBytes(size_t numBytes)
690 {
691 return m_byteAllocator.allocate(numBytes);
692 }
693
694 AGX_FORCE_INLINE void *Thread::deallocateBytes(void *ptr)
695 {
696 m_byteAllocator.deallocateBytes(ptr);
697 }
698
699 template <typename T>
700 AGX_FORCE_INLINE T *Thread::allocate()
701 {
702 return this->getPool<T>()->allocate();
703 }
704
705 template <typename T>
706 AGX_FORCE_INLINE void Thread::deallocate(T *ptr)
707 {
708 this->getPool<T>()->deallocate(ptr);
709 }
710
711
712 template <typename T>
713 AGX_FORCE_INLINE T *Thread::create()
714 {
715 return this->getPool<T>()->create();
716 }
717
718 template <typename T>
719 AGX_FORCE_INLINE void Thread::destroy(T *ptr)
720 {
721 this->getPool<T>()->destroy(ptr);
722 }
723
724 template <typename T>
725 AGX_FORCE_INLINE MemoryPool<T> *Thread::getPool()
726 {
727 uint32_t id = agxData::getType<T>()->getId();
728 if (id >= m_pools.size())
729 m_pools.resize(id+1, 0);
730
731 if (!m_pools[id])
732 m_pools[id] = new MemoryPool<T>;
733
734 return static_cast<MemoryPool<T> *>(m_pools[id]);
735 }
736
737 void setCurrentConstructionObject(void *ptr);
738 void *getCurrentConstructionObject();
739 #endif
740
741
742
743
744
745 AGX_FORCE_INLINE void *Thread::allocateScratchPadBuffer(size_t numBytes)
746 {
747 /* Check if scratch pad is too small, which requires reallocation and updates to all existing container allocations */
748 if (m_scratchPad.head + numBytes > m_scratchPad.end)
749 {
750 size_t currentSize = m_scratchPad.end - m_scratchPad.buffer;
751 const Real growFactor = 1.5;
752 size_t newSize = (size_t)( Real(currentSize + numBytes) * growFactor );
753 // printf("Reallocating scratch pad for thread %d from %u to %u KB\n", this->getId(), (unsigned)(currentSize/1024), (unsigned)(newSize/1024));
754
755 char *newBuffer = (char *)m_scratchPad.m_allocator.allocateBytes(newSize, 64);
756 agxAssertN(newBuffer, "Thread %d could not allocate %u bytes for job scratch pad!", this->getId(), (unsigned)newSize);
757 if (!newBuffer)
758 return nullptr;
759
760 /* Copy active allocations */
761 size_t numUsed = m_scratchPad.head - m_scratchPad.buffer;
762
763 if ( numUsed > 0 )
764 memcpy(newBuffer, m_scratchPad.buffer, numUsed);
765
766 m_scratchPad.m_allocator.deallocateBytes(m_scratchPad.buffer);
767
768 /* Update buffer pointers for active allocations */
769 for (size_t i = 0; i < m_scratchPad.m_activeAllocations.size(); ++i)
770 {
771 Container *container = m_scratchPad.m_activeAllocations[i];
772
773 if (container->m_buffer)
774 {
775 ptrdiff_t offset = (char *)container->m_buffer - (char *)m_scratchPad.buffer;
776 container->m_buffer = newBuffer + offset;
777 }
778 }
779
780 m_scratchPad.buffer = newBuffer;
781 m_scratchPad.end = m_scratchPad.buffer + newSize;
782 m_scratchPad.head = m_scratchPad.buffer + numUsed;
783 }
784
785 void *mem = m_scratchPad.head;
786 m_scratchPad.head += numBytes;
787
788 return mem;
789 }
790
791 AGX_FORCE_INLINE void Thread::deallocateScratchPadBuffer(void *buffer, size_t numBytes)
792 {
793 agxAssert(!buffer || (buffer >= m_scratchPad.buffer && buffer < m_scratchPad.end));
794
795 if ((char *)buffer + numBytes == m_scratchPad.head)
796 m_scratchPad.head = (char *)buffer;
797 }
798
799 AGX_FORCE_INLINE void Thread::registerContainerAllocation(Container *container)
800 {
801 m_scratchPad.m_activeAllocations.push_back(container);
802 }
803
804 #ifdef AGX_DEBUG
805 AGX_FORCE_INLINE void Thread::unregisterContainerAllocation(Container *container)
806 #else
807 AGX_FORCE_INLINE void Thread::unregisterContainerAllocation(Container * /* container */)
808 #endif
809 {
810 agxAssert1(!m_scratchPad.m_activeAllocations.empty() && container == m_scratchPad.m_activeAllocations.back(), "LIFO order required!");
811 m_scratchPad.m_activeAllocations.pop_back();
812
813 /* Reset scratch pad, removing any holes from reallocations */
814 if (m_scratchPad.m_activeAllocations.empty())
815 m_scratchPad.head = m_scratchPad.buffer;
816 }
817
818}
819
820
821#if ENABLE_VERBOSE_THREAD_TIMELINE
822 #define AGX_BEGIN_TIMELINE_REPORT(variable) \
823 auto variable ## _begin_time = agx::Timer::getCurrentTick()
824
825 #define AGX_END_TIMELINE_REPORT(variable, title) \
826 auto variable ## _end_time = agx::Timer::getCurrentTick(); \
827 agx::Thread::getCurrentThread()->reportSystemJob( variable ## _begin_time , variable ## _end_time, title);
828
829 #define AGX_END_TIMELINE_REPORT_DATA(variable, title, title2, data) \
830 auto variable ## _end_time = agx::Timer::getCurrentTick(); \
831 agx::Thread::getCurrentThread()->reportSystemJob( variable ## _begin_time , variable ## _end_time, title, title2, data)
832#else
833 #define AGX_BEGIN_TIMELINE_REPORT(variable)
834 #define AGX_END_TIMELINE_REPORT(variable, title)
835 #define AGX_END_TIMELINE_REPORT_DATA(variable, title, title2, data)
836#endif
837
838
839#ifdef _MSC_VER
840# pragma warning(pop)
841#endif
#define AGX_DECLARE_POINTER_TYPES(type)
Definition: Referenced.h:254
#define AGX_THREAD_FRAME_STACK_MAX_DEPTH
Definition: Thread.h:469
#define AGX_MAX_NUM_THREADS
Definition: Thread.h:49
void agxFlushThreadLogs()
#define AGXCORE_EXPORT
#define AGXPHYSICS_EXPORT
An attribute container.
An abstract description of a data entity stored using SOA (structure of arrays) pattern in a EntitySt...
Definition: EntityModel.h:64
Data storage for a collection of entity instances of a specified EntityModel.
Definition: EntityStorage.h:73
Basic wrapper class aroud std::thread.
Definition: Thread.h:84
BasicThread & operator=(const BasicThread &rhs)=delete
BasicThread()
Default constructor.
bool setThreadAffinity(agx::UInt64 cpumask)
Thread Affinity can be used to influence on which logical cores threads are scheduled and allowed to ...
BasicThread(const BasicThread &other)=delete
void detach()
Detaches the thread to the background.
void cancel()
Threads should normally not need to be killed.
virtual ~BasicThread()=default
Destructor.
std::atomic< unsigned int > m_state
Definition: Thread.h:188
bool join()
Joins the thread.
virtual void run()
This method is invoked by start.
Definition: Thread.h:117
std::thread m_handle
Definition: Thread.h:187
bool joinable()
True if thread is joinable.
Definition: Thread.h:636
ThreadState
States for the thread.
Definition: Thread.h:90
unsigned int getThreadState()
Returns the current thread state.
Definition: Thread.h:643
bool start()
Launches the thread.
static std::thread::native_handle_type getCurrentThreadHandle()
Return a native_handle for the current executing thread.
Block synchronization primitive.
The Container is the base class for several of the container classes proided by AGX,...
Definition: Container.h:35
The object defining a frame of reference and providing transformations operations.
Definition: agx/Frame.h:68
An abstract job/workblock representation, which allows work threads to execute arbitrary tasks.
Definition: Job.h:63
Inheritance with partial specialization due to bug with ref_ptr containers.
Class for handling logging of messages.
Definition: Notify.h:64
Spin-lock mutex.
Definition: SpinMutex.h:47
A representation of a generic task.
Definition: Task.h:58
agx::Thread is a representation of an OS specific implementation of a computational thread.
Definition: Thread.h:200
static void writePerThreadStorage(ThreadStorageKey key, ThreadStorageData data)
Write to the thread-local location owned by the currently executing thread.
HashTable< Thread *, Index > ThreadIdTable
Definition: Thread.h:382
friend void AGXPHYSICS_EXPORT shutdown()
Shutdown of the AGX Dynamics API will be done when the number of shutdown matches the number of calls...
static void resetStartTick()
agx::Uuid generateUuid()
Generates a unique universal identifier.
void stop()
Stop the thread.
static void makeCurrentThreadMainThread()
Register current thread as main thread.
static int log(const char *format,...)
Definition: Thread.h:678
static Thread * getCurrentThread()
static void freePerThreadStorage(ThreadStorageKey key)
Deallocate the storage location.
static void addTask(Task *task)
agxData::EntityStorage * getDefaultStorage(agxData::EntityModel *entity)
Thread(Index id)
static bool isMainThread()
Definition: Thread.h:667
void resetOverheadTime()
Reset the thread overhead time.
bool start()
Start the thread.
static void flushLogs()
void * ThreadStorageData
Definition: Thread.h:211
Thread(const Thread &)=delete
static void initThreadSystem()
static void shutdown()
Shutdown the threading system.
void reportSystemJob(UInt64 startTick, UInt64 endTick, const char *description, const char *extraDataTitle=nullptr, agx::Real64 extraData=0.0)
Add an entry to the job duration log.
static std::string getCurrentThreadDescription()
static void setEnableJobTimeline(bool flag)
Enable or disable job timeline statistics.
static Thread * registerAsAgxThread()
Register the current thread as an AGX thread.
friend void AGXCORE_EXPORT setNumThreads(size_t numThreads)
Set the number of threads to use (including the main thread).
pthread_key_t ThreadStorageKey
Definition: Thread.h:210
std::mt19937 RandomGenerator
Definition: Thread.h:214
Index getIndex() const
Get an index suitable for use when storing per-thread data in e.g.
static ThreadStorageKey allocatePerThreadStorage()
Allocate a storage location that is unique for each thread.
agxData::EntityStorageRef popTimelineEntryStorage()
RandomGenerator & getRandomGenerator()
Return a reference to the mersienne twister used for generating random numbers.
Definition: Thread.h:670
static bool immediateLogging
Definition: Thread.h:369
virtual ~Thread()
static void flushTimelineLogs()
static bool isShuttingDown()
static Thread * getThread(size_t id)
Definition: Thread.h:655
static Thread * getMainThread()
Definition: Thread.h:661
static bool getEnableJobTimeline()
static ThreadStorageData readPerThreadStorage(ThreadStorageKey key)
Read the thread-local value for the currently executing thread associated with the given key.
static void unregisterAsAgxThread()
Remove agx attributes from current thread.
Index getId() const
Returns the thread's AGX thread ID, a value between 0 and N-1, for AGX's internal threads,...
Definition: Thread.h:650
Real getOverheadTime() const
Definition: Thread.h:675
static Thread::ThreadIdTable * getPromotedThreads()
static void exportAllTimelines()
Generator of UUID values based on V4 http://en.wikipedia.org/wiki/Universally_unique_identifier.
Definition: Uuid.h:232
A UUID, or Universally unique identifier, is intended to uniquely identify information in a distribut...
Definition: Uuid.h:42
Vector containing 'raw' data.
Definition: agx/Vector.h:246
#define agxAssert1(expr, msg)
Definition: debug.h:144
#define agxAssertN(expr, format,...)
Definition: debug.h:145
#define agxAssert(expr)
Definition: debug.h:143
#define AGX_STATIC_ASSERT(X)
Definition: macros.h:23
#define AGX_FORCE_INLINE
Definition: macros.h:58
Contains classes for low level data storage for AGX.
Definition: Container.h:23
The agx namespace contains the dynamics/math part of the AGX Dynamics API.
double Real64
Definition: Real.h:45
uint32_t UInt32
Definition: Integer.h:32
uint64_t UInt64
Definition: Integer.h:33
agx::VectorPOD< Thread * > ThreadPtrVector
Definition: Thread.h:625
LinearProbingHashSetImplementation< KeyT, HashT >::iterator end(LinearProbingHashSetImplementation< KeyT, HashT > &set)
double Real
Definition: Real.h:42
VectorPOD< class Job * > JobPtrVector
Definition: Job.h:51
void AGXPHYSICS_EXPORT init()
Initialize AGX Dynamics API including thread resources and must be executed before using the AGX API.
UInt32 Index
Definition: Integer.h:44