Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to properly load the full wasm package from opencascade and/or see better errors #156

Closed
tsdexter opened this issue Apr 20, 2024 · 8 comments

Comments

@tsdexter
Copy link

I'm having trouble loading the full OC wasm. I've changed my worker like:

// import opencascadeWasm from 'replicad-opencascadejs/src/replicad_single.wasm?url';
import opencascadeWasm from 'opencascade.js/dist/opencascade.full.wasm?url';

and now I get the error:

comlink.js?v=2496509d:42 Uncaught (in promise) RuntimeError: Aborted(LinkError: WebAssembly.instantiate(): Import #68 module="a" function="oa": function import requires a callable). Build with -sASSERTIONS for more info.
    at abort (replicad-opencascade…s?v=2496509d:501:15)
    at replicad-opencascade…s?v=2496509d:578:11

Really, my end goal isn't necessarily to have the full wasm library, but just to get better errors than:

image

Which I thought the error message is so basic due to the note on the opencascadejs site, but I don't see the sDISABLE_EXCEPTION_CATCHING flag in the build config. Am I just doing something wrong for getting the errors?

DISABLING EXCEPTIONS GREATLY REDUCES FILE SIZE
For many applications, exception support is not required in OpenCascade.js and can be disabled. For a full build, this reduces the file size by ~45% and greatly improves performance (even if exceptions are never thrown). Pass -sDISABLE_EXCEPTION_CATCHING=1 in the emccFlags of your custom build to disable exceptions. Check out this custom build definition for an example.

@clinuxrulz
Copy link

I believe you gotta sent to url to the wasm file to your web worker from outside your web worker. (Make the url to the wasm file in your non-webworker then sent the result as a string to your webworker to use inside your webworker on startup).

E.g.
Non-webworker:

import opencascadeWasm from "opencascade.js/dist/opencascade.full.wasm?url";
import { generateUUID } from "three/src/math/MathUtils";
import { Request, ShapeId, ShapeMeshWithUVCoords } from "./Request";
import Worker from "./geometric_kernel_worker?worker";
import { Result, err, flatMapResult, mapResult, ok } from "../Result";
import { ReplicadMeshedEdges, ReplicadMeshedFaces } from "replicad-threejs-helper";
import { Transform3D } from "../../math/Transform3D";
import { Mesh3dState } from "../../general_designer/components/Mesh3dComponent";
import { Vec3 } from "../../math/Vec3";
import { Line3D } from "../../math/Line3D";
import { CircularArcState } from "../../general_designer/components/CircularArcComponent";
import { Plane3D } from "../../math/Plane3D";
import { Quaternion } from "../../math/Quaternion";

let callbacks = new Map<string, (result: any) => void>();

const worker = new Worker();
worker.addEventListener("message", (e) => {
    let response = e.data;
    let callback = callbacks.get(response.requestId);
    callbacks.delete(response.requestId);
    if (callback != undefined) {
        callback(response.result);
    }
});

function workerRemoteCall(request: Request): Promise<any> {
    return new Promise((resolve) => {
        callbacks.set(request.requestId, (result) => {
            resolve(result);
        });
        worker.postMessage(request);
    })
}

await workerRemoteCall({
    type: "setupOpenCascade",
    requestId: generateUUID(),
    params: {
        opencascadeWasm,
    }
});

Web worker:

import opencascade, * as OC from "opencascade.js/dist/opencascade.full";
import * as Pako from "pako";
import * as Replicad from "replicad";
import { getOC, setOC } from "replicad";
import { Request, ShapeId } from "./Request";
import { Result, err, ok } from "../Result";
import { generateUUID } from "three/src/math/MathUtils";
import { Quaternion } from "../../math/Quaternion";
import { Vec3 } from "../../math/Vec3";
import { deleteDocument, document_getShapesViaLabels, loadDocumentFromBase64String, newDocument, saveDocumentToBase64String } from "./worker/xcaf_document";
import { base64ToUInt8Array, ocErrorMessage, uint8ArrayToBase64 } from "./worker/util";
import { shapeFaces } from "./worker/shape_explorer";
import { Transform3D } from "../../math/Transform3D";
import { differenceSamePlaneFaces, doesIntersectSamePlaneFaces, flipFaceNormal, intersectLineWithFace, intersectSamePlaneFaces, intersectWithInfinitePrismFromFace, makeBox, makeFacesForLines, makeSolidFromFaces, shapeToMeshWithUVCoords, shapePoints, shapeSolids } from "./worker/worker_callbacks";

let opencascadeWasm: string | undefined = undefined;
// This is the logic to load the web assembly code into replicad
let loaded = false;
const init = async () => {
    if (loaded) return Promise.resolve(true);

    const OC = await (opencascade as any)({
        locateFile: () => opencascadeWasm,
    });

    loaded = true;
    setOC(OC);

    return true;
};

let shapesHeap = new Map<string, Replicad.AnyShape>();

self.addEventListener("message", async (e) => {
    let message = JSON.parse(JSON.stringify(e.data)) as Request;
    switch (message.type) {
        case "setupOpenCascade": {
            opencascadeWasm = message.params.opencascadeWasm;
            await init();
            self.postMessage({
                requestId: message.requestId,
                result: ok({}),
            });
            break;
        }

Please ignore extra imports, I'm doing copy paste on mobile phone and editing text is a bit hard.

@clinuxrulz
Copy link

clinuxrulz commented Jun 29, 2024

Be warned the full version of opencascade is a 50Mb wasm file. It will be slow in production. You'll still need to make a custom build.

@clinuxrulz
Copy link

To convert that number you get into an error message:

export function ocErrorMessage(e: any): string {
    if (typeof e === "number") {
        let oc = getOC();
        const exceptionData = oc.OCJS.getStandard_FailureData(e);
        return exceptionData.GetMessageString();
    }
    return "" + e;
}

The function getStandard_FailureData is provided by the full opencascade.js as extra C++. You can use a try/catch block to get that error number.

@clinuxrulz
Copy link

See this file here how they add that extra C++ function down the bottom:

https://github.com/donalffons/opencascade.js/blob/master/builds/opencascade.full.yml

You really just need to add the same bit to the bottom of the custom build for replicad. Then you can get the error message from the pointer (number) to the error.

@tsdexter
Copy link
Author

tsdexter commented Jul 3, 2024

thx @clinuxrulz I'll take a look and see if I can get it working as you suggest!

@clinuxrulz
Copy link

clinuxrulz commented Jul 6, 2024

no problem... if possible reach out for a custom build. I.E. edit replicad's yml file for their version and add the extra C++ code to the bottom to extract the error message from the pointer to the error (the number thrown in JS).

Sadly I could not custom build opencascade.js through the docker container on my computer. 16 GB of ram was not enough. Probably needed 32 GB. However I have had luck compiling opencascade official version to wasm using emscripten without the docker container, by writing my web-worker in C++ instead of TS and only embind-ing a single function for message passing into/out-of the webworker.

I really thought 16GB ram was plenty for coding tasks, but I guess that is not the case these days.

@sgenoud
Copy link
Owner

sgenoud commented Jul 6, 2024

Good news! I have added a new custom build within opencascade-replicad that supports full exceptions. You can use it in that same way that you use the single build, as I do in the studio now

And the studio has been modified as well to allow for these errors. We will see if they are actually useful!

Capture d’écran 2024-07-06 à 17 08 28

@tsdexter
Copy link
Author

tsdexter commented Jul 7, 2024

@sgenoud amazing, thank you!

@tsdexter tsdexter closed this as completed Jul 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants