summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralaric <alaric@netmythos.org>2024-03-30 01:00:07 -0700
committeralaric <alaric@netmythos.org>2024-03-30 01:00:07 -0700
commit17223eee868dfcd8f12bb2fd3b75acc0b2d0529a (patch)
treee1a5858fba61902e694ecb1e257f5cc7557feeab
parenta9f46fb682935d9637d4466dbb22aad2475859b7 (diff)
downloadcolordots-17223eee868dfcd8f12bb2fd3b75acc0b2d0529a.tar.gz
colordots-17223eee868dfcd8f12bb2fd3b75acc0b2d0529a.zip
Many colored dots
-rw-r--r--src/fragment.glsl7
-rw-r--r--src/matrix.zig2
-rw-r--r--src/shell.html4
-rw-r--r--src/vertex.glsl25
-rw-r--r--src/wasm_ttd.zig332
-rw-r--r--src/webgl.js14
-rw-r--r--src/webgl.zig8
7 files changed, 158 insertions, 234 deletions
diff --git a/src/fragment.glsl b/src/fragment.glsl
index 5df9a71..03d65f6 100644
--- a/src/fragment.glsl
+++ b/src/fragment.glsl
@@ -1,14 +1,11 @@
#version 300 es
precision highp float;
-uniform vec4 u_color;
-uniform sampler2D u_texture;
-
-in vec2 v_texcoord;
+in vec3 v_color;
out vec4 outColor;
void main() {
//outColor = texture(u_texture, v_texcoord * vec2(1, -1)) * u_color;
- outColor = u_color;
+ outColor = vec4(v_color, 1.0);
}
diff --git a/src/matrix.zig b/src/matrix.zig
index 5aff1dd..025af7c 100644
--- a/src/matrix.zig
+++ b/src/matrix.zig
@@ -1,9 +1,9 @@
pub fn Matrix(comptime T: type, rows: usize, cols: usize) type {
- const RowT = [rows]T;
const RowVec = @Vector(rows, T);
const ColVec = @Vector(cols, T);
return struct {
+ pub const RowT = [rows]T;
const Self = @This();
pub const identity: Self = blk: {
diff --git a/src/shell.html b/src/shell.html
index 0f4b598..e05b7e0 100644
--- a/src/shell.html
+++ b/src/shell.html
@@ -19,7 +19,11 @@
<body>
<canvas id="ttd-canvas" width="1280" height="720" tabindex="0"></canvas>
<script src="webgl.js"></script>
+ <script type="text/javascript" src="https://spectorcdn.babylonjs.com/spector.bundle.js"></script>
<script>
+ var spector = new SPECTOR.Spector();
+ spector.displayUI();
+ spector.spyCanvases();
let memory = null;
let last_frame_time = Date.now();
diff --git a/src/vertex.glsl b/src/vertex.glsl
index f3e9a1e..fcdcd5d 100644
--- a/src/vertex.glsl
+++ b/src/vertex.glsl
@@ -1,18 +1,31 @@
#version 300 es
-in vec2 a_position;
-in vec2 a_texcoord;
+in vec2 in_pos;
+in float color;
+in float scale;
+in vec2 translation;
uniform vec2 u_resolution;
-uniform mat3 u_matrix;
out vec2 v_texcoord;
+out vec3 v_color;
+
+// Function from IƱigo Quiles
+// https://www.shadertoy.com/view/MsS3Wc
+vec3 hsb2rgb( in vec3 c ){
+ vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
+ 6.0)-3.0)-1.0,
+ 0.0,
+ 1.0 );
+ rgb = rgb*rgb*(3.0-2.0*rgb);
+ return c.z * mix(vec3(1.0), rgb, c.y);
+}
void main() {
- vec2 position = (vec3(a_position, 1) * u_matrix).xy;
+ vec2 position = (in_pos * scale) + translation;
vec2 zeroToOne = position / u_resolution;
vec2 zeroToTwo = zeroToOne * 2.0;
vec2 clipSpace = zeroToTwo - 1.0;
- gl_Position = vec4(clipSpace * vec2(1, 1), 0, 1);
- v_texcoord = a_texcoord;
+ gl_Position = vec4(clipSpace, 0, 1);
+ v_color = hsb2rgb(vec3(color, 1.0, 1.0));
}
diff --git a/src/wasm_ttd.zig b/src/wasm_ttd.zig
index 03d5941..78b65e7 100644
--- a/src/wasm_ttd.zig
+++ b/src/wasm_ttd.zig
@@ -1,6 +1,8 @@
const std = @import("std");
const webgl = @import("webgl.zig");
+const Mat3 = @import("matrix.zig").Matrix(f32, 3, 3);
+
extern fn consoleLog(ptr: [*c]const u8, len: u32) void;
extern fn rand() f32;
extern fn getScreenWidth() f32;
@@ -27,48 +29,15 @@ const pi = 3.1415926535;
var prog: webgl.Program = undefined;
var pos_attr_location: i32 = undefined;
-var tex_attr_location: i32 = undefined;
+var col_attr_location: i32 = undefined;
+var translation_attr_location: i32 = undefined;
+var scale_attr_location: i32 = undefined;
var res_uniform_location: i32 = undefined;
-var color_uniform_location: i32 = undefined;
-var mat_uniform_location: i32 = undefined;
-var tex_uniform_location: i32 = undefined;
var pos_buffer: webgl.Buffer = undefined;
-var tex_buffer: webgl.Buffer = undefined;
-var tex_info: LoadTextureInfo = .{};
+var col_buffer: webgl.Buffer = undefined;
+var translation_buffer: webgl.Buffer = undefined;
+var scale_buffer: webgl.Buffer = undefined;
var vao: webgl.VertexArrayObject = undefined;
-var tex: webgl.Texture = undefined;
-
-const Texture = struct {
- tex: webgl.Texture,
- width: u32 = 0,
- height: u32 = 0,
- location: i32 = 0,
-
- fn draw(self: Texture, x: f32, y: f32) void {
- webgl.useProgram(prog);
- webgl.bindBuffer(.array_buffer, pos_buffer);
- webgl.bufferData(.array_buffer, &square_verts, square_verts.len, .dynamic_draw);
- webgl.uniform1i(tex_uniform_location, self.location);
-
- webgl.uniform2f(res_uniform_location, 1280, 720);
- webgl.uniform4f(color_uniform_location, Color.white.r, Color.white.g, Color.white.b, Color.white.a);
-
- const mat = Mat3.multiply(Mat3.scale(@floatFromInt(self.width), @floatFromInt(self.height)), Mat3.translation(.{ x, y }));
- webgl.uniformMatrix3fv(mat_uniform_location, &mat.data);
- webgl.drawArrays(.triangles, 0, 6);
- }
-};
-
-var jellybee: Texture = .{ .tex = undefined, .location = 1 };
-
-const square_verts = [_]f32{
- 0, 0,
- 1, 0,
- 0, 1,
- 0, 1,
- 1, 0,
- 1, 1,
-};
var timer: f32 = 0.0;
var rects: [128]Rectangle = undefined;
@@ -152,15 +121,14 @@ const Star = struct {
pdist: f32,
angle: f32,
initial_len: f32,
- col: Color,
+ hue: f32,
};
const max_dist = 100;
var rng: LinearCongruentialGenerator = undefined;
-const star_count = 1000;
+const star_count = 30000;
var stars: [star_count]Star = undefined;
-var accounted_for_degs: [360]bool = [1]bool{false} ** 360;
var right: u32 = 0;
var pright: u32 = 0;
@@ -171,13 +139,14 @@ var pup: u32 = 0;
var down: u32 = 0;
var pdown: u32 = 0;
+const circle = circlePoints(32);
+
export fn init() void {
const f_seed = rand() * (pow(f32, 2, 16) + 1);
const seed: u64 = @intFromFloat(f_seed);
rng = LinearCongruentialGenerator.ZX81(seed);
const width = getScreenWidth();
- _ = width;
const height = getScreenHeight();
for (&stars) |*s| {
@@ -185,69 +154,63 @@ export fn init() void {
s.initial_len = lerp(0, height / 2, rng.randFloat(f32));
s.dist = max_dist;
s.pdist = max_dist;
- s.col = .{
- .r = rng.randFloat(f32),
- .g = rng.randFloat(f32),
- .b = rng.randFloat(f32),
- };
+ s.hue = rng.randFloat(f32);
}
- //const msg = "Welcome to TBD";
- //consoleLog(msg.ptr, msg.len);
const vert = loadShader(.vertex_shader, vs_source) catch return;
const frag = loadShader(.fragment_shader, fs_source) catch return;
prog = createGLProgram(vert, frag) catch return;
webgl.useProgram(prog);
- const attr_name = "a_position";
+ const attr_name = "in_pos";
pos_attr_location = webgl.getAttribLocation(prog, attr_name.ptr, attr_name.len);
- res_uniform_location = getUniformLocation(prog, "u_resolution") catch unreachable;
- //tex_uniform_location = getUniformLocation(prog, "u_texture") catch unreachable;
- color_uniform_location = getUniformLocation(prog, "u_color") catch unreachable;
- mat_uniform_location = getUniformLocation(prog, "u_matrix") catch unreachable;
- //webgl.viewport(0, 0, @intFromFloat(width), @intFromFloat(height));
+ const col_attr = "color";
+ col_attr_location = webgl.getAttribLocation(prog, col_attr.ptr, col_attr.len);
+
+ const translation_attr = "translation";
+ translation_attr_location = webgl.getAttribLocation(
+ prog,
+ translation_attr.ptr,
+ translation_attr.len,
+ );
+
+ const scale_attr = "scale";
+ scale_attr_location = webgl.getAttribLocation(
+ prog,
+ scale_attr.ptr,
+ scale_attr.len,
+ );
+
+ res_uniform_location = getUniformLocation(prog, "u_resolution") catch unreachable;
+ webgl.uniform2f(res_uniform_location, width, height);
pos_buffer = webgl.createBuffer();
webgl.bindBuffer(.array_buffer, pos_buffer);
+ webgl.bufferData(.array_buffer, @ptrCast(&circle), circle.len * 2, .static_draw);
vao = webgl.createVertexArray();
webgl.bindVertexArray(vao);
webgl.enableVertexAttribArray(pos_attr_location);
webgl.vertexAttribPointer(@intCast(pos_attr_location), 2, .f32, .false, 0, 0);
- //for (&rects, &cols) |*r, *c| {
- // const x = lerp(100, 1180, rand());
- // const y = lerp(100, 620, rand());
- // const w = lerp(100, 200, rand());
- // const h = lerp(100, 200, rand());
- // r.pos = .{ x, y };
- // r.size = .{ w, h };
- // c.* = .{ rand(), rand(), rand() };
- //}
-
- webgl.activeTexture(webgl.texture_0);
- tex = webgl.createTexture();
- webgl.bindTexture(.texture_2d, tex);
-
- const px = [4]u8{ 255, 255, 255, 255 };
- webgl.texImage2D(.texture_2d, 0, .rgba, 1, 1, 0, .rgba, .u8, &px, px.len, 0);
-
- //webgl.activeTexture(webgl.texture_0 + 1);
- //jellybee.tex = webgl.createTexture();
- //webgl.bindTexture(.texture_2d, jellybee.tex);
+ col_buffer = webgl.createBuffer();
+ webgl.bindBuffer(.array_buffer, col_buffer);
+ webgl.enableVertexAttribArray(col_attr_location);
+ webgl.vertexAttribPointer(@intCast(col_attr_location), 1, .f32, .false, 0, 0);
+ webgl.vertexAttribDivisor(col_attr_location, 1);
- //webgl.texImage2D(.texture_2d, 0, .rgba, 1, 1, 0, .rgba, .u8, &px, px.len, 0);
- //const p = "assets/jellybee.png";
- //loadTexture(p.ptr, p.len, jellybee.tex, &tex_info);
+ scale_buffer = webgl.createBuffer();
+ webgl.bindBuffer(.array_buffer, scale_buffer);
+ webgl.enableVertexAttribArray(scale_attr_location);
+ webgl.vertexAttribPointer(@intCast(scale_attr_location), 1, .f32, .false, 0, 0);
+ webgl.vertexAttribDivisor(scale_attr_location, 1);
- //const t_attr = "a_texcoord";
- //tex_attr_location = webgl.getAttribLocation(prog, t_attr.ptr, t_attr.len);
- //tex_buffer = webgl.createBuffer();
- //webgl.bindBuffer(.array_buffer, tex_buffer);
- //webgl.bufferData(.array_buffer, &square_verts, square_verts.len, .static_draw);
- //webgl.enableVertexAttribArray(tex_attr_location);
- //webgl.vertexAttribPointer(@intCast(tex_attr_location), 2, .f32, .true, 0, 0);
+ translation_buffer = webgl.createBuffer();
+ webgl.bindBuffer(.array_buffer, translation_buffer);
+ webgl.enableVertexAttribArray(translation_attr_location);
+ webgl.vertexAttribPointer(@intCast(translation_attr_location), 2, .f32, .false, 0, 0);
+ webgl.vertexAttribDivisor(translation_attr_location, 1);
registerKey(.up, &up);
registerKey(.right, &right);
@@ -278,7 +241,40 @@ fn rad2deg(rads: f32) f32 {
var frame_counter: u32 = 0;
var speed: f32 = 0.1;
-var sub_count: u32 = star_count / 2;
+var sub_count: u32 = star_count;
+
+const batch_capacity = 90000;
+var translation_data: [batch_capacity][2]f32 = undefined;
+var scale_data: [batch_capacity]f32 = undefined;
+var color_data: [batch_capacity]f32 = undefined;
+
+const DrawBatch = struct {
+ translation: [][2]f32,
+ scales: []f32,
+ colors: []f32,
+ len: u32 = 0,
+
+ fn addInstance(self: *DrawBatch, translation: [2]f32, hue: f32, scale: f32) !void {
+ if (self.len == self.translation.len or self.len == self.colors.len or self.len == self.scales.len) return error.OutOfSpace;
+
+ self.translation[self.len] = translation;
+ self.colors[self.len] = hue;
+ self.scales[self.len] = scale;
+ self.len += 1;
+ }
+
+ fn draw(self: DrawBatch) void {
+ webgl.bindBuffer(.array_buffer, translation_buffer);
+ webgl.bufferData(.array_buffer, @ptrCast(self.translation), self.len * 2, .dynamic_draw);
+
+ webgl.bindBuffer(.array_buffer, scale_buffer);
+ webgl.bufferData(.array_buffer, @ptrCast(self.scales), self.len, .dynamic_draw);
+
+ webgl.bindBuffer(.array_buffer, col_buffer);
+ webgl.bufferData(.array_buffer, @ptrCast(self.colors), self.len, .dynamic_draw);
+ webgl.drawArraysInstanced(.triangles, 0, circle.len, @intCast(self.len));
+ }
+};
export fn update(elapsed_time: f32) void {
const report_freq = 100;
@@ -288,19 +284,6 @@ export fn update(elapsed_time: f32) void {
const avg = timer / report_freq;
formatLog("{d:.2} Average FPS", .{1.0 / avg});
timer = 0;
-
- for (&stars) |s| {
- const deg = @round(rad2deg(s.angle));
- const i: u32 = @intFromFloat(@min(deg, 359));
- accounted_for_degs[i] = true;
- }
-
- var count: u32 = 0;
- for (&accounted_for_degs) |a| {
- if (!a) count += 1;
- }
-
- formatLog("{d} unseen degrees", .{count});
}
//while (timer >= 2 * pi) {
// timer -= 2 * pi;
@@ -308,8 +291,6 @@ export fn update(elapsed_time: f32) void {
const right_pressed = right != 0 and pright == 0;
const left_pressed = left != 0 and pleft == 0;
- const up_pressed = up != 0 and pup == 0;
- const down_pressed = down != 0 and pdown == 0;
if (right_pressed) {
speed += 0.1;
@@ -319,13 +300,13 @@ export fn update(elapsed_time: f32) void {
}
speed = @min(1.0, @max(speed, -1.0));
- if (up_pressed) {
+ if (up != 0) {
if (sub_count < star_count) {
sub_count += 100;
}
}
- if (down_pressed) {
+ if (down != 0) {
if (sub_count > 0) {
sub_count -= 100;
}
@@ -337,11 +318,19 @@ export fn update(elapsed_time: f32) void {
pdown = down;
webgl.viewport(0, 0, 1280, 720);
+ webgl.useProgram(prog);
+ webgl.bindVertexArray(vao);
webgl.uniform2f(res_uniform_location, 1280, 720);
- webgl.clearColor(0.1, 0.1, 0.1, 1.0);
+ webgl.clearColor(0.0, 0.0, 0.0, 1.0);
webgl.clear(webgl.color_buffer_bit);
+ var batch: DrawBatch = .{
+ .translation = &translation_data,
+ .colors = &color_data,
+ .scales = &scale_data,
+ };
+
const width = getScreenWidth();
const height = getScreenHeight();
for (stars[0..sub_count]) |*s| {
@@ -349,15 +338,20 @@ export fn update(elapsed_time: f32) void {
s.dist -= speed;
const plen = lerp(1000, s.initial_len, s.pdist / max_dist);
const psx = (@cos(s.angle) * plen) + width * 0.5;
+ _ = psx;
const psy = (@sin(s.angle) * plen) + height * 0.5;
+ _ = psy;
const len = lerp(1000, s.initial_len, s.dist / max_dist);
const sx = (@cos(s.angle) * len) + width * 0.5;
const sy = (@sin(s.angle) * len) + height * 0.5;
const scale = lerp(8, 0, s.dist / max_dist);
- drawLine(&.{ .{ psx, psy }, .{ sx, sy } }, s.col);
- drawCircle(.{ sx, sy }, scale, s.col);
+ batch.addInstance(.{ sx, sy }, s.hue, scale) catch unreachable;
+ if (batch.len == batch_capacity) {
+ batch.draw();
+ batch.len = 0;
+ }
if (sx < 0 or sx > 1280 or sy < 0 or sy > height) {
s.angle = lerp(0, 2 * pi, rng.randFloat(f32));
@@ -366,9 +360,9 @@ export fn update(elapsed_time: f32) void {
s.pdist = max_dist;
}
}
-}
-const Mat3 = @import("matrix.zig").Matrix(f32, 3, 3);
+ batch.draw();
+}
fn lerp(a: f32, b: f32, v: f32) f32 {
return a + (b - a) * v;
@@ -397,23 +391,9 @@ const Color = struct {
const white: Color = .{ .r = 1.0, .g = 1.0, .b = 1.0, .a = 1.0 };
};
-fn setDrawColor(col: Color) void {
- webgl.uniform4f(color_uniform_location, col.r, col.g, col.b, col.a);
-}
-
-fn drawCircle(pos: @Vector(2, f32), radius: f32, col: Color) void {
- webgl.uniform4f(color_uniform_location, col.r, col.g, col.b, col.a);
- webgl.uniform1i(tex_uniform_location, 0);
- const segments = 32;
+fn circlePoints(comptime segments: u32) [segments * 3][2]f32 {
const segment_rads = (2 * pi) / @as(comptime_float, segments);
-
- const mat = Mat3.multiply(Mat3.scale(radius, radius), Mat3.translation(pos));
- webgl.uniformMatrix3fv(mat_uniform_location, &mat.data);
- const center: @Vector(2, f32) = @splat(0);
-
- webgl.bindBuffer(.array_buffer, pos_buffer);
- var points: [segments * 3][2]f32 = undefined;
- var tris: TriangleGroup = .{ .points = &points };
+ var r: [segments * 3][2]f32 = undefined;
for (0..segments) |s| {
const sf: f32 = @floatFromInt(s);
const start_angle = segment_rads * sf;
@@ -426,107 +406,15 @@ fn drawCircle(pos: @Vector(2, f32), radius: f32, col: Color) void {
const end_y = @sin(end_angle);
const end: @Vector(2, f32) = .{ end_x, end_y };
- tris.addTriangle(&.{ center, start, end }) catch unreachable;
- }
- tris.draw();
-}
-
-const TriangleGroup = struct {
- points: [][2]f32 = &.{},
- len: usize = 0,
-
- fn addTriangle(self: *TriangleGroup, points: *const [3][2]f32) !void {
- if (self.len + 3 > self.points.len) return error.NoMoreSpace;
-
- @memcpy(self.points[self.len .. self.len + 3], points);
- self.len += 3;
- }
-
- fn draw(self: *const TriangleGroup) void {
- webgl.bufferData(.array_buffer, @ptrCast(self.points), self.len * 3, .dynamic_draw);
-
- webgl.drawArrays(.triangles, 0, @intCast(self.len));
- }
-};
-
-fn drawLine(points: *const [2][2]f32, col: Color) void {
- setDrawColor(col);
- webgl.uniformMatrix3fv(mat_uniform_location, &Mat3.identity.data);
- webgl.bufferData(.array_buffer, @ptrCast(points), 4, .dynamic_draw);
-
- webgl.drawArrays(.lines, 0, 2);
-}
-
-fn drawTri(points: *const [3][2]f32) void {
- //webgl.useProgram(prog);
- //webgl.uniform1i(tex_uniform_location, 0);
- webgl.bufferData(.array_buffer, @ptrCast(points), 6, .dynamic_draw);
-
- webgl.drawArrays(.triangles, 0, 3);
-}
-
-fn drawRectangle(rec: Rectangle, col: ?Color) void {
- webgl.uniform1i(tex_uniform_location, 0);
- if (col) |c| {
- setDrawColor(c);
+ const i = s * 3;
+ r[i] = @Vector(2, f32){ 0, 0 };
+ r[i + 1] = start;
+ r[i + 2] = end;
}
- webgl.useProgram(prog);
- webgl.bindBuffer(.array_buffer, pos_buffer);
- webgl.bufferData(.array_buffer, &square_verts, square_verts.len, .dynamic_draw);
- webgl.uniform2f(res_uniform_location, 1280, 720);
-
- const mat = Mat3.multiply(Mat3.scale(rec.size[0], rec.size[1]), Mat3.translation(rec.pos));
- webgl.uniformMatrix3fv(mat_uniform_location, &mat.data);
- webgl.drawArrays(.triangles, 0, 6);
+ return r;
}
-fn drawRectangleLines(rec: Rectangle, col: ?Color) void {
- if (col) |c| {
- setDrawColor(c);
- }
- const t: Rectangle = .{
- .pos = rec.pos + @Vector(2, f32){ 0, rec.size[1] },
- .size = .{ rec.size[0], 1 },
- };
- const b: Rectangle = .{ .pos = rec.pos, .size = .{ rec.size[0], 1 } };
- const l: Rectangle = .{ .pos = rec.pos, .size = .{ 1, rec.size[1] } };
- const r: Rectangle = .{
- .pos = rec.pos + @Vector(2, f32){ rec.size[0], 0 },
- .size = .{ 1, rec.size[1] },
- };
- drawRectangle(t, null);
- drawRectangle(b, null);
- drawRectangle(l, null);
- drawRectangle(r, null);
-}
-
-const f_vertices = [_]f32{
- // left column
- 0, 0,
- 30, 0,
- 0, 150,
- 0, 150,
- 30, 0,
- 30, 150,
-
- // top rung
- 30, 0,
- 100, 0,
- 30, 30,
- 30, 30,
- 100, 0,
- 100, 30,
-
- // middle rung
- 30, 60,
- 67, 60,
- 30, 90,
- 30, 90,
- 67, 60,
- 67, 90,
-};
-
fn getUniformLocation(program: webgl.Program, name: []const u8) !i32 {
const loc = webgl.getUniformLocation(program, name.ptr, name.len);
if (loc < 0) return error.FailedToGetLocation;
diff --git a/src/webgl.js b/src/webgl.js
index cce477a..7df5be8 100644
--- a/src/webgl.js
+++ b/src/webgl.js
@@ -39,6 +39,9 @@ const bindBuffer = (target, id) => {
const bufferData = (target, ptr, len, usage) => {
const floats = new Float32Array(memory.buffer, ptr, len);
+ if (len === 9) {
+ console.log(floats);
+ }
gfx_ctx.bufferData(target, floats, usage);
};
@@ -192,6 +195,14 @@ const activeTexture = (tex_id) => {
gfx_ctx.activeTexture(tex_id);
};
+const vertexAttribDivisor = (loc, divisor) => {
+ gfx_ctx.vertexAttribDivisor(loc, divisor);
+};
+
+const drawArraysInstanced = (mode, first, count, instanceCount) => {
+ gfx_ctx.drawArraysInstanced(mode, first, count, instanceCount);
+};
+
const getScreenWidth = () => {
return gfx_ctx.drawingBufferWidth;
};
@@ -241,6 +252,9 @@ const webgl = {
texImage2D,
activeTexture,
+ vertexAttribDivisor,
+ drawArraysInstanced,
+
getScreenWidth,
getScreenHeight,
};
diff --git a/src/webgl.zig b/src/webgl.zig
index a3e1ec3..4a1658d 100644
--- a/src/webgl.zig
+++ b/src/webgl.zig
@@ -141,3 +141,11 @@ pub extern fn texImage2D(
count: u32,
offset: u32,
) void;
+
+pub extern fn vertexAttribDivisor(loc: i32, divisor: i32) void;
+pub extern fn drawArraysInstanced(
+ mode: GLDrawMode,
+ first: i32,
+ count: i32,
+ instace_count: i32,
+) void;