Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ShaderProcessor partially updating shader source to support uniform buffers #4402

Merged
merged 5 commits into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/graphics/bind-group-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class BindGroupFormat {
/** @type {BindTextureFormat[]} */
this.textureFormats = textureFormats;

// maps a texture format name to an index
// maps a texture format name to a slot index
/** @type {Map<string, number>} */
this.textureFormatsMap = new Map();
textureFormats.forEach((tf, i) => this.textureFormatsMap.set(tf.name, i));
Expand All @@ -65,6 +65,16 @@ class BindGroupFormat {
this.impl.destroy();
}

/**
* Returns slot index for a texture slot name.
*
* @param {string} name - The name of the texture slot.
* @returns {number} - The slot index.
*/
getTextureSlot(name) {
return this.textureFormatsMap.get(name);
}

loseContext() {
// TODO: implement
}
Expand Down
3 changes: 3 additions & 0 deletions src/graphics/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,9 @@ export const SHADERSTAGE_COMPUTE = 4;
export const BINDGROUP_VIEW = 0;
export const BINDGROUP_MESH = 1;

// names of bind groups
export const bindGroupNames = ['view', 'mesh'];

// map of engine TYPE_*** enums to their corresponding typed array constructors and byte sizes
export const typedArrayTypes = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array];
export const typedArrayTypesByteSize = [1, 1, 2, 2, 4, 4, 4];
Expand Down
3 changes: 2 additions & 1 deletion src/graphics/graphics-device.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ class GraphicsDevice extends EventHandler {
// #endif
tex: 0,
vb: 0,
ib: 0
ib: 0,
ub: 0
};

this._shaderStats = {
Expand Down
68 changes: 59 additions & 9 deletions src/graphics/program-library.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,22 @@ import { SHADER_FORWARD, SHADER_DEPTH, SHADER_PICK, SHADER_SHADOW } from '../sce
import { StandardMaterial } from '../scene/materials/standard-material.js';
import { ShaderPass } from '../scene/shader-pass.js';

// Public interface
/**
* A class responsible for creation and caching of required shaders.
* There is a two level cache. The first level generates the shader based on the provided options.
* The second level processes this generated shader using processing options - in most cases
* modifies it to support uniform buffers.
*
* @ignore
*/
class ProgramLibrary {
/**
* A cache of shaders processed using processing options.
*
* @type {Map<string, Shader>}
*/
processedCache = new Map();

constructor(device) {
this._device = device;
this._cache = {};
Expand Down Expand Up @@ -45,13 +59,7 @@ class ProgramLibrary {
return (generator !== undefined);
}

getProgram(name, options) {
const generator = this._generators[name];
if (!generator) {
Debug.warn(`ProgramLibrary#getProgram: No program library functions registered for: ${name}`);
return null;
}
const key = generator.generateKey(options);
generateShader(generator, name, key, options) {
let shader = this._cache[key];
mvaligursky marked this conversation as resolved.
Show resolved Hide resolved
if (!shader) {
let lights;
Expand All @@ -71,7 +79,7 @@ class ProgramLibrary {
options.lights = lights;

if (this._precached)
console.warn(`ProgramLibrary#getProgram: Cache miss for shader ${name} key ${key} after shaders precaching`);
Debug.log(`ProgramLibrary#getProgram: Cache miss for shader ${name} key ${key} after shaders precaching`);

const device = this._device;
const shaderDefinition = generator.createShaderDefinition(device, options);
Expand All @@ -81,6 +89,44 @@ class ProgramLibrary {
return shader;
}

getProgram(name, options, processingOptions) {
const generator = this._generators[name];
if (!generator) {
Debug.warn(`ProgramLibrary#getProgram: No program library functions registered for: ${name}`);
return null;
}

// we have a key for shader source code generation, a key for its further processing to work with
// uniform buffers, and a final key to get the processed shader from the cache
const generationKey = generator.generateKey(options);
const processingKey = JSON.stringify(processingOptions);
const totalKey = `${generationKey}#${processingKey}`;

// do we have final processed shader
let processedShader = this.processedCache.get(totalKey);
if (!processedShader) {

// get generated shader
const generatedShader = this.generateShader(generator, name, generationKey, options);
Debug.assert(generatedShader);

// create a shader definition for the shader that will include the processingOptions
const generatedShaderDef = generatedShader.definition;
const shaderDefinition = {
attributes: generatedShaderDef.attributes,
vshader: generatedShaderDef.vshader,
fshader: generatedShaderDef.fshader,
processingOptions: processingOptions
};

// add new shader to the processed cache
processedShader = new Shader(this._device, shaderDefinition);
this.processedCache.set(totalKey, processedShader);
}

return processedShader;
}

storeNewProgram(name, options) {
let opt = {};
if (name === "standard") {
Expand Down Expand Up @@ -156,6 +202,9 @@ class ProgramLibrary {
if (cache) {
const shaders = new Array(cache.length);
for (let i = 0; i < cache.length; i++) {

// default options for the standard materials are not stored, and so they are inserted
// back into the loaded options
if (cache[i].name === "standard") {
const opt = cache[i].options;
const defaultMat = this._getDefaultStdMatOptions(opt.pass);
Expand All @@ -164,6 +213,7 @@ class ProgramLibrary {
opt[p] = defaultMat[p];
}
}

shaders[i] = this.getProgram(cache[i].name, cache[i].options);
}
}
Expand Down
54 changes: 54 additions & 0 deletions src/graphics/shader-processor-options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { BINDGROUP_VIEW } from "./constants.js";

/** @typedef {import('./bind-group-format.js').BindGroupFormat} BindGroupFormat */
/** @typedef {import('./uniform-buffer-format.js').UniformBufferFormat} UniformBufferFormat */

class ShaderProcessorOptions {
/** @type {Array<UniformBufferFormat>}*/
uniformFormats = [];

/** @type {Array<BindGroupFormat>}*/
bindGroupFormats = [];

/**
* Constructs shader processing options, used to process the shader for uniform buffer support.
*
* @param {UniformBufferFormat} viewUniformFormat - Format of the uniform buffer.
* @param {BindGroupFormat} viewBindGroupFormat - Format of the bind group.
*/
constructor(viewUniformFormat, viewBindGroupFormat) {

// construct a sparse array
this.uniformFormats[BINDGROUP_VIEW] = viewUniformFormat;
mvaligursky marked this conversation as resolved.
Show resolved Hide resolved
this.bindGroupFormats[BINDGROUP_VIEW] = viewBindGroupFormat;
}

/**
* Get the bind group index for the uniform name.
*
* @param {string} name - The name of the uniform.
* @returns {number} - Returns the index if the uniform exists, -1 otherwise.
*/
has(name) {

// uniform name
for (let i = 0; i < this.uniformFormats.length; i++) {
const uniformFormat = this.uniformFormats[i];
if (uniformFormat.get(name)) {
return i;
}
}

// texture name
for (let i = 0; i < this.bindGroupFormats.length; i++) {
const groupFormat = this.bindGroupFormats[i];
if (groupFormat.getTextureSlot(name)) {
return i;
}
}

return -1;
}
}

export { ShaderProcessorOptions };
Loading