function JobScheduler(budgets) {
  //>>includeStart('debug', pragmas.debug);
  if (defined(budgets) && budgets.length !== JobType.NUMBER_OF_JOB_TYPES) {
    throw new DeveloperError(
      "A budget must be specified for each job type; budgets.length should equal JobType.NUMBER_OF_JOB_TYPES.",
    );
  }
  //>>includeEnd('debug');

  // Total for defaults is half of of one frame at 10 fps
  const jobBudgets = new Array(JobType.NUMBER_OF_JOB_TYPES);
  jobBudgets[JobType.TEXTURE] = new JobTypeBudget(
    defined(budgets) ? budgets[JobType.TEXTURE] : 10.0,
  );
  // On cache miss, this most likely only allows one shader compile per frame
  jobBudgets[JobType.PROGRAM] = new JobTypeBudget(
    defined(budgets) ? budgets[JobType.PROGRAM] : 10.0,
  );
  jobBudgets[JobType.BUFFER] = new JobTypeBudget(
    defined(budgets) ? budgets[JobType.BUFFER] : 30.0,
  );

  const length = jobBudgets.length;
  let i;

  let totalBudget = 0.0;
  for (i = 0; i < length; ++i) {
    totalBudget += jobBudgets[i].total;
  }

  const executedThisFrame = new Array(length);
  for (i = 0; i < length; ++i) {
    executedThisFrame[i] = false;
  }

  this._totalBudget = totalBudget;
  this._totalUsedThisFrame = 0.0;
  this._budgets = jobBudgets;
  this._executedThisFrame = executedThisFrame;
}
Engine for time slicing jobs during a frame to amortize work over multiple frames.  This supports:
<ul>
  <li>
    Separate budgets for different job types, e.g., texture, shader program, and buffer creation.  This
    allows all job types to make progress each frame.
  </li>
  <li>
    Stealing from other jobs type budgets if they were not exhausted in the previous frame.  This allows
    using the entire budget for all job types each frame even if, for example, all the jobs are the same type.
  </li>
  <li>
    Guaranteed progress on all job types each frame, even if it means exceeding the total budget for the frame.
    This prevents, for example, several expensive texture uploads over many frames from prevent a shader compile.
  </li>
</ul>

@private