#include //#include #include #include #include #include #include #include typedef struct { int x, y; int tx, ty; } Vi; typedef struct { float x, y; float tx, ty; } Vf; #define TEX_SIZE 1024 enum BlitMode { BLIT_MODE_BLIT_FRAMEBUFFER, BLIT_MODE_QUADS, BLIT_MODE_TRISTRIP, BLIT_MODE_COPY_TEX_SUBIMAGE, BLIT_MODE_COUNT }; static const char * blit_mode_names[BLIT_MODE_COUNT] = { "glBlitFramebuffer", "glDrawArray (quads)", "glDrawArray (tristrip)", "glCopyTexSubImage2D" }; #define N_FRAMES 2 #define N_RES 11 #define MODE_FOR_FRAME(frame_num) (((frame_num) - 1) / (N_RES*N_FRAMES)) #define RES_FOR_FRAME(frame_num) (((frame_num) - 1) / N_FRAMES % N_RES) static int mode_start_time; static int last_mode; static int frame_num = 0; static GLubyte tex_data[TEX_SIZE * TEX_SIZE * 4]; static void vertex(Vf *v, int x, int y) { v->tx = x / (float) TEX_SIZE; v->ty = y / (float) TEX_SIZE; v->x = 2*v->tx - 1; v->y = 2*v->ty - 1; } static void display_cb (void) { GLuint texa, texb; GLuint fboa, fbob; static const Vi verts[] = { { -1, -1, 0, 0 }, { 1, -1, 1, 0 }, { -1, 1, 0, 1 }, { 1, 1, 1, 1 } }; GLint x, y; enum BlitMode mode; int step; int id; glClear (GL_COLOR_BUFFER_BIT); /* Don't do anything for the first frame in case it affects timing */ if (frame_num == 0) { frame_num = 1; glutSwapBuffers (); return; } mode = MODE_FOR_FRAME (frame_num); step = 1 << RES_FOR_FRAME (frame_num); id = mode << 16 | step; if (id != last_mode) { printf ("%s %dx%d", blit_mode_names[mode], step, step); fflush (stdout); mode_start_time = glutGet (GLUT_ELAPSED_TIME); last_mode = id; } /* Upload it to a texture */ glGenTextures (1, &texa); glBindTexture (GL_TEXTURE_2D, texa); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_data); /* Bind it to a framebuffer */ glGenFramebuffersEXT (1, &fboa); glBindFramebufferEXT (GL_READ_FRAMEBUFFER, fboa); glFramebufferTexture2DEXT (GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texa, 0); /* Create a second texture at the same size and format with no data */ glGenTextures (1, &texb); glBindTexture (GL_TEXTURE_2D, texb); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); if (mode == BLIT_MODE_BLIT_FRAMEBUFFER) { /* Create a separate fbo for the dest texture */ glGenFramebuffersEXT (1, &fbob); glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER, fbob); glFramebufferTexture2DEXT (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texb, 0); /* Fill the texture by blitting one square at a time */ for (y = 0; y < TEX_SIZE; y += step) for (x = 0; x < TEX_SIZE; x += step) glBlitFramebufferEXT (x, y, x + step, y + step, x, y, x + step, y + step, GL_COLOR_BUFFER_BIT, GL_NEAREST); glDeleteFramebuffersEXT (1, &fbob); } else if (mode == BLIT_MODE_QUADS) { Vf *v, *_v; int nx = TEX_SIZE/step; int ny = TEX_SIZE/step; /* Create a separate fbo for the dest texture */ glGenFramebuffersEXT (1, &fbob); glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER, fbob); glFramebufferTexture2DEXT (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texb, 0); glViewport(0, 0, TEX_SIZE, TEX_SIZE); /* Compute the vertex locations */ _v = v = malloc(sizeof(Vf) * nx * ny * 4); for (y = 0; y < TEX_SIZE; y += step) for (x = 0; x < TEX_SIZE; x += step) { vertex(v, x, y); v++; vertex(v, x+step, y); v++; vertex(v, x+step, y+step); v++; vertex(v, x, y+step); v++; } glBindTexture (GL_TEXTURE_2D, texa); glEnableClientState (GL_VERTEX_ARRAY); glVertexPointer (2, GL_FLOAT, sizeof (Vf), &_v->x); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glTexCoordPointer (2, GL_FLOAT, sizeof (Vf), &_v->tx); glEnable (GL_TEXTURE_2D); glDrawArrays (GL_QUADS, 0, v-_v); glDisableClientState (GL_TEXTURE_COORD_ARRAY); glDisableClientState (GL_VERTEX_ARRAY); free (_v); glDeleteFramebuffersEXT (1, &fbob); glBindTexture (GL_TEXTURE_2D, texb); } else if (mode == BLIT_MODE_TRISTRIP) { Vf *v, *_v; int n = TEX_SIZE/step; /* Create a separate fbo for the dest texture */ glGenFramebuffersEXT (1, &fbob); glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER, fbob); glFramebufferTexture2DEXT (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texb, 0); glViewport(0, 0, TEX_SIZE, TEX_SIZE); /* Compute the vertex locations */ _v = v = malloc(sizeof(Vf) * (n+1) * n*2); y = 0; do { x = 0; while (x <= TEX_SIZE) { vertex(v, x, y); v++; vertex(v, x, y+step); v++; x += step; } y += step; if (y == TEX_SIZE) break; x -= step; while (x >= 0) { vertex(v, x, y+step); v++; vertex(v, x, y); v++; x -= step; } y += step; if (y == TEX_SIZE) break; } while (1); glBindTexture (GL_TEXTURE_2D, texa); glEnableClientState (GL_VERTEX_ARRAY); glVertexPointer (2, GL_FLOAT, sizeof (Vf), &_v->x); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glTexCoordPointer (2, GL_FLOAT, sizeof (Vf), &_v->tx); glEnable (GL_TEXTURE_2D); glDrawArrays (GL_TRIANGLE_STRIP, 0, v-_v); glDisableClientState (GL_INDEX_ARRAY); glDisableClientState (GL_TEXTURE_COORD_ARRAY); glDisableClientState (GL_VERTEX_ARRAY); free (_v); glDeleteFramebuffersEXT (1, &fbob); glBindTexture (GL_TEXTURE_2D, texb); } else { /* Fill the texture by blitting one pixel at a time */ for (y = 0; y < TEX_SIZE; y += step) for (x = 0; x < TEX_SIZE; x += step) glCopyTexSubImage2D (GL_TEXTURE_2D, 0, x, y, x, y, step, step); } /* Restore the framebuffer binding to the window */ glBindFramebufferEXT (GL_FRAMEBUFFER, 0); glViewport(0, 0, 512, 512); /* Draw the target texture to verify that it worked */ glEnableClientState (GL_VERTEX_ARRAY); glVertexPointer (2, GL_INT, sizeof (Vi), &verts[0].x); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glTexCoordPointer (2, GL_INT, sizeof (Vi), &verts[0].tx); glEnable (GL_TEXTURE_2D); glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); glDisable (GL_TEXTURE_2D); glDisableClientState (GL_TEXTURE_COORD_ARRAY); glDisableClientState (GL_VERTEX_ARRAY); /* Clean up the resources */ glDeleteFramebuffersEXT (1, &fboa); glBindTexture (GL_TEXTURE_2D, 0); glDeleteTextures (1, &texa); glDeleteTextures (1, &texb); glutSwapBuffers (); frame_num++; mode = MODE_FOR_FRAME (frame_num); step = 1 << RES_FOR_FRAME (frame_num); if (id != (mode << 16 | step)) { printf (" = %i\n", glutGet (GLUT_ELAPSED_TIME) - mode_start_time); if (MODE_FOR_FRAME (frame_num) >= BLIT_MODE_COUNT) exit (0); } } void idle_cb (void) { glutPostRedisplay (); } int main (int argc, char **argv) { GLubyte *p; GLint x, y; glutInit (&argc, argv); glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE); glutInitWindowSize (512, 512); glutCreateWindow ("Inter-texture blit test"); glutDisplayFunc (display_cb); glutIdleFunc (idle_cb); glewInit (); /* Create data for a rainbow texture */ for (p = tex_data, y = 0; y < TEX_SIZE; y++) for (x = 0; x < TEX_SIZE; x++) { *(p++) = x * 255 / TEX_SIZE; *(p++) = y * 255 / TEX_SIZE; *(p++) = 0; *(p++) = 255; } glutMainLoop (); return 0; }