2
\$\begingroup\$

I updated my DirectX11 renderer to render on multiple threads using deferred contexts. Before my change, the Present method took a negligible amount of time (according to VTune analysis). After my change, Present now takes a significant amount of time (15-30ms or so). I've tried using double / triple buffering, disabling vsync, but that makes no difference. My scene runs at about 25 hz, and vtune shows my CPU at 100% up until i call Present, at which points it just waits for a good bit.

Does this mean that I'm GPU bound, or is this just an artifact of using deferred context rendering, since I'm not sending the GPU any work at all until the very last second? And if its the latter, is there anything I can do to get started on the next scene rather than waiting around on Present?

\$\endgroup\$
  • \$\begingroup\$ Remember that Present just tells the system you are done with rendering the current frame and are ready to render the next. It's not necessarily blocking. By design, Windows will let you queue up to 3 frames before Present blocks to let the GPU catch up. \$\endgroup\$ – Chuck Walbourn Oct 13 '15 at 4:51
  • \$\begingroup\$ Right. That's why I was confused as to whether or not it was blocking because it was GPU bound, or if it was blocking because the driver was doing actual CPU work. After more investigation I determined that it was blocking due to the driver doing work, and reordering my scene as suggested by Sean Middleditch let the driver start doing its work much earlier, which increased my frame rate by about 50%. \$\endgroup\$ – default Oct 13 '15 at 18:23
4
\$\begingroup\$

First, please use a real GPU profiling tool rather than inaccurate measurements. There is a good GPU and CPU profiler built into Visual Studio, so you don't have to hunt around to find decent tools. That out of the way...

Standard industry practice is to render first, then do game logic and physics, then finally call Present. That way you parallelize the GPU and CPU workloads.

# !! CORRECT !! game loop for non-trivial games
for N in 1..infinity:
  draw frame N
  prepare new frame N+1 # GOOD - GPU and CPU are working in parallel here!
  present N

The classic render loop you find in most books gets this horribly wrong.

# !! INCORRECT !! game loop from introductory learning materials
for N in 1..infinity:
  prepare new frame N # BAD - GPU is idle!
  draw frame N
  present N # BAD - CPU is idle!

More complex games have to intelligently intermingle the GPU commands and CPU work, since submitting a frame to the GPU by itself takes a considerable amount of time. This is often achieved with a threaded job system that uses a "shared nothing" approach to rendering. While the threading makes things more complicated, the game loop is similar to the first loop above: the difference is just that the "draw current frame" and "prepare next frame" are happening at the same time using separate copies of the game's state.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks. This was very helpful. I ended up refactoring my engine based on your answer and got some nice performance gains. \$\endgroup\$ – default Oct 8 '15 at 22:07
0
\$\begingroup\$

So I think I figured out whats going on here. If I Sleep at the beginning of my frame, then the frame rate slows down, which means I'm not strictly CPU bound.

If I sleep after I call ExecuteCommandList but before Present, my frame rate doesn't change (until I sleep for like 40ms). So I believe what is going on is that the driver needs time to translate the commands before I can continue, which is why Present is blocking.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.