• Stars
    star
    915
  • Rank 49,917 (Top 1.0 %)
  • Language
    C++
  • License
    MIT License
  • Created about 6 years ago
  • Updated almost 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Vulkan API for JavaScript/TypeScript



NPM Version Vulkan Header Version NPM Downloads

This is a low-abstraction, high-performance Vulkan API with interfaces for JavaScript and TypeScript.

Platforms:

nvk comes with pre-built N-API binaries for the following platforms:

OS Status
Windows โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โœ” โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ
Linux โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โœ” โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ
MacOS โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โœ” โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ โ€Œโ€Œ

Examples:

Real-Time RTX Ray Tracer

Why Vulkan in JavaScript?

  • Vulkan is a binding friendly API
  • Less overhead than WebGL/OpenGL
  • Essential features like Compute, Geometry and Tesselation shaders
  • Support for Real-Time Ray Tracing, Mesh shaders, ...
  • Supports Multithreading
  • Low-level memory control using ArrayBuffers

This project is a thin layer on top of native Vulkan, built with simplicity and performance in mind. Native memory for Vulkan gets constructed entirely within JavaScript to reduce trampolining overhead. Bounding checks and type validations are enabled by default, but can be disabled using the --disable-validation-checks flag.

Installation:

npm install nvk

Example:

In most cases the bindings match the C99 style of Vulkan. This allows you to follow existing C/C++ tutorials, but write the implementation itself with nvk. Note that both interfaces end up with a similar amount of code. Optionally you can use some syntactic sugar to write things quicker.

JavaScript/TypeScript:

let instance = new VkInstance();
let appInfo = new VkApplicationInfo();
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "App";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_2;

let validationLayers = [
  "VK_LAYER_KHRONOS_validation"
];
let instanceInfo = new VkInstanceCreateInfo();
instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceInfo.pApplicationInfo = appInfo;
instanceInfo.ppEnabledLayerNames = validationLayers;
instanceInfo.enabledLayerCount = validationLayers.length;
vkCreateInstance(instanceInfo, null, instance);

C++:

VkInstance instance;
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "App";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_2;

const std::vector<const char*> validationLayers = {
  "VK_LAYER_KHRONOS_validation"
};
VkInstanceCreateInfo instanceInfo = {};
instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceInfo.pApplicationInfo = &appInfo;
instanceInfo.ppEnabledLayerNames = validationLayers.data();
instanceInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
vkCreateInstance(&instanceInfo, nullptr, &instance);

TypeScript:

To use the TypeScript definition file, simply follow the installation steps above or use this example as a reference. Afterwards in your .ts file, import and use nvk as follows:

import * as nvk from "nvk";

Object.assign(global, nvk);

let win = new VulkanWindow({
  width: 480,
  height: 320,
  title: "typescript-example"
});

let appInfo = new VkApplicationInfo({
  pApplicationName: "Hello!",
  applicationVersion: VK_MAKE_VERSION(1, 0, 0),
  pEngineName: "No Engine",
  engineVersion: VK_MAKE_VERSION(1, 0, 0),
  apiVersion: VK_API_VERSION_1_2
});

Also note, that it is recommended to enable the --strict mode in the TS compiler options and use the latest version of the TS compiler.

Syntactic Sugar:

The API gives you some sugar to write things quicker, but still gives you the option to write everything explicitly

sType auto-filling

sType members get auto-filled, but you can still set them yourself

let appInfo = new VkApplicationInfo();
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;

Becomes:

let appInfo = new VkApplicationInfo(); // sType auto-filled

Structure creation shortcut

Instead of:

let offset = new VkOffset2D();
offset.x = 0;
offset.y = 0;
let extent = new VkExtent2D();
extent.width = 640;
extent.height = 480;
let renderArea = new VkRect2D();
renderArea.offset = offset;
renderArea.extent = extent;

You can write:

let renderArea = new VkRect2D({
  offset: new VkOffset2D({ x: 0, y: 0 }),
  extent: new VkExtent2D({ width: 640, height: 480 })
});

Nested Structures

nvk allows to use nested structures to improve memory usage and performance. A nested structure is pre-allocated automatically and shares the native memory of it's top-level structure. You can use the --enable-shared-memory-hints flag, to get hints where you could've used a nested structure in your code.

Instead of:

let scissor = new VkRect2D();
scissor.offset = new VkOffset2D();
scissor.extent = new VkExtent2D();
scissor.offset.x = 0;
scissor.offset.y = 0;
scissor.extent.width = 480;
scissor.extent.height = 320;

You can write:

let scissor = new VkRect2D();
scissor.offset.x = 0;
scissor.offset.y = 0;
scissor.extent.width = 480;
scissor.extent.height = 320;

Cached Structures

To reduce GC pressure, nvk allows to use cached structures. Instead of having to allocate a structure every time on the heap, nvk allows to use a caching mechanism to mimic stack allocation.

Imagine the following situation:

let commandBuffers = [...Array(8)].map(() => new VkCommandBuffer());
for (let ii = 0; ii < commandBuffers.length; ++ii) {
  let commandBufferBeginInfo = new VkCommandBufferBeginInfo();
  vkBeginCommandBuffer(commandBuffers[ii], cmdBufferBeginInfo);
  ...
};

This results in 8 allocations of VkCommandBufferBeginInfo structures. When this code gets executed in frequently used code sections, the heap pressure will be high.

Now nvk has a mechanism to simulate stack allocation:

let commandBuffers = [...Array(8)].map(() => new VkCommandBuffer());
for (let ii = 0; ii < commandBuffers.length; ++ii) {
  let commandBufferBeginInfo = VkCommandBufferBeginInfo("0x0");
  vkBeginCommandBuffer(commandBuffers[ii], cmdBufferBeginInfo);
  ...
};

On the first iteration of the loop, a VkCommandBufferBeginInfo structure is allocated on the heap but also gets cached internally. Based on the String id 0x0 you have added, nvk uses this id to identify this structure and return a cached one whenever this code gets executed again.

Obviously, you don't want to add your own ids to each structure by hand. There is a rollup plugin, which detects nvk structure calls (when invoked without new) and inserts a unique id automatically. You can find the rollup plugin here and a project example here.

Project Structure:

  • docs: generated vulkan documentation files
  • generator: code for binding generation
  • generated: the generated binding code
  • examples: ready-to-run examples
  • lib: required third party libs
  • src: classes for e.g. window creation

This tool uses a new JavaScript type called BigInt to represent memory addresses returned by Vulkan. The BigInt type was recently added, so make sure you use a recent node.js version.

Binding Code Generator:

The Generator generates code based on a vk.xml specification file. It first converts the XML file into an AST, which is then used by the code generator. Currently more than ~300.000 lines of code get generated, where ~60.000 lines are JavaScript, ~50.000 lines are TypeScript, ~40.000 lines are C++ code and the rest code for the documentation and AST.

Starting from version 0.5.0, nvk now uses a concept called Hybrid bindings, which reduces the overhead of JavaScript<->C++ context switching. Structures tend to have many members, where each member has to be a getter/setter function. Before this change, these getters/setters were written in C++, so there were many tiny context switches. Now the native memory of Structures and Handles just get filled entirely within JavaScript (see the file here), resulting in much less overhead and much simpler binding and generator code.

Linking:

This section is of interest, if you have an existing C++ project and want to link against this one.

This project mostly doesn't requires to be linked against. All structures and handles have properties to access the underlying memory directly. For example, see VkApplicationInfo (#Default Properties).

Structures and handles come with these 3 properties:

  • .memoryBuffer: Reference to the underlying native memory, wrapped inside an ArrayBuffer
  • .memoryAddress: Native address (BigInt) of memoryBuffer. To convert BigInt into a native type, see e.g. this document
  • .byteLength: Total native bytelength of the structure/handle

Build Instructions:

Warning: You may want to skip this section, as nvk uses N-API and ships pre-compiled binaries. This section is only of interest if you want to generate and build the bindings yourself, which is likely not your intention!

This project requires two-pass compilation which means, after initially compiling the bindings, a second compilation is required. This is necessary, because this project constructs Vulkan memory entirely from within JavaScript.

  • At the first compilation, memory layouts of vulkan structures get stored inside a JSON file
  • At the second pass, these memory layout then get used to inline memory offsets inside the JavaScript binding code

Requirements:

  • node.js >= v10.9.0 recommended

Windows:

If you already have Visual Studio >= 15 installed, then just make sure to have Python 2.7.x installed.

If you don't have Visual Studio, then install the following package:

npm install --global --production windows-build-tools

Now install the corresponding Vulkan SDK version from here.

Next, clone this repository.

To generate and compile the bindings, run:

npm run generate --vkversion=x
npm run build --vkversion=x

Linux:

Download and setup the corresponding Vulkan SDK version from here.

Follow the guide on how to correctly setup the SDK. Make sure that the environment variables are correctly set, e.g. echo $VULKAN_SDK.

Next, clone this repository.

To generate and compile the bindings, run:

npm run generate --vkversion=x
npm run build --vkversion=x

MacOS:

Download and setup the corresponding Vulkan SDK version from here.

Follow the guide on how to correctly setup the SDK. Make sure that the environment variables are correctly set, e.g. echo $VULKAN_SDK.

Next, clone this repository.

To generate and compile the bindings, run:

npm run generate --vkversion=x
npm run build --vkversion=x

Releasing:

When updating the bindings to a newer Vulkan version, or other drastic changes were made:

  • Update the package.json:
    • Add the previously used Vulkan version to config.OUTDATED
    • Add the new Vulkan version to config.POST_DEFAULT_BINDING_VERSION
    • Edit the TS type reference lines at the beginning of index.js to contain the new Vulkan version
  • Update the .npmignore
    • Make sure that the config.OUTDATED section in package.json matches the content in there, so previous/outdated Vulkan bindings dont't get included in the npm package
  • Update the README.md to contain links to the new Vulkan version
  • Update the Website link of the repository

Publishing:

When a new version of this project should be published (e.g. to npm), consider the following steps:

  • Update the package.json:
    • Update the npm package version (if necessary)
  • Make sure that the bindings for all platforms were generated with:
    • The --docs flag enabled, to include a documentation
    • The --disable-minification flag not enabled
  • Before running npm init & npm publish, you should preview the files which will land into the package. This can be done using the command npm pack --dry-run

CLI:

Syntax:

npm run [script] [flag] [value]

Usage:

General:

[--disable-validation-checks]: Disables type and bounding checks for better performance
[--enable-shared-memory-hints]: Enables console hints, reporting to use nested structures when possible - useful for performance optimization

Generating:

You can generate bindings with:

npm run generate --vkversion=1.2.162

The generated bindings can then be found in generated/{vkversion}/${platform}

  • Make sure the specified version to generate bindings for can be found here
  • The binding specification file gets auto-downloaded and is stored in generate/specifications/{vkversion}.xml
  • --incremental flag should only be used if you're a developer of nvk
Flags:
[--vkversion]: The Vulkan version to generate bindings for
[--fake-platform]: Allows to specify a fake platform to generate bindings for. Only use this when the native bindings don't have to be recompiled! A useful but dangerous flag
[--disable-minification]: Disables code minification of the JavaScript interfaces
[--incremental]: Enables incremental builds when building the bindings
[--docs]: Generates HTML-based documentation, also used for TypeScript type annotations

Building:

You can build the generated bindings with:

npm run build --vkversion=1.2.162

The compiled bindings can then be found in generated/{vkversion}/build

Flags:
[--vkversion]: The Vulkan version to build bindings for
[--msvsversion]: The Visual Studio version to build the bindings with

RenderDoc:

Using RenderDoc is simple. Open RenderDoc and in the Launch Application tab, enter e.g.:

  • Executable Path: C:\Program Files\nodejs\node.exe
  • Command-line Arguments: --experimental-modules C:\GitHub\nvk-examples\triangle\index.mjs

More Repositories

1

poxi

A flat pixel art editor
JavaScript
2,479
star
2

Iroh

Dynamic code analysis tool - Exploit, record and analyze running JavaScript
JavaScript
921
star
3

PokeMMO

๐ŸŽฎ Pokemon MMO engine with realtime editor
JavaScript
728
star
4

POGOserver

Pokemon GO server emulator
JavaScript
460
star
5

azula

Lightweight GPU accelerated HTML GUI for native JavaScript
C
320
star
6

dawn-ray-tracing

Hardware Ray tracing extension for Chromium WebGPU
C++
263
star
7

wasm-particles

WASM accelerated particles
JavaScript
253
star
8

mini-c

C to WebAssembly compiler
JavaScript
250
star
9

webgpu

WebGPU for Node [Deprecated, Unmaintained]
C
243
star
10

rokon

3D engine using WebGL2, WebAssembly
JavaScript
233
star
11

glmw

WebAssembly based Matrix and Vector library
C
200
star
12

mini-js

Minimal self-hosted JavaScript compiler in 1k lines of code
JavaScript
158
star
13

WebGPU-Path-Tracer

Toy path tracer using WebGPU RT
JavaScript
135
star
14

tiny-rtx

VK_NV_ray_tracing based Ray tracer
JavaScript
100
star
15

TXT2PNG

๐Ÿ—ป Store ASCII inside PNGs
JavaScript
73
star
16

momo

A Vulkan RTX Path Tracer
JavaScript
69
star
17

hevia-parser

A recursive descent Swift parser written in ES6
JavaScript
63
star
18

nvk-examples

Examples and demos for nvk
JavaScript
58
star
19

chromium-ray-tracing

Chromium build with hardware accelerated ray tracing
57
star
20

pokemon-emerald-rom-hacking

ROM hacking kit for Pokemon Emerald
JavaScript
50
star
21

VK_KHR_ray_tracing

VK_KHR_ray_tracing example
C++
42
star
22

YUE

My personal WebGPU based 3D renderer
TypeScript
38
star
23

html-canvas

Rendering HTML into canvas
JavaScript
26
star
24

canvas-live-stream

Live streaming canvas over websockets
JavaScript
26
star
25

webgpu-examples

Examples for node-webgpu
JavaScript
24
star
26

hevia-compiler

Strongly typed language that compiles to JavaScript
JavaScript
17
star
27

toy-compiler

A tiny self-hosted compiler
JavaScript
16
star
28

emerald-engine

JavaScript
16
star
29

POGO-asset-downloader

๐Ÿš€ Download Pokemon GO 3D models
JavaScript
16
star
30

Lamella

โšช Flow based Visual Programming
JavaScript
12
star
31

tolw

WebGL .obj loader - WebAssembly port of tinyobjloader
JavaScript
11
star
32

src2game

Turn Javascript source code into a Game
JavaScript
10
star
33

nvk-optix-denoiser

Real-time Denoiser for node-vulkan
JavaScript
10
star
34

3d-code-vizard

3D runtime code visualization
JavaScript
9
star
35

object-reflector

Simple & powerful Object reflections
JavaScript
8
star
36

htmlparse

Minimal blazing fast HTML parser
JavaScript
7
star
37

space-shooter

A simple space shooter
JavaScript
7
star
38

banotils

Utilities for the banano cryptocurrency
TypeScript
6
star
39

bitpackr

An efficient low-level packet serializer, down to bit-level
TypeScript
6
star
40

bnsh-decoder

A decoder for Nintendo Switch BNSH shaders
C++
5
star
41

Watcher

Track all changes inside your DOM
JavaScript
5
star
42

webgpu-compute-rasterizer

A simple compute based rasterizer
JavaScript
5
star
43

PokeName

Fiddle with Pokรฉmon names in different languages
JavaScript
4
star
44

Calc

Browserbased Excel
JavaScript
4
star
45

xr-engine

WebXR engine
JavaScript
3
star
46

POGOdecode

Pokemon GO Request<->Response decoder
JavaScript
3
star
47

nvk-essentials

Essential development tools for nvk
GLSL
3
star
48

Interpreter

A small interpreter with asynchronous step based execution support
JavaScript
2
star
49

wast-parser

WebAssembly WAST parser
JavaScript
2
star
50

neural-ocr

Neural network OCR
JavaScript
2
star
51

mc-spawner-upgrade

Lets you upgrade your mob spawners in minecraft
Java
2
star
52

pawtils

Utilities for the paw cryptocurrency
TypeScript
2
star
53

nanpow

TypeScript
1
star
54

Music-Box

Programmable wave music box
JavaScript
1
star
55

Expanding-spiral-animation

Canvas based expanding spiral experiment written in ES6
JavaScript
1
star