开发者问题收集

Emscripten 未捕获 RangeError:源太大,多个 Float32Arrays

2015-07-21
7082

我正在尝试运行一个用于 4x4 矩阵乘法的 C++ 函数。两天后,它终于成功了,但不如预期。

通常,参数会反馈给函数,然后出现以下行:

dataHeap2.set( new Uint8Array(data2.buffer) );

产生错误“Uncaught RangeError: Source is too large”

乍一看,它似乎只是一个包含 16 个元素的普通 Float32Array,但查看其缓冲区大小后,似乎有所不同

console.log(data2.buffer.bufferLength);

结果不是预期的 64 字节,而是像 3342345 这样的大数字。这是问题吗?我找到了一种解决方法,即手动复制值(如下所示),返回该值,然后问题就消失了。不幸的是,它使我的代码比直接在缓冲区上操作慢得多。

// bad solution - which works
    for(var i = 0; i < 16; i++) {
        dataTarget[i] = result[i];
    }

希望今晚我能找到更好的解决方案,我想继续使用这个函数,因为它使用编译的 C++ 代码,使用 ASM.JS + SIMD 来动画多个字符。在纯 JavaScript 中它仍然太慢了。这是整个函数。我很确定 data2 正在使用 Emscripten 的 HEAP,我想避免它。

matrix4multiply = function(data, data2) {
    // Import function from Emscripten generated file
    var mmul_vec4 = Module.cwrap(
      'mmul_vec4', 'number', ['number', 'number', 'number']
    );

    var dataTarget = new Float32Array(16);

    // Get data byte size, allocate memory on Emscripten heap, and get pointer
    var nDataBytes = dataTarget.length * dataTarget.BYTES_PER_ELEMENT;

    // First matrix copy data to Emscripten heap
    var dataPtr = Module._malloc(nDataBytes);
    var dataPtr2 = Module._malloc(nDataBytes);
    var dataPtr3 = Module._malloc(nDataBytes);

    var dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, nDataBytes);
    dataHeap.set( new Uint8Array(data.buffer) );

    // second matrix allocate and copy to emscripten's heap
    var dataHeap2 = new Uint8Array(Module.HEAPU8.buffer, dataPtr2, nDataBytes);
    dataHeap2.set( new Uint8Array(data2.buffer) );

    // target heap
    var targetHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr3, nDataBytes);
    targetHeap.set( new Uint8Array(dataTarget.buffer) );

    // Call the function by passing a number pointing to the byte location of 
    // the array of pointers on the Emscripten heap.  Emscripten knows what to do!
    mmul_vec4(dataHeap.byteOffset, dataHeap2.byteOffset, targetHeap.byteOffset);

    // get result
    var result = new Float32Array(targetHeap.buffer, targetHeap.byteOffset, 16);

    // bad solution - which works
    //for(var i = 0; i < 16; i++) {
    //    dataTarget[i] = result[i];
    //}

    // Free memory
    Module._free(dataHeap.byteOffset);
    Module._free(dataHeap2.byteOffset);
    Module._free(targetHeap.byteOffset);

    return result;
}

编辑:简化版本不用担心 malloc 等

    new Uint8Array(Module.HEAPU8.buffer, this.dataPtr, 64).set( new Uint8Array(data.buffer, data.byteOffset, 64) );

    // second matrix allocate and copy to emscripten's heap
    new Uint8Array(Module.HEAPU8.buffer, this.dataPtr + 72, 64).set( new Uint8Array(data2.buffer, data2.byteOffset, 64) );

    // multiply first two parameters and return in the last one 
    this.mmul_vec4(this.dataPtr, this.dataPtr + 72, this.dataPtr + 144);

    // like that it works, unfotunately copying is being made here
    return new Float32Array(Module.HEAPU8.buffer.slice(this.dataPtr + 144, this.dataPtr + 208));
    // with this version (if uncommented) there's just white screen(but it looks like the game is working.
    //return new Float32Array(Module.HEAPU8.buffer, this.dataPtr + 144, 16);
2个回答

要使用 data2 的前 64 个字节,请指定偏移量和长度。

dataHeap2.set( new Uint8Array(data2.buffer, data2.byteOffset, nDataBytes) );
zakki
2015-07-22

这似乎解决了问题,但我担心 .set 函数仅复制数据,因此它不是真正的解决方案。至少比 for 循环克隆更优雅一点(但它比“Module.HEAPU8.buffer.slice”更好吗)?

matrix4multiplyBlocking = function(data, data2, dataTarget) {
    //console.log(new Float32Array(data2.buffer, data2.byteOffset, 16));
    // Copy data to Emscripten heap
    new Uint8Array(Module.HEAPU8.buffer, this.dataPtr, 64).set( new Uint8Array(data.buffer, data.byteOffset, 64) );

    // second matrix allocate and copy to emscripten's heap
    new Uint8Array(Module.HEAPU8.buffer, this.dataPtr + 72, 64).set( new Uint8Array(data2.buffer, data2.byteOffset, 64) );

    // multiply first two parameters and return in the last one 
    this.mmul_vec4(this.dataPtr, this.dataPtr + 72, this.dataPtr + 144);
    // Free memory
    //Module._free(this.dataPtr);

    dataTarget.set(new Float32Array(Module.HEAPU8.buffer, this.dataPtr + 144, 16));
};

此外,我讨厌创建新的 Float32Array 实例,因为它会减慢一切

Pawel
2015-07-22