2
\$\begingroup\$

I have an application where I need to render some OpenGL based dlls, read the rendered textures back, and send them to another directx based application that will render them in directx.

Right now, I am rendering them using FBOs, reading them back, and then sending the data across the network. However, the "reading them back" step is killing the performance.

My basic loop is as follows:

while(true)
   ... render 5 to 10 textures ... 
   foreach( texture in renderedTextures )
      bind texture
      glGetTexImage( data )
      push data onto a queue to send asyncronously

Commenting out the glGetTextImage call causes performance to improve drastically. What techniques can I use to read the data back faster?

\$\endgroup\$
  • \$\begingroup\$ Without killing performance? It's not possible. If this is for something you're making then just keep track. If it's for some game hack it's just a waste of time to try.... \$\endgroup\$ – Yudrist Aug 17 '16 at 4:28
4
\$\begingroup\$

So, fundamentally, the problem here is that to coerce from OpenGL to Direct3D requires bouncing from GPU memory back to system memory and back to GPU memory once more. You might seriously consider porting the OpenGL DLLs to Direct3D, or the Direct3D application to OpenGL. Ooops, missed that this was over the network anyways.

If you do not, there are two aspects to this performance problem to consider:

1) This is fundamentally going to eat up a lot of bandwidth. You've already discovered the only real technique to help with that: Only call glGetTexImage if you really, really need that data. If the texture didn't change, reuse the result of your last read. If you don't need the texture this frame, wait until the next. Etc.

2) This is fundamentally going to introduce some latency. Worse, your current code introduces a "sync point". GPUs operate asynchronously, a little behind where the CPU is at in terms of drawing your scene - the GPU may very well still be drawing e.g. the previous frame when your CPU is issuing draw commands for the 'current' frame. "glGetTexImage" demands the results for the current frame immediately - forcing the CPU to wait for the GPU to catch up, "synchronizing" with where the CPU is at (hence the term.)

You can reduce this problem by making the readback "asynchronous" - that is, you tell the GPU/driver to start reading the texture back into CPU memory once it's ready, instead of blocking the CPU, and then check the results a frame or two later. This lets you keep the CPU and GPU busy simultaneously. With OpenGL, you can use glReadPixels with a Pixel Buffer Object (PBO) that you copied the texture into (directly from GPU memory to GPU memory) to do this.

Straight from the horse's mouth, here's an article from nVidia on doing this, containing some code snippets, performance benchmarks across GPUs and texture formats, etc.:

http://http.download.nvidia.com/developer/Papers/2005/Fast_Texture_Transfers/Fast_Texture_Transfers.pdf

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Try to change to location of the readback in the loop cycle. Potentially place it right before the "render 5 to 10 textures" (may cause a small overall sync lag).

If the loop cycle contains CPU execution, try to place that directly after "render 5 to 10 textures", as the GPU will probably release the CPU while it executes the render batch.

Try to make the render part of the loop cycle as compact as possible, do CPU right after that. You will have to experiment.

Copying from video memory to CPU memory at least used to be slow.

| 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.