UNPKG

13.3 kBJavaScriptView Raw
1import { ENV, BUFFER_BITS, MSAA_QUALITY } from '@pixi/constants';
2import { ExtensionType, extensions } from '@pixi/extensions';
3import { Rectangle } from '@pixi/math';
4import { settings } from '@pixi/settings';
5import { Framebuffer } from './Framebuffer.mjs';
6import { GLFramebuffer } from './GLFramebuffer.mjs';
7
8const tempRectangle = new Rectangle();
9class FramebufferSystem {
10 constructor(renderer) {
11 this.renderer = renderer;
12 this.managedFramebuffers = [];
13 this.unknownFramebuffer = new Framebuffer(10, 10);
14 this.msaaSamples = null;
15 }
16 contextChange() {
17 this.disposeAll(true);
18 const gl = this.gl = this.renderer.gl;
19 this.CONTEXT_UID = this.renderer.CONTEXT_UID;
20 this.current = this.unknownFramebuffer;
21 this.viewport = new Rectangle();
22 this.hasMRT = true;
23 this.writeDepthTexture = true;
24 if (this.renderer.context.webGLVersion === 1) {
25 let nativeDrawBuffersExtension = this.renderer.context.extensions.drawBuffers;
26 let nativeDepthTextureExtension = this.renderer.context.extensions.depthTexture;
27 if (settings.PREFER_ENV === ENV.WEBGL_LEGACY) {
28 nativeDrawBuffersExtension = null;
29 nativeDepthTextureExtension = null;
30 }
31 if (nativeDrawBuffersExtension) {
32 gl.drawBuffers = (activeTextures) => nativeDrawBuffersExtension.drawBuffersWEBGL(activeTextures);
33 } else {
34 this.hasMRT = false;
35 gl.drawBuffers = () => {
36 };
37 }
38 if (!nativeDepthTextureExtension) {
39 this.writeDepthTexture = false;
40 }
41 } else {
42 this.msaaSamples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES);
43 }
44 }
45 bind(framebuffer, frame, mipLevel = 0) {
46 const { gl } = this;
47 if (framebuffer) {
48 const fbo = framebuffer.glFramebuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer);
49 if (this.current !== framebuffer) {
50 this.current = framebuffer;
51 gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer);
52 }
53 if (fbo.mipLevel !== mipLevel) {
54 framebuffer.dirtyId++;
55 framebuffer.dirtyFormat++;
56 fbo.mipLevel = mipLevel;
57 }
58 if (fbo.dirtyId !== framebuffer.dirtyId) {
59 fbo.dirtyId = framebuffer.dirtyId;
60 if (fbo.dirtyFormat !== framebuffer.dirtyFormat) {
61 fbo.dirtyFormat = framebuffer.dirtyFormat;
62 fbo.dirtySize = framebuffer.dirtySize;
63 this.updateFramebuffer(framebuffer, mipLevel);
64 } else if (fbo.dirtySize !== framebuffer.dirtySize) {
65 fbo.dirtySize = framebuffer.dirtySize;
66 this.resizeFramebuffer(framebuffer);
67 }
68 }
69 for (let i = 0; i < framebuffer.colorTextures.length; i++) {
70 const tex = framebuffer.colorTextures[i];
71 this.renderer.texture.unbind(tex.parentTextureArray || tex);
72 }
73 if (framebuffer.depthTexture) {
74 this.renderer.texture.unbind(framebuffer.depthTexture);
75 }
76 if (frame) {
77 const mipWidth = frame.width >> mipLevel;
78 const mipHeight = frame.height >> mipLevel;
79 const scale = mipWidth / frame.width;
80 this.setViewport(frame.x * scale, frame.y * scale, mipWidth, mipHeight);
81 } else {
82 const mipWidth = framebuffer.width >> mipLevel;
83 const mipHeight = framebuffer.height >> mipLevel;
84 this.setViewport(0, 0, mipWidth, mipHeight);
85 }
86 } else {
87 if (this.current) {
88 this.current = null;
89 gl.bindFramebuffer(gl.FRAMEBUFFER, null);
90 }
91 if (frame) {
92 this.setViewport(frame.x, frame.y, frame.width, frame.height);
93 } else {
94 this.setViewport(0, 0, this.renderer.width, this.renderer.height);
95 }
96 }
97 }
98 setViewport(x, y, width, height) {
99 const v = this.viewport;
100 x = Math.round(x);
101 y = Math.round(y);
102 width = Math.round(width);
103 height = Math.round(height);
104 if (v.width !== width || v.height !== height || v.x !== x || v.y !== y) {
105 v.x = x;
106 v.y = y;
107 v.width = width;
108 v.height = height;
109 this.gl.viewport(x, y, width, height);
110 }
111 }
112 get size() {
113 if (this.current) {
114 return { x: 0, y: 0, width: this.current.width, height: this.current.height };
115 }
116 return { x: 0, y: 0, width: this.renderer.width, height: this.renderer.height };
117 }
118 clear(r, g, b, a, mask = BUFFER_BITS.COLOR | BUFFER_BITS.DEPTH) {
119 const { gl } = this;
120 gl.clearColor(r, g, b, a);
121 gl.clear(mask);
122 }
123 initFramebuffer(framebuffer) {
124 const { gl } = this;
125 const fbo = new GLFramebuffer(gl.createFramebuffer());
126 fbo.multisample = this.detectSamples(framebuffer.multisample);
127 framebuffer.glFramebuffers[this.CONTEXT_UID] = fbo;
128 this.managedFramebuffers.push(framebuffer);
129 framebuffer.disposeRunner.add(this);
130 return fbo;
131 }
132 resizeFramebuffer(framebuffer) {
133 const { gl } = this;
134 const fbo = framebuffer.glFramebuffers[this.CONTEXT_UID];
135 if (fbo.stencil) {
136 gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil);
137 if (fbo.msaaBuffer) {
138 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, fbo.multisample, gl.DEPTH24_STENCIL8, framebuffer.width, framebuffer.height);
139 } else {
140 gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height);
141 }
142 }
143 const colorTextures = framebuffer.colorTextures;
144 let count = colorTextures.length;
145 if (!gl.drawBuffers) {
146 count = Math.min(count, 1);
147 }
148 for (let i = 0; i < count; i++) {
149 const texture = colorTextures[i];
150 const parentTexture = texture.parentTextureArray || texture;
151 this.renderer.texture.bind(parentTexture, 0);
152 if (i === 0 && fbo.msaaBuffer) {
153 gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.msaaBuffer);
154 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, fbo.multisample, parentTexture._glTextures[this.CONTEXT_UID].internalFormat, framebuffer.width, framebuffer.height);
155 }
156 }
157 if (framebuffer.depthTexture && this.writeDepthTexture) {
158 this.renderer.texture.bind(framebuffer.depthTexture, 0);
159 }
160 }
161 updateFramebuffer(framebuffer, mipLevel) {
162 const { gl } = this;
163 const fbo = framebuffer.glFramebuffers[this.CONTEXT_UID];
164 const colorTextures = framebuffer.colorTextures;
165 let count = colorTextures.length;
166 if (!gl.drawBuffers) {
167 count = Math.min(count, 1);
168 }
169 if (fbo.multisample > 1 && this.canMultisampleFramebuffer(framebuffer)) {
170 fbo.msaaBuffer = fbo.msaaBuffer || gl.createRenderbuffer();
171 } else if (fbo.msaaBuffer) {
172 gl.deleteRenderbuffer(fbo.msaaBuffer);
173 fbo.msaaBuffer = null;
174 if (fbo.blitFramebuffer) {
175 fbo.blitFramebuffer.dispose();
176 fbo.blitFramebuffer = null;
177 }
178 }
179 const activeTextures = [];
180 for (let i = 0; i < count; i++) {
181 const texture = colorTextures[i];
182 const parentTexture = texture.parentTextureArray || texture;
183 this.renderer.texture.bind(parentTexture, 0);
184 if (i === 0 && fbo.msaaBuffer) {
185 gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.msaaBuffer);
186 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, fbo.multisample, parentTexture._glTextures[this.CONTEXT_UID].internalFormat, framebuffer.width, framebuffer.height);
187 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, fbo.msaaBuffer);
188 } else {
189 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, texture.target, parentTexture._glTextures[this.CONTEXT_UID].texture, mipLevel);
190 activeTextures.push(gl.COLOR_ATTACHMENT0 + i);
191 }
192 }
193 if (activeTextures.length > 1) {
194 gl.drawBuffers(activeTextures);
195 }
196 if (framebuffer.depthTexture) {
197 const writeDepthTexture = this.writeDepthTexture;
198 if (writeDepthTexture) {
199 const depthTexture = framebuffer.depthTexture;
200 this.renderer.texture.bind(depthTexture, 0);
201 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture._glTextures[this.CONTEXT_UID].texture, mipLevel);
202 }
203 }
204 if ((framebuffer.stencil || framebuffer.depth) && !(framebuffer.depthTexture && this.writeDepthTexture)) {
205 fbo.stencil = fbo.stencil || gl.createRenderbuffer();
206 gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil);
207 if (fbo.msaaBuffer) {
208 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, fbo.multisample, gl.DEPTH24_STENCIL8, framebuffer.width, framebuffer.height);
209 } else {
210 gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height);
211 }
212 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil);
213 } else if (fbo.stencil) {
214 gl.deleteRenderbuffer(fbo.stencil);
215 fbo.stencil = null;
216 }
217 }
218 canMultisampleFramebuffer(framebuffer) {
219 return this.renderer.context.webGLVersion !== 1 && framebuffer.colorTextures.length <= 1 && !framebuffer.depthTexture;
220 }
221 detectSamples(samples) {
222 const { msaaSamples } = this;
223 let res = MSAA_QUALITY.NONE;
224 if (samples <= 1 || msaaSamples === null) {
225 return res;
226 }
227 for (let i = 0; i < msaaSamples.length; i++) {
228 if (msaaSamples[i] <= samples) {
229 res = msaaSamples[i];
230 break;
231 }
232 }
233 if (res === 1) {
234 res = MSAA_QUALITY.NONE;
235 }
236 return res;
237 }
238 blit(framebuffer, sourcePixels, destPixels) {
239 const { current, renderer, gl, CONTEXT_UID } = this;
240 if (renderer.context.webGLVersion !== 2) {
241 return;
242 }
243 if (!current) {
244 return;
245 }
246 const fbo = current.glFramebuffers[CONTEXT_UID];
247 if (!fbo) {
248 return;
249 }
250 if (!framebuffer) {
251 if (!fbo.msaaBuffer) {
252 return;
253 }
254 const colorTexture = current.colorTextures[0];
255 if (!colorTexture) {
256 return;
257 }
258 if (!fbo.blitFramebuffer) {
259 fbo.blitFramebuffer = new Framebuffer(current.width, current.height);
260 fbo.blitFramebuffer.addColorTexture(0, colorTexture);
261 }
262 framebuffer = fbo.blitFramebuffer;
263 if (framebuffer.colorTextures[0] !== colorTexture) {
264 framebuffer.colorTextures[0] = colorTexture;
265 framebuffer.dirtyId++;
266 framebuffer.dirtyFormat++;
267 }
268 if (framebuffer.width !== current.width || framebuffer.height !== current.height) {
269 framebuffer.width = current.width;
270 framebuffer.height = current.height;
271 framebuffer.dirtyId++;
272 framebuffer.dirtySize++;
273 }
274 }
275 if (!sourcePixels) {
276 sourcePixels = tempRectangle;
277 sourcePixels.width = current.width;
278 sourcePixels.height = current.height;
279 }
280 if (!destPixels) {
281 destPixels = sourcePixels;
282 }
283 const sameSize = sourcePixels.width === destPixels.width && sourcePixels.height === destPixels.height;
284 this.bind(framebuffer);
285 gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo.framebuffer);
286 gl.blitFramebuffer(sourcePixels.left, sourcePixels.top, sourcePixels.right, sourcePixels.bottom, destPixels.left, destPixels.top, destPixels.right, destPixels.bottom, gl.COLOR_BUFFER_BIT, sameSize ? gl.NEAREST : gl.LINEAR);
287 }
288 disposeFramebuffer(framebuffer, contextLost) {
289 const fbo = framebuffer.glFramebuffers[this.CONTEXT_UID];
290 const gl = this.gl;
291 if (!fbo) {
292 return;
293 }
294 delete framebuffer.glFramebuffers[this.CONTEXT_UID];
295 const index = this.managedFramebuffers.indexOf(framebuffer);
296 if (index >= 0) {
297 this.managedFramebuffers.splice(index, 1);
298 }
299 framebuffer.disposeRunner.remove(this);
300 if (!contextLost) {
301 gl.deleteFramebuffer(fbo.framebuffer);
302 if (fbo.msaaBuffer) {
303 gl.deleteRenderbuffer(fbo.msaaBuffer);
304 }
305 if (fbo.stencil) {
306 gl.deleteRenderbuffer(fbo.stencil);
307 }
308 }
309 if (fbo.blitFramebuffer) {
310 this.disposeFramebuffer(fbo.blitFramebuffer, contextLost);
311 }
312 }
313 disposeAll(contextLost) {
314 const list = this.managedFramebuffers;
315 this.managedFramebuffers = [];
316 for (let i = 0; i < list.length; i++) {
317 this.disposeFramebuffer(list[i], contextLost);
318 }
319 }
320 forceStencil() {
321 const framebuffer = this.current;
322 if (!framebuffer) {
323 return;
324 }
325 const fbo = framebuffer.glFramebuffers[this.CONTEXT_UID];
326 if (!fbo || fbo.stencil) {
327 return;
328 }
329 framebuffer.stencil = true;
330 const w = framebuffer.width;
331 const h = framebuffer.height;
332 const gl = this.gl;
333 const stencil = gl.createRenderbuffer();
334 gl.bindRenderbuffer(gl.RENDERBUFFER, stencil);
335 if (fbo.msaaBuffer) {
336 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, fbo.multisample, gl.DEPTH24_STENCIL8, w, h);
337 } else {
338 gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, w, h);
339 }
340 fbo.stencil = stencil;
341 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencil);
342 }
343 reset() {
344 this.current = this.unknownFramebuffer;
345 this.viewport = new Rectangle();
346 }
347 destroy() {
348 this.renderer = null;
349 }
350}
351FramebufferSystem.extension = {
352 type: ExtensionType.RendererSystem,
353 name: "framebuffer"
354};
355extensions.add(FramebufferSystem);
356
357export { FramebufferSystem };
358//# sourceMappingURL=FramebufferSystem.mjs.map