I’m performing a long, drawn out process using a Coroutine, where I track the elapsed time and, if it exceeds a threshold (e.g. 1/60 second), I yield return null
and allow a frame to render before continuing the cycle.
While this works fine for a CPU-bound process, however, it runs into unusual dilemmas for a GPU-driven one. My loop, in brief, looks like this:
Stopwatch sw = new Stopwatch();
sw.Start();
bool working = true;
Texture2D inputTexture;
RenderTexture[] outputTexture = new Texture2D[2];
// skipping texture preparations
Material mat; // includes the shader used to process the output texture(s)
int texIndex;
while(working)
{
// Incorporates the previous resulting output in the new pass,
// hence the XOR cycled index
mat.SetTexture("_CurrentSampler", outputTexture[texIndex]
texIndex ^= 1;
Graphics.Blit(inputTexture, outputTexture[texIndex], mat, 0);
if(sw.Elapsed.TotalSeconds >= (1.0 / 60.0))
{
yield return null;
sw.Restart();
}
if([process completed])
{
working = false;
}
}
sw.Stop();
At a fundamental level, this works. When the process takes a while, it renders a new frame and resumes.
However, this has actually been a double-edged sword:
- If the loop is given too little time (e.g. 0.0 seconds, rather than 1/60), the process takes a very long time, running only a single pass per frame.
- If the loop is given too much time (e.g. 1.0 second), the process also takes a long time - I’m presuming this is due to the CPU and GPU processes falling out of sync, but I can’t claim that with 100% certainty.
The truly confounding factor, however, is that it seems like the Blit() calls are falling behind, so if I actually *USE* the 1/60 second delay in my example, it still winds up running slowly and, in turn, taking longer than it otherwise could.
To give an example, I’m currently processing a 256x256 texture, where it takes ~20 seconds with no rendered frames in between, ~13 seconds with a 1/60-second (specified) delay (running poorly), and ~8 seconds with a 1/480-second (specified) delay (running at near-60 fps). (To note, it takes 500+ seconds when yielding on every pass, since it’s very heavy-duty texture processing overall, since it’s getting through ~4000+ passes per frame otherwise)
All of this suggests that a degree of processor and video desynchronization is resulting in a delay that is proving difficult to accurately time, based on the results of 1/60 vs. 1/480-second cycle timers.
Furthermore, because processors, video cards, and texture sizes will vary, there’s no magic number that will inherently prove satisfactory here. While a [256x256] texture works well for my hardware with 1/480-second (specified) timing, a [512x512] texture will choke and take 200+ seconds to process (and yes, it’s still getting through plenty of cycles per rendered frame).
So, my question(s) is(are), essentially:
Is there any reasonable way to accurately track and react to time spent in a Blit()-heavy loop?
Why *does* the process take longer when there are *fewer* rendered frames?
Edit: clarity, cleanup, formatting