+void EffectChain::render_to_screen(unsigned char *src)
+{
+ assert(finalized);
+
+ // Copy the pixel data into the PBO.
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 2);
+ check_error();
+ void *mapped_pbo = glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY);
+ memcpy(mapped_pbo, src, width * height * bytes_per_pixel);
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB);
+ check_error();
+
+ // Re-upload the texture from the PBO.
+ glActiveTexture(GL_TEXTURE0);
+ check_error();
+ glBindTexture(GL_TEXTURE_2D, source_image_num);
+ check_error();
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
+ check_error();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ check_error();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ check_error();
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
+ check_error();
+
+ // Basic state.
+ glDisable(GL_BLEND);
+ check_error();
+ glDisable(GL_DEPTH_TEST);
+ check_error();
+ glDepthMask(GL_FALSE);
+ check_error();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ if (phases.size() > 1) {
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ check_error();
+ }
+
+ for (unsigned phase = 0; phase < phases.size(); ++phase) {
+ // Set up inputs and outputs for this phase.
+ if (phase == 0) {
+ // First phase reads from the input texture (which is already bound).
+ } else {
+ glBindTexture(GL_TEXTURE_2D, temp_textures[(phase + 1) % 2]);
+ check_error();
+ }
+ if (phases[phase].input_needs_mipmaps) {
+ glGenerateMipmap(GL_TEXTURE_2D);
+ check_error();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ check_error();
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ check_error();
+ }
+
+ if (phase == phases.size() - 1) {
+ // Last phase goes directly to the screen.
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ check_error();
+ } else {
+ glFramebufferTexture2D(
+ GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ temp_textures[phase % 2],
+ 0);
+ }
+
+ // We have baked an upside-down transform into the quad coordinates,
+ // since the typical graphics program will have the origin at the upper-left,
+ // while OpenGL uses lower-left. In the next ones, however, the origin
+ // is all right, and we need to reverse that.
+ if (phase == 1) {
+ glTranslatef(0.0f, 1.0f, 0.0f);
+ glScalef(1.0f, -1.0f, 1.0f);
+ }
+
+ // Give the required parameters to all the effects.
+ glUseProgram(phases[phase].glsl_program_num);
+ check_error();
+
+ glUniform1i(glGetUniformLocation(phases[phase].glsl_program_num, "input_tex"), 0);
+ check_error();
+
+ unsigned sampler_num = 1;
+ for (unsigned i = phases[phase].start; i < phases[phase].end; ++i) {
+ char effect_id[256];
+ sprintf(effect_id, "eff%d", i);
+ effects[i]->set_uniforms(phases[phase].glsl_program_num, effect_id, &sampler_num);
+ }
+
+ // Now draw!
+ glBegin(GL_QUADS);
+
+ glTexCoord2f(0.0f, 1.0f);
+ glVertex2f(0.0f, 0.0f);
+
+ glTexCoord2f(1.0f, 1.0f);
+ glVertex2f(1.0f, 0.0f);
+
+ glTexCoord2f(1.0f, 0.0f);
+ glVertex2f(1.0f, 1.0f);
+
+ glTexCoord2f(0.0f, 0.0f);
+ glVertex2f(0.0f, 1.0f);
+
+ glEnd();
+ check_error();
+ }
+}