blob: 0dce7fdff60e4a6e9d95848117f62b65a87167dc [file]
async function helloCube() {
if (!navigator.gpu || GPUBufferUsage.COPY_SRC === undefined) {
document.body.className = 'error';
return;
}
const video = document.querySelector('video');
await video.play();
const canvas = document.querySelector("canvas");
canvas.width = 600;
canvas.height = 600;
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const preferredBackingFormat = navigator.gpu.getPreferredCanvasFormat();
/*** Vertex Buffer Setup ***/
/* Vertex Data */
const vertexStride = 10 * 4;
const vertexDataSize = vertexStride * 36;
/* GPUBufferDfescriptor */
const vertexDataBufferDescriptor = {
size: vertexDataSize,
usage: GPUBufferUsage.VERTEX,
mappedAtCreation: true
};
/* GPUBuffer */
const vertexBuffer = device.createBuffer(vertexDataBufferDescriptor);
const vertexWriteArray = new Float32Array(vertexBuffer.getMappedRange());
vertexWriteArray.set([
// float4 position, float4 color, float2 uv
.5, -.5, .5, 1, 1, 0, 1, 1, 1, 1,
-.5, -.5, .5, 1, 0, 0, 1, 1, 0, 1,
-.5, -.5, -.5, 1, 0, 0, 0, 1, 0, 0,
.5, -.5, -.5, 1, 1, 0, 0, 1, 1, 0,
.5, -.5, .5, 1, 1, 0, 1, 1, 1, 1,
-.5, -.5, -.5, 1, 0, 0, 0, 1, 0, 0,
.5, .5, .5, 1, 1, 1, 1, 1, 1, 1,
.5, -.5, .5, 1, 1, 0, 1, 1, 0, 1,
.5, -.5, -.5, 1, 1, 0, 0, 1, 0, 0,
.5, .5, -.5, 1, 1, 1, 0, 1, 1, 0,
.5, .5, .5, 1, 1, 1, 1, 1, 1, 1,
.5, -.5, -.5, 1, 1, 0, 0, 1, 0, 0,
-.5, .5, .5, 1, 0, 1, 1, 1, 1, 1,
.5, .5, .5, 1, 1, 1, 1, 1, 0, 1,
.5, .5, -.5, 1, 1, 1, 0, 1, 0, 0,
-.5, .5, -.5, 1, 0, 1, 0, 1, 1, 0,
-.5, .5, .5, 1, 0, 1, 1, 1, 1, 1,
.5, .5, -.5, 1, 1, 1, 0, 1, 0, 0,
-.5, -.5, .5, 1, 0, 0, 1, 1, 1, 1,
-.5, .5, .5, 1, 0, 1, 1, 1, 0, 1,
-.5, .5, -.5, 1, 0, 1, 0, 1, 0, 0,
-.5, -.5, -.5, 1, 0, 0, 0, 1, 1, 0,
-.5, -.5, .5, 1, 0, 0, 1, 1, 1, 1,
-.5, .5, -.5, 1, 0, 1, 0, 1, 0, 0,
.5, .5, .5, 1, 1, 1, 1, 1, 1, 1,
-.5, .5, .5, 1, 0, 1, 1, 1, 0, 1,
-.5, -.5, .5, 1, 0, 0, 1, 1, 0, 0,
-.5, -.5, .5, 1, 0, 0, 1, 1, 0, 0,
.5, -.5, .5, 1, 1, 0, 1, 1, 1, 0,
.5, .5, .5, 1, 1, 1, 1, 1, 1, 1,
.5, -.5, -.5, 1, 1, 0, 0, 1, 1, 1,
-.5, -.5, -.5, 1, 0, 0, 0, 1, 0, 1,
-.5, .5, -.5, 1, 0, 1, 0, 1, 0, 0,
.5, .5, -.5, 1, 1, 1, 0, 1, 1, 0,
.5, -.5, -.5, 1, 1, 0, 0, 1, 1, 1,
-.5, .5, -.5, 1, 0, 1, 0, 1, 0, 0,
]);
vertexBuffer.unmap();
const uniformBufferSize = 12;
const uniformBuffer = device.createBuffer({
size: uniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
/* GPUTexture */
const image = new Image();
const imageLoadPromise = new Promise(resolve => {
image.onload = () => resolve();
image.src = "resources/webkit-logo.png"
});
await Promise.resolve(imageLoadPromise);
const textureSize = {
width: image.width,
height: image.height,
depth: 1
};
const textureDescriptor = {
size: textureSize,
arrayLayerCount: 1,
mipLevelCount: 1,
sampleCount: 1,
dimension: "2d",
format: preferredBackingFormat,
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
};
const texture = device.createTexture(textureDescriptor);
const imageBitmap = await createImageBitmap(image);
device.queue.copyExternalImageToTexture(
{ source: imageBitmap },
{ texture: texture },
[image.width, image.height]
);
const sampler = device.createSampler({
magFilter: "linear",
minFilter: "linear"
});
const shader = `
struct VertexIn {
@location(0) position: vec4<f32>,
@location(1) color: vec4<f32>,
@location(2) uv: vec2<f32>,
};
struct VertexOut {
@builtin(position) position: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) uv: vec2<f32>,
};
@group(0) @binding(0) var<uniform> time : vec3<f32>;
@group(0) @binding(1) var colorTexture: texture_2d<f32> ;
@group(0) @binding(2) var colorTexture1: texture_2d<f32> ;
@group(0) @binding(3) var<uniform> uvRemap: mat3x3<f32> ;
@group(0) @binding(4) var<uniform> csc: mat4x3<f32> ;
@group(0) @binding(5) var textureSampler : sampler;
@vertex
fn vsmain(@builtin(vertex_index) VertexIndex: u32, vin: VertexIn) -> VertexOut
{
let alpha = time[0];
let beta = time[1];
let gamma = time[2];
let cA = cos(alpha);
let sA = sin(alpha);
let cB = cos(beta);
let sB = sin(beta);
let cG = cos(gamma);
let sG = sin(gamma);
let m = mat4x4(cA * cB, sA * cB, -sB, 0.,
cA*sB*sG - sA*cG, sA*sB*sG + cA*cG, cB * sG, 0.,
cA*sB*cG + sA*sG, sA*sB*cG - cA*sG, cB * cG, 0.,
0., 0., 0., 1.);
var vout : VertexOut;
vout.position = vin.position * m;
vout.position.z = (vout.position.z + 0.5) * 0.5;
vout.color = vin.color;
vout.uv = vin.uv;
return vout;
}
@fragment
fn fsmain(in: VertexOut) -> @location(0) vec4<f32>
{
var coords = vec3(in.uv, 1.0) * uvRemap;
coords = vec3(in.uv, 1.0);
let uvRemap2 = mat3x2(1.0, 0.0,
0.0, -1.0,
0.0, 1.0);
let coords2 = uvRemap2 * vec3(in.uv, 1.0);
let textureColor0 = textureSample(colorTexture, textureSampler, coords2.rg);
let textureColor1 = textureSample(colorTexture1, textureSampler, coords2.rg);
let ycbcr = vec3<f32>(textureColor0.r, textureColor1.rg);
let localCsc = mat4x3(1.16895, 1.16895, 1.16895,
-0.00012, -0.21399, 2.12073,
1.79968, -0.53503, 0.00012,
-0.97284, 0.30145, -1.13348);
var textureColor = csc * vec4(ycbcr, 1.0);
textureColor = localCsc * vec4(ycbcr, 1.0);
return 0.005 * in.color + 0.995 * vec4(textureColor, 1.0);
}
`;
const shaderModule = device.createShaderModule({ code: shader });
/* GPUPipelineStageDescriptors */
const vertexStageDescriptor = {
module: shaderModule,
entryPoint: "vsmain",
buffers: [{
arrayStride: vertexStride,
stepMode: "vertex",
attributes: [{
format: "float32x4",
offset: 0,
shaderLocation: 0,
}, {
format: "float32x4",
offset: 4 * 4,
shaderLocation: 1,
}, {
format: "float32x2",
offset: 8 * 4,
shaderLocation: 2,
}],
}]
};
const fragmentStageDescriptor = { module: shaderModule, entryPoint: "fsmain", targets: [ {format: preferredBackingFormat }, ], };
/* GPURenderPipelineDescriptor */
const renderPipelineDescriptor = {
layout: "auto",
vertex: vertexStageDescriptor,
fragment: fragmentStageDescriptor,
primitive: {
topology: "triangle-list",
cullMode: "back"
},
};
/* GPURenderPipeline */
const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
/*** Swap Chain Setup ***/
function frameUpdate() {
/*** Shader Setup ***/
const uniformBindGroup = device.createBindGroup({
layout: renderPipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: {
buffer: uniformBuffer,
offset: 0
},
},
{
binding: 1,
resource: device.importExternalTexture({
source: video,
}),
},
{
binding: 2,
resource: sampler,
},
],
});
const secondsBuffer = new Float32Array(3);
const d = new Date();
const seconds = d.getMilliseconds() / 1000.0 + d.getSeconds();
secondsBuffer.set([seconds*10 * (6.28318530718 / 60.0),
seconds*5 * (6.28318530718 / 60.0),
seconds*1 * (6.28318530718 / 60.0)]);
device.queue.writeBuffer(uniformBuffer, 0, secondsBuffer);
const gpuContext = canvas.getContext("webgpu");
/* GPUCanvasConfiguration */
const canvasConfiguration = { device: device, format: preferredBackingFormat };
gpuContext.configure(canvasConfiguration);
/* GPUTexture */
const currentTexture = gpuContext.getCurrentTexture();
/*** Render Pass Setup ***/
/* Acquire Texture To Render To */
/* GPUTextureView */
const renderAttachment = currentTexture.createView();
/* GPUColor */
const darkBlue = { r: 0.15, g: 0.15, b: 0.5, a: 1 };
/* GPURenderPassColorATtachmentDescriptor */
const colorAttachmentDescriptor = {
view: renderAttachment,
loadOp: "clear",
storeOp: "store",
clearColor: darkBlue
};
/* GPURenderPassDescriptor */
const renderPassDescriptor = { colorAttachments: [colorAttachmentDescriptor] };
/*** Rendering ***/
/* GPUCommandEncoder */
const commandEncoder = device.createCommandEncoder();
/* GPURenderPassEncoder */
const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
renderPassEncoder.setPipeline(renderPipeline);
const vertexBufferSlot = 0;
renderPassEncoder.setVertexBuffer(vertexBufferSlot, vertexBuffer);
renderPassEncoder.setBindGroup(0, uniformBindGroup);
renderPassEncoder.draw(36); // 36 vertices
renderPassEncoder.end();
/* GPUComamndBuffer */
const commandBuffer = commandEncoder.finish();
/* GPUQueue */
const queue = device.queue;
queue.submit([commandBuffer]);
requestAnimationFrame(frameUpdate);
}
requestAnimationFrame(frameUpdate);
}
window.addEventListener("DOMContentLoaded", helloCube);