Sign in to follow this  
quaker

Reading Pixels Back

Recommended Posts

quaker    100
How to read pixels from the render target at a certain screen location? Is there any method equivalent to glReadPixels? or is it a bit more tricky?

Share this post


Link to post
Share on other sites
Illco    928
It is more trickier. If you don't have access to the render target, you can obtain it through calling GetSwapChain() on the device and subsequently GetBackBuffer() on the obtained swap chain. Once you have a render target you can lock it and read the raw data -- this requires you to know the back buffer format and how to compute the correct access location. Don't forget to release all the obtained interfaces.

But perhaps there is a simpler approach, I don't know.

Illco

Share this post


Link to post
Share on other sites
quaker    100
And how can I tell the exact order of the color format, bytes are laid out, and what about the pitch thing? All these sound scary to me, as a beginner.

Thanks.

Share this post


Link to post
Share on other sites
Evil Steve    2017
Quote:
Original post by quaker
And how can I tell the exact order of the color format, bytes are laid out, and what about the pitch thing? All these sound scary to me, as a beginner.

Thanks.
The pitch you get told when you lock the backbuffer (You get a D3DLOCKED_RECT structure back, which contains the pointer to the data and the pitch). As for the format - you told D3D what format it was in when you created the device, so you should know [smile]. The format is the same as the format of your presentation parameter's BackBufferFormat member. For instance, if you're using D3DFMT_X8R8G8B8, then each pixel is made up of 32 bits (4 bytes). The first byte is unused, the second contains the red value, the third is the green, and the fourth is the blue.

If you intend to lock the backbuffer, you'll also have to specify D3DPRESENTFLAG_LOCKABLE_BACKBUFFER as the Flags member of your presentation parameters.

Bear in mind that this is very slow (I'd imagine it's probably slow in OpenGL too), and you definitely should avoid doing this every frame unless you want to take a severe performance hit.

Share this post


Link to post
Share on other sites
SirKnight    316
Quote:
Original post by Evil Steve
Bear in mind that this is very slow (I'd imagine it's probably slow in OpenGL too), and you definitely should avoid doing this every frame unless you want to take a severe performance hit.


Yeah doing a glReadPixels isn't exactly the fastest thing in the world.

I had this problem a while back and what I did at first was I created an offscreen surface and rendered to that, locked it and read the data back. Later I changed the program to use a lockable backbuffer instead. In my case, rendering speed didn't mean a thing since it was for a regression test app. I changed to a lockable backbuffer just to simplify the regression tests.

I suggest you try both ways and see which is best for your app like I did.


-SirKnight

Share this post


Link to post
Share on other sites
quaker    100
I'm not intending to read more than a pixel per frame so there will be no performance hit i guess.

But do I have to take into account the pitch value returned by LockRect in order to get correct RGBA values? How? Or does it only return the pixels within the specified screen rect?

Share this post


Link to post
Share on other sites
jollyjeffers    1570
Quote:
Original post by quaker
I'm not intending to read more than a pixel per frame so there will be no performance hit i guess.
So very wrong unfortunately [sad]

Much of the overhead with reading data back is the pipeline flush it requires (before you can read any data all pending rendering operations must be completed), then any internal memory movement - it's possible that the driver will have to move/copy Render Targets to AGP addressable VRAM before it can initate a transfer. Then you have to wait for the AGP bus to be ready and transfer that data.

Also, whilst you hold the lock on a render target you can't use that data as an input or output (especially bad if its the back buffer) and your application isn't going to be making any concurrent use of the GPU...

hth
Jack

Share this post


Link to post
Share on other sites
quaker    100
It's still not a big hit when it comes to only one pixel per frame.

What should be the equation to read say A8R8G8B8 format from a 1x1 rect (1 pixel)? Thanks alot.

Share this post


Link to post
Share on other sites
jollyjeffers    1570
Quote:
Original post by quaker
It's still not a big hit when it comes to only one pixel per frame.
Make sure you check that assumption [wink]

Quote:
Original post by quaker
What should be the equation to read say A8R8G8B8 format from a 1x1 rect (1 pixel)? Thanks alot.
Well casting it to a DWORD should work for a 1x1 rect. Anything beyond a 1x1 rect will be more complex though [smile]

EDIT:

DWORD pix = *reinterpret_cast< DWORD* >( pLockedRect.pBits );

or, to be a bit more clever...
unsigned __int8 p[4] = reinterpret_cast< unsigned __int8* >( pLockedRect.pBits );
// p[0], p[1], p[2] and p[3] should be the individual ARGB's...


Jack

Share this post


Link to post
Share on other sites
quaker    100
Thanks maite!

The Rect for one pixel should be set to:

left = pixel.x
top = pixel.y
right = pixel.x + 1
bottom = pixel.y + 1

right?????

Share this post


Link to post
Share on other sites
LeGreg    754
Quote:
Original post by quaker
It's still not a big hit when it comes to only one pixel per frame.


Unfortunately not true. A lot of the cost comes from the first pixel read.. Subsequent pixel reads may not be fast (not as fast as in a regular CPU read in cached system memory) but not as expensive as the first one.

You have to think of it as two machines operating in parallel.
One is pushing commands, the other accepting them asynchronously.
If the first machine (your cpu) is becoming dependent on the second one in order to push new commands you've introduced a stall, your cpu becomes idle.
And you also run the risk of idling the gpu because while you wait for your last command to execute you cannot push new commands.
It's a very bad usage of the resources at your disposal, and game developers are strongly discouraged from doing anything like that.

There may not be a way to get asynchronous data efficiently, the hardware and the software that deals with the hardware are not architected to handle those situations. The only almost efficient method is the occlusion query mechanism, but it's only efficient if you don't poll actively before doing any more work. And that only works for occlusion obviously.

Reading data back for the occasional screenshots and/or for debug/tests purpose is fine.

LeGreg

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this