2024-01-01 14:58:21 -05:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2023-12-08 19:19:16 -05:00
|
|
|
|
|
|
|
// @ts-check
|
|
|
|
/// <reference path="../../core/lib.deno_core.d.ts" />
|
|
|
|
/// <reference path="../web/internal.d.ts" />
|
|
|
|
/// <reference path="../web/lib.deno_web.d.ts" />
|
|
|
|
/// <reference path="./lib.deno_webgpu.d.ts" />
|
|
|
|
|
2024-01-26 17:46:46 -05:00
|
|
|
import { primordials } from "ext:core/mod.js";
|
|
|
|
import {
|
2024-01-10 17:37:25 -05:00
|
|
|
op_webgpu_surface_configure,
|
2024-01-26 17:46:46 -05:00
|
|
|
op_webgpu_surface_create,
|
2024-01-10 17:37:25 -05:00
|
|
|
op_webgpu_surface_get_current_texture,
|
|
|
|
op_webgpu_surface_present,
|
2024-01-26 17:46:46 -05:00
|
|
|
} from "ext:core/ops";
|
2024-01-10 17:37:25 -05:00
|
|
|
const {
|
|
|
|
ObjectPrototypeIsPrototypeOf,
|
|
|
|
Symbol,
|
|
|
|
SymbolFor,
|
feat:: External webgpu surfaces / BYOW (#21835)
This PR contains the implementation of the External webgpu surfaces /
BYOW proposal. BYOW stands for "Bring your own window".
Closes #21713
Adds `Deno.UnsafeWindowSurface` ( `--unstable-webgpu` API) to the `Deno`
namespace:
```typescript
class UnsafeWindowSurface {
constructor(
system: "cocoa" | "x11" | "win32",
winHandle: Deno.PointerValue,
displayHandle: Deno.PointerValue | null
);
getContext(type: "webgpu"): GPUCanvasContext;
present(): void;
}
```
For the initial pass, I've opted to support the three major windowing
systems. The parameters correspond to the table below:
| system | winHandle | displayHandle |
| ----------------- | ---------- | ------- |
| "cocoa" (macOS) | `NSView*` | - |
| "win32" (Windows) | `HWND` | `HINSTANCE` |
| "x11" (Linux) | Xlib `Window` | Xlib `Display*` |
Ecosystem support:
- [x] deno_sdl2 (sdl2) -
[mod.ts#L1209](https://github.com/littledivy/deno_sdl2/blob/7e177bc6524750a8849c25ce421798b2e71ec943/mod.ts#L1209)
- [x] dwm (glfw) - https://github.com/deno-windowing/dwm/issues/29
- [ ] pane (winit)
<details>
<summary>Example</summary>
```typescript
// A simple clear screen pass, colors based on mouse position.
import { EventType, WindowBuilder } from "https://deno.land/x/sdl2@0.7.0/mod.ts";
const window = new WindowBuilder("sdl2 + deno + webgpu", 640, 480).build();
const [system, windowHandle, displayHandle] = window.rawHandle();
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = Deno.createWindowSurface(system, windowHandle, displayHandle);
context.configure({
device: device,
format: "bgra8unorm",
height: 480,
width: 640,
});
let r = 0.0;
let g = 0.0;
let b = 0.0;
for (const event of window.events()) {
if (event.type === EventType.Quit) {
break;
} else if (event.type === EventType.Draw) {
const textureView = context.getCurrentTexture().createView();
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
{
view: textureView,
clearValue: { r, g, b, a: 1.0 },
loadOp: "clear",
storeOp: "store",
},
],
};
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
Deno.presentGPUCanvasContext(context);
}
if (event.type === EventType.MouseMotion) {
r = event.x / 640;
g = event.y / 480;
b = 1.0 - r - g;
}
}
```
You can find more examples in the linked tracking issue.
</details>
---------
Signed-off-by: Divy Srivastava <dj.srivastava23@gmail.com>
2024-01-19 12:19:14 -05:00
|
|
|
TypeError,
|
2024-01-10 17:37:25 -05:00
|
|
|
} = primordials;
|
|
|
|
|
2023-12-08 19:19:16 -05:00
|
|
|
import * as webidl from "ext:deno_webidl/00_webidl.js";
|
|
|
|
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
|
2024-01-05 09:25:01 -05:00
|
|
|
import { loadWebGPU, webgpu } from "ext:deno_webgpu/00_init.js";
|
2023-12-08 19:19:16 -05:00
|
|
|
|
|
|
|
const _surfaceRid = Symbol("[[surfaceRid]]");
|
|
|
|
const _configuration = Symbol("[[configuration]]");
|
|
|
|
const _canvas = Symbol("[[canvas]]");
|
|
|
|
const _currentTexture = Symbol("[[currentTexture]]");
|
2024-01-05 09:25:01 -05:00
|
|
|
const _present = Symbol("[[present]]");
|
2023-12-08 19:19:16 -05:00
|
|
|
class GPUCanvasContext {
|
|
|
|
/** @type {number} */
|
|
|
|
[_surfaceRid];
|
|
|
|
[_configuration];
|
|
|
|
[_canvas];
|
|
|
|
/** @type {GPUTexture | undefined} */
|
|
|
|
[_currentTexture];
|
|
|
|
|
|
|
|
get canvas() {
|
|
|
|
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
|
|
|
return this[_canvas];
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
webidl.illegalConstructor();
|
|
|
|
}
|
|
|
|
|
|
|
|
configure(configuration) {
|
|
|
|
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
|
|
|
const prefix = "Failed to execute 'configure' on 'GPUCanvasContext'";
|
|
|
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
|
|
configuration = webidl.converters.GPUCanvasConfiguration(configuration, {
|
|
|
|
prefix,
|
|
|
|
context: "Argument 1",
|
|
|
|
});
|
|
|
|
|
2024-01-05 09:25:01 -05:00
|
|
|
const { _device, assertDevice } = webgpu;
|
2023-12-08 19:19:16 -05:00
|
|
|
this[_device] = configuration.device[_device];
|
|
|
|
this[_configuration] = configuration;
|
|
|
|
const device = assertDevice(this, {
|
|
|
|
prefix,
|
|
|
|
context: "configuration.device",
|
|
|
|
});
|
|
|
|
|
2024-01-10 17:37:25 -05:00
|
|
|
const { err } = op_webgpu_surface_configure({
|
2023-12-08 19:19:16 -05:00
|
|
|
surfaceRid: this[_surfaceRid],
|
|
|
|
deviceRid: device.rid,
|
|
|
|
format: configuration.format,
|
|
|
|
viewFormats: configuration.viewFormats,
|
|
|
|
usage: configuration.usage,
|
|
|
|
width: configuration.width,
|
|
|
|
height: configuration.height,
|
|
|
|
alphaMode: configuration.alphaMode,
|
|
|
|
});
|
|
|
|
|
|
|
|
device.pushError(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
unconfigure() {
|
2024-01-05 09:25:01 -05:00
|
|
|
const { _device } = webgpu;
|
|
|
|
|
2023-12-08 19:19:16 -05:00
|
|
|
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
|
|
|
|
|
|
|
this[_configuration] = null;
|
|
|
|
this[_device] = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
getCurrentTexture() {
|
|
|
|
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
|
|
|
const prefix =
|
|
|
|
"Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'";
|
|
|
|
|
|
|
|
if (this[_configuration] === null) {
|
|
|
|
throw new DOMException("context is not configured.", "InvalidStateError");
|
|
|
|
}
|
2024-01-05 09:25:01 -05:00
|
|
|
const { createGPUTexture, assertDevice } = webgpu;
|
2023-12-08 19:19:16 -05:00
|
|
|
|
|
|
|
const device = assertDevice(this, { prefix, context: "this" });
|
|
|
|
|
|
|
|
if (this[_currentTexture]) {
|
|
|
|
return this[_currentTexture];
|
|
|
|
}
|
|
|
|
|
2024-01-10 17:37:25 -05:00
|
|
|
const { rid } = op_webgpu_surface_get_current_texture(
|
2023-12-08 19:19:16 -05:00
|
|
|
device.rid,
|
|
|
|
this[_surfaceRid],
|
|
|
|
);
|
|
|
|
|
|
|
|
const texture = createGPUTexture(
|
|
|
|
{
|
|
|
|
size: {
|
|
|
|
width: this[_configuration].width,
|
|
|
|
height: this[_configuration].height,
|
|
|
|
depthOrArrayLayers: 1,
|
|
|
|
},
|
|
|
|
mipLevelCount: 1,
|
|
|
|
sampleCount: 1,
|
|
|
|
dimension: "2d",
|
|
|
|
format: this[_configuration].format,
|
|
|
|
usage: this[_configuration].usage,
|
|
|
|
},
|
|
|
|
device,
|
|
|
|
rid,
|
|
|
|
);
|
|
|
|
device.trackResource(texture);
|
|
|
|
this[_currentTexture] = texture;
|
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
2024-01-05 09:25:01 -05:00
|
|
|
// Required to present the texture; browser don't need this.
|
|
|
|
[_present]() {
|
|
|
|
const { assertDevice } = webgpu;
|
|
|
|
|
2023-12-08 19:19:16 -05:00
|
|
|
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
|
|
|
const prefix = "Failed to execute 'present' on 'GPUCanvasContext'";
|
|
|
|
const device = assertDevice(this[_currentTexture], {
|
|
|
|
prefix,
|
|
|
|
context: "this",
|
|
|
|
});
|
2024-01-10 17:37:25 -05:00
|
|
|
op_webgpu_surface_present(device.rid, this[_surfaceRid]);
|
2023-12-08 19:19:16 -05:00
|
|
|
this[_currentTexture].destroy();
|
|
|
|
this[_currentTexture] = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
|
|
|
|
return inspect(
|
|
|
|
createFilteredInspectProxy({
|
|
|
|
object: this,
|
|
|
|
evaluate: ObjectPrototypeIsPrototypeOf(GPUCanvasContextPrototype, this),
|
|
|
|
keys: [
|
|
|
|
"canvas",
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
inspectOptions,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const GPUCanvasContextPrototype = GPUCanvasContext.prototype;
|
|
|
|
|
|
|
|
function createCanvasContext(options) {
|
2024-01-05 09:25:01 -05:00
|
|
|
// lazy load webgpu if needed
|
|
|
|
loadWebGPU();
|
|
|
|
|
2023-12-08 19:19:16 -05:00
|
|
|
const canvasContext = webidl.createBranded(GPUCanvasContext);
|
|
|
|
canvasContext[_surfaceRid] = options.surfaceRid;
|
|
|
|
canvasContext[_canvas] = options.canvas;
|
|
|
|
return canvasContext;
|
|
|
|
}
|
|
|
|
|
feat:: External webgpu surfaces / BYOW (#21835)
This PR contains the implementation of the External webgpu surfaces /
BYOW proposal. BYOW stands for "Bring your own window".
Closes #21713
Adds `Deno.UnsafeWindowSurface` ( `--unstable-webgpu` API) to the `Deno`
namespace:
```typescript
class UnsafeWindowSurface {
constructor(
system: "cocoa" | "x11" | "win32",
winHandle: Deno.PointerValue,
displayHandle: Deno.PointerValue | null
);
getContext(type: "webgpu"): GPUCanvasContext;
present(): void;
}
```
For the initial pass, I've opted to support the three major windowing
systems. The parameters correspond to the table below:
| system | winHandle | displayHandle |
| ----------------- | ---------- | ------- |
| "cocoa" (macOS) | `NSView*` | - |
| "win32" (Windows) | `HWND` | `HINSTANCE` |
| "x11" (Linux) | Xlib `Window` | Xlib `Display*` |
Ecosystem support:
- [x] deno_sdl2 (sdl2) -
[mod.ts#L1209](https://github.com/littledivy/deno_sdl2/blob/7e177bc6524750a8849c25ce421798b2e71ec943/mod.ts#L1209)
- [x] dwm (glfw) - https://github.com/deno-windowing/dwm/issues/29
- [ ] pane (winit)
<details>
<summary>Example</summary>
```typescript
// A simple clear screen pass, colors based on mouse position.
import { EventType, WindowBuilder } from "https://deno.land/x/sdl2@0.7.0/mod.ts";
const window = new WindowBuilder("sdl2 + deno + webgpu", 640, 480).build();
const [system, windowHandle, displayHandle] = window.rawHandle();
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = Deno.createWindowSurface(system, windowHandle, displayHandle);
context.configure({
device: device,
format: "bgra8unorm",
height: 480,
width: 640,
});
let r = 0.0;
let g = 0.0;
let b = 0.0;
for (const event of window.events()) {
if (event.type === EventType.Quit) {
break;
} else if (event.type === EventType.Draw) {
const textureView = context.getCurrentTexture().createView();
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
{
view: textureView,
clearValue: { r, g, b, a: 1.0 },
loadOp: "clear",
storeOp: "store",
},
],
};
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
Deno.presentGPUCanvasContext(context);
}
if (event.type === EventType.MouseMotion) {
r = event.x / 640;
g = event.y / 480;
b = 1.0 - r - g;
}
}
```
You can find more examples in the linked tracking issue.
</details>
---------
Signed-off-by: Divy Srivastava <dj.srivastava23@gmail.com>
2024-01-19 12:19:14 -05:00
|
|
|
// External webgpu surfaces
|
|
|
|
|
|
|
|
// TODO(@littledivy): This will extend `OffscreenCanvas` when we add it.
|
|
|
|
class UnsafeWindowSurface {
|
|
|
|
#ctx;
|
|
|
|
#surfaceRid;
|
|
|
|
|
|
|
|
constructor(system, win, display) {
|
2024-01-23 09:15:40 -05:00
|
|
|
this.#surfaceRid = op_webgpu_surface_create(system, win, display);
|
feat:: External webgpu surfaces / BYOW (#21835)
This PR contains the implementation of the External webgpu surfaces /
BYOW proposal. BYOW stands for "Bring your own window".
Closes #21713
Adds `Deno.UnsafeWindowSurface` ( `--unstable-webgpu` API) to the `Deno`
namespace:
```typescript
class UnsafeWindowSurface {
constructor(
system: "cocoa" | "x11" | "win32",
winHandle: Deno.PointerValue,
displayHandle: Deno.PointerValue | null
);
getContext(type: "webgpu"): GPUCanvasContext;
present(): void;
}
```
For the initial pass, I've opted to support the three major windowing
systems. The parameters correspond to the table below:
| system | winHandle | displayHandle |
| ----------------- | ---------- | ------- |
| "cocoa" (macOS) | `NSView*` | - |
| "win32" (Windows) | `HWND` | `HINSTANCE` |
| "x11" (Linux) | Xlib `Window` | Xlib `Display*` |
Ecosystem support:
- [x] deno_sdl2 (sdl2) -
[mod.ts#L1209](https://github.com/littledivy/deno_sdl2/blob/7e177bc6524750a8849c25ce421798b2e71ec943/mod.ts#L1209)
- [x] dwm (glfw) - https://github.com/deno-windowing/dwm/issues/29
- [ ] pane (winit)
<details>
<summary>Example</summary>
```typescript
// A simple clear screen pass, colors based on mouse position.
import { EventType, WindowBuilder } from "https://deno.land/x/sdl2@0.7.0/mod.ts";
const window = new WindowBuilder("sdl2 + deno + webgpu", 640, 480).build();
const [system, windowHandle, displayHandle] = window.rawHandle();
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = Deno.createWindowSurface(system, windowHandle, displayHandle);
context.configure({
device: device,
format: "bgra8unorm",
height: 480,
width: 640,
});
let r = 0.0;
let g = 0.0;
let b = 0.0;
for (const event of window.events()) {
if (event.type === EventType.Quit) {
break;
} else if (event.type === EventType.Draw) {
const textureView = context.getCurrentTexture().createView();
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
{
view: textureView,
clearValue: { r, g, b, a: 1.0 },
loadOp: "clear",
storeOp: "store",
},
],
};
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
Deno.presentGPUCanvasContext(context);
}
if (event.type === EventType.MouseMotion) {
r = event.x / 640;
g = event.y / 480;
b = 1.0 - r - g;
}
}
```
You can find more examples in the linked tracking issue.
</details>
---------
Signed-off-by: Divy Srivastava <dj.srivastava23@gmail.com>
2024-01-19 12:19:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
getContext(context) {
|
|
|
|
if (context !== "webgpu") {
|
|
|
|
throw new TypeError("Only 'webgpu' context is supported.");
|
|
|
|
}
|
|
|
|
this.#ctx = createCanvasContext({ surfaceRid: this.#surfaceRid });
|
|
|
|
return this.#ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
present() {
|
|
|
|
this.#ctx[_present]();
|
|
|
|
}
|
2024-01-05 09:25:01 -05:00
|
|
|
}
|
|
|
|
|
feat:: External webgpu surfaces / BYOW (#21835)
This PR contains the implementation of the External webgpu surfaces /
BYOW proposal. BYOW stands for "Bring your own window".
Closes #21713
Adds `Deno.UnsafeWindowSurface` ( `--unstable-webgpu` API) to the `Deno`
namespace:
```typescript
class UnsafeWindowSurface {
constructor(
system: "cocoa" | "x11" | "win32",
winHandle: Deno.PointerValue,
displayHandle: Deno.PointerValue | null
);
getContext(type: "webgpu"): GPUCanvasContext;
present(): void;
}
```
For the initial pass, I've opted to support the three major windowing
systems. The parameters correspond to the table below:
| system | winHandle | displayHandle |
| ----------------- | ---------- | ------- |
| "cocoa" (macOS) | `NSView*` | - |
| "win32" (Windows) | `HWND` | `HINSTANCE` |
| "x11" (Linux) | Xlib `Window` | Xlib `Display*` |
Ecosystem support:
- [x] deno_sdl2 (sdl2) -
[mod.ts#L1209](https://github.com/littledivy/deno_sdl2/blob/7e177bc6524750a8849c25ce421798b2e71ec943/mod.ts#L1209)
- [x] dwm (glfw) - https://github.com/deno-windowing/dwm/issues/29
- [ ] pane (winit)
<details>
<summary>Example</summary>
```typescript
// A simple clear screen pass, colors based on mouse position.
import { EventType, WindowBuilder } from "https://deno.land/x/sdl2@0.7.0/mod.ts";
const window = new WindowBuilder("sdl2 + deno + webgpu", 640, 480).build();
const [system, windowHandle, displayHandle] = window.rawHandle();
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = Deno.createWindowSurface(system, windowHandle, displayHandle);
context.configure({
device: device,
format: "bgra8unorm",
height: 480,
width: 640,
});
let r = 0.0;
let g = 0.0;
let b = 0.0;
for (const event of window.events()) {
if (event.type === EventType.Quit) {
break;
} else if (event.type === EventType.Draw) {
const textureView = context.getCurrentTexture().createView();
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
{
view: textureView,
clearValue: { r, g, b, a: 1.0 },
loadOp: "clear",
storeOp: "store",
},
],
};
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
Deno.presentGPUCanvasContext(context);
}
if (event.type === EventType.MouseMotion) {
r = event.x / 640;
g = event.y / 480;
b = 1.0 - r - g;
}
}
```
You can find more examples in the linked tracking issue.
</details>
---------
Signed-off-by: Divy Srivastava <dj.srivastava23@gmail.com>
2024-01-19 12:19:14 -05:00
|
|
|
export { GPUCanvasContext, UnsafeWindowSurface };
|