Hardware 3D in a window demo


37 posts   Page 1 of 2   1, 2
by teh_orph » Thu May 24, 2012 6:26 pm
Hello guys,

Quick proof-of-concept: hardware accelerated 3D in an X window. You do not need any fancy X server to make this work. The code provided below only works in a 16-bit 5/6/5 screen though.

Brief summary:
- I took this code: http://wiki.maemo.org/SimpleGL_example
- replaced the eglCreateWindowSurface with eglCreatePixmapSurface
- replaced the swap buffers with a glReadPixels and an XPutImage

Since everything's done in X, all window hiding stuff works fine. You can run multiple EGL programs too. (In the video I run the same program twice - no sharing is being done between them if you're wondering!)

http://www.youtube.com/watch?v=l0i55EndDZQ

CAVEATS:
- eglCreatePixmapSurface seems to be ill-documented for the Broadcom 3D EGL API. Figuring out what to put as the pixmap argument appears to be guesswork!
- X is currently invisible in 24/32-bit mode, and I can't get the pixmap to run in 16-bit mode. So I *in software* glReadPixels the image in 32-bit mode, and then convert to 5/6/5 by hand...this is of course slow. Once it's figured out how to make either a 16-bit pixmap or get 32-bit X working, no conversion will be required.

NB: glReadPixels is powered by the hardware's DMA functionality once the GPU is flushed. The X server it is running on in the video has DMA-enabled blitting, so - assuming the 32->16 conversion is omitted - the whole process would be powered by DMA.

If you're interested, please take the provided code and handle the 16-bit issues for me!
Build with: g++ -lX11 -lEGL -lGLESv2 egl-example.cpp -I /opt/vc/include/ -L /opt/vc/lib/ -g -o egl_example -O3

Cheers :)

NB: I can't upload the file as it won't like any extension I give it. Code inline in next post.
User avatar
Posts: 345
Joined: Mon Jan 30, 2012 2:09 pm
Location: London
by teh_orph » Thu May 24, 2012 6:27 pm
/* Created by exoticorn ( http://talk.maemo.org/showthread.php?t=37356 )
* edited and commented by André Bergner [endboss]
*
* libraries needed: libx11-dev, libgles2-dev
*
* compile with: g++ -lX11 -lEGL -lGLESv2 egl-example.cpp
*/

#include <iostream>
using namespace std;

#include <cmath>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>

#define EGL_EGLEXT_PROTOTYPES

#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include <EGL/eglext_brcm.h>



const char vertex_src [] =
" \
attribute vec4 position; \
varying mediump vec2 pos; \
uniform vec4 offset; \
\
void main() \
{ \
gl_Position = position + offset; \
pos = position.xy; \
} \
";


const char fragment_src [] =
" \
varying mediump vec2 pos; \
uniform mediump float phase; \
\
void main() \
{ \
gl_FragColor = vec4( 1., 0.9, 0.7, 1.0 ) * \
cos( 30.*sqrt(pos.x*pos.x + 1.5*pos.y*pos.y) \
+ atan(pos.y,pos.x) - phase ); \
} \
";
// some more formulas to play with...
// cos( 20.*(pos.x*pos.x + pos.y*pos.y) - phase );
// cos( 20.*sqrt(pos.x*pos.x + pos.y*pos.y) + atan(pos.y,pos.x) - phase );
// cos( 30.*sqrt(pos.x*pos.x + 1.5*pos.y*pos.y - 1.8*pos.x*pos.y*pos.y)
// + atan(pos.y,pos.x) - phase );


void
print_shader_info_log (
GLuint shader // handle to the shader
)
{
GLint length;

glGetShaderiv ( shader , GL_INFO_LOG_LENGTH , &length );

if ( length ) {
char* buffer = new char [ length ];
glGetShaderInfoLog ( shader , length , NULL , buffer );
cout << "shader info: " << buffer << flush;
delete [] buffer;

GLint success;
glGetShaderiv( shader, GL_COMPILE_STATUS, &success );
if ( success != GL_TRUE ) exit ( 1 );
}
}


GLuint
load_shader (
const char *shader_source,
GLenum type
)
{
GLuint shader = glCreateShader( type );

glShaderSource ( shader , 1 , &shader_source , NULL );
glCompileShader ( shader );

print_shader_info_log ( shader );

return shader;
}


Display *x_display;
Window win;
EGLDisplay egl_display;
EGLContext egl_context;

GLfloat
norm_x = 0.0,
norm_y = 0.0,
offset_x = 0.0,
offset_y = 0.0,
p1_pos_x = 0.0,
p1_pos_y = 0.0;

GLint
phase_loc,
offset_loc,
position_loc;


EGLSurface egl_surface;
bool update_pos = false;

const float vertexArray[] = {
0.0, 0.5, 0.0,
-0.5, 0.0, 0.0,
0.0, -0.5, 0.0,
0.5, 0.0, 0.0,
0.0, 0.5, 0.0
};


void render()
{
static float phase = 0;
static int donesetup = 0;

static XWindowAttributes gwa;
static XImage *image = 0;
static GC gc;
static Pixmap pixmap;
//// draw

if ( !donesetup ) {
XGetWindowAttributes ( x_display , win , &gwa );
glViewport ( 0 , 0 , gwa.width , gwa.height );
glClearColor ( 0.08 , 0.06 , 0.07 , 1.); // background color
donesetup = 1;

//gc = XCreateGC(x_display, win, 0, 0);
image = XGetImage( x_display, win, 0, 0, gwa.width, gwa.height, AllPlanes, ZPixmap );
//pixmap = XCreatePixmap(x_display, win, gwa.width, gwa.height, 16);
gc = DefaultGC(x_display, 0);
}
glClear ( GL_COLOR_BUFFER_BIT );

// memset((void*)(&(image->data[0])), 0, gwa.width * gwa.height);

glUniform1f ( phase_loc , phase ); // write the value of phase to the shaders phase
phase = fmodf ( phase + 0.5f , 2.f * 3.141f ); // and update the local variable

if ( update_pos ) { // if the position of the texture has changed due to user action
GLfloat old_offset_x = offset_x;
GLfloat old_offset_y = offset_y;

offset_x = norm_x - p1_pos_x;
offset_y = norm_y - p1_pos_y;

p1_pos_x = norm_x;
p1_pos_y = norm_y;

offset_x += old_offset_x;
offset_y += old_offset_y;

update_pos = false;
}

glUniform4f ( offset_loc , offset_x , offset_y , 0.0 , 0.0 );

glVertexAttribPointer ( position_loc, 3, GL_FLOAT, false, 0, vertexArray );
glEnableVertexAttribArray ( position_loc );
glDrawArrays ( GL_TRIANGLE_STRIP, 0, 5 );

glFinish();
unsigned int *buffer = (unsigned int *)malloc(gwa.height * gwa.width * 4);
glReadPixels(0, 0, gwa.width, gwa.height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

int count;
for (count = 0; count < gwa.width * gwa.height / 2; count++)
{
unsigned int *dest = (unsigned int*)(&(image->data[0]));
unsigned int src0 = buffer[count * 2];
unsigned int src1 = buffer[count * 2 + 1];

unsigned char r0, g0, b0;
unsigned char r1, g1, b1;

r0 = src0 & 0xff;
g0 = (src0 >> 8) & 0xff;
b0 = (src0 >> 16) & 0xff;
r1 = src1 & 0xff;
g1 = (src1 >> 8) & 0xff;
b1 = (src1 >> 16) & 0xff;
dest[count] = ((r0 >> 3) << 27) | ((g0 >> 2) << 21) | ((b0 >> 3) << 16)
| ((r1 >> 3) << 11) | ((g1 >> 2) << 5) | ((b1 >> 3) << 0);
}


free(buffer);
// eglSwapBuffers ( egl_display, egl_surface ); // get the rendered buffer to the screen
XPutImage( x_display, win, gc, image, 0, 0, 0, 0, gwa.width, gwa.height );
}


////////////////////////////////////////////////////////////////////////////////////////////


int main()
{
/////// the X11 part //////////////////////////////////////////////////////////////////
// in the first part the program opens a connection to the X11 window manager
//

bcm_host_init();

x_display = XOpenDisplay ( NULL ); // open the standard display (the primary screen)
if ( x_display == NULL ) {
cerr << "cannot connect to X server" << endl;
return 1;
}

Window root = DefaultRootWindow( x_display ); // get the root window (usually the whole screen)

XSetWindowAttributes swa;
swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask;

const int width = 800;
const int height = 480;
win = XCreateWindow ( // create a window with the provided parameters
x_display, root,
0, 0, width, height, 0,
CopyFromParent, InputOutput,
CopyFromParent, CWEventMask,
&swa );

/* XSetWindowAttributes xattr;
Atom atom;
int one = 1;

xattr.override_redirect = False;
XChangeWindowAttributes ( x_display, win, CWOverrideRedirect, &xattr );

atom = XInternAtom ( x_display, "_NET_WM_STATE_FULLSCREEN", True );
XChangeProperty (
x_display, win,
XInternAtom ( x_display, "_NET_WM_STATE", True ),
XA_ATOM, 32, PropModeReplace,
(unsigned char*) &atom, 1 );

XChangeProperty (
x_display, win,
XInternAtom ( x_display, "_HILDON_NON_COMPOSITED_WINDOW", True ),
XA_INTEGER, 32, PropModeReplace,
(unsigned char*) &one, 1);

XWMHints hints;
hints.input = True;
hints.flags = InputHint;
XSetWMHints(x_display, win, &hints);*/

XMapWindow ( x_display , win ); // make the window visible on the screen
XStoreName ( x_display , win , "GL test" ); // give the window a name
/*
//// get identifiers for the provided atom name strings
Atom wm_state = XInternAtom ( x_display, "_NET_WM_STATE", False );
Atom fullscreen = XInternAtom ( x_display, "_NET_WM_STATE_FULLSCREEN", False );

XEvent xev;
memset ( &xev, 0, sizeof(xev) );

xev.type = ClientMessage;
xev.xclient.window = win;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 1;
xev.xclient.data.l[1] = fullscreen;
XSendEvent ( // send an event mask to the X-server
x_display,
DefaultRootWindow ( x_display ),
False,
SubstructureNotifyMask,
&xev );*/


/////// the egl part //////////////////////////////////////////////////////////////////
// egl provides an interface to connect the graphics related functionality of openGL ES
// with the windowing interface and functionality of the native operation system (X11
// in our case.

//egl_display = eglGetDisplay( (EGLNativeDisplayType) x_display );
egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if ( egl_display == EGL_NO_DISPLAY ) {
cerr << "Got no EGL display." << endl;
return 1;
}

if ( !eglInitialize( egl_display, NULL, NULL ) ) {
cerr << "Unable to initialize EGL" << endl;
return 1;
}

EGLint attr[] = { // some attributes to set up our egl-interface
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE,
EGL_PIXMAP_BIT | EGL_OPENGL_ES2_BIT,
EGL_NONE
};

EGLConfig ecfg;
EGLint num_config;
if ( !eglChooseConfig( egl_display, attr, &ecfg, 1, &num_config ) ) {
cerr << "Failed to choose config (eglError: " << eglGetError() << ")" << endl;
return 1;
}

if ( num_config != 1 ) {
cerr << "Didn't get exactly one config, but " << num_config << endl;
return 1;
}

//egl_surface = eglCreateWindowSurface ( egl_display, ecfg, win, NULL );

EGLint pixel_format = EGL_PIXEL_FORMAT_ARGB_8888_BRCM;
//EGLint pixel_format = EGL_PIXEL_FORMAT_RGB_565_BRCM;
EGLint rt;
eglGetConfigAttrib(egl_display, ecfg, EGL_RENDERABLE_TYPE, &rt);

if (rt & EGL_OPENGL_ES_BIT) {
pixel_format |= EGL_PIXEL_FORMAT_RENDER_GLES_BRCM;
pixel_format |= EGL_PIXEL_FORMAT_GLES_TEXTURE_BRCM;
}
if (rt & EGL_OPENGL_ES2_BIT) {
pixel_format |= EGL_PIXEL_FORMAT_RENDER_GLES2_BRCM;
pixel_format |= EGL_PIXEL_FORMAT_GLES2_TEXTURE_BRCM;
}
if (rt & EGL_OPENVG_BIT) {
pixel_format |= EGL_PIXEL_FORMAT_RENDER_VG_BRCM;
pixel_format |= EGL_PIXEL_FORMAT_VG_IMAGE_BRCM;
}
if (rt & EGL_OPENGL_BIT) {
pixel_format |= EGL_PIXEL_FORMAT_RENDER_GL_BRCM;
}

EGLint pixmap[5];
pixmap[0] = 0;
pixmap[1] = 0;
pixmap[2] = width;
pixmap[3] = height;
pixmap[4] = pixel_format;

eglCreateGlobalImageBRCM(width, height, pixmap[4], 0, width*4, pixmap);

egl_surface = eglCreatePixmapSurface(egl_display, ecfg, pixmap, 0);
if ( egl_surface == EGL_NO_SURFACE ) {
cerr << "Unable to create EGL surface (eglError: " << eglGetError() << ")" << endl;
return 1;
}

//// egl-contexts collect all state descriptions needed required for operation
EGLint ctxattr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
egl_context = eglCreateContext ( egl_display, ecfg, EGL_NO_CONTEXT, ctxattr );
if ( egl_context == EGL_NO_CONTEXT ) {
cerr << "Unable to create EGL context (eglError: " << eglGetError() << ")" << endl;
return 1;
}

//// associate the egl-context with the egl-surface
eglMakeCurrent( egl_display, egl_surface, egl_surface, egl_context );


/////// the openGL part ///////////////////////////////////////////////////////////////

GLuint vertexShader = load_shader ( vertex_src , GL_VERTEX_SHADER ); // load vertex shader
GLuint fragmentShader = load_shader ( fragment_src , GL_FRAGMENT_SHADER ); // load fragment shader

GLuint shaderProgram = glCreateProgram (); // create program object
glAttachShader ( shaderProgram, vertexShader ); // and attach both...
glAttachShader ( shaderProgram, fragmentShader ); // ... shaders to it

glLinkProgram ( shaderProgram ); // link the program
glUseProgram ( shaderProgram ); // and select it for usage

//// now get the locations (kind of handle) of the shaders variables
position_loc = glGetAttribLocation ( shaderProgram , "position" );
phase_loc = glGetUniformLocation ( shaderProgram , "phase" );
offset_loc = glGetUniformLocation ( shaderProgram , "offset" );
if ( position_loc < 0 || phase_loc < 0 || offset_loc < 0 ) {
cerr << "Unable to get uniform location" << endl;
return 1;
}


const float
window_width = 800.0,
window_height = 480.0;

//// this is needed for time measuring --> frames per second
struct timezone tz;
timeval t1, t2;
gettimeofday ( &t1 , &tz );
int num_frames = 0;

XWindowAttributes gwa;
XGetWindowAttributes ( x_display , win , &gwa );

bool quit = false;
while ( !quit ) { // the main loop

while ( XPending ( x_display ) ) { // check for events from the x-server

XEvent xev;
XNextEvent( x_display, &xev );

if ( xev.type == MotionNotify ) { // if mouse has moved
// cout << "move to: << xev.xmotion.x << "," << xev.xmotion.y << endl;
GLfloat window_y = (xev.xmotion.y) - window_height / 2.0;
norm_y = window_y / (window_height / 2.0);
GLfloat window_x = xev.xmotion.x - window_width / 2.0;
norm_x = window_x / (window_width / 2.0);
update_pos = true;
}

if ( xev.type == KeyPress ) quit = true;
}

render(); // now we finally put something on the screen

if ( ++num_frames % 100 == 0 ) {
gettimeofday( &t2, &tz );
float dt = t2.tv_sec - t1.tv_sec + (t2.tv_usec - t1.tv_usec) * 1e-6;
cout << "fps: " << num_frames / dt << endl;
num_frames = 0;
t1 = t2;
}
// usleep( 1000*10 );
}


//// cleaning up...
eglDestroyContext ( egl_display, egl_context );
eglDestroySurface ( egl_display, egl_surface );
eglTerminate ( egl_display );
XDestroyWindow ( x_display, win );
XCloseDisplay ( x_display );

return 0;
}
User avatar
Posts: 345
Joined: Mon Jan 30, 2012 2:09 pm
Location: London
by theHetman » Thu May 24, 2012 8:11 pm
Congrats on getting something running in a window. You are well ahead of me. I hope that Dom will see this post and poke someone in the 3D group to get some results for you.

I'm thinking that we need a framework for ES demos that will detect if the demo is being run under X or not and handle things correctly. Also providing I/O (keyboard, mouse input, timers and a way to display text on the screen) in an agnostic way so that demos can be clean and simple.

I've been reading the "OpenGL ES 2.0 Programming Guide" that arrived here yesterday and am going to dive into some experiments, probably under Windows at first as I'm for comfortable there but with a view to writing and porting some things over to the Pi soon.
Posts: 78
Joined: Tue Jan 10, 2012 5:42 pm
by tufty » Thu May 24, 2012 8:28 pm
I say! Well done, that person.

Also, yay windowmaker. Are you running gnustep there?
Posts: 1368
Joined: Sun Sep 11, 2011 2:32 pm
by teh_orph » Thu May 24, 2012 9:11 pm
theHetman wrote:I'm thinking that we need a framework for ES demos that will detect if the demo is being run under X or not and handle things correctly.
For just this bit I was hoping to overload the eglGetDisplay function with a version that can also target an X server and also eglCreateWindowSurface to then select a window. Then eglSwapBuffers would do the glReadPixels step, so that the code in the original URL will "just work".
Some EGL implementations can allow you to target X11 directly so I don't think this is too radical...
tufty wrote:Also, yay windowmaker. Are you running gnustep there?

Cheers :) I would seriously recommend wmaker on the rpi as it's the fastest-running window manager I've found so far. It also supports 'render' extensions which means I can accelerate it with my new X server. I can tell you about the different window managers (eg which ones aren't hot) but I'll bore you!
Btw no idea on the gnustep question!
User avatar
Posts: 345
Joined: Mon Jan 30, 2012 2:09 pm
Location: London
by Jim Manley » Fri May 25, 2012 12:16 am
Way to go!

I will have to wait until I get home late California time to try this on my Pi board. Taking the Pi with me to work along with the DVI monitor used for output would break my back (no spare monitors there or room for another) and the rest of the rat's nest USB hub, network, keyboard, mouse, etc., rigamarole isn't helping any, either :x I guess making a homemade case to consolidate everything is on my list of things to do this coming Memorial Day long weekend. Can you spell "Compiq" computer? :lol:
The best things in life aren't things ... but, a Pi comes pretty darned close! :D
"Education is not the filling of a pail, but the lighting of a fire." -- W.B. Yeats
In theory, theory & practice are the same - in practice, they aren't!!!
User avatar
Posts: 1357
Joined: Thu Feb 23, 2012 8:41 pm
Location: SillyCon Valley, California, USA
by teh_orph » Fri May 25, 2012 12:31 am
Jim Manley wrote:Taking the Pi with me to work along with the DVI monitor used for output would break my back (no spare monitors there or room for another)
Since this is just boring X code, as long as you match the pixel format I reckon you could do this over the network too. Either VNC or regular network X. Have a go! (for me :))
User avatar
Posts: 345
Joined: Mon Jan 30, 2012 2:09 pm
Location: London
by shirro » Fri May 25, 2012 1:07 am
That is pretty cool. I thought glReadPixels would slow it down much more.

I really don't understand how some of this stuff works in X but any idea how hard it would be to redirect all the drawing offscreen with XDamage/XComposite and composite the windows with gl es? Not for fancy effects, but just to make moving windows about a bit smoother.

I was thinking if you had compositing happening like this then perhaps it might be possible for the client to get a handle to the pixmap with eglCreateGlobalImageBRCM and attach it to a window property or something and then perhaps the compositor could use it to render and save some copying?
Posts: 248
Joined: Tue Jan 24, 2012 4:54 am
by shirro » Fri May 25, 2012 4:38 am
I hope people don't think I am ripping off their code but I like to keep these demos around for reference in a compilable state and they are nice and searchable on github. I have converted this demo to c and put it on github for people to fork and play with. I can confirm it works quite well on Raspbian without a super-duper accelerated X server but you can't run too many windows before it slows down. Sometimes I get an X error on XGetImage and I am not sure why.

https://github.com/shirro/pi-eglonx

git clone https://github.com/shirro/pi-eglonx.git
cd pi-eglonx
make
./eglonx
Posts: 248
Joined: Tue Jan 24, 2012 4:54 am
by adlambert » Fri May 25, 2012 7:52 am
Well done.
I read the follow up comments on the Register review and so many negative people were moaning about not being able to use accelerated. So I really do appreciate your can-do.
Posts: 292
Joined: Tue Jan 03, 2012 3:18 pm
by tufty » Fri May 25, 2012 8:00 am
adlambert wrote:Well done.
I read the follow up comments on the Register review and so many negative people were moaning about not being able to use accelerated. So I really do appreciate your can-do.

This gives a hardware accelerated gl/es context within a window on the x desktop. That would allow, for example, playing quake in a window under x.

It does not accelerate x itself; x and any non gl/es apps running u der it will still be slow.

Simon.
Posts: 1368
Joined: Sun Sep 11, 2011 2:32 pm
by shirro » Fri May 25, 2012 8:29 am
tufty wrote:It does not accelerate x itself; x and any non gl/es apps running u der it will still be slow.


True. Although it is quite separate to the EGL in an X window hack, this video was on accelerated X since @teh_orph has copy and fills dma accelerated on his machine. So AFAIK this was the first video of an accelerated X server on the Pi.

teh_orph wrote:The X server it is running on in the video has DMA-enabled blitting, so - assuming the 32->16 conversion is omitted - the whole process would be powered by DMA.
Posts: 248
Joined: Tue Jan 24, 2012 4:54 am
by adlambert » Fri May 25, 2012 8:49 am
tufty wrote:
adlambert wrote:Well done.
I read the follow up comments on the Register review and so many negative people were moaning about not being able to use accelerated. So I really do appreciate your can-do.

This gives a hardware accelerated gl/es context within a window on the x desktop. That would allow, for example, playing quake in a window under x.

It does not accelerate x itself; x and any non gl/es apps running u der it will still be slow.

Simon.


I wasn't considering x in any way at all. The frequent suggestion in Register comments was that the GPU was useless without documention, as if x desktop was everything. Things like this prove otherwise.
Posts: 292
Joined: Tue Jan 03, 2012 3:18 pm
by teh_orph » Fri May 25, 2012 9:03 am
shirro wrote:I hope people don't think I am ripping off their code but I like to keep these demos around for reference in a compilable state and they are nice and searchable on github.
Nah that's excellent, thanks for doing this. Tbh it was never my code in the first place either :)
Sharing the code will hopefully get people messing with this sort of stuff more. The pixmap creation code seems to only have one example (which I pinched). I'm not sure where the original code came from!
viewtopic.php?f=6&t=6122

shirro wrote:Sometimes I get an X error on XGetImage and I am not sure why.
I get this too, and I don't know why. This is my first X program :)
You can also get egl surface creation errors. "Bad map" and allocation failure are common. This code makes a globally-shared pixmap - if I don't delete the pixmap (which I don't...oops) are the pixmaps referenced counted and background deleted? Running/quitting the program enough times will cause the allocation to fail. Restarting X normally fixes this, for some reason.

adlambert wrote:I wasn't considering x in any way at all. The frequent suggestion in Register comments was that the GPU was useless without documention, as if x desktop was everything. Things like this prove otherwise.
Unfortunately the functionality I am using here seems to be undocumented. If there is documentation, could someone please point me at it?? I'm keen to remove this CPU 888->565 conversion!

shirro wrote:True. Although it is quite separate to the EGL in an X window hack, this video was on accelerated X since @teh_orph has copy and fills dma accelerated on his machine. So AFAIK this was the first video of an accelerated X server on the Pi.
...and I fixed a stack of bugs in it last night :) Lots of work still to go though. I hope to upload some WIP code this weekend.

Cheers,
Simon
User avatar
Posts: 345
Joined: Mon Jan 30, 2012 2:09 pm
Location: London
by tufty » Fri May 25, 2012 9:49 am
shirro wrote:
tufty wrote:It does not accelerate x itself; x and any non gl/es apps running u der it will still be slow.

True. Although it is quite separate to the EGL in an X window hack, this video was on accelerated X since @teh_orph has copy and fills dma accelerated on his machine. So AFAIK this was the first video of an accelerated X server on the Pi.

Oooh, missed that. My bad.

simon
Posts: 1368
Joined: Sun Sep 11, 2011 2:32 pm
by Redrobes » Fri May 25, 2012 11:44 am
This is very cool and a great post. Probably the best post so far from outside of the RPi team.

As I understand it, your using GLES as an off screen buffer and doing the read pixels and then using X to display it so its accelerated rendering but not accelerated display. Its a good temporary hack to get apps going in GLES until the X system gets the hooks to go directly from the GL buffers to the screen with no loop back through the CPU.

Just makes me wonder whether its possible to do the 8888 to 565 conversion using the fragment shader before the read pixels and then your just doing DMA with barely any CPU at all. Could you pack the 565 into the R & G bytes with frag and then do a read pixels with a stride or at least do the full RGB read pixels and then just do a custom memcpy type loop with a 2 byte stride to a buffer then do that put pixels buffer to X. Save at least the bit manipulation part from the CPU anyway.
User avatar
Posts: 75
Joined: Mon Dec 26, 2011 9:19 pm
Location: S.W. UK
by teh_orph » Mon May 28, 2012 9:53 pm
Just to help anyone who chooses to try this thing with a 32-bit X server, it seems pixmaps are internally made as 24-bit. So when you make a 32-bit X image and blit it on top of your window, you will get "bad match" as 24 != 32. Of course it doesn't tell you that...you need to debug XPutImage from the inside :)
I'll get round to sorting this at a later date.
User avatar
Posts: 345
Joined: Mon Jan 30, 2012 2:09 pm
Location: London
by theHetman » Tue May 29, 2012 2:58 am
And I've found out why you were having problems rendering to a 565 surface. Unless you create an EGL config with all zeros in all the channels like this:

EGLint attr[] = {
EGL_RED_SIZE, 0,
EGL_GREEN_SIZE, 0,
EGL_BLUE_SIZE, 0,
EGL_ALPHA_SIZE, 0,
EGL_NONE
};

you will get a config with 8 bits per colour channel. The above should provide you with a 16 bit 565 surface to render into.
Posts: 78
Joined: Tue Jan 10, 2012 5:42 pm
by shirro » Tue May 29, 2012 5:02 am
Can you also get 565 back from glReadPixels? It didn't seem to work for me (and I am guessing teh_orph probably tried all the combos as well).

I thought I could pass any format and type but it doesn't seem to work. Reading the spec it says the choices are limited and implementation specific if I am reading it right:

Only two combinations of format and type are accepted. The first is format RGBA and type UNSIGNED_BYTE. The second is an implementation-chosen format from among those defined in table 3.4, excluding formats LUMINANCE and LUMINANCE_ALPHA. The values of format and type for this format may be determined by calling GetIntegerv with the symbolic constants IMPLEMENTATION_COLOR_READ_FORMAT and IMPLEMENTATION_- COLOR_READ_TYPE, respectively. The implementation-chosen format may vary depending on the format of the currently bound rendering surface.


When I tried glGetIntegerv I just got back GL_RGB and GL_UNSIGNED_BYTE. It would be really great if someone works out how to do this.
Posts: 248
Joined: Tue Jan 24, 2012 4:54 am
by teh_orph » Tue May 29, 2012 7:07 am
theHetman wrote:EGLint attr[] = {
EGL_RED_SIZE, 0,
EGL_GREEN_SIZE, 0,
EGL_BLUE_SIZE, 0,
EGL_ALPHA_SIZE, 0,
EGL_NONE
};
Does this work?? Regardless of what I put in here, I still got 888 out! I think the key is the parameter to global image:
EGLint pixel_format = EGL_PIXEL_FORMAT_RGB_565_BRCM;
...however I could only get EGL_PIXEL_FORMAT_ARGB_8888_BRCM to work.

The caveat to this was that it would not lot me disable the alpha channel. eg I could put 5658 and it would still give me an 8888 render target. But if I put 5650 or 8880 - no go! Time was short though, so I may have missed something.
shirro wrote:Can you also get 565 back from glReadPixels? It didn't seem to work for me (and I am guessing teh_orph probably tried all the combos as well).

I thought I could pass any format and type but it doesn't seem to work. Reading the spec it says the choices are limited and implementation specific if I am reading it right:

In the 2.0 ES spec the options are a little more liberal: http://www.khronos.org/opengles/sdk/doc ... Pixels.xml
With an 8888 render target, I got the 565 glReadPixels to do something - but the result was totally wrong!
User avatar
Posts: 345
Joined: Mon Jan 30, 2012 2:09 pm
Location: London
by theHetman » Tue May 29, 2012 1:14 pm
teh_orph wrote:
theHetman wrote:EGLint attr[] = {
EGL_RED_SIZE, 0,
EGL_GREEN_SIZE, 0,
EGL_BLUE_SIZE, 0,
EGL_ALPHA_SIZE, 0,
EGL_NONE
};
Does this work?? Regardless of what I put in here, I still got 888 out! I think the key is the parameter to global image:
EGLint pixel_format = EGL_PIXEL_FORMAT_RGB_565_BRCM;
...however I could only get EGL_PIXEL_FORMAT_ARGB_8888_BRCM to work.

The caveat to this was that it would not lot me disable the alpha channel. eg I could put 5658 and it would still give me an 8888 render target. But if I put 5650 or 8880 - no go! Time was short though, so I may have missed something.

The actual hardware only has 2 screen depths 5650 and 8888. The trouble is that eglChooseConfig works strangely so that if you put anything but zero in the RED/GREEN/BLUE fields then an 8880 config is returned and if you put anything but zero in the ALPHA field then 8888 is returned. Dom posted about it here: viewtopic.php?p=83263#p83263

I've checked the results coming back from eglChooseConfig using eglGetConfigAttrib and only sending all zeros returned 5650 colour depth. As yet I haven't got as far as seeing if it makes windowed rendering work as I'm working on building a demo framework. I'm taking that slow researching and checking everything as I go. Hence the results of my config testing.
Posts: 78
Joined: Tue Jan 10, 2012 5:42 pm
by teh_orph » Tue May 29, 2012 4:21 pm
Ok, I just did a quick test - using the exact same attr list you provided still seems to give me an 8888 result as the image appears just fine still on the screen with no other code changes! If it had changed to 5650 then the image would be busted.
I then also tried changing the image's pixel format from EGL_PIXEL_FORMAT_ARGB_8888_BRCM to EGL_PIXEL_FORMAT_RGB_565_BRCM and it still fails to create the surface :(

What else should I do? Any chance you could try with my code and see if you can make it work?

btw shirro: I accidently deleted my code, so used your C git version. Got some weird behaviour though...
- won't link on Arch, as it complains about a missing -lm
- the fps counter is busted
- fprintf(stderr segfaults!

err....?
User avatar
Posts: 345
Joined: Mon Jan 30, 2012 2:09 pm
Location: London
by theHetman » Sun Jun 03, 2012 4:01 pm
So I've been playing with this today. I managed to create a 565 pixmap surface and GL seems to be rendering into it ok. My method of RGBA all to 0 in the config does work. The only difference in my code for setting up the surface is that I make sure that the stride param in eglCreateGlobalImageBRCM is width * 2 (not width * 4 as it would be for 8888).

I've not set up an X window but am just dumping parts of the pixmap using glReadPixels. What I'm seeing is that glReadPixels it padding the 565 back out to 8888. I can tell that it's being padded as the high bits of the colour byte are being copied into the lower bits of the byte, just as you do when padding low colour depths up to higher colour depths. So whatever the pixmap depth, glReadPixels is returning 32 bit 8888. You can get it to return 24 bit 888 by passing GL_RGB as the format but using GL_UNSIGNED_SHORT_5_6_5 doesn't seem to be supported.

Of course what we really need is a pointer to the raw data.
Posts: 78
Joined: Tue Jan 10, 2012 5:42 pm
by tipam » Wed Jun 06, 2012 9:50 am
Sorry, I'm a noob at this ... I've been trying to compile this example but I haven't got the 'eglext_brcm.h' file - where do I get this from? I've got all the includes from the hello_triangle example.
Any help appreciated! Thanks.
Posts: 44
Joined: Fri Dec 30, 2011 1:32 pm
by shirro » Wed Jun 06, 2012 10:39 am
eglext_brcm.h is in /opt/vc/include/EGL with any recent firmware from https://github.com/raspberrypi/firmware (see https://github.com/raspberrypi/firmware ... ext_brcm.h)

The line #include <EGL/eglext_brcm.h> will bring the file in as long as you pass -I/opt/vc/include to gcc (this is set in the CFLAGS in the Makefile on github)
Posts: 248
Joined: Tue Jan 24, 2012 4:54 am