blob: 460697998f07cb9d32306a3ff37f1fcfdf55eb46 [file] [log] [blame] [edit]
// Copyright 2021 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 <string.h>
#include <assert.h>
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
#include <GLES3/gl3.h>
#include <stdlib.h>
GLuint CompileShader(GLenum type, const char *src)
{
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &src, NULL);
glCompileShader(shader);
GLint isCompiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
if (!isCompiled)
{
GLint maxLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
char *buf = (char*)malloc(maxLength+1);
glGetShaderInfoLog(shader, maxLength, &maxLength, buf);
printf("%s\n", buf);
free(buf);
return 0;
}
else
{
printf("Shader compiled OK!\n");
}
return shader;
}
GLuint CreateProgram(GLuint vertexShader, GLuint fragmentShader)
{
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glBindAttribLocation(program, 0, "pos");
glLinkProgram(program);
GLint linked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (!linked)
{
GLint infoLen = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen)
{
char *infoLog = (char*)malloc(infoLen);
glGetProgramInfoLog(program, infoLen, NULL, infoLog);
printf("Error linking program:\n%s\n", infoLog);
}
assert(0);
}
return program;
}
int main(int argc, char *argv[])
{
EmscriptenWebGLContextAttributes attr;
emscripten_webgl_init_context_attributes(&attr);
attr.majorVersion = 2;
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context("#canvas", &attr);
emscripten_webgl_make_context_current(ctx);
GLuint vs = CompileShader(GL_VERTEX_SHADER,
"#version 300 es\n"
"in vec4 pos;\n"
"void main() { gl_Position = pos; }");
GLuint ps = CompileShader(GL_FRAGMENT_SHADER,
"#version 300 es\n"
"precision lowp float;\n"
"layout(binding = 4) uniform red { vec4 unused1; float r; };\n"
"layout(binding = 5, std140) uniform Green { float unused2; float g; } green;\n"
"layout(std140, binding = 6) uniform Blue { vec3 unused3; float b; } blue[2];\n"
"out vec4 outColor;\n"
"void main() { outColor = vec4(r, green.g, blue[0].b + blue[1].b, 1.0); }");
GLuint program = CreateProgram(vs, ps);
glUseProgram(program);
int numBlocks;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &numBlocks);
for(int i = 0; i < numBlocks; ++i) {
int size = -1;
glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size);
printf("Uniform block at index %d: size: %d\n", i, size);
}
static const float vb[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
-1.0f, 1.0f,
1.0f, -1.0f,
1.0f, 1.0f,
};
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vb), vb, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, 0);
glEnableVertexAttribArray(0);
struct {
float unused1[4];
float r;
float unused2[3];
} red = { 0, 0, 0, 0, 0.3f };
printf("sizeof(red)=%d\n", (int)sizeof(red));
struct {
float unused1;
float g;
float unused2[2];
} green = { 0, 0.5f };
printf("sizeof(green)=%d\n", (int)sizeof(green));
struct {
float unused1[3];
float b;
} blue[2] = { { 0, 0, 0, 0.3f }, { 0, 0, 0.f, 0.6f } };
printf("sizeof(blue[0])=%d\n", (int)sizeof(blue[0]));
GLuint bufs[4];
glGenBuffers(4, bufs);
glBindBuffer(GL_UNIFORM_BUFFER, bufs[0]);
glBufferData(GL_UNIFORM_BUFFER, sizeof(red), &red, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, bufs[1]);
glBufferData(GL_UNIFORM_BUFFER, sizeof(green), &green, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, bufs[2]);
glBufferData(GL_UNIFORM_BUFFER, sizeof(blue[0]), blue, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, bufs[3]);
glBufferData(GL_UNIFORM_BUFFER, sizeof(blue[1]), blue+1, GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 4, bufs[0]);
glBindBufferBase(GL_UNIFORM_BUFFER, 5, bufs[1]);
glBindBufferBase(GL_UNIFORM_BUFFER, 6, bufs[2]);
glBindBufferBase(GL_UNIFORM_BUFFER, 7, bufs[3]);
glClearColor(0.1f,0.1f,0.1f,1);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
uint8_t data[4];
glReadPixels(160, 85, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data);
printf("output color: %u, %u, %u, %u\n", data[0], data[1], data[2], data[3]);
#define DIFF(x, y) ((x)-(y) < 0 ? (y) - (x) : (x) - (y))
assert(DIFF(data[0], 76) <= 1);
assert(DIFF(data[1], 127) <= 1);
assert(DIFF(data[2], 229) <= 1);
assert(data[3] == 255);
// Try binding to a negative binding point, should fail:
assert(!glGetError());
printf("The following compile should produce an error:\n");
CompileShader(GL_FRAGMENT_SHADER,
"#version 300 es\n"
"precision lowp float;\n"
"layout(binding = -1) uniform red { float r; };\n"
"out vec4 outColor;\n"
"void main() { outColor = vec4(r, r, r, 1.0); }");
assert(glGetError());
// Try binding to a too high binding point, should fail:
GLint bindingPoints = 0;
glGetIntegerv(GL_MAX_COMBINED_UNIFORM_BLOCKS, &bindingPoints);
assert(bindingPoints > 0);
assert(!glGetError());
char str[1024];
sprintf(str,
"#version 300 es\n"
"precision lowp float;\n"
"layout(binding = %d) uniform red { float r; };\n"
"out vec4 outColor;\n"
"void main() { outColor = vec4(r, r, r, 1.0); }", bindingPoints);
printf("The following compile should produce an error:\n");
CompileShader(GL_FRAGMENT_SHADER, str);
assert(glGetError());
assert(!glGetError());
// Test that glUseProgram(0) succeeds without errors.
glUseProgram(0);
assert(!glGetError());
printf("Test passed!\n");
#ifdef REPORT_RESULT
REPORT_RESULT(1);
#endif
}