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