//======================================================================== // Multisample anti-aliasing test // Copyright (c) Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // // This test renders two high contrast, slowly rotating quads, one aliased // and one (hopefully) anti-aliased, thus allowing for visual verification // of whether MSAA is indeed enabled // //======================================================================== #include #define GLFW_INCLUDE_NONE #include #if defined(_MSC_VER) // Make MS math.h define M_PI #define _USE_MATH_DEFINES #endif #include "linmath.h" #include #include #include "getopt.h" static const vec2 vertices[4] = { { -0.6f, -0.6f }, { 0.6f, -0.6f }, { 0.6f, 0.6f }, { -0.6f, 0.6f } }; static const char* vertex_shader_text = "#version 110\n" "uniform mat4 MVP;\n" "attribute vec2 vPos;\n" "void main()\n" "{\n" " gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n" "}\n"; static const char* fragment_shader_text = "#version 110\n" "void main()\n" "{\n" " gl_FragColor = vec4(1.0);\n" "}\n"; static void error_callback(int error, const char* description) { fprintf(stderr, "Error: %s\n", description); } static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (action != GLFW_PRESS) return; switch (key) { case GLFW_KEY_SPACE: glfwSetTime(0.0); break; case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, GLFW_TRUE); break; } } static void usage(void) { printf("Usage: msaa [-h] [-s SAMPLES]\n"); } int main(int argc, char** argv) { int ch, samples = 4; GLFWwindow* window; GLuint vertex_buffer, vertex_shader, fragment_shader, program; GLint mvp_location, vpos_location; // Minimum static damage for both squares. GLFWrect rects[8] = {{ 25, 25, 350, 90}, { 25, 285, 350, 90}, { 25, 115, 90, 170}, {285, 115, 90, 170}, {425, 25, 350, 90}, {425, 285, 350, 90}, {425, 115, 90, 170}, {685, 115, 90, 170}, }; while ((ch = getopt(argc, argv, "hs:")) != -1) { switch (ch) { case 'h': usage(); exit(EXIT_SUCCESS); case 's': samples = atoi(optarg); break; default: usage(); exit(EXIT_FAILURE); } } glfwSetErrorCallback(error_callback); if (!glfwInit()) exit(EXIT_FAILURE); if (samples) printf("Requesting MSAA with %i samples\n", samples); else printf("Requesting that MSAA not be available\n"); glfwWindowHint(GLFW_SAMPLES, samples); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); window = glfwCreateWindow(800, 400, "Aliasing Detector", NULL, NULL); if (!window) { glfwTerminate(); exit(EXIT_FAILURE); } glfwSetKeyCallback(window, key_callback); glfwMakeContextCurrent(window); gladLoadGL(glfwGetProcAddress); glfwSwapInterval(1); glGetIntegerv(GL_SAMPLES, &samples); if (samples) printf("Context reports MSAA is available with %i samples\n", samples); else printf("Context reports MSAA is unavailable\n"); glGenBuffers(1, &vertex_buffer); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); vertex_shader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL); glCompileShader(vertex_shader); fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL); glCompileShader(fragment_shader); program = glCreateProgram(); glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); glLinkProgram(program); mvp_location = glGetUniformLocation(program, "MVP"); vpos_location = glGetAttribLocation(program, "vPos"); glEnableVertexAttribArray(vpos_location); glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), (void*) 0); while (!glfwWindowShouldClose(window)) { float ratio; int buffer_age; int width, height; mat4x4 m, p, mvp; const double angle = glfwGetTime() * M_PI / 180.0; glfwGetFramebufferSize(window, &width, &height); buffer_age = glfwGetBufferAge(window); ratio = width / (float) height; glViewport(0, 0, width, height); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 0.f, 1.f); mat4x4_translate(m, -1.f, 0.f, 0.f); mat4x4_rotate_Z(m, m, (float) angle); mat4x4_mul(mvp, p, m); glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); glDisable(GL_MULTISAMPLE); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); mat4x4_translate(m, 1.f, 0.f, 0.f); mat4x4_rotate_Z(m, m, (float) angle); mat4x4_mul(mvp, p, m); glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); glEnable(GL_MULTISAMPLE); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); // If buffer_age is 0, we can’t assume anything about the previous buffer // so swap the entire buffer. if (buffer_age > 0) glfwSwapBuffersWithDamage(window, rects, 8); else glfwSwapBuffers(window); glfwPollEvents(); } glfwDestroyWindow(window); glfwTerminate(); exit(EXIT_SUCCESS); }