How do I read pixels from an External Framebuffer as RGB values?
September 13, 2015 06:42AM
I wanted to improve upon the gdl::SaveScreen() function of my MTek-GDL library where I wanted it to read pixels from an external framebuffer instead of the embedded framebuffer of the GX as it is much faster to read pixels from main RAM instead of peeking pixels from the GX and it allows me to take screenshots in anti-aliased mode as well.

However, I've read that the pixel format of the external framebuffer is YUV2 which is a pixel format that I'm not exactly familiar with and I couldn't find a single piece of code as to how to read and convert a pixel from it as an RGB8 color value.

Also, is it advisable to just have one XFB and have the GX copy pixels from its EFB into it right after a VSync? To me, I think it'll work well since the GX can copy pixels from its EFB to the XFB fast enough that there's no flicker or screen tearing issues I've seen based on a prototype I made that demonstrates a single XFB and it saves a few hundred kilobytes worth of 1T-SRAM when having only one XFB.

Any useful help will be greatly appreciated...
Re: How do I read pixels from an External Framebuffer as RGB values?
September 13, 2015 07:34AM
Google "convert YUY2 to RGB", plenty of algorithms for that around.

Restricting yourself to one XFB means you can't start drawing the next frame until the previous one has been copied out, so you're potentially wasting useful CPU cycles while waiting for vsync. For simple scenes this won't matter but if you have large scenes that occasionally take longer than the frame period to draw, you will drop frames.
Re: How do I read pixels from an External Framebuffer as RGB values?
September 14, 2015 04:48PM
Well, I figured out how to do YUY2 to RGB conversion and it turns out that it isn't as good as doing an RGB capture from the GX as pixel colors appear to be bleeding into each other which is noticeable when you zoom into the image close enough. I think I'll just use this pixel reading method only when the GX is set to anti-aliased rendering.

Here's some pseudo code on how to convert YUY2 color values from the XFB to RGB (assuming XFB is addressed as K0 and is an unsigned char array pointer):
pix = 2*(x+(rmode->fbWidth*y));

y0 = xfb[pix];     // Y of first pixel
y1 = xfb[pix+2]; // Y of second pixel
u = xfb[pix+1];   // U from first pixel
v = xfb[pix+3];   // V from second pixel

// Convert Y0 U V to RGB (first pixel)
C = y0 - 16;
D = u - 128;
E = v - 128;
r0 = (298*C+409*E+128)/256;
g0 = (298*C-100*D-208*E+128)/256;
b0 = (298*C+516*D+128)/256;

// Convert Y1 U V to RGB (second pixel)
C = y1 - 16;
r1 = (298*C+409*E+128)/256;
g1 = (298*C-100*D-208*E+128)/256;
b1 = (298*C+516*D+128)/256;

As for the single XFB thing, I thought the cycle waste still happens when running in immediate mode since GX_DrawDone() stalls until the GX has finished rendering unless I'm running in multi-buffered mode which is not possible due to a bug in libogc that you've mentioned before (also, doesn't the GX have its own internal framebuffer and the only way to display it is to copy it to main memory with GX_CopyDisp()).

I might end up doing a benchmark where I'll render a bunch of high-poly models (using vertex arrays and display lists for best optimization) and see if using double XFBs or a single XFB has any performance differences.

Edited 1 time(s). Last edit at 09/15/2015 03:00AM by TheCodingBrony.
Re: How do I read pixels from an External Framebuffer as RGB values?
September 15, 2015 12:28AM
You don't have to use GX_DrawDone(), you can use GX_SetDrawDoneCallback() and GX_SetDrawDone() to avoid blocking.
Regardless in the code you posted much more time will be spent blocking in VIDEO_WaitVSync() than GX_DrawDone(), and if you hit a frame where drawing takes longer than the frame period your framerate will drop. If you were using two XFBs you would have a one frame buffer to guard against that happening.
Sorry, only registered users may post in this forum.

Click here to login