Giter Site home page Giter Site logo

maierfelix / nvk Goto Github PK

View Code? Open in Web Editor NEW
907.0 21.0 29.0 355.05 MB

Vulkan API for JavaScript/TypeScript

Home Page: https://maierfelix.github.io/nvk/1.2.162/

License: MIT License

JavaScript 28.84% HTML 0.07% C++ 70.09% C 0.22% Objective-C 0.02% Python 0.41% Makefile 0.36%
vulkan bindings javascript

nvk's Introduction



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

nvk's People

Contributors

grille avatar maierfelix avatar zeeshan595 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nvk's Issues

TextEncoder is not defined

When running on windows with node 10.15, I get error about missing TextEncoder. I solved it temporarily by adding this to interfaces.js:

const {TextEncoder, TextDecoder} = require('util');

Validation Layer name change to "VK_LAYER_KHRONOS_validation"

VK_LAYER_LUNARG_standard_validation was deprecated in favor of VK_LAYER_KHRONOS_validation.
While they both have the same functionality (as its the same code under the hood) the former was re-architected to have less overhead, and VK_LAYER_LUNARG_standard_validation will be removed in a future SDK version.

How to set pNext

There is opportunity to set pNext in this way?

VkPhysicalDeviceFeatures2 features2 = { };
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;

VkDeviceCreateInfo deviceCreateInfo;
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.pNext = &features2;

Vulkan Memory Allocator bindings?

I'm often using VMA for our projects, and still needs VMA in Node.js, so I propose to add Vulkan Memory Allocator (VMA) library into Node.js bindings.

pNext members are not flushed

const descriptorAccelerationStructureInfo = new VkWriteDescriptorSetAccelerationStructureNV;
descriptorAccelerationStructureInfo.accelerationStructureCount = 1;
descriptorAccelerationStructureInfo.pAccelerationStructures = topAS;

const accelerationStructureWrite  = new VkWriteDescriptorSet;
accelerationStructureWrite.pNext = descriptorAccelerationStructureInfo;
accelerationStructureWrite.dstSet = descriptorSet;
accelerationStructureWrite.dstBinding = 0;
accelerationStructureWrite.dstArrayElement = 0;
accelerationStructureWrite.descriptorCount = 1;
accelerationStructureWrite.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV;
accelerationStructureWrite.pImageInfo = null;
accelerationStructureWrite.pBufferInfo = null;
accelerationStructureWrite.pTexelBufferView = null;

const descriptorWrites = [accelerationStructureWrite];
vkUpdateDescriptorSets(device, descriptorWrites.length, descriptorWrites, 0, null);

There is error on line 3, when I call vkUpdateDescriptorSets it fails because pAccelerationStructures is NULL.

No global constants in index.d.ts

This can be fixed by adding

{% for child in enu.children -%}
const {{ child.name }}: {{ enu.name }};
{% endfor %}

between

enum {{ enu.name }} {
    {% for child in enu.children -%}
    {{ child.name }},
    {% endfor %}
}

and

{% endfor %}

in file generator/templates/typescript-ts.njk

pNext: "Member isn't allowed to be filled!" incorrect behavior when 'null'

This error should only popup, when a user actually tries to fill the pNext member with another structure. Currently, when a non-writeable pNext member gets set to null, this error shows

Example:

let accelerationStructureInfo = new VkAccelerationStructureCreateInfoNV();
accelerationStructureInfo.pNext = null; // shouldn't trigger an error

Proposal:
Only trigger the error, when the passed in value is not equal to null

TypeScript types not working

Hey, so VS Code is reporting to me that it can't find the TypeScript declaration file for the module
I'm pretty sure I've followed the TypeScript guide correctly, and I believe my TypeScript version is the latest (4.0.3). Got any idea on what's going on?

image

oculus support

Hello, could you add oculus rift support, with an example?

No render devices available in vmware

The operating system is ubuntu 18.04. And it can run glxgears without a problem.

When I'm trying to run the nvk-examples/triangle the following info shows up:

> node --inspect --experimental-modules ./index.mjs

Debugger listening on ws://127.0.0.1:9229/79ced56b-8ae6-4e39-aab5-cddfb11a6974
For help, see: https://nodejs.org/en/docs/inspector
(node:3214) ExperimentalWarning: The ESM module loader is experimental.
(nvk) Using Vulkan v1.1.114
(nvk) Validation checks are enabled
Error: No render devices available!
Error: Vulkan assertion failed!
    at ASSERT_VK_RESULT (file:///home/dev/Codes/temp/nvk-examples/triangle/index.mjs:27:36)
    at file:///home/dev/Codes/temp/nvk-examples/triangle/index.mjs:172:1
    at ModuleJob.run (internal/modules/esm/module_job.js:95:12)

Here is the info when running glmark2:

=======================================================
    glmark2 2014.03+git20150611.fa71af2d
=======================================================
    OpenGL Information
    GL_VENDOR:     VMware, Inc.
    GL_RENDERER:   SVGA3D; build: RELEASE;  LLVM;
    GL_VERSION:    3.0 Mesa 18.0.5
=======================================================

Don't know if there is a way to run vulkan in Vmware.

Issue with BigUint64Array

Issue on typescript by using the following lines:

   ` var arr =  new BigUint64Array([BigInt(0)]);
      vkCmdBindVertexBuffers(cmdBuffer, 0, 1, [uploadBuffer.vertexBuffer],arr);`

Thats the error

`error TS2345: Argument of type 'BigUint64Array' is not assignable to parameter of type 'Float32Array'.
Types of property 'every' are incompatible.
Type '(predicate: (value: bigint, index: number, array: BigUint64Array) => boolean, thisArg?: any) => boolean' is not assignable to type '(predicate: (value: number, index: number, array: Float32Array) => unknown, thisArg?: any) => boolean'.
Types of parameters 'predicate' and 'predicate' are incompatible.
Types of parameters 'value' and 'value' are incompatible.
Type 'bigint' is not assignable to type 'number '.

Thats the lines in the index.d.ts of nvk:

type BigUint64Array = Float32Array
declare const BigUint64Array: typeof Float32Array
export function vkCmdBindVertexBuffers(commandBuffer: VkCommandBuffer | null, firstBinding: number, bindingCount: number, pBuffers: VkBuffer[] | null, pOffsets: BigUint64Array | null): void;

Somebody have an idea or can help me with this issue?

Running new VkDescriptorBufferInfo() in an electron render process causes crash on getArrayBufferFromAddress() in VkBuffer initialisation.

Running on Windows 10 64x AMD Processor. Don't have access to other platforms so unsure if they are affected.

Versions:
[email protected]
[email protected]
[email protected]

Attempting to create a new VkDescriptorBufferInfo() in an electron render process causes the process to crash. After debugging with a ton of console.logs in interfaces.js the program stops after the call to getArrayBufferFromAddress in the VkBuffer initialisation function setting this.memoryBuffer. (Position [line 5: col 36777] in the unmodified interfaces.js)

Below is a snippet of the function.

function VkBuffer(e){if(this.memoryBuffer=null,this.memoryAddress=BI0,this.memoryOffset=0,void 0!==e){if(void 0===e.$memoryBuffer)throw new Error("'VkBuffer' doesn't take any arguments");if(void 0!==e.$memoryOffset){let t=getAddressFromArrayBuffer(e.$memoryBuffer);this.memoryBuffer=getArrayBufferFromAddress(t+BigInt(e.$memoryOffset),BI8),this.memoryOffset=e.$memoryOffset}else this.memoryBuffer=e.$memoryBuffer}else this.memoryBuffer=new ArrayBuffer(8);this.memoryAddress=getAddressFromArrayBuffer(this.memoryBuffer),this.memoryView=new DataView(this.memoryBuffer,0,8)}

The function never returns and the DevTools for the render process becomes disconnected and the page that was loaded while running the script disappears.

Running the same file through node directly has no problem and the script performs as expected.

The memory addresses and sizes passed to getArrayBufferFromAddress seem fine and other calls to the same function do not cause this issue.

This only happens for VkDescriptorBufferInfo() as far as i am aware. Other calls to VkBuffer() have no issues and no matter where in the application I create a VkDescriptorBufferInfo() it crashes, so it isn't a case of too little memory. Other calls to the nvk.node files and getArrayBufferFromAddress() work fine so it isn't a case of incorrectly built files or missing bindings (electron needs some binaries to be rebuilt for it)

I have attached a project with the node and electron tests as well as the modified interfaces.js with the tracking console.logs. No other modifications have been added and as I said the same modified files run fine in a regular node process.

The project is a fully functional lighting calculator for 2d scenes so there is a fair bit of Vulkan boilerplate code. engine_launcher.js is the script that passes the necessary data to the vulkan app to calculate lighting for a scene. This scene is an empty scene with a single light. and the result is output as ascii art in the console. LightingEngine.js is the actual Vulkan app that implements the compute shader dispatching and result fetching.

I am posting this issue here as I don't have the experience or know-how to debug any further than the call to the .node binaries.

Project Setup:

  1. unzip project contents into a folder
  2. cd into the created directory.
  3. npm install
  4. replace ".\interfaces.js" ".\node_modules\nvk\generated\1.1.126\win32" Adds the debug console.logs to the affected calls.

Running:

//Electron (Crashes)
npm run
//Regular Node (Successfully computes lighting data and outputs it)
node engine_launcher.js

Here is the project: nvk-error-tests.zip

VkImageBlit and VkImageBlit2KHR srcOffsets and dstOffsets cause RangeError crash.

The Issue

When an instance of VkImageBlit or VkImageBlit2KHR is flushed, if the srcOffsets / dstOffsets VkOffset3Ds share the top struct's memory then the copy operation will crash with a RangeError (This example came from the srcOffsets in a VkImageBlit):

RangeError: offset is out of bounds
    at Uint8Array.set (<anonymous>)
    at VkImageBlit.flush (F:\Programming\misc\nvk\generated\1.2.162\win32\interfaces.js:15515:15)

Steps to Reproduce

Running this simple script can highlight the issue:

const blit = new VkImageBlit();
blit.srcOffsets[0].x = 1;
blit.srcOffsets[0].y = 1;
blit.srcOffsets[0].z = 1;
blit.flush();

Running this script as is will cause a RangeError crash as explained above.

However, the following script will run successfully, i.e. no range error crash (albeit wasting memory as explained below):

const blit = new VkImageBlit();
blit.srcOffsets[0] = new VkOffset3D();
blit.srcOffsets[1] = new VkOffset3D();
blit.dstOffsets[0] = new VkOffset3D();
blit.dstOffsets[1] = new VkOffset3D();
blit.flush();

What is known

From some quick digging it appears that when the offsets share the blit's memory the Uint8Array that is used to copy their data is not bounded by the offset struct's offset/length, meaning it copies the entire top-level structure. Due to the offset supplied to the set command this causes the set to attempt to write past the end of the buffer (hence the RangeError).

From a snippet inside the VkImageBlit's flush method: (lines 15495-15518)

  if (this._srcOffsets !== null) {
    let array = this._srcOffsets;
    
    if (array.length !== 2) {
      throw new RangeError("Invalid array length, expected length of '2' for 'VkImageBlit.srcOffsets'");
      return false;
    }
    for (let ii = 0; ii < array.length; ++ii) {
      
      if (!array[ii] || (array[ii].constructor !== VkOffset3D)) {
        throw new TypeError("Invalid type for 'VkImageBlit.srcOffsets[" + ii + "]': Expected 'VkOffset3D' but got '" + typeToString(array[ii]) + "'");
        return false;
      }
      if (!array[ii].flush()) return false;
    };
    
    let dstView = new Uint8Array(this.memoryBuffer);
    let byteOffset = 0x10;
    for (let ii = 0; ii < array.length; ++ii) {
      let srcView = new Uint8Array(array[ii].memoryBuffer); //<========= This line here
      dstView.set(srcView, byteOffset);
      byteOffset += VkOffset3D.byteLength;
    };
  }

It appears that the srcView should be created with an offset and length to match that of the VkOffset3D to prevent copying the whole top-level structure.

Temporary Workaround

For now simply setting these offset elements to new instances of VkOffset3D that do not share the top level memory solves the issue, however, this is inefficient in terms of memory as it requires extra to store the separated instances.

const blit = new VkImageBlit();
blit.srcOffsets[0] = new VkOffset3D(); // Here we set the element to a new instance of VkOffset3D that does not share the VkImageBlit's memory.
blit.srcOffsets[0].x = 0;
blit.srcOffsets[0].y = 0;
blit.srcOffsets[0].z = 0;
blit.srcOffsets[1] = new VkOffset3D(); //And here as well
blit.srcOffsets[1].x = mipWidth;
blit.srcOffsets[1].y = mipHeight;
blit.srcOffsets[1].z = 1;

pNext isn't extended by 'structextends' attribute

With 7637a81 there was added type validation when setting a pNext struct member. This is now partially broken by not handling the structextends attribute in the specification when adding a fix for #7.

When a structure is extended by structextends then the allowed pNext types should be inherited from this structure.

[WebAssembly, ArrayBuffer] Low-level input for structs?

I want to propose low-level structured ArrayBuffer observing. Why it needs? It may more usable with WebAssembly applications, more directly... (fore example, AssemblyScript language). The same rule applicable for Shared memory types.

let arrayBuffer: ArrayBuffer = new ArrayBuffer(VkInstanceCreateInfo.sizeof); // static .sizeof 
let instanceInfo: VkInstanceCreateInfo = new VkInstanceCreateInfo(arrayBuffer); // but just observe, NOT copy

Window does not close through code

When you use VulkanWindow.Close it does not close the window. I also tried using win.pollEvents(); after it as well and gave the same result.

DeepinScreenshot_select-area_20190702020944

VkInout used instead VkInoutAddress in generated index.d.ts

to fix issue in file generator/generators/typescript.mjs

case JavaScriptType.OBJECT_INOUT: {
    return `VkInout | null`;
}

can be replaced by

case JavaScriptType.OBJECT_INOUT: {
    return (jsType.value === `BigInt` ? `VkInoutAddress` : `VkInout`) + ` | null`;
}

Building error

npm run build --vkversion=1.1.97 --msvsversion=2017

nvk\generated\1.1.97\src\enums.h(1869): error C2059: syntax error: 'bad suffix on number' [nvk\generated\1.1.97\build\addon.vcxproj]
nvk\generated\1.1.97\src\enums.h(1869): error C2153: integer literals must have at least one digit [nvk\generated\1.1.97\build\addon.vcxproj]

1.1.85 bulds correctly.

Linux Support

@maierfelix - while I see the window's installation steps, could the documentation include the support (or lack of support) for Linux systems?

Disabling rate limit / vsync using NVK

Hello, I just discovered this library and I was wondering if someone know how to disable the frame limit or vsync to go above the number of FPS my screen have. I searched on google and some forums but couldn't find anything related to that.

By the way, is there any discord for the NVK community right now ? (because the link on the documentation is dead).

Thank you a lot and have a good day !

Unable to build on linux

The vulkan SDK is installed and I can build all the samples and tools etc. My $VULKAN_SDK env is /home/johnathan/Downloads/1.1.101.0/x86_64.

$ npm run generate --vkversion=1.1.101

[email protected] generate /home/johnathan/nvk
node --experimental-modules ./generator/index.mjs

(node:10560) ExperimentalWarning: The ESM module loader is experimental.
Generating bindings for 1.1.101...
Generating AST..
Generating Vk structs..
Generating Vk handles..
Warning: Cannot resolve required platform includes for linux
Generating Vk enums..
Generating Vk calls..
Generating Vk includes..
Generating Typescript definition..
Generating binding.gyp..
Warning: Using fallback SDK at /home/johnathan/Downloads/1.1.101.0/x86_64
Generating package.json..
Generating utils..
Generating indices..
Generating typescript index..

Generation stats:
Total files: 0/13
Total size: 8975.878kb
Total lines: 256.549lines
johnathan@johnathan-desktop:~/nvk$ npm run build --vkversion=1.1.101

[email protected] build /home/johnathan/nvk
node ./build.js

Compiling bindings for version 1.1.101...
Platform: linux | x64
Node: 11.10.1
V8: 7.0.276.38-node.17

Copying files..
Done!

Compiling bindings..
Copying /home/johnathan/Downloads/1.1.101.0/x86_64/lib/libvulkan.so.1.1.97 -> ./generated/1.1.101/linux/build/Release/libvulkan.so.1.1.97
Failed to copy /home/johnathan/Downloads/1.1.101.0/x86_64/lib/libvulkan.so.1.1.97 -> ./generated/1.1.101/linux/build/Release/libvulkan.so.1.1.97

/home/johnathan/nvk/build.js:103
throw error;
^
Error: ENOENT: no such file or directory, lstat '/home/johnathan/Downloads/1.1.101.0/x86_64/lib/libvulkan.so.1.1.97'
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] build: node ./build.js
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /home/johnathan/.npm/_logs/2019-03-05T11_20_06_504Z-debug.log
johnathan@johnathan-desktop:~/nvk$ make: Entering directory '/home/johnathan/nvk/generated/1.1.101/linux/build'
CXX(target) Release/obj.target/addon-linux/src/index.o
../src/index.cpp:14:10: fatal error: window.h: No such file or directory
#include "window.h"
^~~~~~~~~~
compilation terminated.
addon-linux.target.mk:126: recipe for target 'Release/obj.target/addon-linux/src/index.o' failed
make: *** [Release/obj.target/addon-linux/src/index.o] Error 1
make: Leaving directory '/home/johnathan/nvk/generated/1.1.101/linux/build'
gyp ERR! build error
gyp ERR! stack Error: make failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/home/johnathan/.nvm/versions/node/v11.10.1/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:262:23)
gyp ERR! stack at ChildProcess.emit (events.js:197:13)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:254:12)
gyp ERR! System Linux 4.15.0-45-generic
gyp ERR! command "/home/johnathan/.nvm/versions/node/v11.10.1/bin/node" "/home/johnathan/.nvm/versions/node/v11.10.1/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "build"
gyp ERR! cwd /home/johnathan/nvk/generated/1.1.101/linux
gyp ERR! node -v v11.10.1
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
^C

Use NAPI instead of V8

This would allow users to install nvk without the need to download the SDK and compile the bindings. The need of installing Lunar's Vulkan runtime could also be removed by just shipping the runtime files with the binaries.

Edit: this branch contains all necessary conversions, nearly ready to be merged

Automated Vulkan structure caching

A main performance loss is that most Vulkan structs in C are stack-allocated, while in nvk everything is heap-allocated. This is a proposal on how to do automated caching in JavaScript without affecting the Syntax or force users to care about memory management too much.

The problem:

for (let ii = 0; ii < 10; ++ii) {
  let subpassDependency = new VkSubpassDependency();
  subpassDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
  subpassDependency.dstSubpass = 0;
  subpassDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  subpassDependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  subpassDependency.dstAccessMask = (
    VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
    VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
  );
};
// results in 10 allocations of `VkSubpassDependency`

Option 1 (Manual Caching):

let subpassDependency = new VkSubpassDependency();
for (let ii = 0; ii < 10; ++ii) {
  subpassDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
  subpassDependency.dstSubpass = 0;
  subpassDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  subpassDependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  subpassDependency.dstAccessMask = (
    VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
    VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
  );
};
// results in 1 allocation of `VkSubpassDependency`

But when having to deal with many structures, this negatively affects code quality.

Proposal (Automated Caching):

for (let ii = 0; ii < 10; ++ii) {
  let subpassDependency = new VkSubpassDependency({% ULID %});
  subpassDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
  subpassDependency.dstSubpass = 0;
  subpassDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  subpassDependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  subpassDependency.dstAccessMask = (
    VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
    VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
  );
};
//results in 1 allocation of `VkSubpassDependency`

{% ULID %} returns a unique id based on file + code line + code column which is used to do automatic caching internally

How to solve this problem? Is it even worth?

Incorrect TypeScript signature for vkCmdBindVertexBuffers

The fifth argument claims it should be of type Float32Array but should actually be BigUint64Array.

It says:

vkCmdBindVertexBuffers(commandBuffer: VkCommandBuffer | null, firstBinding: number, bindingCount: number, pBuffers: VkBuffer[] | null, pOffsets: Float32Array | null): void

Attempting to use a Float32Array results in a crash with the message: TypeError: Invalid type for argument 5 'pOffsets' in 'vkCmdBindVertexBuffers'

Giving a type of BigUint64Array makes TypeScript complain, so I suppose the only work-around at the moment is to silence the TS compiler.

const offsets = new BigUint64Array();
vkCmdBindVertexBuffers(/*...*/, offsets as any);

compile error

I encountered this problem during the compilation process. Is there any way to solve it?
image

Wrong parameter type in index.d.ts

There's an issue with the generated Typescript definitions, the function must take VkBool32 as pSupported parameter but it takes VkInout.

/**
   * Query if presentation is supported
   * @param physicalDevice is the physical device.
   * @param queueFamilyIndex is the queue family.
   * @param surface is the surface.
   * @param pSupported is a reference to a 'VkBool32', which is set to 'VK_TRUE' to indicate support, and 'VK_FALSE' otherwise.
   */
  function vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice: VkPhysicalDevice | null, queueFamilyIndex: number, surface: VkSurfaceKHR | null, pSupported: VkInout | null): VkResult;

Hybrid bindings

The hybrid-bindings branch contains ongoing efforts into moving Vulkan structures and handles from C++ over to JS. Vulkan function calls and enums remain in C++. Vulkan related C++ memory is filled within JavaScript using TypedArrays, ArrayBuffers and inlined memory offsets (done using pseudo bootstrapping in the code generation process).

Benchmarks with the new hybrid bindings already show some improvements in both memory usage and performance, because the JS->C++ overhead is reduced.

This also results in a reduced package size, less compile times, more flexibility for the interfaces (e.g. error handling) and greatly reduces code complexity in both the code generator and the generated binding code.

Some further notes:

  • Add asm.js for faster memory operations? Further investigate into asm.js context switching performance
  • Strings and Numbers live inside a Structure's memoryBuffer only
  • Structures are now flushed within JavaScript
  • C++ Vulkan Structure reflection gets possibly moved to JavaScript as well

State of 4/13/19:

  • Hybrid bindings tend to be a lot faster than the native bindings
  • Memory usage is now reduced
  • Better error handling
  • Shared memory for nested structures

GPGPU example?

I didn't see this in the documentation, but I'd LOVE to see an example where we don't have to open a new window, and we can just calculate from GPU very very simple math. If you can do this, we could potentially write a backend/driver for http://gpu.rocks/ .

Typescript is broken?

So I am currently working with nvk and would like to use it as its module (ie: nvk.VulkanWindow, not VulkanWindow.) I would do this by omitting the Object.assign(global, nvk) included in the examples. However when I do this typescript throws an error on every nvk.* I write. However when compiled it still works. Furthermore if I use the object assign then everything still works however I get no errors. Is there a reason for this and could you provide some incite as to why nvk works this way?

vkGetAccelerationStructureHandleNV pData is NULL

VUID-vkGetAccelerationStructureHandleNV-pData-parameter(ERROR / SPEC): msgNum: 0 - vkGetAccelerationStructureHandleNV: required parameter pData specified as NULL. The Vulkan spec states: pData must be a valid pointer to an array of dataSize bytes (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkGetAccelerationStructureHandleNV-pData-parameter)
    Objects: 1
        [0] 0, type: 0, name: NULL

According to spec, pData is a pointer to a user-allocated buffer where the results will be written.
I checked call.h and found that it's always NULL.

Consuming via published npm package

I see this on npm:

$ npm info nvk

[email protected] | MIT | deps: none | versions: 30
Vulkan rendering API for node
https://github.com/maierfelix/nvk#readme

keywords: vulkan, opengl, bindings, 3d, 2d, rendering

dist
.tarball: https://registry.npmjs.org/nvk/-/nvk-0.5.3.tgz
.shasum: 249fda90b7f4ade6b740622e59b9b63715beac27
.integrity: sha512-nySaU9zBQefbvGCyUASNDuJokj2seE+HNRPu93VhBbevfWAONWXhaqeOVDbraBLOTPHdtIEhqJ5rmIc9DeG02Q==
.unpackedSize: 28.5 MB

maintainers:
- maierfelix <[email protected]>

dist-tags:
latest: 0.5.3  

published 3 days ago by maierfelix <[email protected]>

but is the recommended usage to actually build everything from source? Regular package installation doesn't seem to work

Drop Vk and VK_ prefixes?

I fully understand this can be considered a controversial idea. 😅

In the examples I see the following code used quite often:

import nvk from 'nvk';

Object.assign(global, nvk);

By dropping the Vk and VK_ prefixes the following code might be considered more clean to Javascript (and Typescript) users:

import * as vk from 'nvk';

const buffer = new vk.Buffer();

const bufferInfo = new vk.bufferCreateInfo();
bufferInfo.sType = vk.STRUCTURE_TYPE_BUFFER_CREATE_INFO;

The above code shares the same fashion as WebGL's often used terminology, where everything is housed under a gl namespace:

const buffer = gl.createBuffer(gl.ARRAY_BUFFER)

Not only that, this should also help IDEs (e.g. Visual Studio Code and Atom) with autocomplete and error checking, making the library more accessible to everyone.

--
Thanks for your time bringing Vulkan to the Javascript audience with nvk!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.