Search Unity

[WebGL] efficiently read a file provided by user

Discussion in 'Web' started by mnml_, Jul 21, 2015.

  1. mnml_

    mnml_

    Joined:
    Jun 17, 2015
    Posts:
    44
    i want to effectively read a file, dropped on the webgl-canvas into the c# scripting context (result: byte[])

    tl;dr should it be possible to hand pinned gchandle to .jslib?

    /verbose

    currently it's working like this:

    read a filehandle, which came from an input element or via dragNdrop,
    with JS (HTML5/FileReader/readAsArrayBuffer)
    in the onload-event i store the arraybuffer in a window-global and sendmessage like this

    Code (JavaScript):
    1. canvas.addEventListener("drop", function (evt) {
    2.     var files = evt.dataTransfer.files;
    3.     for(i=0;i<files.length;i++) {
    4.         var f = files[i];
    5.         var reader = new FileReader();
    6.         reader.onload = (function(file) {
    7.             return function(e) {
    8.                 // .jslib is responsible for cleaning up this global mess
    9.                 (window.filedata = window.filedata ? window.filedata : {})[file.name] = e.target.result;
    10.                 SendMessage('FileHelper', 'FileOpen', file.name);
    11.             }
    12.         })(f);
    13.         reader.readAsArrayBuffer(f);
    14.     }
    15.     evt.preventDefault();
    16. }, false);
    the c#-code handling this message then calls a static extern .jslib function to read the arraybuffer into the emscripten heap, like this

    Code (JavaScript):
    1. GetFileData: function(filenameptr) {
    2. var filename = Pointer_stringify(filenameptr);
    3. var filedata = window.filedata[filename];
    4. //_malloc
    5. var ptr = (window.fileptr = window.fileptr ? window.fileptr : {})[filename] = _malloc(filedata.byteLength);
    6. var dataHeap = new Uint8Array(HEAPU8.buffer, ptr, filedata.byteLength);
    7. //copy data to freshly _malloced emscripten heap
    8. dataHeap.set(new Uint8Array(filedata));
    9. return ptr;
    10. }
    in case you're wondering why i keep a global containing a pointer to the _malloced space, so someone can _free up this mess after the c# code copyied the data into another part of the emscripten heap, like so:

    Code (CSharp):
    1. byte[] SlurpFile(string filename) {
    2.   var length = GetFileDataLength(filename);
    3.   var ptr = GetFileData(filename);
    4.   var data = new byte[length];
    5.   Marshal.Copy(ptr, data, 0, data.Length);
    6.   FreeFileData(filename);
    7.   return data;
    8. }
    FreeFileData is a .jslib function like GetFileDataLength, the former _frees ptr and deletes the window.filedata and window.fileptr entries, the latter just returnes the byteLength of the buffer.

    this looks horrible inefficient to me, i'm copying the whole data twice (the first time: javascript arraybuffer to emscripten heap, the second time to archieve an byte[] from an IntPtr because c#)

    i'm wondering if it should be possible to pass a pinned ptr to a managed array from the c# script like this:

    Code (CSharp):
    1. [DllImport("__Internal")]
    2. private static extern void CopyFileData(string filename, IntPtr dst);
    3.  
    4. public IEnumerator FileOpen(string filename) {
    5.    int len = GetFileDataLength(filename);
    6.    var data = new byte[len];
    7.    var p = GCHandle.Alloc(data, GCHandleType.Pinned);
    8.    CopyFileData(filename, p.AddrOfPinnedObject());
    9.    p.Free();
    10.    //data isnt filled but only zeroes here :'(
    11. }
    with the counterside

    Code (JavaScript):
    1. CopyFileData: function(filenameptr, dstptr) {
    2.     var filename = Pointer_stringify(filenameptr);
    3.     var filedata = window.filedata[filename];
    4.     var dataHeap = new Uint8Array(HEAPU8.buffer, dstptr, filedata.byteLength);
    5.     //copy from JS to emscripten heap
    6.     dataHeap.set(new Uint8Array(filedata));
    7.     delete window.filedata[filename];
    8. }
    so i would at least only have to copy the data once.

    after trying this my byte[] filled with \0\0\0\0\0\0 all the correct length, but not more

    or is everything i did a complete F%@$^% bcs it's somehow possible to SendMessage the original JS/ArrayBuffer in the first place as argument?
     
  2. jonas-echterhoff

    jonas-echterhoff

    Unity Technologies

    Joined:
    Aug 18, 2005
    Posts:
    1,666
    KalyaniKolape and twobob like this.
  3. mnml_

    mnml_

    Joined:
    Jun 17, 2015
    Posts:
    44
    it seems to work :)

    how come i didn't try this in the first place o.0
    i've read "Calling JavaScript functions from a plugin" at least a bazillion times

    ty sir jonas
     
  4. MANISHELITEINFOWORLD

    MANISHELITEINFOWORLD

    Joined:
    Jul 11, 2016
    Posts:
    1
    (function() {
    var JS_WS_CLIENT_TYPE = 'js-websocket';
    var JS_WS_CLIENT_VERSION = '0.0.1';
    var Protocol = window.Protocol;
    var protobuf = window.protobuf;
    var decodeIO_protobuf = window.decodeIO_protobuf;
    var decodeIO_encoder = null;
    var decodeIO_decoder = null;
    var Package = Protocol.Package;
    var Message = Protocol.Message;
    var EventEmitter = window.EventEmitter;
    var rsa = window.rsa;
    ...............


    here some this type of websocket javascript ,
    I need to convert this ,
    but lot of errors coming .

    any other way by which i communicate external javascript to unity.
     
  5. KalyaniKolape

    KalyaniKolape

    Joined:
    May 21, 2019
    Posts:
    3

    I need to pass array from C#. Write data to that array in jslib. And access those values in C# again Is it possible?