Gles2.0 Shaders Are Slow?


chris_c

Member
Joined
Jun 25, 2010
Messages
393
Age
55
I modified a simple example showing one static quad showing and an animated shader and compared it with the simplest shader that I could
I've set the swap interval to zero so it will go full tilt....
heres the two frags I used

Code:
"                                                      \
   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 );            \
  }";
and
Code:
"                                                      \
   varying mediump vec2    pos;                        \
   uniform mediump float   phase;                      \
                                                       \
   void  main()                                        \
   {                                                   \
      gl_FragColor  =  vec4( 1., 0.9, 0.7, 1.0 ); \
   }                                                   \
";

with the simpler shader I got around 189 FPS (not exactly stella!)
and with the more complex shader in the same code I get around 78fps

Are the shaders actually processed on the cpu not gpu ???

I gotta say gles1.1 and its familiar fixed pipeline is looking very appealing...!


heres the complete code if you want to test for your self just comment the
#define ANI
line and recompile to use the simpler shader (yeah I'm that lazy! ;) )
/* Created by exoticorn ( http://talk.maemo.org/showthread.php?t=37356 )
* edited and commented by André Bergner [endboss]
*
* from http://wiki.maemo.org/SimpleGL_example
* minor changes (includes) and XProps to compile for
* Open Pandora Chris_C (2010)
*
* libraries needed: libx11-dev, libgles2-dev
*
* compile with: g++ -lX11 -lEGL -lGLESv2 egl-example.cpp
*/
#include <cstdlib>
#include <string.h>

#include <iostream>
using namespace std;

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

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

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

#define ANI

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


#ifdef ANI
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 ); \
}";
#else
const char fragment_src [] =
" \
varying mediump vec2 pos; \
uniform mediump float phase; \
\
void main() \
{ \
gl_FragColor = vec4( 1., 0.9, 0.7, 1.0 ); \
} \
";
#endif

// 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;

//// draw

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


#ifdef ANI
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
#endif
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;
}
#ifdef ANI
glUniform4f ( offset_loc , offset_x , offset_y , 0.0 , 0.0 );
#endif
glVertexAttribPointer ( position_loc, 3, GL_FLOAT, false, 0, vertexArray );
glEnableVertexAttribArray ( position_loc );
glDrawArrays ( GL_TRIANGLE_STRIP, 0, 5 );

eglSwapBuffers ( egl_display, egl_surface ); // get the rendered buffer to the screen
}


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


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

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;

win = XCreateWindow ( // create a window with the provided parameters
x_display, root,
0, 0, 320, 240, 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 );

XMapWindow ( x_display , win ); // make the window visible on the screen
XStoreName ( x_display , win , "GL test" ); // give the window a name

/////// 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 );
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_BUFFER_SIZE, 16,
EGL_RENDERABLE_TYPE,
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, (void*)win, NULL );
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 );

eglSwapInterval( egl_display, 0);
/////// 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" );
#ifdef ANI
phase_loc = glGetUniformLocation ( shaderProgram , "phase" );
offset_loc = glGetUniformLocation ( shaderProgram , "offset" );
#endif
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;

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 = (window_height - 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;
}

btw setting interval to one gives 60fps, I was expecting 50 being as its a British console :D but then you'll only get that if you know what a VBL interrupt is....
 
Cos, tan AND sqrt in the fragment shader are very expensive. try to substitute cos with a triangle wave function for example. Or pass precomputed cos and tan-tables to the shader.
I messed a bit around with gles2-shaders by myself, I wrote a vertex- and fragshader pair that lights with directional light plus hemilight to simulate ambient lighting (without textures so far). And I was suprised how fast it runs.

Edit: Poke around in my dev-thread here
Note the complex models and I have still an unnessesary matrix multiplication in the vertex shader.
 
Last edited by a moderator:
Don't expect miracles ... in terms of shader processing SGX 530 can be about 1000 times slower than a decent PC based graphic card.
Not in this case, but I see it all the time, people pick up shaders from some PC based book , dump it on SGX and expect miracles.
 
Gotta second warmi here. The fps drop is nothing extraordinary - you replaced a practically pass-through fragment shader for one that does cos, tan and sqrt among others, and your fps dipped more than double. Consider optimisations - try doing some of that work per vertex, and then just use it as interpolants in the frament shader.
 
darkblu said:
Gotta second warmi here. The fps drop is nothing extraordinary - you replaced a practically pass-through fragment shader for one that does cos, tan and sqrt among others, and your fps dipped more than double. Consider optimisations - try doing some of that work per vertex, and then just use it as interpolants in the frament shader.
Still not convinced a LIT *and* TEXTURED surface is taking the same time as the most simple "pass through" shader that does no calculations, how slow is a shader going to be once you calculate per vert lighting and interpolate it over a texture look up, surely its going to be at least as slow as calculating a simple animated spiral?

Obviously I'm no shader expert but even if you are just using shaders to replicate lit textures theres going to be no time left over for effects and I can even see it taking longer.

Has anyone compared doing lit textures with shaders against the fixed pipeline of gles1.1 and produced some kind of simple benchmark?
 
Last edited by a moderator:
chris_c said:
darkblu said:
Gotta second warmi here. The fps drop is nothing extraordinary - you replaced a practically pass-through fragment shader for one that does cos, tan and sqrt among others, and your fps dipped more than double. Consider optimisations - try doing some of that work per vertex, and then just use it as interpolants in the frament shader.
Still not convinced a LIT *and* TEXTURED surface is taking the same time as the most simple "pass through" shader that does no calculations, how slow is a shader going to be once you calculate per vert lighting and interpolate it over a texture look up, surely its going to be at least as slow as calculating a simple animated spiral?

Obviously I'm no shader expert but even if you are just using shaders to replicate lit textures theres going to be no time left over for effects and I can even see it taking longer.

Has anyone compared doing lit textures with shaders against the fixed pipeline of gles1.1 and produced some kind of simple benchmark?

Fixed pipelines on SGX devices are implemented using ... shaders so I doubt you will get much better results.
 
Last edited by a moderator:
warmi said:
chris_c said:
darkblu said:
Gotta second warmi here. The fps drop is nothing extraordinary - you replaced a practically pass-through fragment shader for one that does cos, tan and sqrt among others, and your fps dipped more than double. Consider optimisations - try doing some of that work per vertex, and then just use it as interpolants in the frament shader.
Still not convinced a LIT *and* TEXTURED surface is taking the same time as the most simple "pass through" shader that does no calculations, how slow is a shader going to be once you calculate per vert lighting and interpolate it over a texture look up, surely its going to be at least as slow as calculating a simple animated spiral?

Obviously I'm no shader expert but even if you are just using shaders to replicate lit textures theres going to be no time left over for effects and I can even see it taking longer.

Has anyone compared doing lit textures with shaders against the fixed pipeline of gles1.1 and produced some kind of simple benchmark?

Fixed pipelines on SGX devices are implemented using ... shaders so I doubt you will get much better results.
yeah i know, but you're missing the point! - I cannot see a self written shader being as efficient let alone one with even the simplest effect added - I'm achieving the same speed in 1.1 fully lit and textured as 2.0 with a pass through shader - just a solid colour without lighting or texture...
 
Last edited by a moderator:
You're assuming that the system is doing nothing but running the USSEs and therefore your performance should scale perfectly linearly with shader utilization. This is far from the case. When looking at those high framerates even a relatively small per-frame cost will be immense. To illustrate my point, try removing the triangle rendering entirely. The FPS goes to about 200. Let's look at this in raw time which is more useful:

Complex shader: 12.82ms per frame
Simple shader: 5.29ms per frame
No graphics: 5ms per frame

That doesn't necessarily mean that the flip/synchronization/whatever that entails is taking 5ms of resources from you, it just means that they have to happen at least 5ms apart. So your maximum framerate is 200 FPS, which in all practical terms doesn't hurt you one bit. The fact that you get the same performance with GL ES 1.1 is because you're hitting another bottleneck somewhere else, not because the fixed function shader implementation is magic. This is also illustrated in you only getting slightly worse than half the performance with a complex shader that I guarantee takes far more than twice as many cycles per pixel.

So what I'm saying is that you're looking at a part of the FPS graph where things get flattened or saturated entirely and stop holding any useful data for extrapolation. You'd be much better off doing whatever it is you'd like to be doing on the platform and if you struggle to reach a sensible framerate (30 FPS is pretty standard for mobile graphics) then you can start asking why and looking for improvements or alternatives.

p.s. Use code tags, not spoiler tags.. code is very hard to read w/o indentation :/
 
Exophase said:
You're assuming that the system is doing nothing but...
fair point can you suggest a way of fair way benchmarking gles 1.1 against 2.0 shaders (particularly with lighting and tetxure) I'm still not convinced that 1.1 pipeline isn't at least partially optimised in hardware...

Especially on an arcade game on a mobile device with a small screen can anyone give a compelling reason why at most you would want any more than a simple multi textured lightmap+texture type effect? I just don't get the point of shaders on a mobile platform i guess!

btw i used the spoiler tag to keep the code out of the way, just there so it could be compiled by ppl, notice the shaders were included in code tags seperatly as they were there to be read... ;)
 
Last edited by a moderator:
chris_c said:
fair point can you suggest a way of fair way benchmarking gles 1.1 against 2.0 shaders (particularly with lighting and tetxure) I'm still not convinced that 1.1 pipeline isn't at least partially optimised in hardware...

First I'd start with some scenes that are representative of something you'd really be drawing, maybe some detailed models. Or, if you really want to isolate out vertex shading, at least fill the whole screen, maybe with multiple alpha blended layers to really work the ALUs and not just the memory bandwidth.

I guess the only real way to benchmark the two against each other is to do your own 1.1-like shaders, which have to match whatever pixel-pipeline options you have enabled in 1.1 and nothing else (it probably compiles new shaders on state changes). Unfortunately it's hard to say if whatever shaders you write will be anywhere as efficient as theirs.

chris_c said:
Especially on an arcade game on a mobile device with a small screen can anyone give a compelling reason why at most you would want any more than a simple multi textured lightmap+texture type effect? I just don't get the point of shaders on a mobile platform i guess!

I'm not really a 3D graphics guy so I don't really know what sorts of per-pixel effects people use, I would imagine at the very least it gives some flexibility in how you combine color + textures. Like using dot3 instead of modulation, although I think OGL ES 1.1 supports that too. You could try some level of per-pixel lighting but I'm not sure it'd be fast enough..

chris_c said:
btw i used the spoiler tag to keep the code out of the way, just there so it could be compiled by ppl, notice the shaders were included in code tags seperatly as they were there to be read... ;)

Fair enough, but it was pretty important for getting an idea of the details of your setup >_> Maybe next time you could just link it somewhere? pastebin is good.
 
Last edited by a moderator:
chris_c said:
fair point can you suggest a way of fair way benchmarking gles 1.1 against 2.0 shaders (particularly with lighting and tetxure) I'm still not convinced that 1.1 pipeline isn't at least partially optimised in hardware...


From what I have heard on the iPhone (SGX based iPhones to be precise) GLES 1.x is emulated with shaders but not your vanilla GLSL shaders but something more native ( perhaps optimized native asm code ).

In fact, someone from Imagination Technologies even suggested that trying to write your own emulation layer for GLES 1.x with GLES 2.0 won't come close to what they can do in their native drives in terms of performance.
 
Last edited by a moderator:
Back
Top