pixijs / pixi-react

Write PIXI apps using React declarative style
https://pixijs.io/pixi-react/
MIT License
2.37k stars 178 forks source link

Proper way to use canvas in v5? #126

Closed kalbert312 closed 5 years ago

kalbert312 commented 5 years ago

I'm upgrading from pixi v4 to v5 and I was using the forceCanvas app option. The official pixi docs state to use the pixi.js-legacy package to pull in the canvas renderer, but not entirely sure how to integrate it while still using the webgl renderer in other places. Couldn't find much documentation around it. Any tips?

Additional info

inlet commented 5 years ago

According to pixi.js-legacy it does run on WebGL and fallback to canvas. See: https://www.npmjs.com/package/pixi.js-legacy

In order to use it in your project, you’ll need to alias pixi.js to pixi.js-legacy. Let me know if it works, so I can close this issue

inlet commented 5 years ago

Webpack:

module.exports = {
  //...
  resolve: {
    alias: {
      'pixi.js': 'pixi.js-legacy'
    }
  }
};
kalbert312 commented 5 years ago

Gotcha. Thanks!

kalbert312 commented 5 years ago

Closed it too early. I added that alias to my customized CRA webpack config and I'm getting

pixi-legacy.es.js:25 Uncaught TypeError: Cannot read property 'AccessibilityManager' of undefined
    at Module../node_modules/pixi.js-legacy/lib/pixi-legacy.es.js (pixi-legacy.es.js:25)

followed by

index.js:1375 ./node_modules/@inlet/react-pixi/dist/react-pixi.module-dev.js
Attempted import error: 'Application' is not exported from 'pixi.js'.

and a bunch of similar errors following that.

My CRA rescripts config:

const { appendWebpackPlugin, editWebpackPlugin } = require("@rescripts/utilities");
const webpack = require("webpack");

module.exports = (config) => {
    config.target = "electron-renderer";
    config = appendWebpackPlugin(
        new webpack.IgnorePlugin(/\.stories\./i),
        config
    );
    config = editWebpackPlugin(
        (p) => {
            p.memoryLimit = 4096;
            p.useTypescriptIncrementalApi = false;
            return p;
        },
        "ForkTsCheckerWebpackPlugin",
        config
    );
    config.resolve.alias["pixi.js"] = "pixi.js-legacy";
    return config;
};

pixi.js and pixi.js-legacy 5.0.4 are both present in node_modules.

fabienjuif commented 5 years ago

You can read the full PR or a more accurate information here: https://github.com/inlet/react-pixi/pull/116#issuecomment-510002796

As @Adamfsk says, PIXI.js just move away from canvas since almost all navigators support WebGL.

I was worried about "aliasing" (via webpack or other) didn't work because I was not sure about the fact that pixi.js-legacy has an ISO-API to pixi.js. Or that pixi.js-legacy does use pixi.js under the hood, in which case the alias wouldn't work as expected.

And looking at the pixi.js-legacy package it does use pixi.js:

import { accessibility, interaction, prepare, extract } from 'pixi.js';
export * from 'pixi.js';

Right now I don't have a good solution in mind:

Why do you need canvas @kalbert312 ?

inlet commented 5 years ago

Hi @kalbert312,

Here's how you can use ReactPixi with Canvas fallback support (assuming you're using webpack). Note that this is not elegant, but it works.

Create the following aliases in your webpack config:

module: {
  resolve: {
    'pixi.js': path.resolve(__dirname, './pixi.js'),
    'pixi.js-stable': path.resolve(__dirname, '../node_modules/pixi.js')
  }
}

The point is that pixi.js-legacy is using pixi.js internally, that's why we need to alias both

And create a pixi.js along-side your webpack config:

export * from 'pixi.js-stable';
export * from 'pixi.js-legacy';

which basically exports both.

Now, run your app with the forceCanvas: true option:

import React from 'react';
import { Stage, Graphics } from '@inlet/react-pixi';

function App() {
  return (
    <Stage options={{ forceCanvas: true }}>
      <Graphics draw={g => {
        g.beginFill(0xff3300)
        g.lineStyle(4, 0xffd900, 1)

        g.drawRect(10, 10, 100, 100);
        g.drawRect(110, 110, 100, 100);
        g.drawRect(210, 210, 100, 100);

        g.endFill();
      }}/>
    </Stage>
  );
}

export default App;
Adamfsk commented 5 years ago

@inlet Unless I’m misunderstanding something, using forceCanvas would cause to always use canvas, and not use WebGL with canvas as a fallback!

inlet commented 5 years ago

@Adamfsk I know, just to point out it works with canvas. If you leave out forceCanvas: true it renders over WebGL

inlet commented 5 years ago

I'm going to close this issue as the suggested solution works:

image

kalbert312 commented 5 years ago

I need to use canvas because I'm rendering a list of them and due to the html structure, it cannot be a shared canvas. I get an error that too many WebGL contexts are open with this approach, so I need to use canvas. Alternatively, I can just use a single canvas and convert each pic to an object url. Haven't decided yet. Thanks for the detailed answer.

inlet commented 5 years ago

You're welcome, having a way to fallback to Canvas can be convenient.. so now you know you can 😉

PByH commented 4 years ago

Hi everyone! I've faced with the same issue, but during jest tests launching. The reason is pixi-legacy.js has references to pixi.js. TypeError: Cannot read property 'AccessibilityManager' of undefined Here is the sample app: https://github.com/PByH/react-pixi-fiber-ts-jest I'm so grateful if somebody can help me with jest tests...

inlet commented 4 years ago

The solution is mentioned above, https://github.com/inlet/react-pixi/issues/126#issuecomment-514184770

You’ll need to use moduleNameMapper in Jest

PByH commented 4 years ago

Thanks for the answer! Solution worked for me with a few differences: my pixi.js: export * from 'pixi.js-stable'; export { renderer } from 'pixi.js-legacy'; There is a conflict with redefine of "Application" property if I export all props from pixi.js-legacy. Thanks a lot! :)

inlet commented 4 years ago

Have you tried rewire? https://medium.com/deskera-engineering/creating-path-aliases-in-create-react-app-with-react-app-rewired-c2cde81b472

AskAlice commented 4 years ago

I don't think rewire works for me as i use CRA 3.x

I tried rescripts but I can't seem to make a config that follows the structure of this comment https://github.com/inlet/react-pixi/issues/126#issuecomment-514184770

AskAlice commented 4 years ago

@inlet is this something that you truly believe to rest on the hands of react-pixi's users? Do you think that it's possible that react-pixi could provide a native solution to this issue?

inlet commented 4 years ago

@AskAlice if you have a rock solid solution for this I’m all ears 😉 feel free to collab on this

AskAlice commented 4 years ago

I feel you on that, simply inquiring if you thought it was possible at that level

inlet commented 4 years ago

However, maybe there’s a solution! We could create a legacy export as well:

import { Stage, Sprite } from “@inlet/react-pixi/legacy”

What do you think?

AskAlice commented 4 years ago

That sounds plausible, the only thing is I think it would make sense to have primitives for both types and then a component that joins them with a fallback to prevent unnecessary exceptions/passing the backwards-compatibility logic onto the user

inlet commented 4 years ago

Can you please explain what you mean by having primitives and a component that joins them? Maybe more specific, examples

inlet commented 3 years ago

You can now use pixi legacy without having to create aliases in your project. 🎉 Thanks @AskAlice!

See release v5.2.0

AskAlice commented 3 years ago

oh my, that was fast. you really didn't have to 🥇

AskAlice commented 3 years ago

My point is there could probably be a component that detects if hardware acceleration is available or not, and dynamically decide which stage component to import, optimizing performance based on hardware availability. I'll check out how your latest release works and can probably draft a PR that could implement that, I'm kind of interested in how browsers can detect that sort of thing anyhow, plus our team would need to make something like this anyway

inlet commented 3 years ago

That's exactly what pixi-legacy does, it tries WebGL first and and falls back to canvas.

Here's a code sandbox: https://codesandbox.io/s/pixi-legacy-f1ksf?file=/src/App.js

if you set forceCanvas to false it uses the autoDetectRenderer (does a hardware accel. detection), see https://github.com/pixijs/pixi.js/blob/3cf61cf98cb63409db2f9d6d6a6dddd11d924ec8/packages/utils/src/browser/isWebGLSupported.ts#L12

AskAlice commented 3 years ago

Oh, even better. Thanks for all this, I just sent you a tip. Cheers 🍻

inlet commented 3 years ago

Oh thanks so much! 🙏 you rock!

AskAlice commented 3 years ago

I've noticed that the useTick hook doesn't work with this for some reason.

No Context found with `PIXI.Application`. Make sure to wrap component with `AppProvider`

Perhaps the legacy stage component isn't exporting an app provider?

AskAlice commented 3 years ago

image appears to be at these lines if it helps

AskAlice commented 3 years ago

I believe it to be here https://github.com/inlet/react-pixi/blob/7b10107e73c16541712ba63f9ee6f88e6f179b73/src/hooks/useTick.js#L9

inlet commented 3 years ago

I believe it to be here

https://github.com/inlet/react-pixi/blob/7b10107e73c16541712ba63f9ee6f88e6f179b73/src/hooks/useTick.js#L9

I’ll look into this tomorrow

inlet commented 3 years ago

I've noticed that the useTick hook doesn't work with this for some reason.

No Context found with `PIXI.Application`. Make sure to wrap component with `AppProvider`

Perhaps the legacy stage component isn't exporting an app provider?

@AskAlice I cannot reproduce this issue, see https://codesandbox.io/s/pixi-legacy-f1ksf?file=/src/App.js.

Can you isolate the problem, share your code through codesandbox or something?

AskAlice commented 3 years ago

Ahh I've figured it out. It seems every react-pixi component needs to be imported with the /legacy suffix. Not just the canvas 😅

AskAlice commented 3 years ago

This is so satisfying to see be semi-usable on a raspberry pi running chromium https://giant.gfycat.com/ParchedTeemingBison.webm

inlet commented 3 years ago

Looking good @AskAlice!

AskAlice commented 3 years ago

@inlet My package versions are identical, i've double checked with npm ls, etc, and I copied the src you put in that codesandbox example of the legacy code, but I've been getting this.

I can't replicate this on codesandbox, but I've got this happening consistently even when i delete my node_modules.

no pixi.js in dependencies

{
...
    "@inlet/react-pixi": "~5.2.0",
    "pixi.js-legacy": "~5.3.3",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-scripts": "^3.4.1",
...
}

npm ls pixi.js

`-- pixi.js-legacy@5.3.3
  `-- pixi.js@5.3.3

npm ls pixi.js-legacy

`-- pixi.js-legacy@5.3.3 

npm ls @inlet/react-pixi

`-- @inlet/react-pixi@5.2.0 

I then copied in the file here: https://codesandbox.io/s/pixi-legacy-forked-zeyi8 into my App.jsx which React creates its root with in index.js, and here's the traceback

Uncaught Error: WebGL unsupported in this browser, use "pixi.js-legacy" for fallback canvas2d support.
    at Function.Renderer.create (Renderer.ts:88)
    at autoDetectRenderer (autoDetectRenderer.ts:39)
    at new Application (Application.ts:87)
    at Stage.componentDidMount (react-pixi.es-dev.js:11660)
    at commitLifeCycles (react-dom.development.js:19814)
    at commitLayoutEffects (react-dom.development.js:22803)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at commitRootImpl (react-dom.development.js:22541)
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at commitRoot (react-dom.development.js:22381)
    at finishSyncRender (react-dom.development.js:21807)
    at performSyncWorkOnRoot (react-dom.development.js:21793)
    at scheduleUpdateOnFiber (react-dom.development.js:21188)
    at updateContainer (react-dom.development.js:24373)
    at react-dom.development.js:24758
    at unbatchedUpdates (react-dom.development.js:21903)
    at legacyRenderSubtreeIntoContainer (react-dom.development.js:24757)
    at Object.render (react-dom.development.js:24840)
    at Module../src/index.js (index.js:43)
    at __webpack_require__ (bootstrap:784)
    at fn (bootstrap:150)
    at Object.1 (serviceWorker.js:138)
    at __webpack_require__ (bootstrap:784)
    at checkDeferredModules (bootstrap:45)
    at Array.webpackJsonpCallback [as push] (bootstrap:32)
    at main.chunk.js:1
react-pixi.es-dev.js:9847 Uncaught TypeError: Cannot read property 'current' of undefined
    at Object.updateContainer (react-pixi.es-dev.js:9847)
    at Stage.componentWillUnmount (react-pixi.es-dev.js:11745)
    at callComponentWillUnmountWithTimer (react-dom.development.js:19580)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at safelyCallComponentWillUnmount (react-dom.development.js:19587)
    at commitUnmount (react-dom.development.js:20109)
    at commitNestedUnmounts (react-dom.development.js:20163)
    at unmountHostComponents (react-dom.development.js:20443)
    at commitDeletion (react-dom.development.js:20500)
    at commitMutationEffects (react-dom.development.js:22782)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at commitRootImpl (react-dom.development.js:22509)
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at commitRoot (react-dom.development.js:22381)
    at finishSyncRender (react-dom.development.js:21807)
    at performSyncWorkOnRoot (react-dom.development.js:21793)
    at react-dom.development.js:11089
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11084)
    at flushSyncCallbackQueue (react-dom.development.js:11072)
    at unbatchedUpdates (react-dom.development.js:21909)
    at legacyRenderSubtreeIntoContainer (react-dom.development.js:24757)
    at Object.render (react-dom.development.js:24840)
    at Module../src/index.js (index.js:43)
    at __webpack_require__ (bootstrap:784)
    at fn (bootstrap:150)
    at Object.1 (serviceWorker.js:138)
    at __webpack_require__ (bootstrap:784)
    at checkDeferredModules (bootstrap:45)
    at Array.webpackJsonpCallback [as push] (bootstrap:32)
    at main.chunk.js:1
index.js:1 The above error occurred in the <Stage> component:
    in Stage (at App.jsx:36)
    in div (at App.jsx:33)
    in App (at src/index.js:43)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
console.<computed> @ index.js:1
react-dom.development.js:11102 Uncaught Error: WebGL unsupported in this browser, use "pixi.js-legacy" for fallback canvas2d support.
    at Function.Renderer.create (Renderer.ts:88)
    at autoDetectRenderer (autoDetectRenderer.ts:39)
    at new Application (Application.ts:87)
    at Stage.componentDidMount (react-pixi.es-dev.js:11660)
    at commitLifeCycles (react-dom.development.js:19814)
    at commitLayoutEffects (react-dom.development.js:22803)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at commitRootImpl (react-dom.development.js:22541)
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at commitRoot (react-dom.development.js:22381)
    at finishSyncRender (react-dom.development.js:21807)
    at performSyncWorkOnRoot (react-dom.development.js:21793)
    at scheduleUpdateOnFiber (react-dom.development.js:21188)
    at updateContainer (react-dom.development.js:24373)
    at react-dom.development.js:24758
    at unbatchedUpdates (react-dom.development.js:21903)
    at legacyRenderSubtreeIntoContainer (react-dom.development.js:24757)
    at Object.render (react-dom.development.js:24840)
    at Module../src/index.js (index.js:43)
    at __webpack_require__ (bootstrap:784)
    at fn (bootstrap:150)
    at Object.1 (serviceWorker.js:138)
    at __webpack_require__ (bootstrap:784)
    at checkDeferredModules (bootstrap:45)
    at Array.webpackJsonpCallback [as push] (bootstrap:32)
    at main.chunk.js:1
index.js:1 The above error occurred in the <Stage> component:
    in Stage (at App.jsx:36)
    in div (at App.jsx:33)
    in App (at src/index.js:43)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
console.<computed> @ index.js:1
scheduler.development.js:178 Uncaught TypeError: Cannot read property 'current' of undefined
    at Object.updateContainer (react-pixi.es-dev.js:9847)
    at Stage.componentWillUnmount (react-pixi.es-dev.js:11745)
    at callComponentWillUnmountWithTimer (react-dom.development.js:19580)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at safelyCallComponentWillUnmount (react-dom.development.js:19587)
    at commitUnmount (react-dom.development.js:20109)
    at commitNestedUnmounts (react-dom.development.js:20163)
    at unmountHostComponents (react-dom.development.js:20443)
    at commitDeletion (react-dom.development.js:20500)
    at commitMutationEffects (react-dom.development.js:22782)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at commitRootImpl (react-dom.development.js:22509)
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at commitRoot (react-dom.development.js:22381)
    at finishSyncRender (react-dom.development.js:21807)
    at performSyncWorkOnRoot (react-dom.development.js:21793)
    at react-dom.development.js:11089
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11084)
    at flushSyncCallbackQueue (react-dom.development.js:11072)
    at unbatchedUpdates (react-dom.development.js:21909)
    at legacyRenderSubtreeIntoContainer (react-dom.development.js:24757)
    at Object.render (react-dom.development.js:24840)
    at Module../src/index.js (index.js:43)
    at __webpack_require__ (bootstrap:784)
    at fn (bootstrap:150)
    at Object.1 (serviceWorker.js:138)
    at __webpack_require__ (bootstrap:784)
    at checkDeferredModules (bootstrap:45)
    at Array.webpackJsonpCallback [as push] (bootstrap:32)
    at main.chunk.js:1
Adamfsk commented 3 years ago

@AskAlice

npm ls pixi.js

-- pixi.js-legacy@5.3.3
-- pixi.js@5.3.3

You probably shouldn't be importing pixi.js AND pixi.js-legacy - they both declare the same classes to the global namespace (assuming you are not using ES modules), and with how they are ordered alphabetically what will import first is pixi.js-legacy, which will then be overwritten by the WebGL-only pixi.js classes.

Remove pixi.js (npm uninstall pixi.js) and your problem will likely be solved.

inlet commented 3 years ago

@Adamfsk is right, if you target for canvas make sure to import the legacy only

AskAlice commented 3 years ago

pixi.js is a dependency of pixi.js-legacy, so i believe that is impossible to actually uninstall. pixi.js hasn't been in my package.json since react-pixi 5.2.0 came out.

$ npm uninstall pixi.js
npm WARN @inlet/react-pixi@5.2.0 requires a peer of pixi.js@^5.x.x but none is installed. You must install peer dependencies yourself.    
npm WARN @inlet/react-pixi@5.2.0 requires a peer of react-spring@9.x.x but none is installed. You must install peer dependencies yourself.npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.2 (node_modules\react-scripts\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

audited 2850 packages in 10.121s

159 packages are looking for funding
  run `npm fund` for details

found 7 vulnerabilities (1 low, 6 high)
  run `npm audit fix` to fix them, or `npm audit` for details 
$ npm ls pixi.js       
`-- pixi.js-legacy@5.3.3
  `-- pixi.js@5.3.3
$ npm run start # signifying I restart the react-scripts server after poking NPM

and in my browser still

× Error: WebGL unsupported in this browser, use "pixi.js-legacy" for fallback canvas2d support.

oddly, I can still go to the pixi.js website and view the examples, and webgl still renders with chrome's swiftshader rather than my graphics card (when hardware acceleration is switched off in chrome://settings) image

three.js also manages to render with WebGL, it's just like 3fps on tabiverse.com

image

on the contrary, running ((import.*react-pixi(?!/legacy))|import.*pixi.js(?!-legacy)) as a regex yields zero results.

as you can see I am only importing legacy from pixi and from react-pixi. If I destructure named imports from pixi.js-legacy instead of import * as PIXI from 'pixi.js-legacy the same problem still applies. Regardless, even when I copy the legacy example I linked in codesandbox verbatum as my App.jsx, this is still an issue. Seems there is some issue with my package.json but I'm not quite sure where given pixi.js isn't listed as a dependency, only as a dependency's dependency in package.lock

inlet commented 3 years ago

Can you reproduce the issue on codesandbox? Remove the package/yarn .lock file and try re-installing sometimes oddly works 😉

As you can see, the only diff with legacy is that we alias pixi to pixi-legacy, see https://github.com/inlet/react-pixi/blob/master/rollup.config.js#L83