blob: f9f15fc1eeb9fef46713e17c92e57490c604af69 [file] [log] [blame]
James Kuszmaul36816f32019-08-31 16:58:23 -07001#include <stdio.h>
2#include <stdlib.h>
3#include <assert.h>
4#include <cmath>
5#include <emscripten/emscripten.h>
6#include <emscripten/html5.h>
7#include <iostream>
8#include <unistd.h>
9#include <GLES3/gl3.h>
10
11namespace {
12constexpr int kNPoints = 10 * 1000 * 1000;
13} // namespace
14
15// Shader and program construction taken from examples at
16// https://github.com/emscripten-core/emscripten/blob/incoming/tests/webgl2_draw_packed_triangle.c
17GLuint compile_shader(GLenum shaderType, const char *src) {
18 GLuint shader = glCreateShader(shaderType);
19 glShaderSource(shader, 1, &src, NULL);
20 glCompileShader(shader);
21
22 GLint isCompiled = 0;
23 glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
24 if (!isCompiled) {
25 GLint maxLength = 0;
26 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
27 char *buf = (char *)malloc(maxLength + 1);
28 glGetShaderInfoLog(shader, maxLength, &maxLength, buf);
29 printf("%s\n", buf);
30 free(buf);
31 return 0;
32 }
33
34 return shader;
35}
36
37GLuint create_program(GLuint vertexShader, GLuint fragmentShader) {
38 GLuint program = glCreateProgram();
39 glAttachShader(program, vertexShader);
40 glAttachShader(program, fragmentShader);
41 glBindAttribLocation(program, 0, "apos");
42 glBindAttribLocation(program, 1, "acolor");
43 glLinkProgram(program);
44 return program;
45}
46
47struct Vector {
48 int x;
49 int y;
50 int z;
51 int w;
52};
53
54// Packs a vector for use with GL_INT_2_10_10_10_REV.
55uint32_t PackVector(const Vector &vec) {
56 uint32_t retval = 0;
57 retval = vec.w;
58 retval <<= 10;
59 retval |= vec.z & 0x3FF;
60 retval <<= 10;
61 retval |= vec.y & 0x3FF;
62 retval <<= 10;
63 retval |= vec.x & 0x3FF;
64 return retval;
65}
66
67struct AnimationState {
68 // The time, in seconds, at which the last animation frame occurred.
69 double last_animation_time = 0.0;
70 // The location for the "scale" uniform to modify on each animation call.
71 GLint scale_uniform_location;
72};
73
74// This function modifies the "scale" uniform to vary from 0.5->1.0->0.5
75// in a cycle, and redraws the points on each iteration.
76int Redraw(double time, void *data) {
77 AnimationState *state = reinterpret_cast<AnimationState*>(data);
78 time /= 1000.0;
79 const double difftime = time - state->last_animation_time;
80 const double wrap_time = std::fmod(time, 1.0);
81 const double offset = wrap_time > 0.5 ? 1.0 - wrap_time : wrap_time;
82 glUniform1f(state->scale_uniform_location, std::min(0.5 + offset, 100.0));
83 std::cout << 1.0 / difftime << "fps\n";
84 glDrawArrays(GL_POINTS, 0, kNPoints);
85 state->last_animation_time = time;
86 assert(glGetError() == GL_NO_ERROR && "glDrawArray failed");
87 return 1;
88}
89
90int main() {
91 EmscriptenWebGLContextAttributes attr;
92 emscripten_webgl_init_context_attributes(&attr);
93 attr.majorVersion = 2;
94 EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context("#canvas", &attr);
95 assert(ctx && "Failed to create WebGL2 context");
96 emscripten_webgl_make_context_current(ctx);
97
98 static const char vertex_shader[] =
99 "#version 100\n"
100 "attribute vec4 apos;"
101 "attribute vec4 acolor;"
102 "varying vec4 color;"
103 "uniform float scale;"
104 "void main() {"
105 "color = acolor;"
106 "gl_Position = apos;"
107 "gl_Position.x = apos.x * scale;"
108 "gl_Position.y = apos.y * 0.5 / scale;"
109 "gl_PointSize = 1.0;"
110 "}";
111 GLuint vs = compile_shader(GL_VERTEX_SHADER, vertex_shader);
112
113 static const char fragment_shader[] =
114 "#version 100\n"
115 "precision lowp float;"
116 "varying vec4 color;"
117 "void main() {"
118 "gl_FragColor = color;"
119 "}";
120 GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fragment_shader);
121
122 GLuint program = create_program(vs, fs);
123 glUseProgram(program);
124
125 // Go through and generate randomly located and colored points.
126 static uint32_t pos_and_color[kNPoints * 2];
127 for (int ii = 0; ii < kNPoints; ++ii) {
128 uint16_t w = 1;
129 uint16_t z = 0;
130 uint16_t y = rand() % 1024;
131 uint16_t x = rand() % 1024;
132 uint32_t wzyx = PackVector({x, y, z, w});
133 uint16_t a = 3;
134 uint16_t b = rand() % 1024;
135 uint16_t g = rand() % 1024;
136 uint16_t r = rand() % 1024;
137 uint32_t argb = PackVector({r, g, b, a});
138 pos_and_color[2 * ii] = wzyx;
139 pos_and_color[2 * ii + 1] = argb;
140 }
141
142 GLuint vbo;
143 glGenBuffers(1, &vbo);
144 glBindBuffer(GL_ARRAY_BUFFER, vbo);
145 glBufferData(GL_ARRAY_BUFFER, sizeof(pos_and_color), pos_and_color, GL_STATIC_DRAW);
146 glVertexAttribPointer(0, 4, GL_INT_2_10_10_10_REV, GL_TRUE, 8, 0);
147 assert(glGetError() == GL_NO_ERROR && "glVertexAttribPointer with GL_INT_2_10_10_10_REV failed");
148 glVertexAttribPointer(1, 4, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE, 8, (void*)4);
149 assert(glGetError() == GL_NO_ERROR && "glVertexAttribPointer with GL_UNSIGNED_INT_2_10_10_10_REV failed");
150
151 glEnableVertexAttribArray(0);
152 glEnableVertexAttribArray(1);
153
154 // Note that the animation_state must last until Redraw stops being called,
155 // which we cannot provide any bound on. As such, we don't currently destroy
156 // the memory until the webpage is closed.
157 AnimationState *animation_state = new AnimationState();
158 animation_state->scale_uniform_location =
159 glGetUniformLocation(program, "scale");
160 emscripten_request_animation_frame_loop(&Redraw, animation_state);
161}