Skip to content

Commit

Permalink
Handle abscent of webgpu
Browse files Browse the repository at this point in the history
- If WebGPU is not available, show the issues doc, and don't instantiate the renderer
- if the renderer cannot get an adapter fallback as above
- Dont show the preview button in docs when no webgpu
  • Loading branch information
PollRobots committed Jul 20, 2024
1 parent d4946d0 commit 5410ce7
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 54 deletions.
82 changes: 54 additions & 28 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,17 @@ export const App: React.FC = () => {
const [settings, setSettings] = React.useState(loadSettings());
const width = Math.round(window.visualViewport.width / 2);
const height = Math.round((width * 9) / 16);
const [hasGpu, setHasGpu] = React.useState(Reflect.has(navigator, "gpu"));
const [generated, setGenerated] = React.useState("");
const [errors, setErrors] = React.useState("");
const [uniforms, setUniforms] = React.useState<string[]>([]);
const [offsets, setOffsets] = React.useState<number[]>([]);
const [values, setValues] = React.useState<Map<string, Uniform>>(new Map());
const [editorTop, setEditorTop] = React.useState(true);
const [docs, setDocs] = React.useState(location.hash.length > 0);
const [topic, setTopic] = React.useState(getInitialTopic());
const [docs, setDocs] = React.useState(location.hash.length > 0 || !hasGpu);
const [topic, setTopic] = React.useState(
hasGpu ? getInitialTopic() : "issues"
);
const [view, setView] = React.useState<Vector>({ x: 15, y: 0, z: 0 });

const currTheme = kDefinedThemes.get(settings.themeName) || kSolarizedDark;
Expand Down Expand Up @@ -180,32 +183,54 @@ export const App: React.FC = () => {
gridTemplateRows: "auto 1fr",
}}
>
<WebGPUCanvas
style={{
width: `calc(48vw - 4em)`,
height: `calc(9 * (48vw - 4em) / 16)`,
gridArea: "1/1/2/2",
border: `solid 1px ${currTheme.base00}`,
}}
width={width}
height={height}
view={view}
shader={makeShader(
shader,
generated,
offsets.length == 0
? 0
: offsets.reduce((a, e) => Math.max(a, e)) + 4
)}
vertexShader="vertex_main"
fragmentShader="frag_main"
uniformValues={uniforms
.map((el) => values.get(el) || kDefaultUniform)
.map((el) => el.value)}
uniformOffsets={offsets}
onShaderError={(shaderError) => setErrors(shaderError)}
onViewChange={(u) => setView(u)}
/>
{hasGpu ? (
<WebGPUCanvas
style={{
width: `calc(48vw - 4em)`,
height: `calc(9 * (48vw - 4em) / 16)`,
gridArea: "1/1/2/2",
border: `solid 1px ${currTheme.base00}`,
}}
width={width}
height={height}
view={view}
shader={makeShader(
shader,
generated,
offsets.length == 0
? 0
: offsets.reduce((a, e) => Math.max(a, e)) + 4
)}
vertexShader="vertex_main"
fragmentShader="frag_main"
uniformValues={uniforms
.map((el) => values.get(el) || kDefaultUniform)
.map((el) => el.value)}
uniformOffsets={offsets}
onShaderError={(shaderError) => setErrors(shaderError)}
onViewChange={(u) => setView(u)}
onGpuError={(err) => {
setHasGpu(false);
console.error("GPU initialization error:", err);
}}
/>
) : (
<div
style={{
width: `calc(48vw - 4em)`,
height: `calc(9 * (48vw - 4em) / 16)`,
gridArea: "1/1/2/2",
border: `solid 1px ${currTheme.base00}`,
alignContent: "center",
textAlign: "center",
background: currTheme.red,
color: currTheme.background,
fontSize: "150%",
}}
>
This tool requires WebGPU support.
</div>
)}
<div
style={{
gridArea: "1/1/2/2",
Expand All @@ -215,6 +240,7 @@ export const App: React.FC = () => {
opacity: 0.8,
fontWeight: "bolder",
color: currTheme.base00,
display: hasGpu ? undefined : "none",
}}
>
SDF Tool
Expand Down
37 changes: 24 additions & 13 deletions src/components/example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,29 @@ export const Example: React.FC<ExampleProps> = (props) => {
}),
generated.uniformOffsets
);
gpu
.init(
makeShader(
template,
generated.generated,
generated.uniformOffsets.length == 0
? 0
: Math.max(...generated.uniformOffsets) + 4
),
"vertex_main",
"frag_main"
)
if (!Reflect.has(navigator, "gpu")) {
setGenerating(false);
return;
}
navigator.gpu
.requestAdapter()
.then((adapter) => {
if (!adapter) {
throw new Error("Cannot get GPU adapter");
}
return gpu.init(
adapter,
makeShader(
template,
generated.generated,
generated.uniformOffsets.length == 0
? 0
: Math.max(...generated.uniformOffsets) + 4
),
"vertex_main",
"frag_main"
);
})
.then(() => {
gpu.stop();
requestAnimationFrame((t) => {
Expand Down Expand Up @@ -120,7 +131,7 @@ export const Example: React.FC<ExampleProps> = (props) => {
style={{ gridArea: "1/3/2/4" }}
size="2em"
title="Render"
disabled={generating || !!preview}
disabled={generating || !!preview || !Reflect.has(navigator, "gpu")}
onClick={() => generate()}
>
<Image />
Expand Down
4 changes: 2 additions & 2 deletions src/components/icon-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export const IconButton: React.FC<React.PropsWithChildren<IconButtonProps>> = (
...{
cursor: props.disabled ? undefined : "pointer",
background: isDown ? theme.boldBackground : theme.background,
opacity: props.disabled ? 0.7 : 1,
opacity: props.disabled ? 0.5 : 1,
borderWidth: 1,
borderStyle: "solid",
borderStyle: props.disabled ? "dotted" : "solid",
borderColor: theme.base00,
display: "flex",
width: props.size,
Expand Down
36 changes: 25 additions & 11 deletions src/components/web-gpu-canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface WebGPUCanvasProps {
style?: React.CSSProperties;
onShaderError: (error: string) => void;
onViewChange: (update: Vector) => void;
onGpuError: (err: Error) => void;
}

interface MouseDragPoint {
Expand Down Expand Up @@ -94,14 +95,23 @@ export const WebGPUCanvas: React.FC<WebGPUCanvasProps> = (props) => {
return;
}

gpu.current = new WebGpuWidget(canvasRef.current);

gpu.current
.init(props.shader, props.vertexShader, props.fragmentShader)
.then(() => console.log("initialized"))
.catch((err) => {
console.error("Initialization error:", err);
});
navigator.gpu
.requestAdapter()
.then((adapter) => {
if (!adapter) {
props.onGpuError(new Error("Cannot get GPU adapter"));
return;
}
gpu.current = new WebGpuWidget(canvasRef.current);
return gpu.current.init(
adapter,
props.shader,
props.vertexShader,
props.fragmentShader
);
})
.then(() => console.log("GPU initialized"))
.catch((err) => props.onGpuError(err));
}, [canvasRef.current]);

React.useEffect(() => {
Expand Down Expand Up @@ -413,9 +423,13 @@ export class WebGpuWidget {
}) as unknown as GPUCanvasContext;
}

async init(shaderSrc: string, vertex: string, fragment: string) {
const adapter = await navigator.gpu.requestAdapter();
this.device = await adapter!.requestDevice();
async init(
adapter: GPUAdapter,
shaderSrc: string,
vertex: string,
fragment: string
) {
this.device = await adapter.requestDevice();

this.noiseTexture = this.createNoise(256);

Expand Down

0 comments on commit 5410ce7

Please sign in to comment.