View Single Post
09-04-21, 10:50 AM   #5
DahkCeles
A Cliff Giant
 
DahkCeles's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2020
Posts: 73
This function takes a workload and chips away at it, moving to the next frame after a budgeted amount of time. The budget is set to 50% of the target FPS, or 50% of the current FPS (if there is no target).

Better alternatives might check after X iterations, or sample the frame rate at more than a single point and maybe average it out.

Lua Code:
  1. --  Budgets 50% of target or current FPS to perform a workload.
  2. --  finished = start(workload, onFinish, onDelay)
  3. --  Arguments:
  4. --      workload        table       Stack (last in, first out) of functions to call.
  5. --      onFinish        function?   Optional callback when the table is empty.
  6. --      onDelay         function?   Optional callback each time work delays to the next frame.
  7. --  Returns:
  8. --      finished        boolean     True when finished without any delay; false otherwise.
  9. local function start(workload, onFinish, onDelay)
  10.     local maxDuration = 500/(tonumber(C_CVar.GetCVar("targetFPS")) or GetFrameRate())
  11.     local startTime = debugprofilestop()
  12.     local function continue()
  13.         local startTime = debugprofilestop()
  14.         local task = tremove(workload)
  15.         while (task) do
  16.             task()
  17.             if (debugprofilestop() - startTime > maxDuration) then
  18.                 C_Timer.After(0, continue)
  19.                 if (onDelay) then
  20.                     onDelay()
  21.                 end
  22.                 return false
  23.             end
  24.             task = tremove(workload)
  25.         end
  26.         if (onFinish) then
  27.             onFinish()
  28.         end
  29.         return true
  30.     end
  31.     return continue()
  32. end


And here is the testing code I used to make sure it works...
Lua Code:
  1. -- Simple testing code
  2. -- /sillyloop           Runs a brief atomic workload (same as /sillyloop 1)
  3. -- /sillyloop 0         Runs an empty workload
  4. -- /sillyloop 10000     Runs a long workload with 10000 parts
  5. -- This overwrites existing workloads, so call /sillyloop to stop an ongoing workload
  6.  
  7. local workload = {}
  8.  
  9. local function sillyLoop()
  10.     local x=1
  11.     for i=1, 10000 do
  12.         x = x+1+2+3+4+5+6+7+8+9
  13.         x = x-1-2-3-4-5-6-7-8-9
  14.         x = x/2/3/4/5/6/7/8/9
  15.         x = x*i*2*3*4*5*6*7*8*9
  16.     end
  17.    
  18. end
  19.  
  20. SlashCmdList["DEBUG_SILLYLOOP"] = function(msg)
  21.     wipe(workload)
  22.     for i=1, tonumber(msg) or 1 do
  23.         workload[i] = sillyLoop
  24.     end
  25.     local overallStart = debugprofilestop()
  26.     start(
  27.         workload,
  28.         function() print("done!") end,
  29.         function() print(#workload.." remaining after "..("%.2d"):format(debugprofilestop()-overallStart)/1000) end
  30.     )
  31. end
  32. SLASH_DEBUG_SILLYLOOP1 = "/sillyloop"

Last edited by DahkCeles : 09-04-21 at 02:31 PM. Reason: I've now tested this in-game and fleshed it out with an example
  Reply With Quote