diff --git a/CWasm.sublime-project b/CWasm.sublime-project index e33dba2..6a110a3 100644 --- a/CWasm.sublime-project +++ b/CWasm.sublime-project @@ -67,6 +67,7 @@ "#define ArrayCount(array)", "#define Assert(condition)", "#define IsAlignedTo(pntr, alignment)", + "#define NotNull(expression)", "#define PrintLine_D(formatStrNt, ...)", "#define PrintLine_E(formatStrNt, ...)", "#define PrintLine_I(formatStrNt, ...)", @@ -92,16 +93,24 @@ "ArenaMark GetArenaMark(Arena* arena)", "ArenaMark GetScratch()", "ArenaMark GetScratch1(Arena* conflictArena)", + "GlEnum jsGlGetParameterEnum(GlEnum parameter)", "GlId jsGlCreateBuffer()", "GlId jsGlCreateProgram()", "GlId jsGlCreateShader(GlEnum shaderType)", "GlId jsGlCreateTexture()", "GlId jsGlCreateVertexArray()", "GlId jsGlGetUniformLocation(GlId programId, int nameLength, const char* namePntr)", + "bool jsGlGetParameterBool(GlEnum parameter)", "bool jsGlGetProgramParameterBool(GlId programId, GlEnum parameter)", "bool jsGlGetShaderParameterBool(GlId shaderId, GlEnum parameter)", + "char* jsGlGetParameterString(void* arenaPntr, GlEnum parameter)", + "char* jsGlGetProgramInfoLog(void* arenaPntr, GlId programId)", + "char* jsGlGetShaderInfoLog(void* arenaPntr, GlId shaderId)", + "float jsGlGetParameterFloat(GlEnum parameter)", "inline void* ReallocMem(Arena* arena, void* oldPntr, u32 oldSize, u32 newSize)", "inline void* ReallocMemUnaligned(Arena* arena, void* oldPntr, u32 oldSize, u32 newSize)", + "int jsGlGetError()", + "int jsGlGetParameterInt(GlEnum parameter)", "int jsGlGetProgramParameterInt(GlId programId, GlEnum parameter)", "int jsGlGetShaderParameterInt(GlId shaderId, GlEnum parameter)", "void InitGlobalArenas(u32 scratchArenasSize)", diff --git a/cwasm.c b/cwasm.c index 927466d..067e552 100644 --- a/cwasm.c +++ b/cwasm.c @@ -20,3 +20,11 @@ void InitializeCWasm(u32 scratchArenasSize) { InitGlobalArenas(scratchArenasSize); } + +WASM_EXPORT(cAllocMem) void* cAllocMem(Arena* arenaPntr, int numBytes, int alignment) +{ + NotNull(arenaPntr); + Assert(numBytes >= 0); + Assert(alignment >= 0); + return AllocMemAligned(arenaPntr, (u32)numBytes, (u32)alignment); +} diff --git a/cwasm.h b/cwasm.h index 1163c1a..3a0e6ed 100644 --- a/cwasm.h +++ b/cwasm.h @@ -129,6 +129,7 @@ Description: #define Assert(condition) sizeof(condition) //do nothing, but make sure anything used in condition is not treated as "unused" when assertions are disabled #define AssertMsg(condition, message) sizeof(condition) #endif +#define NotNull(expression) Assert((expression) != nullptr) //Actual Value of Pi: 3.1415926535897932384626433832795... #define Pi64 3.14159265358979311599796346854 //accurate to 15 digits diff --git a/cwasm_arena.c b/cwasm_arena.c index d25eb2e..9df9c4c 100644 --- a/cwasm_arena.c +++ b/cwasm_arena.c @@ -178,5 +178,5 @@ ArenaMark GetScratch1(Arena* conflictArena) static inline ArenaMark GetScratch() { return GetScratch1(nullptr); } #define ScratchBegin1(scratchName, conflictArenaPntr) ArenaMark scratchName##Mark = GetScratch1(conflictArenaPntr); Arena* scratchName = scratchName##Mark.arena -#define ScratchBegin(scratchName) ScratchBegin(scratchName, nullptr) +#define ScratchBegin(scratchName) ScratchBegin1(scratchName, nullptr) #define ScratchEnd(scratchName) ResetToArenaMark(scratchName##Mark) diff --git a/cwasm_webgl_js_imports.h b/cwasm_webgl_js_imports.h index 05b7a6d..d4f278e 100644 --- a/cwasm_webgl_js_imports.h +++ b/cwasm_webgl_js_imports.h @@ -10,6 +10,12 @@ Date: 09\01\2025 typedef int GlId; // Really this is just an index into a javascript array that holds the real reference to the WebGL object typedef int GlEnum; +MAYBE_EXTERN_C int jsGlGetError(); +MAYBE_EXTERN_C bool jsGlGetParameterBool(GlEnum parameter); +MAYBE_EXTERN_C GlEnum jsGlGetParameterEnum(GlEnum parameter); +MAYBE_EXTERN_C int jsGlGetParameterInt(GlEnum parameter); +MAYBE_EXTERN_C float jsGlGetParameterFloat(GlEnum parameter); +MAYBE_EXTERN_C char* jsGlGetParameterString(void* arenaPntr, GlEnum parameter); MAYBE_EXTERN_C void jsGlEnable(GlEnum capability); MAYBE_EXTERN_C void jsGlDisable(GlEnum capability); MAYBE_EXTERN_C void jsGlBlendFunc(GlEnum srcFactor, GlEnum dstFactor); @@ -38,10 +44,12 @@ MAYBE_EXTERN_C void jsGlShaderSource(GlId shaderId, int sourceLength, const char MAYBE_EXTERN_C void jsGlCompileShader(GlId shaderId); MAYBE_EXTERN_C bool jsGlGetShaderParameterBool(GlId shaderId, GlEnum parameter); MAYBE_EXTERN_C int jsGlGetShaderParameterInt(GlId shaderId, GlEnum parameter); +MAYBE_EXTERN_C char* jsGlGetShaderInfoLog(void* arenaPntr, GlId shaderId); MAYBE_EXTERN_C void jsGlDeleteProgram(GlId programId); MAYBE_EXTERN_C GlId jsGlCreateProgram(); MAYBE_EXTERN_C void jsGlAttachShader(GlId programId, GlId shaderId); MAYBE_EXTERN_C void jsGlLinkProgram(GlId programId); +MAYBE_EXTERN_C char* jsGlGetProgramInfoLog(void* arenaPntr, GlId programId); MAYBE_EXTERN_C void jsGlUseProgram(GlId programId); MAYBE_EXTERN_C bool jsGlGetProgramParameterBool(GlId programId, GlEnum parameter); MAYBE_EXTERN_C int jsGlGetProgramParameterInt(GlId programId, GlEnum parameter); diff --git a/data/gl_functions.js b/data/gl_functions.js index 694d129..6c03f44 100644 --- a/data/gl_functions.js +++ b/data/gl_functions.js @@ -1,6 +1,6 @@ import { appGlobals } from './globals.js' -import { wasmPntrToJsString, wasmPntrAndLengthToJsString } from './wasm_functions.js' +import { wasmPntrToJsString, wasmPntrAndLengthToJsString, jsStringToWasmPntr } from './wasm_functions.js' export var glObjects = { buffers: [ null ], @@ -79,6 +79,43 @@ function verifyParameter(verifyResult, functionName, parameterName, parameterVal // +--------------------------------------------------------------+ // | WebGL API | // +--------------------------------------------------------------+ +export function jsGlGetError(capability) +{ + return appGlobals.glContext.getError(); +} + +export function jsGlGetParameterBool(parameter) +{ + let paramValue = appGlobals.glContext.getParameter(parameter); + if (typeof(paramValue) != "boolean") { console.error("Tried to get GL parameter " + parameter + " as bool when it's actually: " + typeof(paramValue)); return false; } + return paramValue; +} +export function jsGlGetParameterEnum(parameter) +{ + let paramValue = appGlobals.glContext.getParameter(parameter); + if (typeof(paramValue) != "number") { console.error("Tried to get GL parameter " + parameter + " as enum when it's actually: " + typeof(paramValue)); return false; } + return paramValue; +} +export function jsGlGetParameterInt(parameter) +{ + let paramValue = appGlobals.glContext.getParameter(parameter); + if (typeof(paramValue) != "number") { console.error("Tried to get GL parameter " + parameter + " as int when it's actually: " + typeof(paramValue)); return false; } + return paramValue; +} +export function jsGlGetParameterFloat(parameter) +{ + let paramValue = appGlobals.glContext.getParameter(parameter); + if (typeof(paramValue) != "number") { console.error("Tried to get GL parameter " + parameter + " as float when it's actually: " + typeof(paramValue)); return false; } + return paramValue; +} +export function jsGlGetParameterString(arenaPntr, parameter) +{ + let paramValue = appGlobals.glContext.getParameter(parameter); + if (typeof(paramValue) != "string") { console.error("Tried to get GL parameter " + parameter + " as string when it's actually: " + typeof(paramValue)); return false; } + let paramValuePntr = jsStringToWasmPntr(arenaPntr, paramValue, true); + return paramValuePntr; +} + export function jsGlEnable(capability) { appGlobals.glContext.enable(capability); @@ -168,7 +205,6 @@ export function jsGlBindTexture(target, textureId) export function jsGlTexImage2D(target, level, internalFormat, width, height, border, format, type, dataLength, dataPntr) { - // let dataBuffer = appGlobals.memDataView.buffer.slice(dataPntr, dataPntr + dataLength); let dataBuffer = new Uint8Array(appGlobals.memDataView.buffer, dataPntr, dataLength); appGlobals.glContext.texImage2D(target, level, internalFormat, width, height, border, format, type, dataBuffer); } @@ -266,6 +302,16 @@ export function jsGlGetShaderParameterInt(shaderId, parameter) return paramValue; } +export function jsGlGetShaderInfoLog(arenaPntr, shaderId) +{ + if (!verifyParameter(verifyGlShaderId(shaderId, false), "gl.getShaderInfoLog", "shaderId", shaderId)) { return false; } + let shader = glObjects.shaders[shaderId]; + let logString = appGlobals.glContext.getShaderInfoLog(shader); + if (logString.length == 0) { return null; } + let logStringPntr = jsStringToWasmPntr(arenaPntr, logString, true); + return logStringPntr; +} + export function jsGlDeleteProgram(programId) { if (!verifyParameter(verifyGlProgramId(programId, false), "gl.deleteProgram", "programId", programId)) { return; } @@ -296,6 +342,16 @@ export function jsGlLinkProgram(programId) appGlobals.glContext.linkProgram(program); } +export function jsGlGetProgramInfoLog(arenaPntr, programId) +{ + if (!verifyParameter(verifyGlProgramId(programId, false), "gl.getProgramInfoLog", "programId", programId)) { return false; } + let program = glObjects.programs[programId]; + let logString = appGlobals.glContext.getProgramInfoLog(program); + if (logString.length == 0) { return null; } + let logStringPntr = jsStringToWasmPntr(arenaPntr, logString, true); + return logStringPntr; +} + export function jsGlUseProgram(programId) { if (!verifyParameter(verifyGlProgramId(programId, true), "gl.useProgram", "programId", programId)) { return; } @@ -497,6 +553,12 @@ export function jsGlUniformMatrix4fv(locationId, valuesPntr) // | Functions List | // +==============================+ export let jsGlFunctions = { + jsGlGetError: jsGlGetError, + jsGlGetParameterBool: jsGlGetParameterBool, + jsGlGetParameterEnum: jsGlGetParameterEnum, + jsGlGetParameterInt: jsGlGetParameterInt, + jsGlGetParameterFloat: jsGlGetParameterFloat, + jsGlGetParameterString: jsGlGetParameterString, jsGlEnable: jsGlEnable, jsGlDisable: jsGlDisable, jsGlBlendFunc: jsGlBlendFunc, @@ -525,10 +587,12 @@ export let jsGlFunctions = { jsGlCompileShader: jsGlCompileShader, jsGlGetShaderParameterBool: jsGlGetShaderParameterBool, jsGlGetShaderParameterInt: jsGlGetShaderParameterInt, + jsGlGetShaderInfoLog: jsGlGetShaderInfoLog, jsGlDeleteProgram: jsGlDeleteProgram, jsGlCreateProgram: jsGlCreateProgram, jsGlAttachShader: jsGlAttachShader, jsGlLinkProgram: jsGlLinkProgram, + jsGlGetProgramInfoLog: jsGlGetProgramInfoLog, jsGlUseProgram: jsGlUseProgram, jsGlGetProgramParameterBool: jsGlGetProgramParameterBool, jsGlGetProgramParameterInt: jsGlGetProgramParameterInt, diff --git a/data/globals.js b/data/globals.js index 0c9a20d..5c959c2 100644 --- a/data/globals.js +++ b/data/globals.js @@ -10,5 +10,6 @@ export var appGlobals = glContext: null, memDataView: null, wasmModule: null, + textEncoder: null, textDecoder: null, }; diff --git a/data/main.js b/data/main.js index 4eb4c7b..4f878e1 100644 --- a/data/main.js +++ b/data/main.js @@ -26,6 +26,7 @@ function AcquireCanvas(canvasWidth, canvasHeight) async function LoadWasmModule(wasmFilePath, initialWasmPageCount) { appGlobals.textDecoder = new TextDecoder("utf-8"); + appGlobals.textEncoder = new TextEncoder("utf-8"); appGlobals.wasmModule = await loadWasmModule(wasmFilePath, { ...jsStdFunctions, ...jsGlFunctions }); appGlobals.memDataView = new DataView(new Uint8Array(appGlobals.wasmModule.exports.memory.buffer).buffer); let memorySize = appGlobals.wasmModule.exports.memory.buffer.byteLength; @@ -50,16 +51,23 @@ async function MainLoop() console.log("Loading WASM Module..."); await LoadWasmModule("app.wasm", 4); - appGlobals.wasmModule.exports.App_Initialize(); + let initSuccess = appGlobals.wasmModule.exports.App_Initialize(); - console.log("Running!"); - function renderFrame(currentTime) + if (initSuccess) { - let shouldContinue = appGlobals.wasmModule.exports.App_UpdateAndRender(currentTime); - if (shouldContinue) { window.requestAnimationFrame(renderFrame); } - else { appGlobals.wasmModule.exports.App_Close(); } + console.log("Running!"); + function renderFrame(currentTime) + { + let shouldContinue = appGlobals.wasmModule.exports.App_UpdateAndRender(currentTime); + if (shouldContinue) { window.requestAnimationFrame(renderFrame); } + else { appGlobals.wasmModule.exports.App_Close(); } + } + window.requestAnimationFrame(renderFrame); + } + else + { + console.error("Initialization failed!"); } - window.requestAnimationFrame(renderFrame); } MainLoop(); \ No newline at end of file diff --git a/data/wasm_functions.js b/data/wasm_functions.js index 7177602..6c09b9d 100644 --- a/data/wasm_functions.js +++ b/data/wasm_functions.js @@ -40,3 +40,14 @@ export function wasmPntrAndLengthToJsString(ptr, length) appGlobals.memDataView.buffer.slice(ptr, ptr + length) ); } + +export function jsStringToWasmPntr(arenaPntr, jsString, addNullTerm) +{ + let encodedBytes = appGlobals.textEncoder.encode(jsString); + let memPntr = appGlobals.wasmModule.exports.cAllocMem(arenaPntr, encodedBytes.length + (addNullTerm ? 1 : 0), 0); + if (memPntr == 0) { return memPntr; } + const writeArray = new Uint8Array(appGlobals.wasmModule.exports.memory.buffer); + writeArray.set(encodedBytes, memPntr); + if (addNullTerm) { writeArray[encodedBytes.length] = 0; } + return memPntr; +} diff --git a/test_app.c b/test_app.c index 9b1fc91..fd49513 100644 --- a/test_app.c +++ b/test_app.c @@ -41,9 +41,10 @@ r32 OscillateBy(u64 timeSource, r32 min, r32 max, u64 periodMs, u64 offset) // +--------------------------------------------------------------+ // | Initialize | // +--------------------------------------------------------------+ -WASM_EXPORT(App_Initialize) void App_Initialize() +WASM_EXPORT(App_Initialize) bool App_Initialize() { InitializeCWasm(Kilobytes(128)); + memset(&app, 0x00, sizeof(app)); #if 0 Write_D("Hello\nWorld!"); @@ -54,7 +55,10 @@ WASM_EXPORT(App_Initialize) void App_Initialize() WriteLine_E("Where"); #endif - memset(&app, 0x00, sizeof(app)); + ScratchBegin(scratch); + + PrintLine_D("GL_VERSION: \"%s\"", jsGlGetParameterString(scratch, GL_VERSION)); + PrintLine_D("GL_VENDOR: \"%s\"", jsGlGetParameterString(scratch, GL_VENDOR)); r32 positions[] = { 0.0, 0.0, @@ -163,9 +167,9 @@ WASM_EXPORT(App_Initialize) void App_Initialize() 0xFFEEEEEE, 0xFFDDDDEE, 0xFFCCCCEE, 0xFFBBBBEE, 0xFFAAAAEE, 0xFF9999EE, 0xFF8888EE, 0xFF7777EE, 0xFF6666EE, 0xFF5555EE, 0xFFEEEEDD, 0xFFDDDDDD, 0xFFCCCCDD, 0xFFBBBBDD, 0xFFAAAADD, 0xFF9999DD, 0xFF8888DD, 0xFF7777DD, 0xFF6666DD, 0xFF5555DD, 0xFFEEEECC, 0xFFDDDDCC, 0xFFCCCCCC, 0xFFBBBBCC, 0xFFAAAACC, 0xFF9999CC, 0xFF8888CC, 0xFF7777CC, 0xFF6666CC, 0xFF5555CC, - 0xFFEEEEBB, 0xFFDDDDBB, 0xFFCCCCBB, 0xFFBBBBBB, 0xFFAAAABB, 0xFF9999BB, 0xFF8888BB, 0xFF7777BB, 0xFF6666BB, 0xFF5555BB, - 0xFFEEEEAA, 0xFFDDDDAA, 0xFFCCCCAA, 0xFFBBBBAA, 0xFFAAAAAA, 0xFF9999AA, 0xFF8888AA, 0xFF7777AA, 0xFF6666AA, 0xFF5555AA, - 0xFFEEEE99, 0xFFDDDD99, 0xFFCCCC99, 0xFFBBBB99, 0xFFAAAA99, 0xFF999999, 0xFF888899, 0xFF777799, 0xFF666699, 0xFF555599, + 0xFFEEEEBB, 0xFFDDDDBB, 0xFFCCCCBB, 0xFFBBBBBB, 0xFFFF0000, 0xFF9999BB, 0xFF8888BB, 0xFF7777BB, 0xFF6666BB, 0xFF5555BB, + 0xFFEEEEAA, 0xFFDDDDAA, 0xFFCCCCAA, 0xFFBBBBAA, 0xFF00FF00, 0xFF9999AA, 0xFF8888AA, 0xFF7777AA, 0xFF6666AA, 0xFF5555AA, + 0xFFEEEE99, 0xFFDDDD99, 0xFFCCCC99, 0xFFBBBB99, 0xFF0000FF, 0xFF999999, 0xFF888899, 0xFF777799, 0xFF666699, 0xFF555599, 0xFFEEEE88, 0xFFDDDD88, 0xFFCCCC88, 0xFFBBBB88, 0xFFAAAA88, 0xFF999988, 0xFF888888, 0xFF777788, 0xFF666688, 0xFF555588, 0xFFEEEE77, 0xFFDDDD77, 0xFFCCCC77, 0xFFBBBB77, 0xFFAAAA77, 0xFF999977, 0xFF888877, 0xFF777777, 0xFF666677, 0xFF555577, 0xFFEEEE66, 0xFFDDDD66, 0xFFCCCC66, 0xFFBBBB66, 0xFFAAAA66, 0xFF999966, 0xFF888866, 0xFF777766, 0xFF666666, 0xFF555566, @@ -192,17 +196,17 @@ WASM_EXPORT(App_Initialize) void App_Initialize() app.vertexShader = jsGlCreateShader(GL_VERTEX_SHADER); jsGlShaderSource(app.vertexShader, (int)strlen(VertexShaderCodeStr), VertexShaderCodeStr); jsGlCompileShader(app.vertexShader); - if (!jsGlGetShaderParameterBool(app.vertexShader, GL_COMPILE_STATUS)) { WriteLine_E("Failed to compile vertex shader!"); } //TODO: jsGlGetShaderInfoLog + if (!jsGlGetShaderParameterBool(app.vertexShader, GL_COMPILE_STATUS)) { PrintLine_E("Failed to compile vertex shader: \"%s\"", jsGlGetShaderInfoLog(scratch, app.vertexShader)); return false; } app.fragmentShader = jsGlCreateShader(GL_FRAGMENT_SHADER); jsGlShaderSource(app.fragmentShader, (int)strlen(FragmentShaderCodeStr), FragmentShaderCodeStr); jsGlCompileShader(app.fragmentShader); - if (!jsGlGetShaderParameterBool(app.fragmentShader, GL_COMPILE_STATUS)) { WriteLine_E("Failed to compile fragment shader!"); } //TODO: jsGlGetShaderInfoLog + if (!jsGlGetShaderParameterBool(app.fragmentShader, GL_COMPILE_STATUS)) { PrintLine_E("Failed to compile fragment shader: \"%s\"", jsGlGetShaderInfoLog(scratch, app.fragmentShader)); return false; } app.testShader = jsGlCreateProgram(); jsGlAttachShader(app.testShader, app.vertexShader); jsGlAttachShader(app.testShader, app.fragmentShader); jsGlLinkProgram(app.testShader); // also debug the program status - if (!jsGlGetProgramParameterBool(app.testShader, GL_LINK_STATUS)) { WriteLine_E("Failed to link shader program!"); } //TODO: jsGlGetProgramInfoLog + if (!jsGlGetProgramParameterBool(app.testShader, GL_LINK_STATUS)) { PrintLine_E("Failed to link shader program: \"%s\"", jsGlGetProgramInfoLog(scratch, app.testShader)); return false; } // const char* uniformName = "TestUniform"; // app.testUniformLocation = jsGlGetUniformLocation(app.testShader, (int)strlen(uniformName), uniformName); @@ -214,6 +218,9 @@ WASM_EXPORT(App_Initialize) void App_Initialize() app.viewMatrixLocation = jsGlGetUniformLocation(app.testShader, (int)strlen(viewMatrixName), viewMatrixName); const char* projMatrixName = "ProjMatrix"; app.projMatrixLocation = jsGlGetUniformLocation(app.testShader, (int)strlen(projMatrixName), projMatrixName); + + ScratchEnd(scratch); + return true; } // +--------------------------------------------------------------+