EngineStore.LastCreatedEngine becoming empty

Hello everyone,

It’s been a long time since I last posted on this forum. Thank you all again for both this amazing library and community !

I’m running into an issue with EngineStore.LastCreatedEngine in the context of creating a simple GUI Element (directly copied from this playground

Explanations:

Even though the involved code is simple, the project structure is not:
I have 2 different projects, the first one (let’s call it ‘mainApp’) is importing the second one (call it ‘core’) as an npm package (through npm link if it helps). I am the maintainer of both projects.

Basically what happens is:

  • mainApp imports core as an npm package
  • mainApp imports and uses two classes from core:
    • one that performs the general setup of a baseScene (equivalent of the Playground, call it ‘SceneManager’ with a CreateScene method)
    • the other one that is abstract (‘SceneLayoutBuilder’) and extended in the mainApp project (as ‘MySceneLayoutBuilder’) and called by the CreateScene method through the ‘SceneLayoutBuilder’ type.

Here is a playground. in this context it is working but it is better to illustrate the structure of my code (that I cannot share unfortunately). The facts that it is working in Playground makes me think that it could be related to the way the packages are imported but I couldn’t go further on my own.

Although this complicated setup works fine when I am creating simple objects as spheres, planes etc… it is not working when I try to add some GUI elements.
Actually only those three lines are sufficient to make the error appear.

const advancedTexture = AdvancedDynamicTexture.CreateFullscreenUI("myUI", true, scene);
var button1 = Button.CreateSimpleButton("but1", "Click Me");            advancedTexture.addControl(button1);

Here is the error I get :

babylon.gui.js:6730 
       Uncaught Error: Invalid engine. Unable to create a canvas.
    at Control._GetFontOffset (babylon.gui.js:6730:1)
    at TextBlock._processMeasures (babylon.gui.js:15921:1)
    at Control._layout (babylon.gui.js:6091:1)
    at Container._layout (babylon.gui.js:4245:1)
    at Container._layout (babylon.gui.js:4245:1)
    at AdvancedDynamicTexture._render (babylon.gui.js:1244:1)
    at AdvancedDynamicTexture._checkUpdate (babylon.gui.js:1219:1)
    at AdvancedDynamicTexture._render (babylon.gui.js:1237:1)
    at AdvancedDynamicTexture._checkUpdate (babylon.gui.js:1219:1)
    at c.callback (babylon.gui.js:620:94)

When looking into the line where the error is thrown, it seems that EngineStore.LastCreatedEngine becomes empty at some point in the code. I seems to happen just before the call to the ‘initScene’ method (through the abstract class).

I’ve looked into this forum post, talking about the same error, and tried several suggestions such as checking that the versions of babylonjs packages were the same in both projects but it seems to be the case (5.42.1 which must be the latest version at this time). I also tried to downgrade to an arbitrary version (5.20.0) but the error is still the same.

I hope this is understandable and I would appreciate any help that could preserve some of my nerves!
Thank you for your time !

Howdy!

We don’t clear EngineStore.LastCreatedEngine until an engine is disposed (or replaced), which leads me to believe there is a context issue here.
Is it possible you are using both UMD and es6 packages? would you be able to share part of the project? or at least your package.json?

2 Likes

Thank you for your answer. I didn’t mention it but I also tried to put a ‘console.log’ in the onDisposeObservable of the engine but it didn’t print anything.

As far as I know I am not using UMD modules, but this is still an obscure part of node that I currently don’t master enough so… maybe :person_shrugging: :smile:

Unfortunately I cannot share the code but here are relevant parts of both package.json (mainApp and core), with names adapted to my example:

mainApp:

{
  "name": "mainApp",
  "version": "0.0.0",
  "private": true,
  "type": "commonjs",
  "scripts": {
    /* ... */
  },
  "dependencies": {
    "@thescope/core": "file:../../core", //the other package
    "babylonjs": "^5.42.2",
    "babylonjs-gltf2interface": "^5.42.2",
    "babylonjs-gui": "^5.42.2",
    "babylonjs-loaders": "^5.42.2",
    /*...*/
  },
  "devDependencies": {
    /*...*/
    "@types/node": "^18.11.18",
    "file-loader": "^6.2.0",
    "nodemon": "^2.0.20",
    "ts-loader": "^9.4.2",
    "ts-node": "^10.9.1",
    "typescript": "^4.9.4",
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.1"
  }
}

core:

{
    "name": "core",
    "scope": "thescope",
    "version": "0.3.0",
    "private": false,
    "main": "index.ts",
    "dependencies": {
        /*...*/
        "babylonjs": "^5.42.2",
        "babylonjs-gltf2interface": "^5.42.2",
        "babylonjs-gui": "^5.42.2",
        "babylonjs-loaders": "^5.42.2",
        "recast-detour": "^1.5.0",
    },
    "devDependencies": {
        "sass-loader": "^10",
        "ts-node": "^10.9.1",
        "typescript": "4.9.4"
    }
}

While answering I noticed the “type”: “commonjs”, in the first package.json which is a bit random, I don’t know if this could be the cause of the problem as it looks to be related to what you suggest.

These are the UMD packages :slight_smile:
Are these 2 different apps running in two different contexts? how are they connected?

The package ‘core’ is supposed to be “just” a library providing classes and utilities that can be imported and called by ‘mainApp’ (but also other projects, that is why it is separated).
Unless I missed something (and I will check that again) there should be only one context, as all the code from both packages is initially called from the mainApp package.

Could you advise me about what I am supposed to look for ? What kind of code could create another context ?

Well, if you have creqated an engine, it should be available in the BABYLON namespace (BABYLON.EngineStore.LastEngineCreated) this can be verified when creating the engine. A simple test to check your context would be to add some temp variable to the BABYLON namespace you are using after creating the engine. i.e. - BABYLON.TEMPTEST = 1234;. Afterwards, when creating the GUI (or using the GUI), make sure that this variable is available. if it is available but the engine isn’t, it is a different issue that we need to check. If it isn’t, you are running two different contexts.

From looking at your code, it does seem like you are importing certain parts of the framework. You don’t use the BABYLON namespace, even though you are using the UMD packages. I would suggest to try the ES6 packages instead and see if that helps (i.e. @babylonjs/??? instead of all of the babylonjs-??? packages).

And, if possible, a simple reproduction - like a project on github - would be very helpful to help you resolve this.

1 Like

Thank you for you suggestions, I managed to go a little further.

I replaced the UMD packages with the ES6 version (I wasn’t aware of the difference), even though it seems more appropriate, it doesn’t solve the problem.

I have done this test and the BABYLON.TEMPTEST property is indeed empty in the code section where I try to use the GUI. So there must be two contexts but I cannot determine why for now.

However, I tried to move the class ‘MySceneLayoutBuilder’ to the ‘core’ package and it does solve the problem. This is not ideal from an architectural point of view but I will settle for this solution for now. This makes me think that it is indeed more related to the way it is imported than to the code itself.

I will try to dig further into this problem when I have more time and let you know what I find.

Thank you for your help and availability ! :slight_smile:

2 Likes

Hello !
I had a little to time to spend on this issue, here is a (actually two) GitHub repos that illustrate the problem in a “simple” way : 3D-Bear/37492-babylonjs-bug-repro-main-app (github.com)

The architecture is a bit different from what I wrote in the Playground in my first post, but it is closer to what happened in my actual project.

For the record, I found out that the child class would be better located in the ‘core-library’ project so this is no longer an important issue for me, but it may be worth it to solve it anyway.

I can help if there is trouble running the example. :slight_smile:

As previously said - this is a context issue. You have two versions of babylon in your build file. Just check the dist file in the main app to understand what is going on.

Without diving really deep into the structure I can’t really say what the issue is. But you can solve this by not running npm install on the core library, and not having babylonjs installed there. Usually npm install on the main directory will resolve correctly, but having an npm-installed package resolves from the nearest node_modules directory, which shouldn’t exist.

I know it’s not entirely your issue - it’s more of an npm/webpack issue but the quickest solution would be to force any import from “babylon.js” to resolve to a single directory. That, or not installing the node_modules on the core library (which I assume is a no-go).
IMO - the best solution (unless not possible due to architecture) is a mono-repo - have both packages on the same repository, maintaining their dependencies using npm workspace(s). That would solve your issue entirely.

I am facing the same problem. I have a base library I develop where I import Babylonjs. I externalize Babylonjs in this library to make sure I dont have 2 versions of babylonjs in my build. When I import this library into a new project and try to create a GUI Textblock, I get the exact same error as @3D-Bear and EngineStore.LastCreatedEngine is null. I am using vite to build everything and this is how I externalize Babylonjs from my base library:

import { resolve} from "path";
import { defineConfig } from 'vite';
import pkg from './package.json';

export default defineConfig({
  server: {
    host: true,
    open: true
  },
  resolve: {
    alias: {
      '@': resolve(__dirname, './src')
    }
  },
  build: {
    copyPublicDir: false,
    emptyOutDir: true,
    sourcemap: true,
    minify: true,
    outDir: "../dist",
    lib: {
      entry: {
        base-library: "../src/index.ts",
      },
      formats: ["es"]
    },
    rollupOptions: {
      external: Object.keys(pkg.dependencies || {}),
    }
  },  
})

Not sure if I still have 2 versions of babylonjs here or if the problem is something else…

As a workarround I use EngineStore.Instances.push(scene.getEngine()); which will fix the issue but thats for sure not the correct way.

if you can share the project I will be able to debug this and tell you if you have two contexts

Thanks, but unfortunately these are all private repositories I cannot share…

So you will need to debug this yourself :slight_smile:
My recommendation is - put a breakpoint during engine creation and see that the engine is actually added to the array. If the engine was added to the array, but in a future point it doesn’t - you have two contexts. Check if all dependencies are running on the same version just to be sure.

I sadly can’t help further without a reproduction

1 Like