+ // Draw the texture stretched over a long quad, interpolating it out.
+ float vertices[] = {
+ 0.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f
+ };
+
+ GLuint vao;
+ glGenVertexArrays(1, &vao);
+ check_error();
+ glBindVertexArray(vao);
+ check_error();
+
+ GLuint position_vbo = fill_vertex_attribute(glsl_program_num, "position", 2, GL_FLOAT, sizeof(vertices), vertices);
+ GLuint texcoord_vbo = fill_vertex_attribute(glsl_program_num, "texcoord", 2, GL_FLOAT, sizeof(vertices), vertices); // Same data.
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ check_error();
+
+ cleanup_vertex_attribute(glsl_program_num, "position", position_vbo);
+ cleanup_vertex_attribute(glsl_program_num, "texcoord", texcoord_vbo);
+
+ glUseProgram(0);
+ check_error();
+
+ // Now read the data back and see what the card did. (Ignore the last value.)
+ // (We only look at the red channel; the others will surely be the same.)
+ unsigned char out_data[512 * 4];
+ glReadPixels(0, 0, 512, 1, GL_RGBA, GL_UNSIGNED_BYTE, out_data);
+ check_error();
+
+ int wrongly_rounded = 0;
+ for (unsigned i = 0; i < 255; ++i) {
+ if (out_data[(i * 2 + 0) * 4] != i) {
+ ++wrongly_rounded;
+ }
+ if (out_data[(i * 2 + 1) * 4] != i + 1) {
+ ++wrongly_rounded;
+ }
+ }
+
+ movit_num_wrongly_rounded = wrongly_rounded;
+
+ // Clean up.
+ glBindTexture(GL_TEXTURE_2D, 0);
+ check_error();
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ check_error();
+ glDeleteFramebuffers(1, &fbo);
+ check_error();
+ glDeleteTextures(1, &dst_texnum);
+ check_error();
+ glDeleteTextures(1, &src_texnum);
+ check_error();
+
+ resource_pool.release_glsl_program(glsl_program_num);
+ glDeleteVertexArrays(1, &vao);
+ check_error();
+}
+
+struct RequiredExtension {
+ int min_equivalent_gl_version;
+ const char extension_name[64];
+};
+const RequiredExtension required_extensions[] = {