securityos/node_modules/regl/lib/shader.js

261 lines
7.0 KiB
JavaScript

var check = require('./util/check')
var extend = require('./util/extend')
var values = require('./util/values')
var GL_FRAGMENT_SHADER = 35632
var GL_VERTEX_SHADER = 35633
var GL_ACTIVE_UNIFORMS = 0x8B86
var GL_ACTIVE_ATTRIBUTES = 0x8B89
module.exports = function wrapShaderState (gl, stringStore, stats, config) {
// ===================================================
// glsl compilation and linking
// ===================================================
var fragShaders = {}
var vertShaders = {}
function ActiveInfo (name, id, location, info) {
this.name = name
this.id = id
this.location = location
this.info = info
}
function insertActiveInfo (list, info) {
for (var i = 0; i < list.length; ++i) {
if (list[i].id === info.id) {
list[i].location = info.location
return
}
}
list.push(info)
}
function getShader (type, id, command) {
var cache = type === GL_FRAGMENT_SHADER ? fragShaders : vertShaders
var shader = cache[id]
if (!shader) {
var source = stringStore.str(id)
shader = gl.createShader(type)
gl.shaderSource(shader, source)
gl.compileShader(shader)
check.shaderError(gl, shader, source, type, command)
cache[id] = shader
}
return shader
}
// ===================================================
// program linking
// ===================================================
var programCache = {}
var programList = []
var PROGRAM_COUNTER = 0
function REGLProgram (fragId, vertId) {
this.id = PROGRAM_COUNTER++
this.fragId = fragId
this.vertId = vertId
this.program = null
this.uniforms = []
this.attributes = []
this.refCount = 1
if (config.profile) {
this.stats = {
uniformsCount: 0,
attributesCount: 0
}
}
}
function linkProgram (desc, command, attributeLocations) {
var i, info
// -------------------------------
// compile & link
// -------------------------------
var fragShader = getShader(GL_FRAGMENT_SHADER, desc.fragId)
var vertShader = getShader(GL_VERTEX_SHADER, desc.vertId)
var program = desc.program = gl.createProgram()
gl.attachShader(program, fragShader)
gl.attachShader(program, vertShader)
if (attributeLocations) {
for (i = 0; i < attributeLocations.length; ++i) {
var binding = attributeLocations[i]
gl.bindAttribLocation(program, binding[0], binding[1])
}
}
gl.linkProgram(program)
check.linkError(
gl,
program,
stringStore.str(desc.fragId),
stringStore.str(desc.vertId),
command)
// -------------------------------
// grab uniforms
// -------------------------------
var numUniforms = gl.getProgramParameter(program, GL_ACTIVE_UNIFORMS)
if (config.profile) {
desc.stats.uniformsCount = numUniforms
}
var uniforms = desc.uniforms
for (i = 0; i < numUniforms; ++i) {
info = gl.getActiveUniform(program, i)
if (info) {
if (info.size > 1) {
for (var j = 0; j < info.size; ++j) {
var name = info.name.replace('[0]', '[' + j + ']')
insertActiveInfo(uniforms, new ActiveInfo(
name,
stringStore.id(name),
gl.getUniformLocation(program, name),
info))
}
}
var uniName = info.name
if (info.size > 1) {
uniName = uniName.replace('[0]', '')
}
insertActiveInfo(uniforms, new ActiveInfo(
uniName,
stringStore.id(uniName),
gl.getUniformLocation(program, uniName),
info))
}
}
// -------------------------------
// grab attributes
// -------------------------------
var numAttributes = gl.getProgramParameter(program, GL_ACTIVE_ATTRIBUTES)
if (config.profile) {
desc.stats.attributesCount = numAttributes
}
var attributes = desc.attributes
for (i = 0; i < numAttributes; ++i) {
info = gl.getActiveAttrib(program, i)
if (info) {
insertActiveInfo(attributes, new ActiveInfo(
info.name,
stringStore.id(info.name),
gl.getAttribLocation(program, info.name),
info))
}
}
}
if (config.profile) {
stats.getMaxUniformsCount = function () {
var m = 0
programList.forEach(function (desc) {
if (desc.stats.uniformsCount > m) {
m = desc.stats.uniformsCount
}
})
return m
}
stats.getMaxAttributesCount = function () {
var m = 0
programList.forEach(function (desc) {
if (desc.stats.attributesCount > m) {
m = desc.stats.attributesCount
}
})
return m
}
}
function restoreShaders () {
fragShaders = {}
vertShaders = {}
for (var i = 0; i < programList.length; ++i) {
linkProgram(programList[i], null, programList[i].attributes.map(function (info) {
return [info.location, info.name]
}))
}
}
return {
clear: function () {
var deleteShader = gl.deleteShader.bind(gl)
values(fragShaders).forEach(deleteShader)
fragShaders = {}
values(vertShaders).forEach(deleteShader)
vertShaders = {}
programList.forEach(function (desc) {
gl.deleteProgram(desc.program)
})
programList.length = 0
programCache = {}
stats.shaderCount = 0
},
program: function (vertId, fragId, command, attribLocations) {
check.command(vertId >= 0, 'missing vertex shader', command)
check.command(fragId >= 0, 'missing fragment shader', command)
var cache = programCache[fragId]
if (!cache) {
cache = programCache[fragId] = {}
}
var prevProgram = cache[vertId]
if (prevProgram) {
prevProgram.refCount++
if (!attribLocations) {
return prevProgram
}
}
var program = new REGLProgram(fragId, vertId)
stats.shaderCount++
linkProgram(program, command, attribLocations)
if (!prevProgram) {
cache[vertId] = program
}
programList.push(program)
return extend(program, {
destroy: function () {
program.refCount--
if (program.refCount <= 0) {
gl.deleteProgram(program.program)
var idx = programList.indexOf(program)
programList.splice(idx, 1)
stats.shaderCount--
}
// no program is linked to this vert anymore
if (cache[program.vertId].refCount <= 0) {
gl.deleteShader(vertShaders[program.vertId])
delete vertShaders[program.vertId]
delete programCache[program.fragId][program.vertId]
}
// no program is linked to this frag anymore
if (!Object.keys(programCache[program.fragId]).length) {
gl.deleteShader(fragShaders[program.fragId])
delete fragShaders[program.fragId]
delete programCache[program.fragId]
}
}
})
},
restore: restoreShaders,
shader: getShader,
frag: -1,
vert: -1
}
}