Skip to content

THREE.Renderer: .render() called before the backend is initialized. Try using .renderAsync() instead. #214

@jb-san

Description

@jb-san

Hi, really appreciate the project, I'm trying to get this to work with an expo project and react-three-fiber
for web I got an error that I could get around by just creating a FiberCanvas.web.tsx and just use the regular Canvas from fiber.
but on ios when I try following the guide in the readme, I get this error

(NOBRIDGE) WARN  THREE.Renderer: .render() called before the backend is initialized. Try using .renderAsync() instead

I dug into the threejs code and changed the state.gl.render to renderAsync, which did remove the warning but rendered nothing.

so I found this pmndrs/react-three-fiber#3403 issue, and the suggested fix does not work because I never get past

await state.gl.init();

when modifying a bit to try to do the fix like this

import type { ReconcilerRoot, RootState } from "@react-three/fiber";
import {
  createRoot,
  events,
  extend,
  unmountComponentAtNode,
} from "@react-three/fiber";
import React, { useRef, useState } from "react";
import type { ViewProps } from "react-native";
import { PixelRatio } from "react-native";
import { useCanvasEffect, Canvas as WCanvas } from "react-native-wgpu";
import * as THREE from "three";

import {
  makeWebGPURenderer,
  ReactNativeCanvas,
} from "@/utils/makeWebGPURenderer";

//global.THREE = global.THREE || THREE;

interface FiberCanvasProps {
  children: React.ReactNode;
  style?: ViewProps["style"];
  camera?: THREE.PerspectiveCamera;
  scene?: THREE.Scene;
}

export const Canvas = ({
  children,
  style,
  scene,
  camera,
}: FiberCanvasProps) => {
  const root = useRef<ReconcilerRoot<OffscreenCanvas>>(null!);

  React.useMemo(() => extend(THREE), []);
  const [frameloop, setFrameloop] = useState<"always" | "never">("never");
  const canvasRef = useCanvasEffect(async () => {
    const context = canvasRef.current!.getContext("webgpu")!;
    const renderer = makeWebGPURenderer(context);

    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-expect-error
    const canvas = new ReactNativeCanvas(context.canvas) as HTMLCanvasElement;
    canvas.width = canvas.clientWidth * PixelRatio.get();
    canvas.height = canvas.clientHeight * PixelRatio.get();
    const size = {
      top: 0,
      left: 0,
      width: canvas.clientWidth,
      height: canvas.clientHeight,
    };

    if (!root.current) {
      root.current = createRoot(canvas);
    }
    root.current.configure({
      size,
      events,
      scene,
      camera,
      gl: (context) => {
        const renderer = new THREE.WebGPURenderer({
          antialias: true,
          canvas,
          context,
        });
        renderer.init().then(() => setFrameloop("always"));
        renderer.xr = { addEventListener: () => {} };
        return renderer;
      },
      frameloop,
      dpr: 1, //PixelRatio.get(),
      onCreated: async (state: RootState) => {
        //@ts-ignore
        await state.gl.init();
        const renderFrame = state.gl.render.bind(state.gl);
        state.gl.render = (s: THREE.Scene, c: THREE.Camera) => {
          renderFrame(s, c);
          context?.present();
        };
      },
    });
    root.current.render(children);
    return () => {
      if (canvas != null) {
        unmountComponentAtNode(canvas!);
      }
    };
  });

  return <WCanvas ref={canvasRef} style={style} />;
};

I get this

(NOBRIDGE) WARN THREE.WebGPURenderer: WebGPU is not available, running under WebGL2 backend.

And also, I don't think the useCanvasEffect will rerun if I change the component state.
Any help would be appreciated

PS. sometimes it does render but it is not consistent

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions