| /* |
| * Copyright 2013 The Emscripten Authors. All rights reserved. |
| * Emscripten is available under two separate licenses, the MIT license and the |
| * University of Illinois/NCSA Open Source License. Both these licenses can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdbool.h> |
| #include <assert.h> |
| |
| #include <emscripten.h> |
| #include <emscripten/console.h> |
| |
| #define BUFFER_OFFSET(i) ((char *)NULL + (i)) |
| |
| static const int WINDOWS_SIZE = 500; |
| |
| static GLfloat vertices[] = { 0.0f, 250.f, 0.0f, |
| -250.f, -250.f, 0.0f, |
| 250.f, -250.f, 0.0f }; |
| |
| static GLfloat vertices2[] = { 0.0f, 250.f, -1.0f, |
| -250.f, -250.f, -1.0f, |
| 250.f, -250.f, -1.0f }; |
| |
| static GLuint shaderProgram = 0; |
| static GLuint verticesVBO = 0; |
| static GLuint verticesVBO2 = 0; |
| |
| static unsigned char backgroundColor[4] = {255, 255, 255, 255}; |
| static unsigned char triangleColor[4] = {255, 0, 0, 255}; |
| static unsigned char triangleColor2[4] = {0, 255, 0, 255}; |
| |
| static char vertexShaderSrc[] = |
| "precision highp float;" |
| "precision highp int;" |
| |
| "uniform mat4 u_mvpMatrix;" |
| "uniform vec4 u_color;" |
| |
| "attribute vec3 a_position;" |
| |
| "varying vec4 v_color;" |
| |
| "void main() {" |
| " gl_Position = u_mvpMatrix * vec4(a_position, 1.0);" |
| " v_color = u_color;" |
| "}" |
| ; |
| |
| static char fragmentShaderSrc[] = |
| "precision highp float;" |
| "precision highp int;" |
| |
| "varying vec4 v_color;" |
| |
| "void main() {" |
| " gl_FragColor = v_color;" |
| "}" |
| ; |
| |
| static GLuint createShader(const char *source, int type) { |
| GLuint shader = glCreateShader(type); |
| glShaderSource(shader, 1, (const GLchar**)(&source), NULL); |
| glCompileShader(shader); |
| return shader; |
| } |
| |
| static GLuint createShaderProgram(const char *vertexShaderSrc, const char *fragmentShaderSrc) { |
| GLuint program = glCreateProgram(); |
| glAttachShader(program, createShader(vertexShaderSrc, GL_VERTEX_SHADER)); |
| glAttachShader(program, createShader(fragmentShaderSrc, GL_FRAGMENT_SHADER)); |
| glLinkProgram(program); |
| return program; |
| } |
| |
| void ortho(float left, float right, float bottom, float top, float nearVal, float farVal, GLfloat *projMatrix) { |
| float tx = -(right+left)/(right-left); |
| float ty = -(top+bottom)/(top-bottom); |
| float tz = -(farVal+nearVal)/(farVal-nearVal); |
| memset(projMatrix, 0, 16 * sizeof(GLfloat)); |
| projMatrix[0] = 2.0f / (right-left); |
| projMatrix[3] = tx; |
| projMatrix[1*4+1] = 2.0f / (top-bottom); |
| projMatrix[1*4+3] = ty; |
| projMatrix[2*4+2] = -2.0f / (farVal-nearVal); |
| projMatrix[2*4+3] = tz; |
| projMatrix[3*4+3] = 1.0f; |
| } |
| |
| static void initGlObjects() { |
| glGenBuffers(1, &verticesVBO); |
| glBindBuffer(GL_ARRAY_BUFFER, verticesVBO); |
| glBufferData(GL_ARRAY_BUFFER, 9*sizeof(float), vertices, GL_STATIC_DRAW); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| |
| glGenBuffers(1, &verticesVBO2); |
| glBindBuffer(GL_ARRAY_BUFFER, verticesVBO2); |
| glBufferData(GL_ARRAY_BUFFER, 9*sizeof(float), vertices2, GL_STATIC_DRAW); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| |
| shaderProgram = createShaderProgram(vertexShaderSrc, fragmentShaderSrc); |
| } |
| |
| static void drawTriangle(GLuint verticesVBO, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { |
| glUseProgram(shaderProgram); |
| GLuint posLoc = glGetAttribLocation(shaderProgram, "a_position"); |
| GLuint mvpLoc = glGetUniformLocation(shaderProgram, "u_mvpMatrix"); |
| GLuint colorLoc = glGetUniformLocation(shaderProgram, "u_color"); |
| |
| GLfloat mvpMat[16]; |
| ortho(-WINDOWS_SIZE/2, WINDOWS_SIZE/2, -WINDOWS_SIZE/2, WINDOWS_SIZE/2, -100, 100, mvpMat); |
| |
| glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, mvpMat); |
| glUniform4f(colorLoc, r/255.f, g/255.f, b/255.f, a/255.f); |
| |
| glBindBuffer(GL_ARRAY_BUFFER, verticesVBO); |
| glEnableVertexAttribArray(posLoc); |
| glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), BUFFER_OFFSET(0)); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 3); |
| |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| glUseProgram(0); |
| } |
| |
| // Draw a red triangle on a white background. If antialiasing is disabled, resulting pixels |
| // will only have white and red colors. If antialiasing is enabled, there will be pixels |
| // whose color is different from red and white. |
| static bool testAntiAliasing(bool activated) { |
| glViewport(0, 0, WINDOWS_SIZE, WINDOWS_SIZE); |
| glClearColor(backgroundColor[0]/255.f, backgroundColor[1]/255.f, backgroundColor[2]/255.f, backgroundColor[3]/255.f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| drawTriangle(verticesVBO, triangleColor[0], triangleColor[1], triangleColor[2], triangleColor[3]); |
| |
| bool antialiased = false; |
| |
| unsigned char buffer[(WINDOWS_SIZE*WINDOWS_SIZE)*4]; |
| glReadPixels(0, 0, WINDOWS_SIZE, WINDOWS_SIZE, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); |
| glFinish(); |
| for (unsigned int i = 0 ; i < WINDOWS_SIZE ; ++i) { |
| for (unsigned int j = 0 ; j < WINDOWS_SIZE ; ++j) { |
| unsigned char r = buffer[4*(i*WINDOWS_SIZE+j)]; |
| unsigned char g = buffer[4*(i*WINDOWS_SIZE+j)+1]; |
| unsigned char b = buffer[4*(i*WINDOWS_SIZE+j)+2]; |
| unsigned char a = buffer[4*(i*WINDOWS_SIZE+j)+3]; |
| if ((r == backgroundColor[0] && g == backgroundColor[1] && b == backgroundColor[2] && a == backgroundColor[3]) || |
| (r == triangleColor[0] && g == triangleColor[1] && b == triangleColor[2] && a == triangleColor[3])) { |
| continue; |
| } else { |
| antialiased = true; |
| break; |
| } |
| } |
| } |
| |
| return (activated && antialiased) || (!activated && !antialiased); |
| } |
| |
| // Draw a red triangle with depth equals to 0 then a green triangle whose depth equals -1. |
| // If there is an attached depth buffer, the resulting image will be a red triangle. If not, |
| // the resulting image will be a green triangle. |
| static bool testDepth(bool activated) { |
| glViewport(0, 0, WINDOWS_SIZE, WINDOWS_SIZE); |
| glClearColor(backgroundColor[0]/255.f, backgroundColor[1]/255.f, backgroundColor[2]/255.f, backgroundColor[3]/255.f); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| glEnable(GL_DEPTH_TEST); |
| glDepthFunc(GL_LEQUAL); |
| |
| drawTriangle(verticesVBO, triangleColor[0], triangleColor[1], triangleColor[2], triangleColor[3]); |
| drawTriangle(verticesVBO2, triangleColor2[0], triangleColor2[1], triangleColor2[2], triangleColor2[3]); |
| |
| glDisable(GL_DEPTH_TEST); |
| |
| // read the pixel at the center of the resulting image. |
| unsigned char buffer[4]; |
| glReadPixels(WINDOWS_SIZE/2, WINDOWS_SIZE/2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); |
| |
| bool frontTriangleColor = (buffer[0] == triangleColor[0] && buffer[1] == triangleColor[1] && |
| buffer[2] == triangleColor[2] && buffer[3] == triangleColor[3]); |
| |
| bool backTriangleColor = (buffer[0] == triangleColor2[0] && buffer[1] == triangleColor2[1] && |
| buffer[2] == triangleColor2[2] && buffer[3] == triangleColor2[3]); |
| |
| return (activated && frontTriangleColor) || (!activated && backTriangleColor); |
| } |
| |
| // The stencil function is set to GL_LEQUAL so fragments will be written to the |
| // back buffer only if the ref value is less or equal than the one in the stencil buffer. |
| // The content of the stencil buffer is initialized to 0xFF. |
| // First draw a red triangle whose stencil ref value is 0x1. |
| // Then draw a green triangle whose stencil ref value is 0xFF. |
| // If there is an attached stencil buffer, the resulting image will be a red triangle. If not, |
| // the resulting image will be a green triangle. |
| static bool testStencil(bool activated) { |
| glViewport(0, 0, WINDOWS_SIZE, WINDOWS_SIZE); |
| glClearColor(backgroundColor[0]/255.f, backgroundColor[1]/255.f, backgroundColor[2]/255.f, backgroundColor[3]/255.f); |
| glClearStencil(0xFF); |
| glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); |
| glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| |
| glEnable(GL_STENCIL_TEST); |
| |
| glStencilFunc(GL_LEQUAL, 0x1, 0xFF); |
| drawTriangle(verticesVBO, triangleColor[0], triangleColor[1], triangleColor[2], triangleColor[3]); |
| |
| glStencilFunc(GL_LEQUAL, 0xFF, 0xFF); |
| drawTriangle(verticesVBO, triangleColor2[0], triangleColor2[1], triangleColor2[2], triangleColor2[3]); |
| |
| glDisable(GL_STENCIL_TEST); |
| |
| unsigned char buffer[4]; |
| glReadPixels(WINDOWS_SIZE/2, WINDOWS_SIZE/2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); |
| |
| bool firstTriangleColor = (buffer[0] == triangleColor[0] && buffer[1] == triangleColor[1] && |
| buffer[2] == triangleColor[2] && buffer[3] == triangleColor[3]); |
| |
| bool secondTriangleColor = (buffer[0] == triangleColor2[0] && buffer[1] == triangleColor2[1] && |
| buffer[2] == triangleColor2[2] && buffer[3] == triangleColor2[3]); |
| |
| return (activated && firstTriangleColor) || (!activated && secondTriangleColor); |
| } |
| |
| // Clear to a color with alpha = 0. If alpha is enabled then all pixels will have alpha = 0. |
| // If alpha is disabled then pixels will have alpha of 255 |
| static bool testAlpha(bool activated) { |
| glViewport(0, 0, WINDOWS_SIZE, WINDOWS_SIZE); |
| glClearColor(backgroundColor[0]/255.f, backgroundColor[1]/255.f, backgroundColor[2]/255.f, 0.0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| bool hasAlpha = true; |
| |
| unsigned char buffer[(WINDOWS_SIZE*WINDOWS_SIZE)*4]; |
| glReadPixels(0, 0, WINDOWS_SIZE, WINDOWS_SIZE, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); |
| glFinish(); |
| for (unsigned int i = 0 ; i < WINDOWS_SIZE ; ++i) { |
| for (unsigned int j = 0 ; j < WINDOWS_SIZE ; ++j) { |
| unsigned char r = buffer[4*(i*WINDOWS_SIZE+j)]; |
| unsigned char g = buffer[4*(i*WINDOWS_SIZE+j)+1]; |
| unsigned char b = buffer[4*(i*WINDOWS_SIZE+j)+2]; |
| unsigned char a = buffer[4*(i*WINDOWS_SIZE+j)+3]; |
| if (r == backgroundColor[0] && g == backgroundColor[1] && b == backgroundColor[2] && a == 0) { |
| continue; |
| } else { |
| hasAlpha = false; |
| break; |
| } |
| } |
| } |
| |
| return (activated && hasAlpha) || (!activated && !hasAlpha); |
| } |
| |
| static bool antiAliasingActivated = false; |
| static bool depthActivated = false; |
| static bool stencilActivated = false; |
| static bool alphaActivated = false; |
| |
| static bool resultAA = 0; |
| static bool resultDepth = 0; |
| static bool resultStencil = 0; |
| static bool resultAlpha = 0; |
| |
| static void draw() { |
| if (!resultAA) resultAA = testAntiAliasing(antiAliasingActivated); |
| assert(resultAA); |
| |
| if (!resultDepth) resultDepth = testDepth(depthActivated); |
| assert(resultDepth); |
| |
| if (!resultStencil) resultStencil = testStencil(stencilActivated); |
| assert(resultStencil); |
| |
| if (!resultAlpha) resultAlpha = testAlpha(alphaActivated); |
| assert(resultAlpha); |
| } |
| |
| extern int webglAntialiasSupported(void); |
| extern int webglDepthSupported(void); |
| extern int webglStencilSupported(void); |
| extern int webglAlphaSupported(void); |
| |
| // Check attributes support in the WebGL implementation (see test_webgl_context_attributes function in test_browser.py) |
| // Tests will succeed if they are not. |
| static void checkContextAttributesSupport() { |
| if (!webglAntialiasSupported()) { |
| resultAA = true; |
| emscripten_err("warning: no antialiasing"); |
| } |
| if (!webglDepthSupported()) { |
| resultDepth = true; |
| emscripten_err("warning: no depth"); |
| } |
| if (!webglStencilSupported()) { |
| resultStencil = true; |
| emscripten_err("warning: no stencil"); |
| } |
| if (!webglAlphaSupported()) { |
| resultAlpha = true; |
| emscripten_err("warning: no alpha"); |
| } |
| } |