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
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(const 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 acquire 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 #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
271 const char * GetName() const { return mJobName; }
272 #endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
273
274 static constexpr uint32 cExecutingState = 0xe0e0e0e0;
275 static constexpr uint32 cDoneState = 0xd0d0d0d0;
276
277 static constexpr intptr_t cBarrierDoneState = ~intptr_t(0);
278
279private:
280 #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
281 const char * mJobName;
282 Color mColor;
283 #endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
284 JobSystem * mJobSystem;
285 atomic<intptr_t> mBarrier = 0;
286 JobFunction mJobFunction;
287 atomic<uint32> mReferenceCount = 0;
288 atomic<uint32> mNumDependencies;
289 };
290
292 virtual void QueueJob(Job *inJob) = 0;
293
295 virtual void QueueJobs(Job **inJobs, uint inNumJobs) = 0;
296
298 virtual void FreeJob(Job *inJob) = 0;
299};
300
302
304
305#include "JobSystem.inl"
#define JPH_EXPORT
Definition: Core.h:236
unsigned int uint
Definition: Core.h:452
#define JPH_NAMESPACE_END
Definition: Core.h:378
std::uint32_t uint32
Definition: Core.h:455
#define JPH_NAMESPACE_BEGIN
Definition: Core.h:372
#define JPH_ASSERT(...)
Definition: IssueReporting.h:33
#define JPH_OVERRIDE_NEW_DELETE
Macro to override the new and delete functions.
Definition: Memory.h:31
#define JPH_PROFILE(...)
Definition: Profiler.h:268
Class that holds an RGBA color with 8-bits per component.
Definition: Color.h:16
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
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
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
const char * GetName() const
Get the name of the job.
Definition: JobSystem.h:271
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 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
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
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
Simple variable length array backed by a fixed size buffer.
Definition: StaticArray.h:14
const T * data() const
Definition: StaticArray.h:138
size_type size() const
Returns amount of elements in the array.
Definition: StaticArray.h:89