Last active
June 25, 2024 19:17
-
-
Save naphipps/08b4b1cd304141bce205a1aaf7e11a40 to your computer and use it in GitHub Desktop.
Example of how to hook up to box2d V3-alpha task API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
I used this in sample.cpp/hpp of the box2d V3-alpha project. | |
This is just an example of a non-enkiTS job system used with box2d V3-alpha | |
- Nathan Phipps 1/14/24 | |
*/ | |
// --------- begin of inside sample.cpp --------- | |
struct NpeSubTaskPayload | |
{ | |
b2TaskCallback* task = nullptr; | |
int32_t begin = 0; | |
int32_t end = 0; | |
void* taskContext = nullptr; | |
void* userContext = nullptr; | |
}; | |
static void NpeExecuteSubTask(np::mem::Delegate& d) | |
{ | |
NpeSubTaskPayload* payload = static_cast<NpeSubTaskPayload*>(d.GetPayload()); | |
payload->task(payload->begin, payload->end, d.GetId(), payload->taskContext); | |
Sample* sample = static_cast<Sample*>(payload->userContext); | |
::np::mem::Destroy(sample->_npe_allocator, payload); | |
} | |
static void* NpeEnqueueTask(b2TaskCallback* task, int32_t itemCount, int32_t minRange, void* taskContext, void* userContext) | |
{ | |
using namespace np; //please ignore, just for clarity and convenience | |
Sample* sample = static_cast<Sample*>(userContext); | |
jsys::JobSystem& job_sys = sample->_npe_job_system; | |
mem::sptr<jsys::Job> completion = job_sys.CreateJob(); | |
int sub_job_count = (int)::std::ceil((float)itemCount / (float)minRange); | |
// you might want to plan for N-jobs per worker: | |
int job_count_per_worker = 3; | |
int threshold = job_count_per_worker * job_sys.GetJobWokerCount(); | |
if (sub_job_count > threshold) | |
{ | |
sub_job_count = threshold; | |
// note that this could disrupt the distribution of items per job, | |
// so minRange may need to be adjusted: | |
minRange = itemCount / sub_job_count; | |
} | |
// You will need to play-test this to figure out the best balance for your target platform! | |
for (int i=0; i<sub_job_count; ++i) | |
{ | |
NpeSubTaskPayload* payload = mem::Create<NpeSubTaskPayload>(sample->_npe_allocator, NpeSubTaskPayload{ | |
task, | |
i * minRange, | |
i == sub_job_count - 1 ? itemCount : (i + 1) * minRange, | |
// ^ make sure last sub-task-payload's range includes all remaining items - there are easier/faster ways to do this | |
taskContext, | |
userContext | |
}); | |
mem::sptr<jsys::Job> sub_task_job = job_sys.CreateJob(); | |
sub_task_job->SetPayload(payload); | |
sub_task_job->SetCallback(NpeExecuteSubTask); | |
jsys::Job::AddDependency(completion, sub_task_job); | |
job_sys.SubmitJob(jsys::JobPriority::Normal, sub_task_job); | |
} | |
void* completion_ptr = mem::Create<mem::sptr<jsys::Job>>(sample->_npe_allocator, completion); | |
job_sys.SubmitJob(jsys::JobPriority::Normal, completion); | |
return completion_ptr; | |
// If we want NpeFinishTask to be an empty function, | |
// or if we do not want to allocate that completion_ptr, | |
// or if we want this function to be synchronous, | |
// then replace the above 3 lines with: | |
/* | |
while (!completion->IsComplete()) | |
thr::ThisThread::yield(); | |
return nullptr; | |
// If you use the this, NpeFinishTask needs to be an empty function! | |
*/ | |
} | |
static void NpeFinishTask(void* taskPtr, void* userContext) | |
{ | |
using namespace np; //please ignore, just for clarity and convenience | |
mem::sptr<jsys::Job> completion = *static_cast<mem::sptr<jsys::Job>*>(taskPtr); | |
// completion job was made dependent on all sub-task jobs | |
// so we can proceed when the completion job is complete | |
while (!completion->IsComplete()) | |
thr::ThisThread::yield(); | |
Sample* sample = static_cast<Sample*>(userContext); | |
mem::Destroy(sample->_npe_allocator, static_cast<mem::sptr<jsys::Job>*>(taskPtr)); | |
} | |
// ... other stuff from sample.cpp here ... | |
Sample::Sample(const Settings& settings) | |
{ | |
// ... other Sample() stuff ... | |
b2WorldDef worldDef = b2_defaultWorldDef; | |
worldDef.workerCount = settings.workerCount; | |
worldDef.enqueueTask = &NpeEnqueueTask; | |
worldDef.finishTask = &NpeFinishTask; | |
// ... other Sample() stuff ... | |
_npe_job_system.SetJobWorkerCount(worldDef.workerCount); | |
_npe_job_system.Start(); | |
} | |
Sample::~Sample() | |
{ | |
_npe_job_system.Stop(); | |
// ... other ~Sample() stuff ... | |
} | |
// --------- end of inside sample.cpp --------- | |
// --------- begin of inside sample.hpp --------- | |
#include <NP-Engine/NP-Engine.hpp> | |
class Sample | |
{ | |
public: | |
// ... other Sample stuff ... | |
::np::mem::TraitAllocator _npe_allocator; //really should not be public, alas this is an example | |
::np::jsys::JobSystem _npe_job_system; //really should not be public, alas this is an example | |
}; | |
// --------- end of inside sample.hpp --------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment