Example of using EnkiTS within game loop and work queue? #133
-
I'm evaluating enkiTS for a C/C++ game project. I've been wondering what is the best approach to handle enki tasks within a game loop and a work queue that contains the data that would be used by the enki tasks. Here is a barebones example I could come up with: typedef struct enkits_work_item{
glm::vec3 pos;
engine_instance* engine; //engine instance containing rendering methods
}enkits_work_item;
enkiTaskSet* task_set;
std::queue<enkits_work_item> work_queue;
void initialize(){
//initialize code
//...
pETS = enkiNewTaskScheduler();
config = enkiGetTaskSchedulerConfig(pETS);
enkiInitTaskSchedulerWithConfig(pETS, config);
task_set = enkiCreateTaskSet(pETS, task_set_function);
}
void update(float delta){
populate_work_queue();//add work items to queue
for (int i = 0; i < config.numTaskThreadsToCreate; i++) {
if (!enkits_work_item.empty()) {
enkits_work_item entry = work_queue.front();
work_queue.pop();
enkiSetArgsTaskSet(task_set, &entry);
enkiAddTaskSet(pETS, task_set);
}
}
enkiWaitForTaskSet(pETS, task_set);
}
void task_set_function(uint32_t start, uint32_t end, uint32_t threadnum, void* pArgs)
{
enkits_work_item * entry_struct = (enkits_work_item *)pArgs;
printf("Test entry pos: %s, threadnum: %d, addr %p \n", glm::to_string(cache_tree_struct->pos).c_str(),threadnum,(void*)cache_tree_struct);
} Though I seem to be running into an issue where the different tasks seem to all get the same entry struct data on the different threads. Please let me know, I will greatly appreciate any assistance that can be provided. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
Hi @the-brickster, I have some generic recommendations first: First off, since you are making a C/C++ game I would use the C++ interface, as the C interface is built on top of that and the C++ interface is likely easier to understand, and should make things a bit more obvious as to what you are doing. In this case it would make it clear that calling I would also run in debug occasionally to check you are using the API correctly. In your case this should assert in For the specifics of your project I don't know enough to give precise recommendations. I see you are using a queue, which would imply that each item might need to be completed before the next item is started, which would not allow for a great deal of multithreading. If however the work items are independent this can be easily performed by having a single task set which is passed an array (or Additional note: I would pass the typedef struct enkits_work_item{
glm::vec3 pos;
engine_instance* engine; //engine instance containing rendering methods
}enkits_work_item;
enkiTaskSet* task_set;
std::vector<enkits_work_item> work_queue; // vector not queue
void initialize(){
//initialize code
//...
pETS = enkiNewTaskScheduler();
config = enkiGetTaskSchedulerConfig(pETS);
enkiInitTaskSchedulerWithConfig(pETS, config);
task_set = enkiCreateTaskSet(pETS, task_set_function);
}
void update(float delta){
work_queue.clear();
populate_work_queue();//add work items to queue
enkiSetArgsTaskSet(task_set, &work_queue);
enkiAddTaskSet(pETS, task_set);
enkiWaitForTaskSet(pETS, task_set);
}
void task_set_function(uint32_t start, uint32_t end, uint32_t threadnum, void* pArgs)
{
std::vector<enkits_work_item>* pWork_queue = (std::vector<enkits_work_item>*)pArgs;
for( uint32_t i = start; i < end; ++i )
{
enkits_work_item * entry_struct = (*pWork_queue)[i];
printf("Test entry pos: %s, threadnum: %d, addr %p \n", glm::to_string(cache_tree_struct->pos).c_str(),threadnum,(void*)cache_tree_struct);
}
} |
Beta Was this translation helpful? Give feedback.
-
FYI In Avoyd a trick I use for populating work queues like this is to have one input array per thread, so that during processing any work can add items by adding them to the array for that thread using the Then, after the tasks complete a single task concatenates all the work queue arrays into one output array for the next frame's (or stage's) work. |
Beta Was this translation helpful? Give feedback.
Hi @the-brickster,
I have some generic recommendations first:
First off, since you are making a C/C++ game I would use the C++ interface, as the C interface is built on top of that and the C++ interface is likely easier to understand, and should make things a bit more obvious as to what you are doing. In this case it would make it clear that calling
enkiSetArgsTaskSet
overwrites the data fortask_set
.I would also run in debug occasionally to check you are using the API correctly. In your case this should assert in
TaskScheduler::AddTaskSetToPipe
as you are adding the same task multiple times before it completes.For the specifics of your project I don't know enough to give precise recomm…