1 | 'use strict'
|
2 |
|
3 | exports.shader = getShaderReference
|
4 | exports.program = createProgram
|
5 |
|
6 | var formatCompilerError = require('gl-format-compiler-error');
|
7 |
|
8 | var weakMap = typeof WeakMap === 'undefined' ? require('weakmap-shim') : WeakMap
|
9 | var CACHE = new weakMap()
|
10 |
|
11 | var SHADER_COUNTER = 0
|
12 |
|
13 | function ShaderReference(id, src, type, shader, programs, count, cache) {
|
14 | this.id = id
|
15 | this.src = src
|
16 | this.type = type
|
17 | this.shader = shader
|
18 | this.count = count
|
19 | this.programs = []
|
20 | this.cache = cache
|
21 | }
|
22 |
|
23 | ShaderReference.prototype.dispose = function() {
|
24 | if(--this.count === 0) {
|
25 | var cache = this.cache
|
26 | var gl = cache.gl
|
27 |
|
28 |
|
29 | var programs = this.programs
|
30 | for(var i=0, n=programs.length; i<n; ++i) {
|
31 | var p = cache.programs[programs[i]]
|
32 | if(p) {
|
33 | delete cache.programs[i]
|
34 | gl.deleteProgram(p)
|
35 | }
|
36 | }
|
37 |
|
38 |
|
39 | gl.deleteShader(this.shader)
|
40 | delete cache.shaders[(this.type === gl.FRAGMENT_SHADER)|0][this.src]
|
41 | }
|
42 | }
|
43 |
|
44 | function ContextCache(gl) {
|
45 | this.gl = gl
|
46 | this.shaders = [{}, {}]
|
47 | this.programs = {}
|
48 | }
|
49 |
|
50 | var proto = ContextCache.prototype
|
51 |
|
52 | function compileShader(gl, type, src) {
|
53 | var shader = gl.createShader(type)
|
54 | gl.shaderSource(shader, src)
|
55 | gl.compileShader(shader)
|
56 | if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
57 | var errLog = gl.getShaderInfoLog(shader)
|
58 | try {
|
59 | var fmt = formatCompilerError(errLog, src, type);
|
60 | } catch (e){
|
61 | console.warn('Failed to format compiler error: ' + e);
|
62 | throw new Error('gl-shader: Error compiling shader:\n' + errLog)
|
63 | }
|
64 | console.warn('gl-shader: ' + fmt.long);
|
65 | throw new Error('gl-shader: ' + fmt.short)
|
66 | }
|
67 | return shader
|
68 | }
|
69 |
|
70 | proto.getShaderReference = function(type, src) {
|
71 | var gl = this.gl
|
72 | var shaders = this.shaders[(type === gl.FRAGMENT_SHADER)|0]
|
73 | var shader = shaders[src]
|
74 | if(!shader || !gl.isShader(shader.shader)) {
|
75 | var shaderObj = compileShader(gl, type, src)
|
76 | shader = shaders[src] = new ShaderReference(
|
77 | SHADER_COUNTER++,
|
78 | src,
|
79 | type,
|
80 | shaderObj,
|
81 | [],
|
82 | 1,
|
83 | this)
|
84 | } else {
|
85 | shader.count += 1
|
86 | }
|
87 | return shader
|
88 | }
|
89 |
|
90 | function linkProgram(gl, vshader, fshader, attribs, locations) {
|
91 | var program = gl.createProgram()
|
92 | gl.attachShader(program, vshader)
|
93 | gl.attachShader(program, fshader)
|
94 | for(var i=0; i<attribs.length; ++i) {
|
95 | gl.bindAttribLocation(program, locations[i], attribs[i])
|
96 | }
|
97 | gl.linkProgram(program)
|
98 | if(!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
99 | var errLog = gl.getProgramInfoLog(program)
|
100 | console.error('gl-shader: Error linking program:', errLog)
|
101 | throw new Error('gl-shader: Error linking program:' + errLog)
|
102 | }
|
103 | return program
|
104 | }
|
105 |
|
106 | proto.getProgram = function(vref, fref, attribs, locations) {
|
107 | var token = [vref.id, fref.id, attribs.join(':'), locations.join(':')].join('@')
|
108 | var prog = this.programs[token]
|
109 | if(!prog || !this.gl.isProgram(prog)) {
|
110 | this.programs[token] = prog = linkProgram(
|
111 | this.gl,
|
112 | vref.shader,
|
113 | fref.shader,
|
114 | attribs,
|
115 | locations)
|
116 | vref.programs.push(token)
|
117 | fref.programs.push(token)
|
118 | }
|
119 | return prog
|
120 | }
|
121 |
|
122 | function getCache(gl) {
|
123 | var ctxCache = CACHE.get(gl)
|
124 | if(!ctxCache) {
|
125 | ctxCache = new ContextCache(gl)
|
126 | CACHE.set(gl, ctxCache)
|
127 | }
|
128 | return ctxCache
|
129 | }
|
130 |
|
131 | function getShaderReference(gl, type, src) {
|
132 | return getCache(gl).getShaderReference(type, src)
|
133 | }
|
134 |
|
135 | function createProgram(gl, vref, fref, attribs, locations) {
|
136 | return getCache(gl).getProgram(vref, fref, attribs, locations)
|
137 | }
|