How to Share Context Across Multiple gpu.js Kernels
This article explains how to define and execute multiple compute kernels that share the same WebGL context in gpu.js. By instantiating a single GPU instance and leveraging kernel pipelining, you can chain multiple computational steps together directly on the hardware, eliminating the performance overhead of transferring intermediate results back and forth between the GPU and CPU.
Using a Single GPU Instance
In gpu.js, the execution context is tied directly to the
GPU class instance. When you create kernels from the same
GPU object, they automatically share the underlying WebGL
rendering context.
To begin, instantiate the GPU object once:
const { GPU } = require('gpu.js'); // or imported via <script> tag in the browser
const gpu = new GPU();Any kernel generated using this gpu object will reside
in the same execution context.
Preserving Data on the GPU via Pipelining
Sharing a context is most beneficial when one kernel's output serves
as the next kernel's input. By default, gpu.js reads data back to the
CPU as a JavaScript array after a kernel executes. To keep intermediate
data on the GPU within the shared context, enable kernel pipelining
using the .setPipeline(true) method.
When pipelining is enabled, the kernel outputs a WebGL texture instead of a raw array, which can be passed directly to subsequent kernels.
Implementation Example
Below is a complete implementation demonstrating two kernels sharing a context to perform sequential operations:
const { GPU } = require('gpu.js');
const gpu = new GPU();
// First kernel: Multiplies each element by 2
const multiplyKernel = gpu.createKernel(function(arr) {
return arr[this.thread.x] * 2;
})
.setOutput([5])
.setPipeline(true); // Keeps output in the shared GPU context as a texture
// Second kernel: Adds 10 to each element from the first kernel
const addKernel = gpu.createKernel(function(texture) {
return texture[this.thread.x] + 10;
})
.setOutput([5]); // Final kernel returns data to the CPU (pipeline is false by default)
// Initial CPU data
const inputData = [1, 2, 3, 4, 5];
// Execute Kernel 1: Output is a texture stored on the GPU
const intermediateTexture = multiplyKernel(inputData);
// Execute Kernel 2: Uses the intermediate texture directly
const finalResult = addKernel(intermediateTexture);
console.log(finalResult); // Float32Array [12, 14, 16, 18, 20]Memory Management
Because textures created by pipelined kernels persist in the GPU memory context, running multiple iterations can lead to memory leaks if not properly managed.
If you are running chained kernels inside a loop or processing real-time data, manually free intermediate textures once they are no longer needed:
const intermediateTexture = multiplyKernel(inputData);
const finalResult = addKernel(intermediateTexture);
// Free the intermediate GPU memory
intermediateTexture.delete();Reusing the single GPU instance and chaining kernels via
.setPipeline(true) ensures that all operations remain
localized to a single hardware context for maximum throughput.