Path: blob/master/modules/jolt_physics/spaces/jolt_job_system.cpp
10278 views
/**************************************************************************/1/* jolt_job_system.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "jolt_job_system.h"3132#include "../jolt_project_settings.h"3334#include "core/debugger/engine_debugger.h"35#include "core/object/worker_thread_pool.h"36#include "core/os/os.h"37#include "core/os/time.h"3839#include "Jolt/Physics/PhysicsSettings.h"4041void JoltJobSystem::Job::_execute(void *p_user_data) {42Job *job = static_cast<Job *>(p_user_data);4344#ifdef DEBUG_ENABLED45const uint64_t time_start = Time::get_singleton()->get_ticks_usec();46#endif4748job->Execute();4950#ifdef DEBUG_ENABLED51const uint64_t time_end = Time::get_singleton()->get_ticks_usec();52const uint64_t time_elapsed = time_end - time_start;5354timings_lock.lock();55timings_by_job[job->name] += time_elapsed;56timings_lock.unlock();57#endif5859job->Release();60}6162JoltJobSystem::Job::Job(const char *p_name, JPH::ColorArg p_color, JPH::JobSystem *p_job_system, const JPH::JobSystem::JobFunction &p_job_function, JPH::uint32 p_dependency_count) :63JPH::JobSystem::Job(p_name, p_color, p_job_system, p_job_function, p_dependency_count)64#ifdef DEBUG_ENABLED65,66name(p_name)67#endif68{69}7071JoltJobSystem::Job::~Job() {72if (task_id != -1) {73WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id);74}75}7677void JoltJobSystem::Job::push_completed(Job *p_job) {78Job *prev_head = nullptr;7980do {81prev_head = completed_head.load(std::memory_order_relaxed);82p_job->completed_next = prev_head;83} while (!completed_head.compare_exchange_weak(prev_head, p_job, std::memory_order_release, std::memory_order_relaxed));84}8586JoltJobSystem::Job *JoltJobSystem::Job::pop_completed() {87Job *prev_head = nullptr;8889do {90prev_head = completed_head.load(std::memory_order_relaxed);91if (prev_head == nullptr) {92return nullptr;93}94} while (!completed_head.compare_exchange_weak(prev_head, prev_head->completed_next, std::memory_order_acquire, std::memory_order_relaxed));9596return prev_head;97}9899void JoltJobSystem::Job::queue() {100AddRef();101102// Ideally we would use Jolt's actual job name here, but I'd rather not incur the overhead of a memory allocation or103// thread-safe lookup every time we create/queue a task. So instead we use the same cached description for all of them.104static const String task_name("Jolt Physics");105106task_id = WorkerThreadPool::get_singleton()->add_native_task(&_execute, this, true, task_name);107}108109int JoltJobSystem::GetMaxConcurrency() const {110return thread_count;111}112113JPH::JobHandle JoltJobSystem::CreateJob(const char *p_name, JPH::ColorArg p_color, const JPH::JobSystem::JobFunction &p_job_function, JPH::uint32 p_dependency_count) {114Job *job = nullptr;115116while (true) {117JPH::uint32 job_index = jobs.ConstructObject(p_name, p_color, this, p_job_function, p_dependency_count);118119if (job_index != JPH::FixedSizeFreeList<Job>::cInvalidObjectIndex) {120job = &jobs.Get(job_index);121break;122}123124WARN_PRINT_ONCE("Jolt Physics job system exceeded the maximum number of jobs. This should not happen. Please report this. Waiting for jobs to become available...");125126OS::get_singleton()->delay_usec(100);127128_reclaim_jobs();129}130131// This will increment the job's reference count, so must happen before we queue the job132JPH::JobHandle job_handle(job);133134if (p_dependency_count == 0) {135QueueJob(job);136}137138return job_handle;139}140141void JoltJobSystem::QueueJob(JPH::JobSystem::Job *p_job) {142static_cast<Job *>(p_job)->queue();143}144145void JoltJobSystem::QueueJobs(JPH::JobSystem::Job **p_jobs, JPH::uint p_job_count) {146for (JPH::uint i = 0; i < p_job_count; ++i) {147QueueJob(p_jobs[i]);148}149}150151void JoltJobSystem::FreeJob(JPH::JobSystem::Job *p_job) {152Job::push_completed(static_cast<Job *>(p_job));153}154155void JoltJobSystem::_reclaim_jobs() {156while (Job *job = Job::pop_completed()) {157jobs.DestructObject(job);158}159}160161JoltJobSystem::JoltJobSystem() :162JPH::JobSystemWithBarrier(JPH::cMaxPhysicsBarriers),163thread_count(MAX(1, WorkerThreadPool::get_singleton()->get_thread_count())) {164jobs.Init(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsJobs);165}166167void JoltJobSystem::pre_step() {168// Nothing to do.169}170171void JoltJobSystem::post_step() {172_reclaim_jobs();173}174175#ifdef DEBUG_ENABLED176177void JoltJobSystem::flush_timings() {178static const StringName profiler_name("servers");179180EngineDebugger *engine_debugger = EngineDebugger::get_singleton();181182if (engine_debugger->is_profiling(profiler_name)) {183Array timings;184185for (const KeyValue<const void *, uint64_t> &E : timings_by_job) {186timings.push_back(static_cast<const char *>(E.key));187timings.push_back(USEC_TO_SEC(E.value));188}189190timings.push_front("physics_3d");191192engine_debugger->profiler_add_frame_data(profiler_name, timings);193}194195for (KeyValue<const void *, uint64_t> &E : timings_by_job) {196E.value = 0;197}198}199200#endif201202203