This commit is contained in:
commit
9349a57781
14 changed files with 3973 additions and 0 deletions
45
.gitignore
vendored
Normal file
45
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Dependency directory
|
||||
node_modules
|
||||
modules/
|
||||
ts-node--*/
|
||||
rss.xml
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.ssh
|
||||
*.ppk
|
||||
v8-compile-cache-0/
|
||||
Thumbs.db
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
bin
|
||||
ts-node
|
||||
|
||||
# Personal Scripts
|
||||
*.bat
|
||||
*.ssh
|
||||
*.sh
|
||||
!system.min.js
|
||||
|
||||
# Editors
|
||||
.vscode
|
||||
.markdownlint.json
|
||||
|
||||
# Build Files
|
||||
temp
|
||||
*.js
|
||||
*.map
|
||||
!webpack.*
|
||||
|
||||
dist
|
||||
6
.prettierrc
Normal file
6
.prettierrc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"singleQuote": true
|
||||
}
|
||||
55
README.md
Normal file
55
README.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# 🔺 WebGPU Seed
|
||||
|
||||
[![License][license-img]][license-url]
|
||||
|
||||
A WebGPU repo you can use to get started with your own renderer.
|
||||
|
||||
- [🔳 Codepen Example](https://codepen.io/alaingalvan/pen/GRgvLGw)
|
||||
|
||||
- [💬 Blog Post](https://alain.xyz/blog/raw-webgpu)
|
||||
|
||||
## Setup
|
||||
|
||||
First install:
|
||||
|
||||
- [Git](https://git-scm.com/)
|
||||
|
||||
- [Node.js](https://nodejs.org/en/)
|
||||
|
||||
- A Text Editor such as [Visual Studio Code](https://code.visualstudio.com/).
|
||||
|
||||
Then type the following in any terminal your such as [VS Code's Integrated Terminal](https://code.visualstudio.com/docs/editor/integrated-terminal).
|
||||
|
||||
```bash
|
||||
# 🐑 Clone the repo
|
||||
git clone https://github.com/alaingalvan/webgpu-seed
|
||||
|
||||
# 💿 go inside the folder
|
||||
cd webgpu-seed
|
||||
|
||||
# 🔨 Start installing dependencies, building, and running at localhost:8080
|
||||
npm start
|
||||
```
|
||||
|
||||
> Refer to [this blog post on designing web libraries and apps](https://alain.xyz/blog/designing-a-web-app) for more details on Node.js, packages, etc.
|
||||
|
||||
## Project Layout
|
||||
|
||||
As your project becomes more complex, you'll want to separate files and organize your application to something more akin to a game or renderer, check out this post on [game engine architecture](https://alain.xyz/blog/game-engine-architecture) and this one on [real time renderer architecture](https://alain.xyz/blog/realtime-renderer-architectures) for more details.
|
||||
|
||||
```bash
|
||||
├─ 📂 node_modules/ # 👶 Dependencies
|
||||
│ ├─ 📁 gl-matrix # ➕ Linear Algebra
|
||||
│ └─ 📁 ... # 🕚 Other Dependencies (TypeScript, Webpack, etc.)
|
||||
├─ 📂 src/ # 🌟 Source Files
|
||||
│ ├─ 📄 index.html # 📇 Main HTML file
|
||||
│ └─ 📄 renderer.ts # 🔺 Triangle Renderer
|
||||
├─ 📄 .gitignore # 👁️ Ignore certain files in git repo
|
||||
├─ 📄 package.json # 📦 Node Package File
|
||||
├─ 📄 license.md # ⚖️ Your License (Unlicense)
|
||||
└─ 📃readme.md # 📖 Read Me!
|
||||
```
|
||||
|
||||
|
||||
[license-img]: https://img.shields.io/:license-unlicense-blue.svg?style=flat-square
|
||||
[license-url]: https://unlicense.org/
|
||||
1
definitions.d.ts
vendored
Normal file
1
definitions.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
declare module '*.wgsl';
|
||||
28
index.html
Normal file
28
index.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>WebGPU Hello Triangle</title>
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="gfx"></canvas>
|
||||
<script type="text/javascript" src="dist/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
9
license.md
Normal file
9
license.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
3357
package-lock.json
generated
Normal file
3357
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
43
package.json
Normal file
43
package.json
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "webgpu-seed",
|
||||
"version": "0.1.0",
|
||||
"description": "🔺 A simple hello triangle example introducing WebGPU.",
|
||||
"main": "dist/main.js",
|
||||
"scripts": {
|
||||
"start": "npm i && npm run build && npm run dev",
|
||||
"dev": "http-server",
|
||||
"build": "cross-env NODE_ENV=production ts-node webpack.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/alaingalvan/webgpu-seed.git"
|
||||
},
|
||||
"keywords": [
|
||||
"webgpu",
|
||||
"webgl",
|
||||
"example",
|
||||
"seed",
|
||||
"types",
|
||||
"typescript"
|
||||
],
|
||||
"author": "Alain Galvan",
|
||||
"license": "Unlicense",
|
||||
"bugs": {
|
||||
"url": "https://github.com/alaingalvan/webgpu-seed/issues"
|
||||
},
|
||||
"homepage": "https://github.com/alaingalvan/webgpu-seed#readme",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.x",
|
||||
"@webgpu/types": "^0.1.26",
|
||||
"clean-webpack-plugin": "^4.0.x",
|
||||
"cross-env": "^7.0.x",
|
||||
"http-server": "^14.1.x",
|
||||
"ts-loader": "^9.4.x",
|
||||
"ts-node": "^10.9.x",
|
||||
"typescript": "^4.9.x",
|
||||
"webpack": "^5.75.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"gl-matrix": "^3.4.3"
|
||||
}
|
||||
}
|
||||
6
src/main.ts
Normal file
6
src/main.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import Renderer from './renderer';
|
||||
|
||||
const canvas = document.getElementById('gfx') as HTMLCanvasElement;
|
||||
canvas.width = canvas.height = 640;
|
||||
const renderer = new Renderer(canvas);
|
||||
renderer.start();
|
||||
257
src/renderer.ts
Normal file
257
src/renderer.ts
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
import vertShaderCode from './shaders/triangle.vert.wgsl';
|
||||
import fragShaderCode from './shaders/triangle.frag.wgsl';
|
||||
|
||||
const positions = new Float32Array([
|
||||
1.0, -1.0, 0.0, -1.0, -1.0, 0.0, 0.0, 1.0, 0.0
|
||||
]);
|
||||
const colors = new Float32Array([
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]);
|
||||
|
||||
const indices = new Uint16Array([0, 1, 2]);
|
||||
|
||||
export default class Renderer {
|
||||
canvas: HTMLCanvasElement;
|
||||
|
||||
adapter: GPUAdapter;
|
||||
device: GPUDevice;
|
||||
queue: GPUQueue;
|
||||
|
||||
context: GPUCanvasContext;
|
||||
colorTexture: GPUTexture;
|
||||
colorTextureView: GPUTextureView;
|
||||
depthTexture: GPUTexture;
|
||||
depthTextureView: GPUTextureView;
|
||||
|
||||
positionBuffer: GPUBuffer;
|
||||
colorBuffer: GPUBuffer;
|
||||
indexBuffer: GPUBuffer;
|
||||
vertModule: GPUShaderModule;
|
||||
fragModule: GPUShaderModule;
|
||||
pipeline: GPURenderPipeline;
|
||||
|
||||
commandEncoder: GPUCommandEncoder;
|
||||
passEncoder: GPURenderPassEncoder;
|
||||
|
||||
constructor(canvas: HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
}
|
||||
|
||||
async start() {
|
||||
if (await this.initializeAPI()) {
|
||||
this.resizeBackings();
|
||||
await this.initializeResources();
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
async initializeAPI(): Promise<boolean> {
|
||||
try {
|
||||
const entry: GPU = navigator.gpu;
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.adapter = await entry.requestAdapter();
|
||||
this.device = await this.adapter.requestDevice();
|
||||
this.queue = this.device.queue;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async initializeResources() {
|
||||
const createBuffer = (
|
||||
arr: Float32Array | Uint16Array,
|
||||
usage: number
|
||||
) => {
|
||||
// 📏 Align to 4 bytes (thanks @chrimsonite)
|
||||
let desc = {
|
||||
size: (arr.byteLength + 3) & ~3,
|
||||
usage,
|
||||
mappedAtCreation: true
|
||||
};
|
||||
let buffer = this.device.createBuffer(desc);
|
||||
const writeArray =
|
||||
arr instanceof Uint16Array
|
||||
? new Uint16Array(buffer.getMappedRange())
|
||||
: new Float32Array(buffer.getMappedRange());
|
||||
writeArray.set(arr);
|
||||
buffer.unmap();
|
||||
return buffer;
|
||||
};
|
||||
|
||||
this.positionBuffer = createBuffer(positions, GPUBufferUsage.VERTEX);
|
||||
this.colorBuffer = createBuffer(colors, GPUBufferUsage.VERTEX);
|
||||
this.indexBuffer = createBuffer(indices, GPUBufferUsage.INDEX);
|
||||
|
||||
const vsmDesc = {
|
||||
code: vertShaderCode
|
||||
};
|
||||
this.vertModule = this.device.createShaderModule(vsmDesc);
|
||||
|
||||
const fsmDesc = {
|
||||
code: fragShaderCode
|
||||
};
|
||||
this.fragModule = this.device.createShaderModule(fsmDesc);
|
||||
|
||||
const positionAttribDesc: GPUVertexAttribute = {
|
||||
shaderLocation: 0, // [[location(0)]]
|
||||
offset: 0,
|
||||
format: 'float32x3'
|
||||
};
|
||||
const colorAttribDesc: GPUVertexAttribute = {
|
||||
shaderLocation: 1, // [[location(1)]]
|
||||
offset: 0,
|
||||
format: 'float32x3'
|
||||
};
|
||||
const positionBufferDesc: GPUVertexBufferLayout = {
|
||||
attributes: [positionAttribDesc],
|
||||
arrayStride: 4 * 3, // sizeof(float) * 3
|
||||
stepMode: 'vertex'
|
||||
};
|
||||
const colorBufferDesc: GPUVertexBufferLayout = {
|
||||
attributes: [colorAttribDesc],
|
||||
arrayStride: 4 * 3, // sizeof(float) * 3
|
||||
stepMode: 'vertex'
|
||||
};
|
||||
|
||||
const depthStencil: GPUDepthStencilState = {
|
||||
depthWriteEnabled: true,
|
||||
depthCompare: 'less',
|
||||
format: 'depth24plus-stencil8'
|
||||
};
|
||||
|
||||
const pipelineLayoutDesc = { bindGroupLayouts: [] };
|
||||
const layout = this.device.createPipelineLayout(pipelineLayoutDesc);
|
||||
|
||||
const vertex: GPUVertexState = {
|
||||
module: this.vertModule,
|
||||
entryPoint: 'main',
|
||||
buffers: [positionBufferDesc, colorBufferDesc]
|
||||
};
|
||||
|
||||
const colorState: GPUColorTargetState = {
|
||||
format: 'bgra8unorm'
|
||||
};
|
||||
|
||||
const fragment: GPUFragmentState = {
|
||||
module: this.fragModule,
|
||||
entryPoint: 'main',
|
||||
targets: [colorState]
|
||||
};
|
||||
|
||||
const primitive: GPUPrimitiveState = {
|
||||
frontFace: 'cw',
|
||||
cullMode: 'none',
|
||||
topology: 'triangle-list'
|
||||
};
|
||||
|
||||
const pipelineDesc: GPURenderPipelineDescriptor = {
|
||||
layout,
|
||||
|
||||
vertex,
|
||||
fragment,
|
||||
|
||||
primitive,
|
||||
depthStencil
|
||||
};
|
||||
this.pipeline = this.device.createRenderPipeline(pipelineDesc);
|
||||
}
|
||||
|
||||
resizeBackings() {
|
||||
if (!this.context) {
|
||||
this.context = this.canvas.getContext('webgpu');
|
||||
const canvasConfig: GPUCanvasConfiguration = {
|
||||
device: this.device,
|
||||
format: 'bgra8unorm',
|
||||
usage:
|
||||
GPUTextureUsage.RENDER_ATTACHMENT |
|
||||
GPUTextureUsage.COPY_SRC,
|
||||
alphaMode: 'opaque'
|
||||
};
|
||||
this.context.configure(canvasConfig);
|
||||
}
|
||||
|
||||
const depthTextureDesc: GPUTextureDescriptor = {
|
||||
size: [this.canvas.width, this.canvas.height, 1],
|
||||
dimension: '2d',
|
||||
format: 'depth24plus-stencil8',
|
||||
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
|
||||
};
|
||||
|
||||
this.depthTexture = this.device.createTexture(depthTextureDesc);
|
||||
this.depthTextureView = this.depthTexture.createView();
|
||||
}
|
||||
|
||||
encodeCommands() {
|
||||
let colorAttachment: GPURenderPassColorAttachment = {
|
||||
view: this.colorTextureView,
|
||||
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
||||
loadOp: 'clear',
|
||||
storeOp: 'store'
|
||||
};
|
||||
|
||||
const depthAttachment: GPURenderPassDepthStencilAttachment = {
|
||||
view: this.depthTextureView,
|
||||
depthClearValue: 1,
|
||||
depthLoadOp: 'clear',
|
||||
depthStoreOp: 'store',
|
||||
stencilClearValue: 0,
|
||||
stencilLoadOp: 'clear',
|
||||
stencilStoreOp: 'store'
|
||||
};
|
||||
|
||||
const renderPassDesc: GPURenderPassDescriptor = {
|
||||
colorAttachments: [colorAttachment],
|
||||
depthStencilAttachment: depthAttachment
|
||||
};
|
||||
|
||||
this.commandEncoder = this.device.createCommandEncoder();
|
||||
|
||||
// 🖌️ Encode drawing commands
|
||||
this.passEncoder = this.commandEncoder.beginRenderPass(renderPassDesc);
|
||||
this.passEncoder.setPipeline(this.pipeline);
|
||||
this.passEncoder.setViewport(
|
||||
0,
|
||||
0,
|
||||
this.canvas.width,
|
||||
this.canvas.height,
|
||||
0,
|
||||
1
|
||||
);
|
||||
this.passEncoder.setScissorRect(
|
||||
0,
|
||||
0,
|
||||
this.canvas.width,
|
||||
this.canvas.height
|
||||
);
|
||||
this.passEncoder.setVertexBuffer(0, this.positionBuffer);
|
||||
this.passEncoder.setVertexBuffer(1, this.colorBuffer);
|
||||
this.passEncoder.setIndexBuffer(this.indexBuffer, 'uint16');
|
||||
this.passEncoder.drawIndexed(3, 1);
|
||||
this.passEncoder.end();
|
||||
|
||||
this.queue.submit([this.commandEncoder.finish()]);
|
||||
}
|
||||
|
||||
render = () => {
|
||||
this.colorTexture = this.context.getCurrentTexture();
|
||||
this.colorTextureView = this.colorTexture.createView();
|
||||
|
||||
this.encodeCommands();
|
||||
|
||||
requestAnimationFrame(this.render);
|
||||
};
|
||||
}
|
||||
4
src/shaders/triangle.frag.wgsl
Normal file
4
src/shaders/triangle.frag.wgsl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
@fragment
|
||||
fn main(@location(0) inColor: vec3<f32>) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(inColor, 1.0);
|
||||
}
|
||||
13
src/shaders/triangle.vert.wgsl
Normal file
13
src/shaders/triangle.vert.wgsl
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
struct VSOut {
|
||||
@builtin(position) Position: vec4<f32>,
|
||||
@location(0) color: vec3<f32>,
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn main(@location(0) inPos: vec3<f32>,
|
||||
@location(1) inColor: vec3<f32>) -> VSOut {
|
||||
var vsOut: VSOut;
|
||||
vsOut.Position = vec4<f32>(inPos, 1.0);
|
||||
vsOut.color = inColor;
|
||||
return vsOut;
|
||||
}
|
||||
37
tsconfig.json
Normal file
37
tsconfig.json
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"lib": [
|
||||
"es2017",
|
||||
"es2017.object",
|
||||
"es2017.sharedmemory",
|
||||
"es2016",
|
||||
"es2016.array.include",
|
||||
"es2015",
|
||||
"es2015.core",
|
||||
"es2015.promise",
|
||||
"es2015.collection",
|
||||
"es5",
|
||||
"dom"
|
||||
],
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"noEmitHelpers": false,
|
||||
"sourceMap": false,
|
||||
"strictNullChecks": false,
|
||||
"jsx": "react",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": [
|
||||
"definitions.d.ts",
|
||||
"src/**/*",
|
||||
"node_modules/@webgpu/types/**/*"
|
||||
],
|
||||
"compileOnSave": false,
|
||||
"buildOnSave": false
|
||||
}
|
||||
112
webpack.ts
Normal file
112
webpack.ts
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import webpack from 'webpack';
|
||||
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
|
||||
import path from 'path';
|
||||
import { argv } from 'process';
|
||||
|
||||
let env = process.env['NODE_ENV'];
|
||||
let isProduction =
|
||||
(env && env.match(/production/)) ||
|
||||
argv.reduce((prev, cur) => prev || cur === '--production', false);
|
||||
|
||||
let config: webpack.Configuration = {
|
||||
context: path.join(__dirname, 'src'),
|
||||
entry: {
|
||||
app: './main.ts'
|
||||
},
|
||||
output: {
|
||||
filename: 'main.js',
|
||||
path: path.resolve(__dirname, 'dist')
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', 'js'],
|
||||
modules: [path.resolve(__dirname, 'src'), 'node_modules']
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
transpileOnly: true,
|
||||
compilerOptions: {
|
||||
isolatedModules: true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.wgsl/,
|
||||
type: 'asset/source'
|
||||
}
|
||||
]
|
||||
},
|
||||
node: false,
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify(
|
||||
isProduction ? 'production' : 'development'
|
||||
)
|
||||
}
|
||||
})
|
||||
],
|
||||
optimization: {
|
||||
minimize: isProduction ? true : false
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Start Build
|
||||
*/
|
||||
const compiler: webpack.Compiler = webpack(config);
|
||||
|
||||
if (!argv.reduce((prev, cur) => prev || cur === '--watch', false)) {
|
||||
compiler.run((err, stats) => {
|
||||
if (err) return console.error(err);
|
||||
|
||||
if (stats.hasErrors()) {
|
||||
let statsJson = stats.toJson();
|
||||
console.log(
|
||||
'❌' + ' · Error · ' + 'webgpu-seed failed to compile:'
|
||||
);
|
||||
for (let error of statsJson.errors) {
|
||||
console.log(error.message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
console.log(
|
||||
'✔️️' +
|
||||
' · Success · ' +
|
||||
'webgpu-seed' +
|
||||
(isProduction ? ' (production) ' : ' (development) ') +
|
||||
'built in ' +
|
||||
(+stats.endTime - +stats.startTime + ' ms.')
|
||||
);
|
||||
});
|
||||
} else {
|
||||
compiler.watch({}, (err, stats) => {
|
||||
if (err) return console.error(err);
|
||||
|
||||
if (stats.hasErrors()) {
|
||||
let statsJson = stats.toJson();
|
||||
console.log(
|
||||
'❌' + ' · Error · ' + 'webgpu-seed failed to compile:'
|
||||
);
|
||||
for (let error of statsJson.errors) {
|
||||
console.log(error.message);
|
||||
}
|
||||
console.log('\n👀 · Watching for changes... · \n');
|
||||
return;
|
||||
}
|
||||
console.log(
|
||||
'✔️️' +
|
||||
' · Success · ' +
|
||||
'webgpu-seed' +
|
||||
(isProduction ? ' (production) ' : ' (development) ') +
|
||||
'built in ' +
|
||||
(+stats.endTime - +stats.startTime + ' ms.') +
|
||||
'\n👀 · Watching for changes... · \n'
|
||||
);
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue