Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
JobSystem.h
Go to the documentation of this file.
1// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
8#include <Jolt/Core/Color.h>
12#include <Jolt/Core/Atomics.h>
13
15
69class JobSystem : public NonCopyable
70{
71protected:
72 class Job;
73
74public:
76
79 class JobHandle : private Ref<Job>
80 {
81 public:
83 inline JobHandle() = default;
84 inline JobHandle(const JobHandle &inHandle) = default;
85 inline JobHandle(JobHandle &&inHandle) noexcept : Ref<Job>(std::move(inHandle)) { }
86
88 inline explicit JobHandle(Job *inJob) : Ref<Job>(inJob) { }
89
91 inline JobHandle & operator = (const JobHandle &inHandle) = default;
92 inline JobHandle & operator = (JobHandle &&inHandle) noexcept = default;
93
95 inline bool IsValid() const { return GetPtr() != nullptr; }
96
98 inline bool IsDone() const { return GetPtr() != nullptr && GetPtr()->IsDone(); }
99
101 inline void AddDependency(int inCount = 1) const { GetPtr()->AddDependency(inCount); }
102
105 inline void RemoveDependency(int inCount = 1) const { GetPtr()->RemoveDependencyAndQueue(inCount); }
106
108 static inline void sRemoveDependencies(JobHandle *inHandles, uint inNumHandles, int inCount = 1);
109
111 template <uint N>
112 static inline void sRemoveDependencies(StaticArray<JobHandle, N> &inHandles, int inCount = 1)
113 {
114 sRemoveDependencies(inHandles.data(), inHandles.size(), inCount);
115 }
116
118 using Ref<Job>::GetPtr;
119 };
120
122 class Barrier : public NonCopyable
123 {
124 public:
126
129 virtual void AddJob(const JobHandle &inJob) = 0;
130
133 virtual void AddJobs(const JobHandle *inHandles, uint inNumHandles) = 0;
134
135 protected:
137 friend class Job;
138
140 virtual ~Barrier() = default;
141
143 virtual void OnJobFinished(Job *inJob) = 0;
144 };
145
147 using JobFunction = function<void()>;
148
150 virtual ~JobSystem() = default;
151
153 virtual int GetMaxConcurrency() const = 0;
154
157 virtual JobHandle CreateJob(const char *inName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies = 0) = 0;
158
160 virtual Barrier * CreateBarrier() = 0;
161
163 virtual void DestroyBarrier(Barrier *inBarrier) = 0;
164
166 virtual void WaitForJobs(Barrier *inBarrier) = 0;
167
168protected:
170 class Job
171 {
172 public:
174
176 Job([[maybe_unused]] const char *inJobName, [[maybe_unused]] ColorArg inColor, JobSystem *inJobSystem, const JobFunction &inJobFunction, uint32 inNumDependencies) :
177 #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
178 mJobName(inJobName),
179 mColor(inColor),
180 #endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
181 mJobSystem(inJobSystem),
182 mJobFunction(inJobFunction),
183 mNumDependencies(inNumDependencies)
184 {
185 }
186
188 inline JobSystem * GetJobSystem() { return mJobSystem; }
189
191 inline void AddRef()
192 {
193 // Adding a reference can use relaxed memory ordering
194 mReferenceCount.fetch_add(1, memory_order_relaxed);
195 }
196 inline void Release()
197 {
198 // Releasing a reference must use release semantics...
199 if (mReferenceCount.fetch_sub(1, memory_order_release) == 1)
200 {
201 // ... so that we can use aquire to ensure that we see any updates from other threads that released a ref before freeing the job
202 atomic_thread_fence(memory_order_acquire);
203 mJobSystem->FreeJob(this);
204 }
205 }
206
208 inline void AddDependency(int inCount);
209
212 inline bool RemoveDependency(int inCount);
213
216 inline void RemoveDependencyAndQueue(int inCount);
217
219 inline bool SetBarrier(Barrier *inBarrier)
220 {
221 intptr_t barrier = 0;
222 if (mBarrier.compare_exchange_strong(barrier, reinterpret_cast<intptr_t>(inBarrier), memory_order_relaxed))
223 return true;
224 JPH_ASSERT(barrier == cBarrierDoneState, "A job can only belong to 1 barrier");
225 return false;
226 }
227
230 {
231 // Transition job to executing state
232 uint32 state = 0; // We can only start running with a dependency counter of 0
233 if (!mNumDependencies.compare_exchange_strong(state, cExecutingState, memory_order_acquire))
234 return state; // state is updated by compare_exchange_strong to the current value
235
236 // Run the job function
237 {
238 JPH_PROFILE(mJobName, mColor.GetUInt32());
239 mJobFunction();
240 }
241
242 // Fetch the barrier pointer and exchange it for the done state, so we're sure that no barrier gets set after we want to call the callback
243 intptr_t barrier = mBarrier.load(memory_order_relaxed);
244 for (;;)
245 {
246 if (mBarrier.compare_exchange_weak(barrier, cBarrierDoneState, memory_order_relaxed))
247 break;
248 }
249 JPH_ASSERT(barrier != cBarrierDoneState);
250
251 // Mark job as done
252 state = cExecutingState;
253 mNumDependencies.compare_exchange_strong(state, cDoneState, memory_order_relaxed);
254 JPH_ASSERT(state == cExecutingState);
255
256 // Notify the barrier after we've changed the job to the done state so that any thread reading the state after receiving the callback will see that the job has finished
257 if (barrier != 0)
258 reinterpret_cast<Barrier *>(barrier)->OnJobFinished(this);
259
260 return cDoneState;
261 }
262
264 inline bool CanBeExecuted() const { return mNumDependencies.load(memory_order_relaxed) == 0; }
265
267 inline bool IsDone() const { return mNumDependencies.load(memory_order_relaxed) == cDoneState; }
268
269 static constexpr uint32 cExecutingState = 0xe0e0e0e0;
270 static constexpr uint32 cDoneState = 0xd0d0d0d0;
271
272 static constexpr intptr_t cBarrierDoneState = ~intptr_t(0);
273
274private:
275 #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
276 const char * mJobName;
277 Color mColor;
278 #endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
279 JobSystem * mJobSystem;
280 atomic<intptr_t> mBarrier = 0;
281 JobFunction mJobFunction;
282 atomic<uint32> mReferenceCount = 0;
283 atomic<uint32> mNumDependencies;
284 };
285
287 virtual void QueueJob(Job *inJob) = 0;
288
290 virtual void QueueJobs(Job **inJobs, uint inNumJobs) = 0;
291
293 virtual void FreeJob(Job *inJob) = 0;
294};
295
297
299
300#include "JobSystem.inl"
uint32_t uint32
Definition: Core.h:312
unsigned int uint
Definition: Core.h:309
#define JPH_NAMESPACE_END
Definition: Core.h:240
#define JPH_NAMESPACE_BEGIN
Definition: Core.h:234
#define JPH_ASSERT(...)
Definition: IssueReporting.h:33
#define JPH_OVERRIDE_NEW_DELETE
Macro to override the new and delete functions.
Definition: Memory.h:29
#define JPH_PROFILE(...)
Definition: Profiler.h:233
Class that holds an RGBA color with 8-bits per component.
Definition: Color.h:16
uint32 GetUInt32() const
Convert to uint32.
Definition: Color.h:30
A job barrier keeps track of a number of jobs and allows waiting until they are all completed.
Definition: JobSystem.h:123
virtual ~Barrier()=default
Destructor, you should call JobSystem::DestroyBarrier instead of destructing this object directly.
virtual void OnJobFinished(Job *inJob)=0
Called by a Job to mark that it is finished.
virtual void AddJobs(const JobHandle *inHandles, uint inNumHandles)=0
virtual JPH_OVERRIDE_NEW_DELETE void AddJob(const JobHandle &inJob)=0
Definition: JobSystem.h:80
JobHandle()=default
Constructor.
JobHandle(Job *inJob)
Constructor, only to be used by JobSystem.
Definition: JobSystem.h:88
static void sRemoveDependencies(JobHandle *inHandles, uint inNumHandles, int inCount=1)
Remove a dependency from a batch of jobs at once, this can be more efficient than removing them one b...
Definition: JobSystem.inl:28
void RemoveDependency(int inCount=1) const
Definition: JobSystem.h:105
JobHandle(JobHandle &&inHandle) noexcept
Definition: JobSystem.h:85
bool IsDone() const
Check if this job has finished executing.
Definition: JobSystem.h:98
static void sRemoveDependencies(StaticArray< JobHandle, N > &inHandles, int inCount=1)
Helper function to remove dependencies on a static array of job handles.
Definition: JobSystem.h:112
bool IsValid() const
Check if this handle contains a job.
Definition: JobSystem.h:95
JobHandle(const JobHandle &inHandle)=default
JobHandle & operator=(const JobHandle &inHandle)=default
Assignment.
void AddDependency(int inCount=1) const
Add to the dependency counter.
Definition: JobSystem.h:101
A class that contains information for a single unit of work.
Definition: JobSystem.h:171
static constexpr intptr_t cBarrierDoneState
Value to use when the barrier has been triggered.
Definition: JobSystem.h:272
void Release()
Definition: JobSystem.h:196
JPH_OVERRIDE_NEW_DELETE Job(const char *inJobName, ColorArg inColor, JobSystem *inJobSystem, const JobFunction &inJobFunction, uint32 inNumDependencies)
Constructor.
Definition: JobSystem.h:176
uint32 Execute()
Run the job function, returns the number of dependencies that this job still has or cExecutingState o...
Definition: JobSystem.h:229
bool RemoveDependency(int inCount)
Definition: JobSystem.inl:13
void AddDependency(int inCount)
Add to the dependency counter.
Definition: JobSystem.inl:7
bool SetBarrier(Barrier *inBarrier)
Set the job barrier that this job belongs to and returns false if this was not possible because the j...
Definition: JobSystem.h:219
static constexpr uint32 cDoneState
Value of mNumDependencies when job is done executing.
Definition: JobSystem.h:270
JobSystem * GetJobSystem()
Get the jobs system to which this job belongs.
Definition: JobSystem.h:188
bool CanBeExecuted() const
Test if the job can be executed.
Definition: JobSystem.h:264
bool IsDone() const
Test if the job finished executing.
Definition: JobSystem.h:267
void AddRef()
Add or release a reference to this object.
Definition: JobSystem.h:191
static constexpr uint32 cExecutingState
Value of mNumDependencies when job is executing.
Definition: JobSystem.h:269
void RemoveDependencyAndQueue(int inCount)
Definition: JobSystem.inl:22
Definition: JobSystem.h:70
virtual JobHandle CreateJob(const char *inName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies=0)=0
virtual Barrier * CreateBarrier()=0
Create a new barrier, used to wait on jobs.
virtual void WaitForJobs(Barrier *inBarrier)=0
Wait for a set of jobs to be finished, note that only 1 thread can be waiting on a barrier at a time.
virtual void DestroyBarrier(Barrier *inBarrier)=0
Destroy a barrier when it is no longer used. The barrier should be empty at this point.
virtual void QueueJobs(Job **inJobs, uint inNumJobs)=0
Adds a number of jobs at once to the job queue.
virtual int GetMaxConcurrency() const =0
Get maximum number of concurrently executing jobs.
virtual void FreeJob(Job *inJob)=0
Frees a job.
virtual ~JobSystem()=default
Destructor.
function< void()> JobFunction
Main function of the job.
Definition: JobSystem.h:147
virtual void QueueJob(Job *inJob)=0
Adds a job to the job queue.
Class that makes another class non-copyable. Usage: Inherit from NonCopyable.
Definition: NonCopyable.h:11
Definition: Reference.h:101
Job * GetPtr() const
Get pointer.
Definition: Reference.h:131
Simple variable length array backed by a fixed size buffer.
Definition: StaticArray.h:12
const T * data() const
Definition: StaticArray.h:136
size_type size() const
Returns amount of elements in the array.
Definition: StaticArray.h:87