commit 09f7cdd7434f94d62d635c9d386d1ad2e4510ee7
parent e3e6a1a07cfe80b68f2e35f41dc86ab7c08af43b
Author: amin <dev@aminmesbah.com>
Date: Thu, 20 Jun 2019 07:06:02 +0000
Get wasm build compiling and partially running
FossilOrigin-Name: fc698c85303b25f95b9c642849eaee8721b25ab3c74332bd83b8cd41afd4db1e
Diffstat:
12 files changed, 451 insertions(+), 47 deletions(-)
diff --git a/build_wasm.sh b/build_wasm.sh
@@ -1,11 +1,23 @@
+
mkdir -p ./out/wasm
+cat js/globals.js js/utils.js js/imports.js js/loader.js > ./out/wasm/script.js
+cp index.html ./out/wasm/index.html
+cp -r shader/ ./out/wasm/
+
clang -cc1 -Ofast -emit-llvm-bc -triple=wasm32-unknown-unknown-unknown-wasm -std=c11 \
+ -ffreestanding \
+ -fno-builtin \
-DGAME_WEBGL \
- src/game.c
+ src/platform_wasm.c
llvm-link -o wasm.bc src/*.bc
opt -O3 -disable-simplify-libcalls wasm.bc -o wasm.bc
llc -O3 -disable-simplify-libcalls -filetype=obj wasm.bc -o wasm.o
-wasm-ld --no-entry wasm.o -o binary.wasm --strip-all -allow-undefined-file wasm_js_implemented_symbols.txt --import-memory
+
+wasm-ld --no-entry wasm.o \
+ -o ./out/wasm/binary.wasm \
+ -allow-undefined-file wasm_js_implemented_symbols.txt \
+ --export-all \
+ --import-memory
#clang \
# --target=wasm32 \
diff --git a/index.html b/index.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>WASM Demo</title>
+<meta name=viewport content='width=device-width,initial-scale=1' charset='utf-8'>
+
+<style>
+body{
+ margin: 0;
+ overflow: hidden;
+}
+#mcanvas{
+ width: 100vw;
+ height: 100vh;
+}
+</style>
+
+<canvas id=mcanvas></canvas>
+<script src='script.js' defer></script>
diff --git a/js/globals.js b/js/globals.js
@@ -0,0 +1,8 @@
+let utf8decoder = new TextDecoder("utf-8");
+let memory = null;
+let exports = {};
+let imports = {};
+let files = [];
+let gl = null;
+let gl_id_freelist = [];
+let gl_id_map = [null];
diff --git a/js/imports.js b/js/imports.js
@@ -0,0 +1,165 @@
+imports["webglAttachShader"] = function(program_id, shader_id) {
+ let program = gl_id_map[program_id];
+ let shader = gl_id_map[shader_id];
+ gl.attachShader(program, shader);
+}
+imports["webglBindBuffer"] = function(target, buffer_id) {
+ let buffer = gl_id_map[buffer_id];
+ gl.bindBuffer(target, buffer);
+}
+imports["webglBindVertexArray"] = function(vao_id) {
+ let vao = gl_id_map[vao_id];
+ gl.bindVertexArray(vao);
+}
+imports["webglBlendColor"] = function(red, green, blue, alpha) {
+ gl.blendColor(red, green, blue, alpha);
+}
+imports["webglBlendFunc"] = function(sfactor, dfactor) {
+ gl.blendFunc(sfactor, dfactor);
+}
+imports["webglBufferData"] = function(target, size, data, usage) {
+ let dataslice = memory.subarray(data, data+length);
+ gl.bufferData(target, dataslice, usage);
+}
+imports["webglClear"] = function(mask) {
+ gl.clear(mask);
+}
+imports["webglClearColor"] = function(red, green, blue, alpha) {
+ gl.clearColor(red, green, blue, alpha);
+}
+imports["webglCompileShader"] = function(shader_id) {
+ let shader = gl_id_map[shader_id];
+ gl.compileShader(shader);
+}
+imports["webglCreateBuffer"] = function() {
+ let buffer = gl.createBuffer();
+ let buffer_id = webgl_id_new(buffer);
+ return buffer_id
+}
+imports["webglCreateProgram"] = function() {
+ let program = gl.createProgram();
+ let program_id = webgl_id_new(program);
+ return program_id;
+}
+imports["webglCreateShader"] = function(type) {
+ let shader = gl.createShader(type);
+ let shader_id = webgl_id_new(shader);
+ return shader_id;
+}
+imports["webglCreateVertexArray"] = function() {
+ let vao = gl.createVertexArray()
+ let vao_id = webgl_id_new(vao);
+ return vao_id
+}
+imports["webglDeleteBuffer"] = function(buffer_id) {
+ let buffer = gl_id_map[buffer_id];
+ gl.deleteBuffer(buffer);
+ webgl_id_remove(buffer_id);
+}
+imports["webglDeleteShader"] = function(shader_id) {
+ let shader = gl_id_map[shader_id];
+ gl.deleteShader(shader);
+ webgl_id_remove(shader_id);
+}
+imports["webglDeleteVertexArray"] = function(vao_id) {
+ let vao = gl_id_map[vao_id];
+ gl.deleteVertexArray(vao);
+ webgl_id_remove(vao_id);
+}
+imports["webglDepthMask"] = function(flag) {
+ gl.depthMask(flag);
+}
+imports["webglDisable"] = function(cap) {
+ gl.disable(cap);
+}
+imports["webglDrawElements"] = function(mode, count, type, offset) {
+ gl.drawElements(mode, count, type, offset);
+}
+imports["webglEnable"] = function(cap) {
+ gl.enable(cap);
+}
+imports["webglEnableVertexAttribArray"] = function(index) {
+ gl.enableVertexAttribArray(index);
+}
+imports["webglGetProgramInfoLog"] = function() {
+}
+imports["webglGetProgramParameter"] = function(program_id, param) {
+ let program = gl_id_map[program_id];
+ return gl.getProgramParameter(program, param);
+}
+imports["webglGetShaderInfoLog"] = function(shader_id, out_buf) {
+ let shader = gl_id_map[shader_id];
+ let info_log = gl.getShaderInfoLog(shader);
+
+ // TODO: remove this once we get sprintf workingk
+ console.log(info_log);
+
+ let arr = memory.subarray(info_log, out_buf + info_log.byteLength);
+ arr.set(new Uint8Array(info_log));
+ return true;
+}
+imports["webglGetShaderParameter"] = function(shader_id, param) {
+ let shader = gl_id_map[shader_id];
+ let result = gl.getShaderParameter(shader, param);
+ return result;
+}
+imports["webglGetUniformLocation"] = function(program_id, name_ptr, name_len) {
+ let program = gl_id_map[program_id];
+ let name = utf8decoder.decode(memory.subarray(name_ptr, name_ptr+name_len));
+ let loc = gl.getUniformLocation(program, name);
+ let location_id = webgl_id_new(loc);
+ return location_id;
+}
+imports["webglLinkProgram"] = function(program_id) {
+ let program = gl_id_map[program_id];
+ gl.linkProgram(program);
+}
+imports["webglShaderSource"] = function(shader_id, source_ptr, src_len) {
+ let shader = gl_id_map[shader_id];
+ let s = buf2str(source_ptr, src_len);
+ gl.shaderSource(shader, s);
+}
+imports["webglUniform1f"] = function(location_id, value) {
+ let loc = gl_id_map[location_id];
+ gl['uniform1i'](loc, value);
+}
+imports["webglUniform1i"] = function(location_id, value) {
+ let loc = gl_id_map[location_id];
+ gl['uniform1i'](loc, value);
+}
+imports["webglUniform3f"] = function(location_id, x, y, z) {
+ let loc = gl_id_map[location_id];
+ gl['uniform3fv'](loc, [x, y, z]);
+}
+imports["webglUniformMatrix4fv"] = function(location_id, data) {
+ let loc = gl_id_map[location_id];
+ let dataslice = memory.slice(data, data + 4 * 16);
+ gl.uniformMatrix4fv(loc, false, new Float32Array(dataslice.buffer));
+}
+imports["webglUseProgram"] = function(program_id) {
+ let program = gl_id_map[program_id];
+ gl.useProgram(program);
+}
+imports["webglVertexAttribPointer"] = function(index, size, type, normalized, stride, offset) {
+ gl.vertexAttribPointer(index, size, type, normalized, stride, offset);
+}
+
+imports["js_read_entire_file"] = function(name, name_len, out_buf) {
+ let file_name = utf8decoder.decode(memory.subarray(name, name + name_len))
+ if (file_name == "shader/cube_f.glsl") {
+ var file = files[1];
+ } else if (file_name == "shader/cube_v.glsl") {
+ var file = files[2];
+ } else {
+ return false;
+ }
+ let arr = memory.subarray(out_buf, out_buf + file.byteLength);
+ let s = String.fromCharCode.apply(null, arr);
+ arr.set(new Uint8Array(file));
+ return true;
+}
+
+imports["js_print"] = function(s, len) {
+ let arr = memory.subarray(s, s + len);
+ console.log(utf8decoder.decode(arr));
+}
diff --git a/js/loader.js b/js/loader.js
@@ -0,0 +1,62 @@
+function canvas_resize() {
+ let pr = window.devicePixelRatio;
+ let w = window.innerWidth;
+ let h = window.innerHeight;
+ gl.canvas.width = (w * pr) | 0;
+ gl.canvas.height = (h * pr) | 0;
+ exports['window_resize']((w * pr) | 0, (h * pr) | 0);
+ console.log("resize: [" + (w * pr | 0) + ", " + (h * pr | 0) + "] > [" + (w | 0) + ", " + (h | 0) + "]");
+}
+
+function canvas_render() {
+ exports['render']();
+ window.requestAnimationFrame(canvas_render);
+}
+
+function file_load(name) {
+ let promise = new Promise((ok, nope) => {
+ fetch(name).then(resp => {
+ resp.arrayBuffer().then(arr => ok(arr));
+ });
+ });
+ return promise;
+}
+
+window.onload = async function() {
+ let ctxopts = {
+ alpha: false,
+ depth: true,
+ stencil: false,
+ antialias: true,
+ preserveDrawingBuffer: false
+ };
+
+ gl = document.getElementById("mcanvas").getContext("webgl2", ctxopts);
+ if(!gl) {
+ error_fatal("Your browser does not support WebGL 2");
+ }
+
+ files[0] = file_load("binary.wasm");
+ files[1] = file_load("shader/cube_f.glsl");
+ files[2] = file_load("shader/cube_v.glsl");
+ for(var i=0; i<files.length; i++) {
+ files[i] = await files[i];
+ }
+
+ let binary = files[0];
+ imports['memory'] = new WebAssembly['Memory']({'initial':32});
+ memory = new Uint8Array(imports['memory']['buffer']);
+ let program = await WebAssembly['instantiate'](binary, {"env":imports});
+
+ let instance = program['instance'];
+ exports = instance['exports'];
+
+ canvas_resize();
+ window.addEventListener("resize", canvas_resize);
+
+ if(!exports['init']()) {
+ error_fatal("Engine initialization failed");
+ }
+
+ canvas_render();
+}
diff --git a/js/utils.js b/js/utils.js
@@ -0,0 +1,28 @@
+function buf2str(ptr, len) {
+ let arr = memory.subarray(ptr, ptr + len);
+ return utf8decoder.decode(arr);
+}
+
+function error_fatal(message) {
+ console.log(message);
+ throw message;
+}
+
+function webgl_id_new(obj) {
+ if(gl_id_freelist.length == 0)
+ {
+ gl_id_map.push(obj);
+ return gl_id_map.length - 1;
+ }
+ else
+ {
+ let id = gl_id_freelist.shift();
+ gl_id_map[id] = obj;
+ return id;
+ }
+}
+
+function webgl_id_remove(id){
+ delete gl_id_map[id];
+ gl_id_freelist.push(id);
+}
diff --git a/src/game.c b/src/game.c
@@ -106,6 +106,7 @@ void game_update_and_render(struct GameState *game_state, float dt, u32 screen_w
shader_use(&game_state->cube_shader);
shader_setm4(&game_state->cube_shader, "view", &view);
+ print("boooooooork");
shader_setm4(&game_state->cube_shader, "projection", &projection);
m4 model = glmth_m4_init_id();
diff --git a/src/game.h b/src/game.h
@@ -1,30 +1,39 @@
#pragma once
-// TODO: Clean up
-#ifndef __has_builtin
-#define __has_builtin(x) 0
-#endif
-
#include <stddef.h>
#include <stdint.h>
#define static_assert _Static_assert
-static_assert(__has_builtin(__builtin_cosf), "cosf");
-static_assert(__has_builtin(__builtin_sinf), "sinf");
-static_assert(__has_builtin(__builtin_tanf), "tanf");
-static_assert(__has_builtin(__builtin_sqrtf), "sqrtf");
-static_assert(__has_builtin(__builtin_powf), "powf");
-#define cosf __builtin_cosf
-#define sinf __builtin_sinf
-#define tanf __builtin_tanf
-#define sqrtf __builtin_sqrtf
-#define powf __builtin_powf
-
#define NULL ((void*)0)
// TODO: fix this
#define assert(x) (void)0
+inline float sinf(float a)
+{
+ return a;
+}
+
+inline float cosf(float a)
+{
+ return a;
+}
+
+inline float powf(float x, float p)
+{
+ return x;
+}
+
+inline float sqrtf(float a)
+{
+ return a;
+}
+
+inline float tanf(float a)
+{
+ return a;
+}
+
typedef _Bool bool;
#define true 1
#define false 0
diff --git a/src/platform_wasm.c b/src/platform_wasm.c
@@ -0,0 +1,87 @@
+#include "game.c"
+#include "platform.h"
+#include "platform_wasm.h"
+
+#define export __attribute__( ( visibility( "default" ) ) )
+
+char *js_read_entire_file(i32 file_path, i32 name_len, char *file_data);
+int js_print(i32 string, i32 len);
+
+struct GameState g_game_state = {0};
+i32 g_width = PLATFORM_SCR_WIDTH;
+i32 g_height = PLATFORM_SCR_HEIGHT;
+char g_mem_buffer[1000] = {0};
+i32 g_mem_buffer_i = 0;
+
+export bool init(void)
+{
+ struct GameState g_game_state = {0};
+ g_game_state.platform.platform_read_entire_file = &wasm_read_entire_file;
+ g_game_state.platform.platform_print = &wasm_print;
+ g_game_state.platform.platform_memory_free = &wasm_memory_free;
+ game_init(&g_game_state, g_width, g_height);
+ return true;
+}
+
+export void render(void)
+{
+ game_update_and_render(&g_game_state, 16, g_width, g_height);
+}
+
+export void window_resize(int w, int h)
+{
+ g_width = w;
+ g_height = h;
+}
+
+i32 str_length(const char *str)
+{
+ i32 i = 0;
+ while(str[i] != '\0')
+ {
+ i++;
+ }
+ return i;
+}
+
+void *memset(void *s, int c, size_t n)
+{
+ u8 *p = s;
+ while(n > 0)
+ {
+ *p = (u8)c;
+ p++;
+ n--;
+ }
+ return s;
+}
+
+PLATFORM_READ_ENTIRE_FILE(wasm_read_entire_file)
+{
+ g_mem_buffer_i++;
+ char *buf = &g_mem_buffer[g_mem_buffer_i];
+ if(js_read_entire_file((i32)file_path, str_length(file_path), buf))
+ {
+ i32 file_data_length = str_length(buf);
+ g_mem_buffer_i += file_data_length;
+ g_mem_buffer[g_mem_buffer_i] = '\0';
+ wasm_print("Succeeded in reading file.");
+ }
+ else
+ {
+ wasm_print("Failed to read file.");
+ }
+ return buf;
+}
+
+PLATFORM_PRINT(wasm_print)
+{
+ i32 len = str_length(format);
+ js_print((i32)format, len);
+ return 0;
+}
+
+PLATFORM_MEMORY_FREE(wasm_memory_free)
+{
+ g_mem_buffer_i = 0;
+}
diff --git a/src/platform_wasm.h b/src/platform_wasm.h
@@ -0,0 +1,6 @@
+#define PLATFORM_SCR_WIDTH 600
+#define PLATFORM_SCR_HEIGHT 600
+
+PLATFORM_READ_ENTIRE_FILE(wasm_read_entire_file);
+PLATFORM_PRINT(wasm_print);
+PLATFORM_MEMORY_FREE(wasm_memory_free);
diff --git a/src/webgl.h b/src/webgl.h
@@ -6,7 +6,7 @@ typedef double f64;
// NOTE(amin): Since these functions will be implemented in javascript, we can
// only use i32, f32, and f64 params.
-void webglAttachShader(i32 shader, i32 program);
+void webglAttachShader(i32 program, i32 shader);
void webglBindBuffer(i32 target, i32 buffer);
void webglBindVertexArray(i32 vao);
void webglBlendColor(f32 r, f32 g, f32 b, f32 a);
@@ -15,25 +15,25 @@ void webglBufferData(i32 target, i32 size, i32 data, i32 usage);
void webglClear(i32 mask);
void webglClearColor(f32 r, f32 g, f32 b, f32 a);
void webglCompileShader(i32 shader);
+i32 webglCreateBuffer(void);
i32 webglCreateProgram(void);
i32 webglCreateShader(i32 type);
-void webglDeleteBuffers(i32 bo);
+i32 webglCreateVertexArray(void);
+void webglDeleteBuffer(i32 bo);
void webglDeleteShader(i32 shader);
-void webglDeleteVertexArrays(i32 vao);
-void webglDepthMask(i32 b);
+void webglDeleteVertexArray(i32 vao);
+void webglDepthMask(i32 flag);
void webglDisable(i32 cap);
void webglDrawElements(i32 mode, i32 count, i32 type, i32 offset);
void webglEnable(i32 cap);
void webglEnableVertexAttribArray(i32 index);
-void webglGenBuffers(i32 n, i32 buffers);
-void webglGenVertexArrays(i32 n, i32 arrays);
void webglGetProgramInfoLog(void);
-int webglGetProgramiv(i32 program, i32 param);
-void webglGetShaderInfoLog(void);
-int webglGetShaderiv(i32 shader, i32 param);
+int webglGetProgramParameter(i32 program, i32 param);
+void webglGetShaderInfoLog(i32 shader, char *out_buf);
+int webglGetShaderParameter(i32 shader, i32 param);
i32 webglGetUniformLocation(i32 program, const char name[static 1], i32 name_len);
void webglLinkProgram(i32 program);
-void webglShaderSource(i32 shader, i32 count, const char source[static 1], i32 source_len);
+void webglShaderSource(i32 shader, const char source[static 1], i32 source_len);
void webglUniform1f(i32 location, f32 value);
void webglUniform1i(i32 location, i32 value);
void webglUniform3f(i32 location, f32 x, f32 y, f32 z);
@@ -141,9 +141,9 @@ inline GLuint glCreateShader(GLenum type)
inline void glDeleteBuffers(GLsizei n, const GLuint *buffers)
{
- // TODO: assert n == 1
+ assert(n == 1);
i32 the_buffer = WEBGL_CAST_I32(buffers[0]);
- webglDeleteBuffers(the_buffer);
+ webglDeleteBuffer(the_buffer);
}
inline void glDeleteShader(GLuint shader)
@@ -153,9 +153,9 @@ inline void glDeleteShader(GLuint shader)
inline void glDeleteVertexArrays(GLsizei n, const GLuint *arrays)
{
- // TODO: assert n == 1
+ assert(n == 1);
i32 the_array = WEBGL_CAST_I32(arrays[0]);
- webglDeleteVertexArrays(the_array);
+ webglDeleteVertexArray(the_array);
}
inline void glDepthMask(GLboolean flag)
@@ -185,12 +185,18 @@ inline void glEnableVertexAttribArray(GLuint index)
inline void glGenBuffers(GLsizei n, GLuint *buffers)
{
- webglGenBuffers(WEBGL_CAST_I32(n), WEBGL_CAST_I32(*buffers));
+ assert(n == 1);
+ i32 buffer_id = webglCreateBuffer();
+ assert(buffer_id >= 0);
+ *buffers = (GLuint)buffer_id;
}
inline void glGenVertexArrays(GLsizei n, GLuint *arrays)
{
- webglGenVertexArrays(WEBGL_CAST_I32(n), WEBGL_CAST_I32(*arrays));
+ assert(n == 1);
+ i32 vao_id = webglCreateVertexArray();
+ assert(vao_id >= 0);
+ *arrays = (GLuint)vao_id;
}
inline void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
@@ -201,18 +207,17 @@ inline void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length
inline void glGetProgramiv(GLuint program, GLenum pname, GLint *params)
{
- *params = webglGetProgramiv(WEBGL_CAST_I32(program), WEBGL_CAST_I32(pname));
+ *params = webglGetProgramParameter(WEBGL_CAST_I32(program), WEBGL_CAST_I32(pname));
}
inline void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
{
- // TODO: implement
- //webglGetShaderInfoLog
+ webglGetShaderInfoLog(WEBGL_CAST_I32(shader), infoLog);
}
inline void glGetShaderiv(GLuint shader, GLenum pname, GLint *params)
{
- *params = webglGetShaderiv(WEBGL_CAST_I32(shader), WEBGL_CAST_I32(pname));
+ *params = webglGetShaderParameter(WEBGL_CAST_I32(shader), WEBGL_CAST_I32(pname));
}
inline GLint glGetUniformLocation(GLuint program, const GLchar *name)
@@ -226,9 +231,11 @@ inline void glLinkProgram(GLuint program)
webglLinkProgram(WEBGL_CAST_I32(program));
}
-inline void glShaderSource(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length)
+inline void glShaderSource(GLuint shader, GLsizei count, const GLchar *const *string, const GLint *length)
{
- webglShaderSource(WEBGL_CAST_I32(shader), WEBGL_CAST_I32(count), *string, WEBGL_CAST_I32(*length));
+ const GLchar *s = (void *)*string;
+ i32 l = _webgl_strlen(s);
+ webglShaderSource(WEBGL_CAST_I32(shader), s, l);
}
inline void glUniform1f(GLint location, GLfloat v0)
@@ -248,7 +255,7 @@ inline void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2)
inline void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
{
- webglUniformMatrix4fv(WEBGL_CAST_I32(location), value);
+ webglUniformMatrix4fv(WEBGL_CAST_I32(location), WEBGL_CAST_I32(value));
}
inline void glUseProgram(GLuint program)
diff --git a/wasm_js_implemented_symbols.txt b/wasm_js_implemented_symbols.txt
@@ -7,22 +7,22 @@ webglBufferData
webglClear
webglClearColor
webglCompileShader
+webglCreateBuffer
webglCreateProgram
webglCreateShader
-webglDeleteBuffers
+webglCreateVertexArray
+webglDeleteBuffer
webglDeleteShader
-webglDeleteVertexArrays
+webglDeleteVertexArray
webglDepthMask
webglDisable
webglDrawElements
webglEnable
webglEnableVertexAttribArray
-webglGenBuffers
-webglGenVertexArrays
webglGetProgramInfoLog
-webglGetProgramiv
+webglGetProgramParameter
webglGetShaderInfoLog
-webglGetShaderiv
+webglGetShaderParameter
webglGetUniformLocation
webglLinkProgram
webglShaderSource
@@ -32,3 +32,5 @@ webglUniform3f
webglUniformMatrix4fv
webglUseProgram
webglVertexAttribPointer
+js_read_entire_file
+js_print