Add cAllocMem to cwasm.c which the javascript side can use to allocate memory from an opaque Arena pointer that was passed to it. Used this to implement jsGlGetParameterString, jsGlGetShaderInfoLog, and jsGlGetProgramInfoLog in gl_functions.js. Added NotNull macro and fixed bug in ScratchBegin macro. Added a bool return for App_Initialize which allows us to stop the main loop from beginning (which prevents log spew when something goes wrong)

This commit is contained in:
2025-09-01 21:53:24 -07:00
parent 7c61189d3a
commit b62f0d279b
10 changed files with 135 additions and 18 deletions

View File

@@ -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)",

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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,

View File

@@ -10,5 +10,6 @@ export var appGlobals =
glContext: null,
memDataView: null,
wasmModule: null,
textEncoder: null,
textDecoder: null,
};

View File

@@ -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,8 +51,10 @@ 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();
if (initSuccess)
{
console.log("Running!");
function renderFrame(currentTime)
{
@@ -61,5 +64,10 @@ async function MainLoop()
}
window.requestAnimationFrame(renderFrame);
}
else
{
console.error("Initialization failed!");
}
}
MainLoop();

View File

@@ -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;
}

View File

@@ -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;
}
// +--------------------------------------------------------------+