252 lines
8.8 KiB
JavaScript
252 lines
8.8 KiB
JavaScript
'use strict';
|
|
|
|
var Extensions = require('../../../../extensions/Extensions.js');
|
|
var warn = require('../../../../utils/logging/warn.js');
|
|
var ensureAttributes = require('../../gl/shader/program/ensureAttributes.js');
|
|
var _const = require('../../shared/state/const.js');
|
|
var createIdFromString = require('../../shared/utils/createIdFromString.js');
|
|
var GpuStencilModesToPixi = require('../state/GpuStencilModesToPixi.js');
|
|
|
|
"use strict";
|
|
const topologyStringToId = {
|
|
"point-list": 0,
|
|
"line-list": 1,
|
|
"line-strip": 2,
|
|
"triangle-list": 3,
|
|
"triangle-strip": 4
|
|
};
|
|
function getGraphicsStateKey(geometryLayout, shaderKey, state, blendMode, topology) {
|
|
return geometryLayout << 24 | shaderKey << 16 | state << 10 | blendMode << 5 | topology;
|
|
}
|
|
function getGlobalStateKey(stencilStateId, multiSampleCount, colorMask, renderTarget) {
|
|
return colorMask << 6 | stencilStateId << 3 | renderTarget << 1 | multiSampleCount;
|
|
}
|
|
class PipelineSystem {
|
|
constructor(renderer) {
|
|
this._moduleCache = /* @__PURE__ */ Object.create(null);
|
|
this._bufferLayoutsCache = /* @__PURE__ */ Object.create(null);
|
|
this._bindingNamesCache = /* @__PURE__ */ Object.create(null);
|
|
this._pipeCache = /* @__PURE__ */ Object.create(null);
|
|
this._pipeStateCaches = /* @__PURE__ */ Object.create(null);
|
|
this._colorMask = 15;
|
|
this._multisampleCount = 1;
|
|
this._renderer = renderer;
|
|
}
|
|
contextChange(gpu) {
|
|
this._gpu = gpu;
|
|
this.setStencilMode(_const.STENCIL_MODES.DISABLED);
|
|
this._updatePipeHash();
|
|
}
|
|
setMultisampleCount(multisampleCount) {
|
|
if (this._multisampleCount === multisampleCount)
|
|
return;
|
|
this._multisampleCount = multisampleCount;
|
|
this._updatePipeHash();
|
|
}
|
|
setRenderTarget(renderTarget) {
|
|
this._multisampleCount = renderTarget.msaaSamples;
|
|
this._depthStencilAttachment = renderTarget.descriptor.depthStencilAttachment ? 1 : 0;
|
|
this._updatePipeHash();
|
|
}
|
|
setColorMask(colorMask) {
|
|
if (this._colorMask === colorMask)
|
|
return;
|
|
this._colorMask = colorMask;
|
|
this._updatePipeHash();
|
|
}
|
|
setStencilMode(stencilMode) {
|
|
if (this._stencilMode === stencilMode)
|
|
return;
|
|
this._stencilMode = stencilMode;
|
|
this._stencilState = GpuStencilModesToPixi.GpuStencilModesToPixi[stencilMode];
|
|
this._updatePipeHash();
|
|
}
|
|
setPipeline(geometry, program, state, passEncoder) {
|
|
const pipeline = this.getPipeline(geometry, program, state);
|
|
passEncoder.setPipeline(pipeline);
|
|
}
|
|
getPipeline(geometry, program, state, topology) {
|
|
if (!geometry._layoutKey) {
|
|
ensureAttributes.ensureAttributes(geometry, program.attributeData);
|
|
this._generateBufferKey(geometry);
|
|
}
|
|
topology = topology || geometry.topology;
|
|
const key = getGraphicsStateKey(
|
|
geometry._layoutKey,
|
|
program._layoutKey,
|
|
state.data,
|
|
state._blendModeId,
|
|
topologyStringToId[topology]
|
|
);
|
|
if (this._pipeCache[key])
|
|
return this._pipeCache[key];
|
|
this._pipeCache[key] = this._createPipeline(geometry, program, state, topology);
|
|
return this._pipeCache[key];
|
|
}
|
|
_createPipeline(geometry, program, state, topology) {
|
|
const device = this._gpu.device;
|
|
const buffers = this._createVertexBufferLayouts(geometry, program);
|
|
const blendModes = this._renderer.state.getColorTargets(state);
|
|
blendModes[0].writeMask = this._stencilMode === _const.STENCIL_MODES.RENDERING_MASK_ADD ? 0 : this._colorMask;
|
|
const layout = this._renderer.shader.getProgramData(program).pipeline;
|
|
const descriptor = {
|
|
// TODO later check if its helpful to create..
|
|
// layout,
|
|
vertex: {
|
|
module: this._getModule(program.vertex.source),
|
|
entryPoint: program.vertex.entryPoint,
|
|
// geometry..
|
|
buffers
|
|
},
|
|
fragment: {
|
|
module: this._getModule(program.fragment.source),
|
|
entryPoint: program.fragment.entryPoint,
|
|
targets: blendModes
|
|
},
|
|
primitive: {
|
|
topology,
|
|
cullMode: state.cullMode
|
|
},
|
|
layout,
|
|
multisample: {
|
|
count: this._multisampleCount
|
|
},
|
|
// depthStencil,
|
|
label: `PIXI Pipeline`
|
|
};
|
|
if (this._depthStencilAttachment) {
|
|
descriptor.depthStencil = {
|
|
...this._stencilState,
|
|
format: "depth24plus-stencil8",
|
|
depthWriteEnabled: state.depthTest,
|
|
depthCompare: state.depthTest ? "less" : "always"
|
|
};
|
|
}
|
|
const pipeline = device.createRenderPipeline(descriptor);
|
|
return pipeline;
|
|
}
|
|
_getModule(code) {
|
|
return this._moduleCache[code] || this._createModule(code);
|
|
}
|
|
_createModule(code) {
|
|
const device = this._gpu.device;
|
|
this._moduleCache[code] = device.createShaderModule({
|
|
code
|
|
});
|
|
return this._moduleCache[code];
|
|
}
|
|
_generateBufferKey(geometry) {
|
|
const keyGen = [];
|
|
let index = 0;
|
|
const attributeKeys = Object.keys(geometry.attributes).sort();
|
|
for (let i = 0; i < attributeKeys.length; i++) {
|
|
const attribute = geometry.attributes[attributeKeys[i]];
|
|
keyGen[index++] = attribute.offset;
|
|
keyGen[index++] = attribute.format;
|
|
keyGen[index++] = attribute.stride;
|
|
keyGen[index++] = attribute.instance;
|
|
}
|
|
const stringKey = keyGen.join("|");
|
|
geometry._layoutKey = createIdFromString.createIdFromString(stringKey, "geometry");
|
|
return geometry._layoutKey;
|
|
}
|
|
_generateAttributeLocationsKey(program) {
|
|
const keyGen = [];
|
|
let index = 0;
|
|
const attributeKeys = Object.keys(program.attributeData).sort();
|
|
for (let i = 0; i < attributeKeys.length; i++) {
|
|
const attribute = program.attributeData[attributeKeys[i]];
|
|
keyGen[index++] = attribute.location;
|
|
}
|
|
const stringKey = keyGen.join("|");
|
|
program._attributeLocationsKey = createIdFromString.createIdFromString(stringKey, "programAttributes");
|
|
return program._attributeLocationsKey;
|
|
}
|
|
/**
|
|
* Returns a hash of buffer names mapped to bind locations.
|
|
* This is used to bind the correct buffer to the correct location in the shader.
|
|
* @param geometry - The geometry where to get the buffer names
|
|
* @param program - The program where to get the buffer names
|
|
* @returns An object of buffer names mapped to the bind location.
|
|
*/
|
|
getBufferNamesToBind(geometry, program) {
|
|
const key = geometry._layoutKey << 16 | program._attributeLocationsKey;
|
|
if (this._bindingNamesCache[key])
|
|
return this._bindingNamesCache[key];
|
|
const data = this._createVertexBufferLayouts(geometry, program);
|
|
const bufferNamesToBind = /* @__PURE__ */ Object.create(null);
|
|
const attributeData = program.attributeData;
|
|
for (let i = 0; i < data.length; i++) {
|
|
for (const j in attributeData) {
|
|
if (attributeData[j].location === i) {
|
|
bufferNamesToBind[i] = j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
this._bindingNamesCache[key] = bufferNamesToBind;
|
|
return bufferNamesToBind;
|
|
}
|
|
_createVertexBufferLayouts(geometry, program) {
|
|
if (!program._attributeLocationsKey)
|
|
this._generateAttributeLocationsKey(program);
|
|
const key = geometry._layoutKey << 16 | program._attributeLocationsKey;
|
|
if (this._bufferLayoutsCache[key]) {
|
|
return this._bufferLayoutsCache[key];
|
|
}
|
|
const vertexBuffersLayout = [];
|
|
geometry.buffers.forEach((buffer) => {
|
|
const bufferEntry = {
|
|
arrayStride: 0,
|
|
stepMode: "vertex",
|
|
attributes: []
|
|
};
|
|
const bufferEntryAttributes = bufferEntry.attributes;
|
|
for (const i in program.attributeData) {
|
|
const attribute = geometry.attributes[i];
|
|
if ((attribute.divisor ?? 1) !== 1) {
|
|
warn.warn(`Attribute ${i} has an invalid divisor value of '${attribute.divisor}'. WebGPU only supports a divisor value of 1`);
|
|
}
|
|
if (attribute.buffer === buffer) {
|
|
bufferEntry.arrayStride = attribute.stride;
|
|
bufferEntry.stepMode = attribute.instance ? "instance" : "vertex";
|
|
bufferEntryAttributes.push({
|
|
shaderLocation: program.attributeData[i].location,
|
|
offset: attribute.offset,
|
|
format: attribute.format
|
|
});
|
|
}
|
|
}
|
|
if (bufferEntryAttributes.length) {
|
|
vertexBuffersLayout.push(bufferEntry);
|
|
}
|
|
});
|
|
this._bufferLayoutsCache[key] = vertexBuffersLayout;
|
|
return vertexBuffersLayout;
|
|
}
|
|
_updatePipeHash() {
|
|
const key = getGlobalStateKey(
|
|
this._stencilMode,
|
|
this._multisampleCount,
|
|
this._colorMask,
|
|
this._depthStencilAttachment
|
|
);
|
|
if (!this._pipeStateCaches[key]) {
|
|
this._pipeStateCaches[key] = /* @__PURE__ */ Object.create(null);
|
|
}
|
|
this._pipeCache = this._pipeStateCaches[key];
|
|
}
|
|
destroy() {
|
|
this._renderer = null;
|
|
this._bufferLayoutsCache = null;
|
|
}
|
|
}
|
|
/** @ignore */
|
|
PipelineSystem.extension = {
|
|
type: [Extensions.ExtensionType.WebGPUSystem],
|
|
name: "pipeline"
|
|
};
|
|
|
|
exports.PipelineSystem = PipelineSystem;
|
|
//# sourceMappingURL=PipelineSystem.js.map
|