Jump to content
  • Advertisement
Sign in to follow this  
kosmonaut

Reading pixel from renderTarget awfully slow?

This topic is 1012 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi guys,

 

I hope some of you can help me. I am working in MonoGame.

In my game I have a special render pass where I render all models in the color of their ids ( id 0 being {0,0,0,1} id 256 being {1, 255, 0, 1} and so forth ) and I obviously draw this directly to a renderTarget.

 

The idea is to have pixel perfect model selection for the mouse, so i can just query my mouse x/y in the renderTarget and get the correct id of the model i hover over with the mouse.

 

This operation i do in .net and the code looks as follows:
 

Rectangle sourceRectangle =
          new Rectangle(Mouse.GetState().X, Mouse.GetState().Y, 1, 1);

Color[] retrievedColor = new Color[1];

pickingRenderTarget2D.GetData<Color>(0, sourceRectangle, retrievedColor, 0, 1);

selectedEntityID = IdGenerator.GetIdFromColor(retrievedColor[0]);

This works very well BUT it is very slow.

 

If i check for it every frame (I don't have much room here) it doubles my frametime (from 1 ms to 2 ms).

 

Is there a more efficient way to read the texture?

 

Bonus Question: "newrenderTarget = oldrenderTarget;" is a copy, not a reference right?

 

(I tried outsourcing this operation to a seperate thread, but if I update as fast as possible , it still for some reason doesn't help much.)

Edited by kosmonaut

Share this post


Link to post
Share on other sites
Advertisement

Bonus Question: "newrenderTarget = oldrenderTarget;" is a copy, not a reference right?

 

No. RenderTarget2D is a class, so this is just a reference assignment. No copying going on.

 

 

As for your main problem, you can't read from a render target until all the pending operations on it have completed. And those happen

asynchronously. So your GetData actually has to block and until all the pending draw operations against that render target have completed.

 

You could try ping ponging between 2 (or more) different render targets on alternate frames to alleviate this. e.g.:

 

frame 0:

    - render to render target 0

    - don't read

frame 1:

    - render to render target 1

    - read from render target 0 (which hopefully is "complete" now)

frame 2:

    - render to render target 0

    - read from render target 1

 

etc...

 

 

That will probably improve things, but reading from the render target may still be slow. I've found with XNA reading from any render target (even one "several frames behind"), causes some stalls, and you will never achieve 60 FPS. It should be possible with DirectX with render target ping-ponging, so it must be some issue with the way XNA talks to DX - not sure if the same is true for MonoGame. So you might consider an alternate implementation (e.g. ray casting against a bounding volume for your objects on the CPU) for your scenario, if you don't need pixel-perfect hit testing.

 

Or if you do need pixel-perfect hit testing, you might look to occlusion queries (which asynchronously answer the question "how many pixels were drawn during this set of draw calls") to get your answer. It'll take a big of cleverness to get the answer for "which model id is here", but it can be done. I've used this technique to measure scene luminance for exposure control.

Edited by phil_t

Share this post


Link to post
Share on other sites

thanks for the answer. I just noticed who you are, actually reading your blog was one of the most fun parts of my days lately (i have just started with Monogame/XNA 3 days ago, and I am seriously impressed with your rendering achievements). I guess I'll stick with diffuse/specular/shadows/normalmapping and some post processing for now though haha

 

So in the end it makes sense why using a different thread for reading the targetRender is helping nothing, since the graphicsDevice can probably not write to said targetRender during that time, which is likely stalling the main thread. A deep copy might help, I would need to check that out.

 

Regardless, I just added some 10 ms sleep (sad story ) to my readout thread (plus the adjustment delay for synchronization) and i am back to 1000 Hertz. The whole thing is "good enough" for now, same at fixed 60 or 30 Hz but I'll see if i can improve it eventually.

Edited by kosmonaut

Share this post


Link to post
Share on other sites

What if you draw a 1x1 pixel 2d box to sample a pixel into a 1x1 rendertarget, which will be fast to read from?

You would just supply it with an UV to sample with (a constant buffer is probably easiest here), rendertarget to read from etc., and afterwards you Map()/Unmap() the rendertarget (needs to be created with suitable flags of course) to get the pixel value..

 

EDIT: Sorry, didn't notice "Monogame" before now, my answer is probably not valid then...

Edited by vinterberg

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!