@@ -79,7 +79,14 @@ typedef void ujob_job_t;
7979typedef long ujob_handle_t ;
8080typedef void ujob_dependency_chain;
8181typedef int WorkStealingRange;
82- typedef void * (*JobsCallbackFunctions)(void *, int );
82+
83+ struct JobsCallbackFunctions {
84+ void (*execute)(void *, int );
85+ void (*completed)(void *);
86+ };
87+
88+ // typedef void (*JobsCallbackFunctions)(void*);
89+ // typedef void (*JobsCallbackFunctionsParallel)(void*, int);
8390typedef void ScriptingBackendNativeObjectPtrOpaque;
8491class JobScheduleParameters ;
8592class JobFence ;
@@ -681,15 +688,33 @@ static unsigned long U6_ujob_schedule_job_internal(ujob_control_t* x, ujob_handl
681688}
682689
683690struct callback_args_t {
684- JobsCallbackFunctions* func;
691+ int count;
692+ JobsCallbackFunctions funcs;
685693 void * arg;
686- int loop_index;
694+ // std::mutex mutex;
695+ // std::condition_variable cv;
696+ // bool completed;
687697};
688698
689- static void * job_callback (void * arg, int )
699+ static void job_callback_execute (void * arg, int )
700+ {
701+ callback_args_t * args = reinterpret_cast <callback_args_t *>(arg);
702+
703+ /* Perform all iterations of the loop inside this job */
704+ for (int i = 0 ; i < args->count ; i++)
705+ args->funcs .execute (args->arg , i);
706+ }
707+
708+ static void job_callback_completed (void * arg)
690709{
691710 callback_args_t * args = reinterpret_cast <callback_args_t *>(arg);
692- return (*args->func )(args->arg , args->loop_index );
711+ if (args->funcs .completed ) {
712+ args->funcs .completed (args->arg );
713+ }
714+
715+ // std::unique_lock<std::mutex> lock(args->mutex);
716+ // args->completed = true;
717+ // args->cv.notify_all();
693718}
694719
695720/* The function parameters from the symbol are *wrong*! 6th parameter must be
@@ -703,50 +728,42 @@ static void* job_callback(void* arg, int)
703728 */
704729static ujob_handle_t U6_ujob_schedule_parallel_for_internal (ujob_control_t * x, JobsCallbackFunctions* y, void * job_callback_arg, WorkStealingRange* a, unsigned int count, unsigned long c, ujob_handle_t const * d, long e)
705730{
706- LOG (LL_TRACE, LCF_HACKS, " U6_ujob_schedule_parallel_for_internal called with callback %p , steal mode %d, unknown uint %d " , *y , a?(*a):0 , count);
731+ LOG (LL_TRACE, LCF_HACKS, " U6_ujob_schedule_parallel_for_internal called with callback args %p , steal mode %d, count %d, ujob_handle_t %p " , job_callback_arg , a?(*a):0 , count, d );
707732
708733 if (!(Global::shared_config.game_specific_sync & SharedConfig::GC_SYNC_UNITY_JOBS))
709734 return orig::U6_ujob_schedule_parallel_for_internal (x, y, job_callback_arg, a, count, c, d, e);
710735
711736 ujob_handle_t ret = 0 ;
712- static JobsCallbackFunctions loop_callback = &job_callback;
737+ static JobsCallbackFunctions loop_callbacks = {&job_callback_execute, job_callback_completed};
738+ /* Instead of scheduling all the jobs in one call, we schedule one
739+ * individual job that will perform all the iterations in order. The job may still
740+ * run on a worker thread, but it should be fine for determinism.
741+ * Normally, the job callback function is receiving the loop index as
742+ * second argument, so we pass our own callback function, which receives
743+ * the original callback, the original callback argument, and the iteration
744+ * count. */
745+ callback_args_t * args = new callback_args_t ;
746+ args->count = count;
747+ args->funcs .execute = y->execute ;
748+ args->funcs .completed = y->completed ;
749+ args->arg = job_callback_arg;
750+ // args->completed = false;
751+
752+ // std::unique_lock<std::mutex> lock(args->mutex);
753+
754+ ret = orig::U6_ujob_schedule_parallel_for_internal (x, &loop_callbacks, args, a, 1 , c, d, e);
713755
714- if (count == 1 ) {
715- ret = orig::U6_ujob_schedule_parallel_for_internal (x, y, job_callback_arg, a, count, c, d, e);
716-
717- /* In newer Unity 6 versions, there is a dedicated internal function for
718- * waiting on a job */
719- if (orig::U6_ujob_wait_for)
720- orig::U6_ujob_wait_for (x, ret, 1 );
721- else if (orig::U2K_JobQueue_WaitForJobGroupID)
722- orig::U2K_JobQueue_WaitForJobGroupID (reinterpret_cast <JobQueue*>(x), reinterpret_cast <JobGroup*>(ret), 0 , true );
723- }
724- else {
725- /* Instead of scheduling all the jobs in one call, we schedule each
726- * individual job and wait for the job to complete. The job may still
727- * run on a worker thread, but it should be fine for determinism.
728- * Normally, the job callback function is receiving the loop index as
729- * second argument, so we pass our own callback function, which receives
730- * the original callback, the original callback argument, and the loop
731- * index.
732- */
733- for (int i=0 ; i < count; i++) {
734- callback_args_t * args = new callback_args_t ;
735- args->func = y;
736- args->arg = job_callback_arg;
737- args->loop_index = i;
738-
739- ret = orig::U6_ujob_schedule_parallel_for_internal (x, &loop_callback, args, a, 1 , c, d, e);
740-
741- if (orig::U6_ujob_wait_for)
742- orig::U6_ujob_wait_for (x, ret, 1 );
743- else if (orig::U2K_JobQueue_WaitForJobGroupID)
744- orig::U2K_JobQueue_WaitForJobGroupID (reinterpret_cast <JobQueue*>(x), reinterpret_cast <JobGroup*>(ret), 0 , true );
745-
746- /* It should be safe to delete our custom callback argument here */
747- delete args;
748- }
749- }
756+ /* Manually waiting on all jobs to execute */
757+ // args->cv.wait(lock, [&args] { return args->completed; });
758+
759+ if (orig::U6_ujob_wait_for)
760+ orig::U6_ujob_wait_for (x, ret, 1 );
761+ else if (orig::U2K_JobQueue_WaitForJobGroupID)
762+ orig::U2K_JobQueue_WaitForJobGroupID (reinterpret_cast <JobQueue*>(x), reinterpret_cast <JobGroup*>(ret), 0 , true );
763+
764+ /* It should be safe to delete our custom callback argument here */
765+ // delete args->loop_index;
766+ delete args;
750767
751768 return ret;
752769}
0 commit comments