UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

202 lines (201 loc) 8.59 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { TRACEID_SHADER_ALLOC } from "../../core/constants.js"; import { Debug } from "../../core/debug.js"; import { platform } from "../../core/platform.js"; import { Preprocessor } from "../../core/preprocessor.js"; import { SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL } from "./constants.js"; import { DebugGraphics } from "./debug-graphics.js"; import { ShaderDefinitionUtils } from "./shader-definition-utils.js"; import halfTypes from "./shader-chunks/frag/half-types.js"; let id = 0; class Shader { /** * Creates a new Shader instance. * * Consider {@link ShaderUtils.createShader} as a simpler and more powerful way to create * a shader. * * @param {GraphicsDevice} graphicsDevice - The graphics device used to manage this shader. * @param {object} definition - The shader definition from which to build the shader. * @param {string} [definition.name] - The name of the shader. * @param {Object<string, string>} [definition.attributes] - Object detailing the mapping of * vertex shader attribute names to semantics SEMANTIC_*. This enables the engine to match * vertex buffer data as inputs to the shader. When not specified, rendering without vertex * buffer is assumed. * @param {string[]} [definition.feedbackVaryings] - A list of shader output variable * names that will be captured when using transform feedback. This setting is only effective * if the useTransformFeedback property is enabled. * @param {string} [definition.vshader] - Vertex shader source (GLSL code). Optional when * compute shader is specified. * @param {string} [definition.fshader] - Fragment shader source (GLSL code). Optional when * useTransformFeedback or compute shader is specified. * @param {string} [definition.cshader] - Compute shader source (WGSL code). Only supported on * WebGPU platform. * @param {string} [definition.computeEntryPoint] - The entry point function name for the compute * shader. Defaults to 'main'. * @param {Map<string, string>} [definition.vincludes] - A map containing key-value pairs of * include names and their content. These are used for resolving #include directives in the * vertex shader source. * @param {Map<string, string>} [definition.fincludes] - A map containing key-value pairs * of include names and their content. These are used for resolving #include directives in the * fragment shader source. * @param {Map<string, string>} [definition.cincludes] - A map containing key-value pairs * of include names and their content. These are used for resolving #include directives in the * compute shader source. * @param {Map<string, string>} [definition.cdefines] - A map containing key-value pairs of * define names and their values. These are used for resolving defines in the compute shader. * @param {boolean} [definition.useTransformFeedback] - Specifies that this shader outputs * post-VS data to a buffer. * @param {string | string[]} [definition.fragmentOutputTypes] - Fragment shader output types, * which default to vec4. Passing a string will set the output type for all color attachments. * Passing an array will set the output type for each color attachment. * @param {string} [definition.shaderLanguage] - Specifies the shader language of vertex and * fragment shaders. Defaults to {@link SHADERLANGUAGE_GLSL}. * @example * // Create a shader that renders primitives with a solid red color * * // Vertex shader * const vshader = ` * attribute vec3 aPosition; * * void main(void) { * gl_Position = vec4(aPosition, 1.0); * } * `; * * // Fragment shader * const fshader = ` * precision ${graphicsDevice.precision} float; * * void main(void) { * gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); * } * `; * * const shaderDefinition = { * attributes: { * aPosition: pc.SEMANTIC_POSITION * }, * vshader, * fshader * }; * * const shader = new pc.Shader(graphicsDevice, shaderDefinition); */ constructor(graphicsDevice, definition) { /** * Format of the uniform buffer for mesh bind group. * * @type {UniformBufferFormat} * @ignore */ __publicField(this, "meshUniformBufferFormat"); /** * Format of the bind group for the mesh bind group. * * @type {BindGroupFormat} * @ignore */ __publicField(this, "meshBindGroupFormat"); /** * The attributes that this shader code uses. The location is the key, the value is the name. * These attributes are queried / extracted from the final shader. * * @type {Map<number, string>} * @ignore */ __publicField(this, "attributes", /* @__PURE__ */ new Map()); this.id = id++; this.device = graphicsDevice; this.definition = definition; this.name = definition.name || "Untitled"; this.init(); if (definition.cshader) { Debug.assert(graphicsDevice.supportsCompute, "Compute shaders are not supported on this device."); Debug.assert(!definition.vshader && !definition.fshader, "Vertex and fragment shaders are not supported when creating a compute shader."); Debug.call(() => { this.cUnmodified = definition.cshader; }); const enablesCode = ShaderDefinitionUtils.getWGSLEnables(graphicsDevice, "compute"); const definesCode = ShaderDefinitionUtils.getDefinesCode(graphicsDevice, definition.cdefines); const cshader = enablesCode + definesCode + definition.cshader; const cincludes = definition.cincludes ?? /* @__PURE__ */ new Map(); if (!cincludes.has("halfTypesCS")) { cincludes.set("halfTypesCS", halfTypes); } definition.cshader = Preprocessor.run(cshader, cincludes, { sourceName: `compute shader for ${this.label}`, stripDefines: true }); } else { Debug.assert(definition.vshader, "No vertex shader has been specified when creating a shader."); Debug.assert(definition.fshader, "No fragment shader has been specified when creating a shader."); Debug.call(() => { this.vUnmodified = definition.vshader; this.fUnmodified = definition.fshader; }); const wgsl = definition.shaderLanguage === SHADERLANGUAGE_WGSL; definition.vshader = Preprocessor.run(definition.vshader, definition.vincludes, { sourceName: `vertex shader for ${this.label}`, stripDefines: wgsl }); if (definition.shaderLanguage === SHADERLANGUAGE_GLSL) { definition.attributes ?? (definition.attributes = ShaderDefinitionUtils.collectAttributes(definition.vshader)); } const stripUnusedColorAttachments = graphicsDevice.isWebGL2 && (platform.name === "osx" || platform.name === "ios"); definition.fshader = Preprocessor.run(definition.fshader, definition.fincludes, { stripUnusedColorAttachments, stripDefines: wgsl, sourceName: `fragment shader for ${this.label}` }); if (!definition.vshader || !definition.fshader) { Debug.error(`Shader: Failed to create shader ${this.label}. Vertex or fragment shader source is empty.`, this); this.failed = true; return; } } this.impl = graphicsDevice.createShaderImpl(this); Debug.trace(TRACEID_SHADER_ALLOC, `Alloc: ${this.label}, stack: ${DebugGraphics.toString()}`, { instance: this }); } /** * Initialize a shader back to its default state. * * @private */ init() { this.ready = false; this.failed = false; } /** @ignore */ get label() { return `Shader Id ${this.id} (${this.definition.shaderLanguage === SHADERLANGUAGE_WGSL ? "WGSL" : "GLSL"}) ${this.name}`; } /** * Frees resources associated with this shader. */ destroy() { Debug.trace(TRACEID_SHADER_ALLOC, `DeAlloc: Id ${this.id} ${this.name}`); this.device.onDestroyShader(this); this.impl.destroy(this); } /** * Called when the WebGL context was lost. It releases all context related resources. * * @ignore */ loseContext() { this.init(); this.impl.loseContext(); } /** @ignore */ restoreContext() { this.impl.restoreContext(this.device, this); } } export { Shader };