Modified triangle.c from raspberry pi, showing significant slowdown with draw calls.
| /* | |
| Copyright (c) 2012, Broadcom Europe Ltd | |
| All rights reserved. | |
| Redistribution and use in source and binary forms, with or without | |
| modification, are permitted provided that the following conditions are met: | |
| * Redistributions of source code must retain the above copyright | |
| notice, this list of conditions and the following disclaimer. | |
| * Redistributions in binary form must reproduce the above copyright | |
| notice, this list of conditions and the following disclaimer in the | |
| documentation and/or other materials provided with the distribution. | |
| * Neither the name of the copyright holder nor the | |
| names of its contributors may be used to endorse or promote products | |
| derived from this software without specific prior written permission. | |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY | |
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| // A rotating cube rendered with OpenGL|ES. Three images used as textures on the cube faces. | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <math.h> | |
| #include <assert.h> | |
| #include <unistd.h> | |
| #include "bcm_host.h" | |
| #include "GLES/gl.h" | |
| #include "EGL/egl.h" | |
| #include "EGL/eglext.h" | |
| #include "cube_texture_and_coords.h" | |
| #define PATH "./" | |
| #define IMAGE_SIZE 128 | |
| #ifndef M_PI | |
| #define M_PI 3.141592654 | |
| #endif | |
| typedef struct | |
| { | |
| uint32_t screen_width; | |
| uint32_t screen_height; | |
| // OpenGL|ES objects | |
| DISPMANX_DISPLAY_HANDLE_T dispman_display; | |
| DISPMANX_ELEMENT_HANDLE_T dispman_element; | |
| EGLDisplay display; | |
| EGLSurface surface; | |
| EGLContext context; | |
| GLuint tex[6]; | |
| // model rotation vector and direction | |
| GLfloat rot_angle_x_inc; | |
| GLfloat rot_angle_y_inc; | |
| GLfloat rot_angle_z_inc; | |
| // current model rotation angles | |
| GLfloat rot_angle_x; | |
| GLfloat rot_angle_y; | |
| GLfloat rot_angle_z; | |
| // current distance from camera | |
| GLfloat distance; | |
| GLfloat distance_inc; | |
| // pointers to texture buffers | |
| char *tex_buf1; | |
| char *tex_buf2; | |
| char *tex_buf3; | |
| } CUBE_STATE_T; | |
| static void init_ogl(CUBE_STATE_T *state); | |
| static void init_model_proj(CUBE_STATE_T *state); | |
| static void reset_model(CUBE_STATE_T *state); | |
| static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); | |
| static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); | |
| static void redraw_scene(CUBE_STATE_T *state); | |
| static void update_model(CUBE_STATE_T *state); | |
| static void init_textures(CUBE_STATE_T *state); | |
| static void load_tex_images(CUBE_STATE_T *state); | |
| static void exit_func(void); | |
| static volatile int terminate; | |
| static CUBE_STATE_T _state, *state=&_state; | |
| /*********************************************************** | |
| * Name: init_ogl | |
| * | |
| * Arguments: | |
| * CUBE_STATE_T *state - holds OGLES model info | |
| * | |
| * Description: Sets the display, OpenGL|ES context and screen stuff | |
| * | |
| * Returns: void | |
| * | |
| ***********************************************************/ | |
| static void init_ogl(CUBE_STATE_T *state) | |
| { | |
| int32_t success = 0; | |
| EGLBoolean result; | |
| EGLint num_config; | |
| static EGL_DISPMANX_WINDOW_T nativewindow; | |
| DISPMANX_UPDATE_HANDLE_T dispman_update; | |
| VC_RECT_T dst_rect; | |
| VC_RECT_T src_rect; | |
| static const EGLint attribute_list[] = | |
| { | |
| EGL_RED_SIZE, 8, | |
| EGL_GREEN_SIZE, 8, | |
| EGL_BLUE_SIZE, 8, | |
| EGL_ALPHA_SIZE, 8, | |
| EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | |
| EGL_NONE | |
| }; | |
| EGLConfig config; | |
| // get an EGL display connection | |
| state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| printf( "Display: %d\n", state->display ); | |
| assert(state->display!=EGL_NO_DISPLAY); | |
| // initialize the EGL display connection | |
| result = eglInitialize(state->display, NULL, NULL); | |
| assert(EGL_FALSE != result); | |
| // get an appropriate EGL frame buffer configuration | |
| result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); | |
| printf( "Config: %d\n", num_config ); | |
| assert(EGL_FALSE != result); | |
| static const EGLint context_attribute_list[] = { | |
| EGL_CONTEXT_CLIENT_VERSION, 2, | |
| EGL_NONE | |
| }; | |
| // create an EGL rendering context | |
| state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, 0); | |
| printf( "CONTEXT: %d\n", state->context ); | |
| assert(state->context!=EGL_NO_CONTEXT); | |
| // create an EGL window surface | |
| // success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); | |
| // assert( success >= 0 ); | |
| state->screen_width = 2160; | |
| state->screen_height = 1200; | |
| printf( "%d %d\n", state->screen_width, state->screen_height ); | |
| state->screen_width = 2048; | |
| dst_rect.x = 56; | |
| dst_rect.y = 0; | |
| dst_rect.width = state->screen_width; | |
| dst_rect.height = state->screen_height; | |
| src_rect.x = 0; | |
| src_rect.y = 0; | |
| src_rect.width = state->screen_width << 16; | |
| src_rect.height = state->screen_height << 16; | |
| state->dispman_display = vc_dispmanx_display_open( 0 /* LCD */); | |
| dispman_update = vc_dispmanx_update_start( 0 ); | |
| state->dispman_element = vc_dispmanx_element_add ( dispman_update, state->dispman_display, | |
| 0/*layer*/, &dst_rect, 0/*src*/, | |
| &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); | |
| nativewindow.element = state->dispman_element; | |
| nativewindow.width = state->screen_width; | |
| nativewindow.height = state->screen_height; | |
| vc_dispmanx_update_submit_sync( dispman_update ); | |
| static EGLint window_attribute_list[] = { | |
| EGL_NONE | |
| }; | |
| state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, | |
| window_attribute_list); | |
| assert(state->surface != EGL_NO_SURFACE); | |
| // connect the context to the surface | |
| result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); | |
| assert(EGL_FALSE != result); | |
| // Set background color and clear buffers | |
| glClearColor(0.15f, 0.25f, 0.35f, 1.0f); | |
| // Enable back face culling. | |
| glEnable(GL_CULL_FACE); | |
| glMatrixMode(GL_MODELVIEW); | |
| } | |
| /*********************************************************** | |
| * Name: init_model_proj | |
| * | |
| * Arguments: | |
| * CUBE_STATE_T *state - holds OGLES model info | |
| * | |
| * Description: Sets the OpenGL|ES model to default values | |
| * | |
| * Returns: void | |
| * | |
| ***********************************************************/ | |
| static void init_model_proj(CUBE_STATE_T *state) | |
| { | |
| float nearp = 1.0f; | |
| float farp = 500.0f; | |
| float hht; | |
| float hwd; | |
| glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); | |
| glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); | |
| glMatrixMode(GL_PROJECTION); | |
| glLoadIdentity(); | |
| hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); | |
| hwd = hht * (float)state->screen_width / (float)state->screen_height; | |
| glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); | |
| glEnableClientState( GL_VERTEX_ARRAY ); | |
| glVertexPointer( 3, GL_BYTE, 0, quadx ); | |
| reset_model(state); | |
| } | |
| /*********************************************************** | |
| * Name: reset_model | |
| * | |
| * Arguments: | |
| * CUBE_STATE_T *state - holds OGLES model info | |
| * | |
| * Description: Resets the Model projection and rotation direction | |
| * | |
| * Returns: void | |
| * | |
| ***********************************************************/ | |
| static void reset_model(CUBE_STATE_T *state) | |
| { | |
| // reset model position | |
| glMatrixMode(GL_MODELVIEW); | |
| glLoadIdentity(); | |
| glTranslatef(0.f, 0.f, -50.f); | |
| // reset model rotation | |
| state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; | |
| state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; | |
| state->distance = 40.f; | |
| } | |
| /*********************************************************** | |
| * Name: update_model | |
| * | |
| * Arguments: | |
| * CUBE_STATE_T *state - holds OGLES model info | |
| * | |
| * Description: Updates model projection to current position/rotation | |
| * | |
| * Returns: void | |
| * | |
| ***********************************************************/ | |
| static void update_model(CUBE_STATE_T *state) | |
| { | |
| // update position | |
| state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); | |
| state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); | |
| state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); | |
| state->distance = inc_and_clip_distance(state->distance, state->distance_inc); | |
| glLoadIdentity(); | |
| // move camera back to see the cube | |
| glTranslatef(0.f, 0.f, -state->distance); | |
| // Rotate model to new position | |
| glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); | |
| glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); | |
| glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); | |
| } | |
| /*********************************************************** | |
| * Name: inc_and_wrap_angle | |
| * | |
| * Arguments: | |
| * GLfloat angle current angle | |
| * GLfloat angle_inc angle increment | |
| * | |
| * Description: Increments or decrements angle by angle_inc degrees | |
| * Wraps to 0 at 360 deg. | |
| * | |
| * Returns: new value of angle | |
| * | |
| ***********************************************************/ | |
| static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) | |
| { | |
| angle += angle_inc; | |
| if (angle >= 360.0) | |
| angle -= 360.f; | |
| else if (angle <=0) | |
| angle += 360.f; | |
| return angle; | |
| } | |
| /*********************************************************** | |
| * Name: inc_and_clip_distance | |
| * | |
| * Arguments: | |
| * GLfloat distance current distance | |
| * GLfloat distance_inc distance increment | |
| * | |
| * Description: Increments or decrements distance by distance_inc units | |
| * Clips to range | |
| * | |
| * Returns: new value of angle | |
| * | |
| ***********************************************************/ | |
| static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) | |
| { | |
| distance += distance_inc; | |
| if (distance >= 120.0f) | |
| distance = 120.f; | |
| else if (distance <= 40.0f) | |
| distance = 40.0f; | |
| return distance; | |
| } | |
| /*********************************************************** | |
| * Name: redraw_scene | |
| * | |
| * Arguments: | |
| * CUBE_STATE_T *state - holds OGLES model info | |
| * | |
| * Description: Draws the model and calls eglSwapBuffers | |
| * to render to screen | |
| * | |
| * Returns: void | |
| * | |
| ***********************************************************/ | |
| static void redraw_scene(CUBE_STATE_T *state) | |
| { | |
| // Start with a clear screen | |
| glClear( GL_COLOR_BUFFER_BIT ); | |
| // Draw first (front) face: | |
| // Bind texture surface to current vertices | |
| glBindTexture(GL_TEXTURE_2D, state->tex[0]); | |
| // Need to rotate textures - do this by rotating each cube face | |
| glRotatef(270.f, 0.f, 0.f, 1.f ); // front face normal along z axis | |
| //XXX MODDED To make more draw calls. | |
| int x, y; | |
| for( y= 0; y < 20; y++ ) | |
| { | |
| glPushMatrix(); | |
| glTranslatef( (y-10)*10, 0, 0 ); | |
| for( x = 0; x < 20; x++ ) | |
| { | |
| glPushMatrix(); | |
| glTranslatef( 0, (x-10)*10, 0 ); | |
| glScalef( 0.05, 0.05, 0.05 ); //Make boxes really small to make sure it's not fill rate. | |
| // draw first 4 vertices | |
| glDrawArrays( GL_TRIANGLE_STRIP, 0, 4); | |
| // same pattern for other 5 faces - rotation chosen to make image orientation 'nice' | |
| glBindTexture(GL_TEXTURE_2D, state->tex[1]); | |
| glRotatef(90.f, 0.f, 0.f, 1.f ); // back face normal along z axis | |
| glDrawArrays( GL_TRIANGLE_STRIP, 4, 4); | |
| glBindTexture(GL_TEXTURE_2D, state->tex[2]); | |
| glRotatef(90.f, 1.f, 0.f, 0.f ); // left face normal along x axis | |
| glDrawArrays( GL_TRIANGLE_STRIP, 8, 4); | |
| glBindTexture(GL_TEXTURE_2D, state->tex[3]); | |
| glRotatef(90.f, 1.f, 0.f, 0.f ); // right face normal along x axis | |
| glDrawArrays( GL_TRIANGLE_STRIP, 12, 4); | |
| glBindTexture(GL_TEXTURE_2D, state->tex[4]); | |
| glRotatef(270.f, 0.f, 1.f, 0.f ); // top face normal along y axis | |
| glDrawArrays( GL_TRIANGLE_STRIP, 16, 4); | |
| glBindTexture(GL_TEXTURE_2D, state->tex[5]); | |
| glRotatef(90.f, 0.f, 1.f, 0.f ); // bottom face normal along y axis | |
| glDrawArrays( GL_TRIANGLE_STRIP, 20, 4); | |
| glPopMatrix(); | |
| } | |
| glPopMatrix(); | |
| } | |
| printf( "." ); fflush( stdout ); //Used in conjunction with PV to determine FPS. | |
| eglSwapBuffers(state->display, state->surface); | |
| } | |
| /*********************************************************** | |
| * Name: init_textures | |
| * | |
| * Arguments: | |
| * CUBE_STATE_T *state - holds OGLES model info | |
| * | |
| * Description: Initialise OGL|ES texture surfaces to use image | |
| * buffers | |
| * | |
| * Returns: void | |
| * | |
| ***********************************************************/ | |
| static void init_textures(CUBE_STATE_T *state) | |
| { | |
| // load three texture buffers but use them on six OGL|ES texture surfaces | |
| load_tex_images(state); | |
| glGenTextures(6, &state->tex[0]); | |
| // setup first texture | |
| glBindTexture(GL_TEXTURE_2D, state->tex[0]); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, | |
| GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf1); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); | |
| // setup second texture - reuse first image | |
| glBindTexture(GL_TEXTURE_2D, state->tex[1]); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, | |
| GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf1); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); | |
| // third texture | |
| glBindTexture(GL_TEXTURE_2D, state->tex[2]); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, | |
| GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf2); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); | |
| // fourth texture - reuse second image | |
| glBindTexture(GL_TEXTURE_2D, state->tex[3]); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, | |
| GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf2); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); | |
| //fifth texture | |
| glBindTexture(GL_TEXTURE_2D, state->tex[4]); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, | |
| GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf3); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); | |
| // sixth texture - reuse third image | |
| glBindTexture(GL_TEXTURE_2D, state->tex[5]); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, | |
| GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf3); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); | |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); | |
| // setup overall texture environment | |
| glTexCoordPointer(2, GL_FLOAT, 0, texCoords); | |
| glEnableClientState(GL_TEXTURE_COORD_ARRAY); | |
| glEnable(GL_TEXTURE_2D); | |
| } | |
| /*********************************************************** | |
| * Name: load_tex_images | |
| * | |
| * Arguments: | |
| * void | |
| * | |
| * Description: Loads three raw images to use as textures on faces | |
| * | |
| * Returns: void | |
| * | |
| ***********************************************************/ | |
| static void load_tex_images(CUBE_STATE_T *state) | |
| { | |
| FILE *tex_file1 = NULL, *tex_file2=NULL, *tex_file3 = NULL; | |
| int bytes_read, image_sz = IMAGE_SIZE*IMAGE_SIZE*3; | |
| state->tex_buf1 = malloc(image_sz); | |
| state->tex_buf2 = malloc(image_sz); | |
| state->tex_buf3 = malloc(image_sz); | |
| tex_file1 = fopen(PATH "Lucca_128_128.raw", "rb"); | |
| if (tex_file1 && state->tex_buf1) | |
| { | |
| bytes_read=fread(state->tex_buf1, 1, image_sz, tex_file1); | |
| assert(bytes_read == image_sz); // some problem with file? | |
| fclose(tex_file1); | |
| } | |
| tex_file2 = fopen(PATH "Djenne_128_128.raw", "rb"); | |
| if (tex_file2 && state->tex_buf2) | |
| { | |
| bytes_read=fread(state->tex_buf2, 1, image_sz, tex_file2); | |
| assert(bytes_read == image_sz); // some problem with file? | |
| fclose(tex_file2); | |
| } | |
| tex_file3 = fopen(PATH "Gaudi_128_128.raw", "rb"); | |
| if (tex_file3 && state->tex_buf3) | |
| { | |
| bytes_read=fread(state->tex_buf3, 1, image_sz, tex_file3); | |
| assert(bytes_read == image_sz); // some problem with file? | |
| fclose(tex_file3); | |
| } | |
| } | |
| //------------------------------------------------------------------------------ | |
| static void exit_func(void) | |
| // Function to be passed to atexit(). | |
| { | |
| DISPMANX_UPDATE_HANDLE_T dispman_update; | |
| int s; | |
| // clear screen | |
| glClear( GL_COLOR_BUFFER_BIT ); | |
| eglSwapBuffers(state->display, state->surface); | |
| glDeleteTextures(6, state->tex); | |
| eglDestroySurface( state->display, state->surface ); | |
| dispman_update = vc_dispmanx_update_start( 0 ); | |
| s = vc_dispmanx_element_remove(dispman_update, state->dispman_element); | |
| assert(s == 0); | |
| vc_dispmanx_update_submit_sync( dispman_update ); | |
| s = vc_dispmanx_display_close(state->dispman_display); | |
| assert (s == 0); | |
| // Release OpenGL resources | |
| eglMakeCurrent( state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); | |
| eglDestroyContext( state->display, state->context ); | |
| eglTerminate( state->display ); | |
| // release texture buffers | |
| free(state->tex_buf1); | |
| free(state->tex_buf2); | |
| free(state->tex_buf3); | |
| printf("\ncube closed\n"); | |
| } // exit_func() | |
| //============================================================================== | |
| int main () | |
| { | |
| bcm_host_init(); | |
| // Clear application state | |
| memset( state, 0, sizeof( *state ) ); | |
| // Start OGLES | |
| init_ogl(state); | |
| // Setup the model world | |
| init_model_proj(state); | |
| // initialise the OGLES texture(s) | |
| init_textures(state); | |
| while (!terminate) | |
| { | |
| update_model(state); | |
| redraw_scene(state); | |
| } | |
| exit_func(); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment