Part of the Khronos Group

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Page 2 of 2 FirstFirst 12
Results 11 to 13 of 13

Thread: Work groups with priorities

  1. #11
    Advanced Member Frequent Contributor
    Join Date
    Apr 2009
    I am just wondering aloud here, but it seems to me that the core issue that thomas wants to resolve is the reasonably common situation of asking the GPU to compute something but not needing the result ASAP, but later might be next frame, or many frames away.

    One idea is the following, is to introduce the idea of "tasks". A task is just a collection of commands to send to GL to get processed. A task can be dependent on other tasks, i.e. the dependent tasks need to be done before the task itself. Lastly, one may want the ability to cancel a task as well (for example new data comes, and if the task is still waiting, then go ahead and cancel it). With that idea clear, here is one way to express that idea:

    Code :
      Generate task objects
      \param n number of tasks objects to create
      \param tasks specifies an array in which to store the task object ID's
    GenTasks(sizei n, uint *tasks)
      Delete task objects
      \param n number of tasks objects to delete
      \param tasks specifies an array of task objects to delete
    DeleteTasks(sizei n, uint *tasks)
       Specify that all subsequent commands are to 
       be for a task to saved for later execution
       \param task task object to make active.
    BeginTask(uint task);
      Specifies resume of normal GL operation, i.e.
      commands are no longer added to a task.
      Specifies that a target task, target_task, may only
      be executed after a set of other tasks are completed
    AddTaskDependecy(uint target_task, sizei n, uint *tasks);
      Specifies that a target task, target_task, no longer
      needs to wait for other tasks to complete before being 
    RemoveTaskDepenency(uint target_task, sizei n, uint *tasks);
      Place a task to be executed, return immediately. An implementation
      will select when it is executed. 
    ExecuteTaskNonBlock(uint task);
      Forces a GL implementation to finish the named task before
      executing any subsequent GL commands. Does not block. 
      The task must have had ExecuteTaskNonBlock already called.
    TaskBarrier(uint task);
     If a task has not yet completed, but ExecuteTaskNonBlock has been called,
     cancel the task. The return code is one of
      -PARTIALLY_EXECUTED task was only partially run
      -NOT_EXECUTED task never had a chance to run at all
      -COMPLETED task finished it's run anyways
    enum CancelTask(uint task)
      Query a task
      \param task task to query
      \param prop what property to query, values may be: TASK_STATUS 
                       NUMBER_DEPENDENT_TASKS, DEPENDENT_TASKS, others?
      \param out location which to write values     
      TASK_STATUS gives one of:
      - INACTIVE: task is either complete or not queued
      - ACTIVE: task is either running right now or queued to be run
    QueryTask(uint task, enum prop, uint *out);

    Naturally this opens up lots of messy cans of worms like what happens when a task is cancelled but it is relied upon by another running task, or even if task should be cancellable. Moreover, weather or not the same task can be queued up more than once. Along those lines, weather or not a task is modifiable while it is queued up. Going further state of stuff on which the task resides. These are my preferences for each of these worms:
    - A given task may not be queued up more than once, i.e. calling ExecuteTaskNonBlock on a task that has not yet finished but had ExecuteTaskNonBlock called on it before is either an error or ignored. Having the same task queued repeatedly opens up a mess to specify with respect to dependent tasks, i.e. should they get rerun too, etc. This is messy regardless once dependent tasks come into play, I am on the side that calling ExecuteTaskNonBlock on a task already queued or running is ignored
    - A task is unchangeable once TaskEnd() is called.
    - If a task T relies on any GL objects, such as buffer objects and textures, it uses the state of those objects when it is run. Thus creating a task that uses a buffer object B the values used by the task in it's run are not the values of B when the task was defined; rather they are whatever values B has when the task is run. To get well defined results an application needs to not write to B (except in a task on which T depends) until T completes.
    - If a task T is cancelled where T has dependent tasks, then those dependent tasks that were made to run so that T may ran are also cancelled.
    - If a task T depends on a task S and S is cancelled where as T is not cancelled, then I don't know. Error? Ignored?
    - Circular task dependency is an error.
    - Order of operations of tasks is only guaranteed on dependency graph, i.e. the only task guaranteed to be done before a task T are those tasks that T depends on.

    More worms are the nature of GL state, I would want that the GL state be per task, thus GL state changes are isolated to the task and each task has their own GL state vector.

    This sounds like the D3D's deferred command stuff.

    This suggestion forces the developer to say exactly how important something is by specifying there dependency task. An ideal implementation of the above would interpret TaskBarrier() as moving the task and it's dependency up to most important thing to get done. Not too sure if there should be a blocking version of TaskBarrier as well though.....
    Last edited by kRogue; 06-11-2013 at 10:34 AM. Reason: Tweaks

  2. #12
    Junior Member Newbie
    Join Date
    Apr 2013
    Adding priorities to contexts sounds fine, but I don't see the benefits of multiple "threads" per context.

    I think to original suggestion is to split up GPU tasks into many prioritized command streams (glBindTexture isn't one, mostly glDraw* matters) and to use implicit synchronization between them. So the execution seems to be in order, but the driver gets a hint which results aren't needed soon.

    But to use this feature wisely, explicit synchronization should be used:
    - We need to check if the task has finished to not stall the rendering and to not wait much too long.
    - We also have to check for space on this command stream, else the driver will have to wait.

    So the only usage of this implicit synchronization are games which knows that they need some offscreen renderings in exactly some frames. Everyone else would use explicit synchronization and could also use shared prioritized context without additional overhead.

    btw: EGL_IMG_context_priority
    Last edited by degasus; 06-17-2013 at 06:27 AM.

  3. #13
    Junior Member Newbie
    Join Date
    Jan 2008
    the sequential execution model is very simple [...]
    The sequential execution model includes, among other things glBeginConditionalRender/glEndConditionalRender.

    What is so much different for conditional render, what issues exist with my proposal that conditional render does not have too? I don't see that much of a difference. You group some commands together, logically isolate them from the "main stream" and tell the driver to only process them based on some query. It might process the commands speculatively anyway if the result is not ready in time. Or it might start processing and stop in the middle and discard the results. Or, something else. It might ignore the conditional render commands alltogether. Who knows, and who cares -- but you do have the possibility to tell the driver your intent.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts