如何在JS中调用C函数并基于SDL在Canvas渲染,实现函数持续调用
Emscripten+SDL2模块持续调用C函数的问题解决
问题
我用SDL2结合Emscripten构建了一个渲染到Canvas的模块,需要在SSE事件触发时持续调用C函数set_num传递数据,但目前该函数只能被调用一次。
原因
当前代码把Module.onRuntimeInitialized赋值放在SSE.onmessage回调里,而onRuntimeInitialized是Emscripten模块初始化完成时仅触发一次的回调。第一次收到SSE消息时赋值的回调会在模块初始化后执行一次,但后续收到消息时重新赋值的回调不会再被触发,导致无法继续调用C函数。
解决方案
调整代码逻辑,跟踪模块初始化状态,直接在SSE事件中调用C函数(初始化完成后),同时暂存初始化前的消息避免丢失:
修改后的JavaScript代码:
var Module = { canvas: (function () { var canvas = document.getElementById('canvas'); canvas.addEventListener('webglcontextlost', e => { alert('WebGL上下文丢失,请重新加载页面。'); e.preventDefault(); }, false); return canvas; })(), // 标记模块是否初始化完成 isInitialized: false, // 暂存初始化前收到的消息 pendingMessages: [], onRuntimeInitialized: function() { this.isInitialized = true; // 处理初始化前积压的消息 this.pendingMessages.forEach(msg => { this.ccall('set_num', null, ['number'], [parseInt(msg.data)]); }); this.pendingMessages = []; } }; SSE.onmessage = msg => { // 提取SSE消息中的数字数据(C函数接收int类型) const num = parseInt(msg.data); if (Module.isInitialized) { // 模块已初始化,直接调用C函数 Module.ccall('set_num', null, ['number'], [num]); } else { // 模块未初始化,先暂存消息 Module.pendingMessages.push(msg); } }
关键修改点
- 给
Module添加isInitialized标志位,跟踪初始化状态 - 新增
pendingMessages队列,避免模块初始化前收到的消息丢失 onRuntimeInitialized仅在Module定义时赋值一次,初始化完成后处理暂存消息- SSE事件回调中根据初始化状态选择直接调用C函数或暂存消息
- 注意:SSE的
msg是MessageEvent对象,实际数据在msg.data属性中,需要转换为整数后传递给C函数
编译配置
override CFLAGS += -std=c11\ -Os\ -sUSE_SDL_TTF=2\ -sUSE_SDL_IMAGE=2\ -sSDL2_IMAGE_FORMATS=["png"]\ -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap']\ -sUSE_SDL=2\ -sWASM=1\ -sASSERTIONS\ --preload-file ./res
C代码
#ifdef __EMSCRIPTEN__ EMSCRIPTEN_KEEPALIVE #endif void set_num(int num) { g_obj->num = num; }
内容的提问来源于stack exchange,提问作者nim
相关产品推荐
相关产品推荐

