Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pkg-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
run: pnpm nightly-build

- name: Publish (pkg.pr.new)
run: pnpm exec pkg-pr-new publish './packages/typegpu' './packages/typegpu-noise' './packages/unplugin-typegpu' --json output.json --comment=off --pnpm --no-compact
run: pnpm exec pkg-pr-new publish './packages/typegpu' './packages/typegpu-noise' './packages/typegpu-react' './packages/unplugin-typegpu' --json output.json --comment=off --pnpm --no-compact
- name: Post or update comment
uses: actions/github-script@v6
with:
Expand Down
14 changes: 11 additions & 3 deletions apps/typegpu-docs/src/examples/react/spinning-triangle/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,21 @@ function App() {

const { ref, ctxRef } = useConfigureContext({ alphaMode: 'premultiplied' });
useFrame(({ elapsedSeconds }) => {
if (!ctxRef.current) return;
const ctx = ctxRef.current;
if (!ctx) return;

time.write(elapsedSeconds);
renderPipeline.withColorAttachment({ view: ctxRef.current }).draw(3);
renderPipeline.withColorAttachment({ view: ctx }).draw(3);
});

return <canvas ref={ref} className="aspect-square h-full max-h-[100vw]" />;
return (
<>
<canvas ref={ref} className="aspect-square h-full max-h-[100vw]" />
<button type="button" onClick={() => root.destroy()}>
Destroy
</button>
</>
);
}

// #region Example controls and cleanup
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"test:browser": "vitest run --browser.enabled --project browser",
"test:browser:watch": "vitest --browser.enabled --project browser",
"test:coverage": "vitest --coverage run",
"nightly-build": "SKIP_TESTS=true pnpm --filter typegpu --filter @typegpu/noise --filter unplugin-typegpu prepublishOnly --skip-publish-tag-check",
"nightly-build": "SKIP_TESTS=true pnpm --filter typegpu --filter @typegpu/noise --filter @typegpu/react --filter unplugin-typegpu prepublishOnly --skip-publish-tag-check",
"changes": "tgpu-dev-cli changes"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/typegpu-react/src/browser/use-configure-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ const useResizer: UseResizerHook = () => {
return;
}

el.width = Math.round(box.inlineSize * dpr);
el.height = Math.round(box.blockSize * dpr);
el.width = Math.max(1, Math.round(box.inlineSize * dpr));
el.height = Math.max(1, Math.round(box.blockSize * dpr));
});

const attachResizing = useEffectEvent((el: HTMLCanvasElement | OffscreenCanvas | null) => {
Expand Down
41 changes: 40 additions & 1 deletion packages/typegpu-react/src/core/root-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import tgpu, { type TgpuRoot } from 'typegpu';
Expand Down Expand Up @@ -58,6 +59,7 @@
| { status: 'rejected'; error: unknown };

interface RootContext {
readonly rootRequested: boolean;
initOrGetRoot(): RootContextResult;
}

Expand All @@ -71,6 +73,10 @@
promise: tgpu.init().then(
(root) => {
this.#result = { status: 'resolved', value: root };
root.device.lost.then(() => {
// TODO: React to reason
this.#result = undefined;
});

Check warning on line 79 in packages/typegpu-react/src/core/root-context.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

typescript-eslint(no-floating-promises)

Promises must be awaited, add void operator to ignore.
return root;
},
(error) => {
Expand All @@ -83,6 +89,10 @@

return this.#result;
}

get rootRequested() {
return this.#result !== undefined;
}
}

class ExistingRootContext implements RootContext {
Expand All @@ -95,6 +105,10 @@
initOrGetRoot(): RootContextResult {
return this.result;
}

get rootRequested() {
return this.result !== undefined;
}
}

/**
Expand Down Expand Up @@ -144,7 +158,32 @@
if (result.status === 'rejected') {
throw result.error as Error;
}
return result.status === 'pending' ? use(result.promise) : result.value;
const root = result.status === 'pending' ? use(result.promise) : result.value;

// NOTE: Useful docs: https://toji.dev/webgpu-best-practices/device-loss.html
const [_, rerender] = useState(0);
const lostRootsRef = useRef<WeakSet<TgpuRoot>>(new WeakSet());
useEffect(() => {
let cancelled = false;

root.device.lost.then(() => {
// TODO: React to reason
if (cancelled || lostRootsRef.current.has(root)) {
return;
}
lostRootsRef.current.add(root);

if (!context.rootRequested) {
rerender((a) => a + 1);
}
});

Check warning on line 179 in packages/typegpu-react/src/core/root-context.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

typescript-eslint(no-floating-promises)

Promises must be awaited, add void operator to ignore.

return () => {
cancelled = true;
};
}, [root]);

return root;
}

/**
Expand Down
Loading