/src/mozilla-central/dom/canvas/WebGLFramebuffer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "WebGLFramebuffer.h" |
7 | | |
8 | | // You know it's going to be fun when these two show up: |
9 | | #include <algorithm> |
10 | | #include <iterator> |
11 | | |
12 | | #include "GLContext.h" |
13 | | #include "GLScreenBuffer.h" |
14 | | #include "mozilla/dom/WebGLRenderingContextBinding.h" |
15 | | #include "nsPrintfCString.h" |
16 | | #include "WebGLContext.h" |
17 | | #include "WebGLContextUtils.h" |
18 | | #include "WebGLExtensions.h" |
19 | | #include "WebGLRenderbuffer.h" |
20 | | #include "WebGLTexture.h" |
21 | | |
22 | | namespace mozilla { |
23 | | |
24 | | WebGLFBAttachPoint::WebGLFBAttachPoint() |
25 | | : mFB(nullptr) |
26 | | , mAttachmentPoint(0) |
27 | | , mTexImageTarget(LOCAL_GL_NONE) |
28 | | , mTexImageLayer(0) |
29 | | , mTexImageLevel(0) |
30 | 0 | { } |
31 | | |
32 | | WebGLFBAttachPoint::WebGLFBAttachPoint(WebGLFramebuffer* fb, GLenum attachmentPoint) |
33 | | : mFB(fb) |
34 | | , mAttachmentPoint(attachmentPoint) |
35 | | , mTexImageTarget(LOCAL_GL_NONE) |
36 | | , mTexImageLayer(0) |
37 | | , mTexImageLevel(0) |
38 | 0 | { } |
39 | | |
40 | | WebGLFBAttachPoint::~WebGLFBAttachPoint() |
41 | 0 | { |
42 | 0 | MOZ_ASSERT(mFB, "Should have been Init'd."); |
43 | 0 | MOZ_ASSERT(!mRenderbufferPtr); |
44 | 0 | MOZ_ASSERT(!mTexturePtr); |
45 | 0 | } |
46 | | |
47 | | void |
48 | | WebGLFBAttachPoint::Unlink() |
49 | 0 | { |
50 | 0 | Clear(); |
51 | 0 | } |
52 | | |
53 | | bool |
54 | | WebGLFBAttachPoint::IsDeleteRequested() const |
55 | 0 | { |
56 | 0 | return Texture() ? Texture()->IsDeleteRequested() |
57 | 0 | : Renderbuffer() ? Renderbuffer()->IsDeleteRequested() |
58 | 0 | : false; |
59 | 0 | } |
60 | | |
61 | | bool |
62 | | WebGLFBAttachPoint::IsDefined() const |
63 | 0 | { |
64 | 0 | /* |
65 | 0 | return (Renderbuffer() && Renderbuffer()->IsDefined()) || |
66 | 0 | (Texture() && Texture()->ImageInfoAt(mTexImageTarget, |
67 | 0 | mTexImageLevel).IsDefined()); |
68 | 0 | */ |
69 | 0 | return (Renderbuffer() || Texture()); |
70 | 0 | } |
71 | | |
72 | | const webgl::FormatUsageInfo* |
73 | | WebGLFBAttachPoint::Format() const |
74 | 0 | { |
75 | 0 | MOZ_ASSERT(IsDefined()); |
76 | 0 |
|
77 | 0 | if (Texture()) |
78 | 0 | return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).mFormat; |
79 | 0 | |
80 | 0 | if (Renderbuffer()) |
81 | 0 | return Renderbuffer()->Format(); |
82 | 0 | |
83 | 0 | return nullptr; |
84 | 0 | } |
85 | | |
86 | | uint32_t |
87 | | WebGLFBAttachPoint::Samples() const |
88 | 0 | { |
89 | 0 | MOZ_ASSERT(IsDefined()); |
90 | 0 |
|
91 | 0 | if (mRenderbufferPtr) |
92 | 0 | return mRenderbufferPtr->Samples(); |
93 | 0 | |
94 | 0 | return 0; |
95 | 0 | } |
96 | | |
97 | | bool |
98 | | WebGLFBAttachPoint::HasAlpha() const |
99 | 0 | { |
100 | 0 | return Format()->format->a; |
101 | 0 | } |
102 | | |
103 | | bool |
104 | | WebGLFBAttachPoint::IsReadableFloat() const |
105 | 0 | { |
106 | 0 | auto formatUsage = Format(); |
107 | 0 | MOZ_ASSERT(formatUsage); |
108 | 0 |
|
109 | 0 | auto format = formatUsage->format; |
110 | 0 | if (!format->IsColorFormat()) |
111 | 0 | return false; |
112 | 0 | |
113 | 0 | return format->componentType == webgl::ComponentType::Float; |
114 | 0 | } |
115 | | |
116 | | void |
117 | | WebGLFBAttachPoint::Clear() |
118 | 0 | { |
119 | 0 | if (mRenderbufferPtr) { |
120 | 0 | MOZ_ASSERT(!mTexturePtr); |
121 | 0 | mRenderbufferPtr->UnmarkAttachment(*this); |
122 | 0 | } else if (mTexturePtr) { |
123 | 0 | mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).RemoveAttachPoint(this); |
124 | 0 | } |
125 | 0 |
|
126 | 0 | mTexturePtr = nullptr; |
127 | 0 | mRenderbufferPtr = nullptr; |
128 | 0 |
|
129 | 0 | OnBackingStoreRespecified(); |
130 | 0 | } |
131 | | |
132 | | void |
133 | | WebGLFBAttachPoint::SetTexImage(WebGLTexture* tex, |
134 | | TexImageTarget target, GLint level, GLint layer) |
135 | 0 | { |
136 | 0 | Clear(); |
137 | 0 |
|
138 | 0 | mTexturePtr = tex; |
139 | 0 | mTexImageTarget = target; |
140 | 0 | mTexImageLevel = level; |
141 | 0 | mTexImageLayer = layer; |
142 | 0 |
|
143 | 0 | if (mTexturePtr) { |
144 | 0 | mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).AddAttachPoint(this); |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | | void |
149 | | WebGLFBAttachPoint::SetRenderbuffer(WebGLRenderbuffer* rb) |
150 | 0 | { |
151 | 0 | Clear(); |
152 | 0 |
|
153 | 0 | mRenderbufferPtr = rb; |
154 | 0 |
|
155 | 0 | if (mRenderbufferPtr) { |
156 | 0 | mRenderbufferPtr->MarkAttachment(*this); |
157 | 0 | } |
158 | 0 | } |
159 | | |
160 | | bool |
161 | | WebGLFBAttachPoint::HasUninitializedImageData() const |
162 | 0 | { |
163 | 0 | if (!HasImage()) |
164 | 0 | return false; |
165 | 0 | |
166 | 0 | if (mRenderbufferPtr) |
167 | 0 | return mRenderbufferPtr->HasUninitializedImageData(); |
168 | 0 | |
169 | 0 | MOZ_ASSERT(mTexturePtr); |
170 | 0 |
|
171 | 0 | auto& imageInfo = mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel); |
172 | 0 | MOZ_ASSERT(imageInfo.IsDefined()); |
173 | 0 |
|
174 | 0 | return !imageInfo.IsDataInitialized(); |
175 | 0 | } |
176 | | |
177 | | void |
178 | | WebGLFBAttachPoint::SetImageDataStatus(WebGLImageDataStatus newStatus) const |
179 | 0 | { |
180 | 0 | if (!HasImage()) |
181 | 0 | return; |
182 | 0 | |
183 | 0 | if (mRenderbufferPtr) { |
184 | 0 | mRenderbufferPtr->mImageDataStatus = newStatus; |
185 | 0 | return; |
186 | 0 | } |
187 | 0 | |
188 | 0 | MOZ_ASSERT(mTexturePtr); |
189 | 0 |
|
190 | 0 | auto& imageInfo = mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel); |
191 | 0 | MOZ_ASSERT(imageInfo.IsDefined()); |
192 | 0 |
|
193 | 0 | const bool isDataInitialized = (newStatus == WebGLImageDataStatus::InitializedImageData); |
194 | 0 | imageInfo.SetIsDataInitialized(isDataInitialized, mTexturePtr); |
195 | 0 | } |
196 | | |
197 | | bool |
198 | | WebGLFBAttachPoint::HasImage() const |
199 | 0 | { |
200 | 0 | if (Texture() && Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).IsDefined()) |
201 | 0 | return true; |
202 | 0 | |
203 | 0 | if (Renderbuffer() && Renderbuffer()->IsDefined()) |
204 | 0 | return true; |
205 | 0 | |
206 | 0 | return false; |
207 | 0 | } |
208 | | |
209 | | void |
210 | | WebGLFBAttachPoint::Size(uint32_t* const out_width, uint32_t* const out_height) const |
211 | 0 | { |
212 | 0 | MOZ_ASSERT(HasImage()); |
213 | 0 |
|
214 | 0 | if (Renderbuffer()) { |
215 | 0 | *out_width = Renderbuffer()->Width(); |
216 | 0 | *out_height = Renderbuffer()->Height(); |
217 | 0 | return; |
218 | 0 | } |
219 | 0 | |
220 | 0 | MOZ_ASSERT(Texture()); |
221 | 0 | MOZ_ASSERT(Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).IsDefined()); |
222 | 0 | const auto& imageInfo = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel); |
223 | 0 |
|
224 | 0 | *out_width = imageInfo.mWidth; |
225 | 0 | *out_height = imageInfo.mHeight; |
226 | 0 | } |
227 | | |
228 | | void |
229 | | WebGLFBAttachPoint::OnBackingStoreRespecified() const |
230 | 0 | { |
231 | 0 | mFB->InvalidateFramebufferStatus(); |
232 | 0 | } |
233 | | |
234 | | void |
235 | | WebGLFBAttachPoint::AttachmentName(nsCString* out) const |
236 | 0 | { |
237 | 0 | switch (mAttachmentPoint) { |
238 | 0 | case LOCAL_GL_DEPTH_ATTACHMENT: |
239 | 0 | out->AssignLiteral("DEPTH_ATTACHMENT"); |
240 | 0 | return; |
241 | 0 |
|
242 | 0 | case LOCAL_GL_STENCIL_ATTACHMENT: |
243 | 0 | out->AssignLiteral("STENCIL_ATTACHMENT"); |
244 | 0 | return; |
245 | 0 |
|
246 | 0 | case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: |
247 | 0 | out->AssignLiteral("DEPTH_STENCIL_ATTACHMENT"); |
248 | 0 | return; |
249 | 0 |
|
250 | 0 | default: |
251 | 0 | MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0); |
252 | 0 | out->AssignLiteral("COLOR_ATTACHMENT"); |
253 | 0 | const uint32_t n = mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0; |
254 | 0 | out->AppendInt(n); |
255 | 0 | return; |
256 | 0 | } |
257 | 0 | } |
258 | | |
259 | | bool |
260 | | WebGLFBAttachPoint::IsComplete(WebGLContext* webgl, nsCString* const out_info) const |
261 | 0 | { |
262 | 0 | MOZ_ASSERT(IsDefined()); |
263 | 0 |
|
264 | 0 | if (!HasImage()) { |
265 | 0 | AttachmentName(out_info); |
266 | 0 | out_info->AppendLiteral("'s image is not defined"); |
267 | 0 | return false; |
268 | 0 | } |
269 | 0 | |
270 | 0 | uint32_t width; |
271 | 0 | uint32_t height; |
272 | 0 | Size(&width, &height); |
273 | 0 | if (!width || !height) { |
274 | 0 | AttachmentName(out_info); |
275 | 0 | out_info->AppendLiteral(" has no width or height"); |
276 | 0 | return false; |
277 | 0 | } |
278 | 0 | |
279 | 0 | const auto formatUsage = Format(); |
280 | 0 | if (!formatUsage->IsRenderable()) { |
281 | 0 | nsAutoCString attachName; |
282 | 0 | AttachmentName(&attachName); |
283 | 0 |
|
284 | 0 | *out_info = nsPrintfCString("%s has an effective format of %s, which is not" |
285 | 0 | " renderable", |
286 | 0 | attachName.BeginReading(), formatUsage->format->name); |
287 | 0 | return false; |
288 | 0 | } |
289 | 0 | |
290 | 0 | if (webgl->IsWebGL2() && Texture() && |
291 | 0 | Texture()->IsCubeMap() && !Texture()->IsCubeComplete()) |
292 | 0 | { |
293 | 0 | AttachmentName(out_info); |
294 | 0 | out_info->AppendLiteral(" is not cube complete"); |
295 | 0 | return false; |
296 | 0 | } |
297 | 0 | |
298 | 0 | const auto format = formatUsage->format; |
299 | 0 |
|
300 | 0 | bool hasRequiredBits; |
301 | 0 |
|
302 | 0 | switch (mAttachmentPoint) { |
303 | 0 | case LOCAL_GL_DEPTH_ATTACHMENT: |
304 | 0 | hasRequiredBits = format->d; |
305 | 0 | break; |
306 | 0 |
|
307 | 0 | case LOCAL_GL_STENCIL_ATTACHMENT: |
308 | 0 | hasRequiredBits = format->s; |
309 | 0 | break; |
310 | 0 |
|
311 | 0 | case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: |
312 | 0 | MOZ_ASSERT(!webgl->IsWebGL2()); |
313 | 0 | hasRequiredBits = (format->d && format->s); |
314 | 0 | break; |
315 | 0 |
|
316 | 0 | default: |
317 | 0 | MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0); |
318 | 0 | hasRequiredBits = format->IsColorFormat(); |
319 | 0 | break; |
320 | 0 | } |
321 | 0 |
|
322 | 0 | if (!hasRequiredBits) { |
323 | 0 | AttachmentName(out_info); |
324 | 0 | out_info->AppendLiteral("'s format is missing required color/depth/stencil bits"); |
325 | 0 | return false; |
326 | 0 | } |
327 | 0 | |
328 | 0 | if (!webgl->IsWebGL2()) { |
329 | 0 | bool hasSurplusPlanes = false; |
330 | 0 |
|
331 | 0 | switch (mAttachmentPoint) { |
332 | 0 | case LOCAL_GL_DEPTH_ATTACHMENT: |
333 | 0 | hasSurplusPlanes = format->s; |
334 | 0 | break; |
335 | 0 |
|
336 | 0 | case LOCAL_GL_STENCIL_ATTACHMENT: |
337 | 0 | hasSurplusPlanes = format->d; |
338 | 0 | break; |
339 | 0 | } |
340 | 0 |
|
341 | 0 | if (hasSurplusPlanes) { |
342 | 0 | AttachmentName(out_info); |
343 | 0 | out_info->AppendLiteral("'s format has depth or stencil bits when it" |
344 | 0 | " shouldn't"); |
345 | 0 | return false; |
346 | 0 | } |
347 | 0 | } |
348 | 0 | |
349 | 0 | return true; |
350 | 0 | } |
351 | | |
352 | | void |
353 | | WebGLFBAttachPoint::Resolve(gl::GLContext* gl) const |
354 | 0 | { |
355 | 0 | if (!HasImage()) |
356 | 0 | return; |
357 | 0 | |
358 | 0 | if (Renderbuffer()) { |
359 | 0 | Renderbuffer()->DoFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint); |
360 | 0 | return; |
361 | 0 | } |
362 | 0 | MOZ_ASSERT(Texture()); |
363 | 0 |
|
364 | 0 | MOZ_ASSERT(gl == Texture()->mContext->GL()); |
365 | 0 |
|
366 | 0 | const auto& texName = Texture()->mGLName; |
367 | 0 |
|
368 | 0 | //// |
369 | 0 |
|
370 | 0 | const auto fnAttach2D = [&](GLenum attachmentPoint) { |
371 | 0 | gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachmentPoint, |
372 | 0 | mTexImageTarget.get(), texName, mTexImageLevel); |
373 | 0 | }; |
374 | 0 |
|
375 | 0 | //// |
376 | 0 |
|
377 | 0 | switch (mTexImageTarget.get()) { |
378 | 0 | case LOCAL_GL_TEXTURE_2D: |
379 | 0 | case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: |
380 | 0 | case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: |
381 | 0 | case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: |
382 | 0 | case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: |
383 | 0 | case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: |
384 | 0 | case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: |
385 | 0 | if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { |
386 | 0 | fnAttach2D(LOCAL_GL_DEPTH_ATTACHMENT); |
387 | 0 | fnAttach2D(LOCAL_GL_STENCIL_ATTACHMENT); |
388 | 0 | } else { |
389 | 0 | fnAttach2D(mAttachmentPoint); |
390 | 0 | } |
391 | 0 | break; |
392 | 0 |
|
393 | 0 | case LOCAL_GL_TEXTURE_2D_ARRAY: |
394 | 0 | case LOCAL_GL_TEXTURE_3D: |
395 | 0 | // If we have fFramebufferTextureLayer, we can rely on having |
396 | 0 | // DEPTH_STENCIL_ATTACHMENT. |
397 | 0 | gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint, texName, |
398 | 0 | mTexImageLevel, mTexImageLayer); |
399 | 0 | break; |
400 | 0 | } |
401 | 0 | } |
402 | | |
403 | | JS::Value |
404 | | WebGLFBAttachPoint::GetParameter(WebGLContext* webgl, JSContext* cx, |
405 | | GLenum target, GLenum attachment, GLenum pname, |
406 | | ErrorResult* const out_error) const |
407 | 0 | { |
408 | 0 | const bool hasAttachment = (mTexturePtr || mRenderbufferPtr); |
409 | 0 | if (!hasAttachment) { |
410 | 0 | // Divergent between GLES 3 and 2. |
411 | 0 |
|
412 | 0 | // GLES 2.0.25 p127: |
413 | 0 | // "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, then querying any |
414 | 0 | // other pname will generate INVALID_ENUM." |
415 | 0 |
|
416 | 0 | // GLES 3.0.4 p240: |
417 | 0 | // "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, no framebuffer is |
418 | 0 | // bound to target. In this case querying pname |
419 | 0 | // FRAMEBUFFER_ATTACHMENT_OBJECT_NAME will return zero, and all other queries |
420 | 0 | // will generate an INVALID_OPERATION error." |
421 | 0 | switch (pname) { |
422 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: |
423 | 0 | return JS::Int32Value(LOCAL_GL_NONE); |
424 | 0 |
|
425 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: |
426 | 0 | if (webgl->IsWebGL2()) |
427 | 0 | return JS::NullValue(); |
428 | 0 | |
429 | 0 | break; |
430 | 0 |
|
431 | 0 | default: |
432 | 0 | break; |
433 | 0 | } |
434 | 0 | nsCString attachmentName; |
435 | 0 | WebGLContext::EnumName(attachment, &attachmentName); |
436 | 0 | if (webgl->IsWebGL2()) { |
437 | 0 | webgl->ErrorInvalidOperation("No attachment at %s.", |
438 | 0 | attachmentName.BeginReading()); |
439 | 0 | } else { |
440 | 0 | webgl->ErrorInvalidEnum("No attachment at %s.", |
441 | 0 | attachmentName.BeginReading()); |
442 | 0 | } |
443 | 0 | return JS::NullValue(); |
444 | 0 | } |
445 | 0 |
|
446 | 0 | bool isPNameValid = false; |
447 | 0 | switch (pname) { |
448 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: |
449 | 0 | return JS::Int32Value(mTexturePtr ? LOCAL_GL_TEXTURE |
450 | 0 | : LOCAL_GL_RENDERBUFFER); |
451 | 0 |
|
452 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: |
453 | 0 | return (mTexturePtr ? webgl->WebGLObjectAsJSValue(cx, mTexturePtr.get(), |
454 | 0 | *out_error) |
455 | 0 | : webgl->WebGLObjectAsJSValue(cx, mRenderbufferPtr.get(), |
456 | 0 | *out_error)); |
457 | 0 |
|
458 | 0 | ////// |
459 | 0 |
|
460 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: |
461 | 0 | if (mTexturePtr) |
462 | 0 | return JS::Int32Value(MipLevel()); |
463 | 0 | break; |
464 | 0 |
|
465 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: |
466 | 0 | if (mTexturePtr) { |
467 | 0 | GLenum face = 0; |
468 | 0 | if (mTexturePtr->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) { |
469 | 0 | face = ImageTarget().get(); |
470 | 0 | } |
471 | 0 | return JS::Int32Value(face); |
472 | 0 | } |
473 | 0 | break; |
474 | 0 |
|
475 | 0 | ////// |
476 | 0 |
|
477 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: |
478 | 0 | if (webgl->IsWebGL2() && mTexturePtr) { |
479 | 0 | int32_t layer = 0; |
480 | 0 | if (ImageTarget() == LOCAL_GL_TEXTURE_2D_ARRAY || |
481 | 0 | ImageTarget() == LOCAL_GL_TEXTURE_3D) |
482 | 0 | { |
483 | 0 | layer = Layer(); |
484 | 0 | } |
485 | 0 | return JS::Int32Value(layer); |
486 | 0 | } |
487 | 0 | break; |
488 | 0 |
|
489 | 0 | ////// |
490 | 0 |
|
491 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: |
492 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: |
493 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: |
494 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: |
495 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: |
496 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: |
497 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: |
498 | 0 | isPNameValid = webgl->IsWebGL2(); |
499 | 0 | break; |
500 | 0 |
|
501 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: |
502 | 0 | isPNameValid = (webgl->IsWebGL2() || |
503 | 0 | webgl->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)); |
504 | 0 | break; |
505 | 0 | } |
506 | 0 |
|
507 | 0 | if (!isPNameValid) { |
508 | 0 | webgl->ErrorInvalidEnum("Invalid pname: 0x%04x", pname); |
509 | 0 | return JS::NullValue(); |
510 | 0 | } |
511 | 0 | |
512 | 0 | const auto usage = Format(); |
513 | 0 | if (!usage) { |
514 | 0 | if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) |
515 | 0 | return JS::NumberValue(LOCAL_GL_LINEAR); |
516 | 0 |
|
517 | 0 | return JS::NullValue(); |
518 | 0 | } |
519 | 0 | |
520 | 0 | auto format = usage->format; |
521 | 0 |
|
522 | 0 | GLint ret = 0; |
523 | 0 | switch (pname) { |
524 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: |
525 | 0 | ret = format->r; |
526 | 0 | break; |
527 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: |
528 | 0 | ret = format->g; |
529 | 0 | break; |
530 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: |
531 | 0 | ret = format->b; |
532 | 0 | break; |
533 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: |
534 | 0 | ret = format->a; |
535 | 0 | break; |
536 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: |
537 | 0 | ret = format->d; |
538 | 0 | break; |
539 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: |
540 | 0 | ret = format->s; |
541 | 0 | break; |
542 | 0 |
|
543 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: |
544 | 0 | ret = (format->isSRGB ? LOCAL_GL_SRGB |
545 | 0 | : LOCAL_GL_LINEAR); |
546 | 0 | break; |
547 | 0 |
|
548 | 0 | case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: |
549 | 0 | MOZ_ASSERT(attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT); |
550 | 0 |
|
551 | 0 | if (format->componentType == webgl::ComponentType::Special) { |
552 | 0 | // Special format is used for DS mixed format(e.g. D24S8 and D32FS8). |
553 | 0 | MOZ_ASSERT(format->unsizedFormat == webgl::UnsizedFormat::DEPTH_STENCIL); |
554 | 0 | MOZ_ASSERT(attachment == LOCAL_GL_DEPTH_ATTACHMENT || |
555 | 0 | attachment == LOCAL_GL_STENCIL_ATTACHMENT); |
556 | 0 |
|
557 | 0 | if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) { |
558 | 0 | switch (format->effectiveFormat) { |
559 | 0 | case webgl::EffectiveFormat::DEPTH24_STENCIL8: |
560 | 0 | format = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT24); |
561 | 0 | break; |
562 | 0 | case webgl::EffectiveFormat::DEPTH32F_STENCIL8: |
563 | 0 | format = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT32F); |
564 | 0 | break; |
565 | 0 | default: |
566 | 0 | MOZ_ASSERT(false, "no matched DS format"); |
567 | 0 | break; |
568 | 0 | } |
569 | 0 | } else if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) { |
570 | 0 | switch (format->effectiveFormat) { |
571 | 0 | case webgl::EffectiveFormat::DEPTH24_STENCIL8: |
572 | 0 | case webgl::EffectiveFormat::DEPTH32F_STENCIL8: |
573 | 0 | format = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8); |
574 | 0 | break; |
575 | 0 | default: |
576 | 0 | MOZ_ASSERT(false, "no matched DS format"); |
577 | 0 | break; |
578 | 0 | } |
579 | 0 | } |
580 | 0 | } |
581 | 0 |
|
582 | 0 | switch (format->componentType) { |
583 | 0 | case webgl::ComponentType::None: |
584 | 0 | ret = LOCAL_GL_NONE; |
585 | 0 | break; |
586 | 0 | case webgl::ComponentType::Int: |
587 | 0 | ret = LOCAL_GL_INT; |
588 | 0 | break; |
589 | 0 | case webgl::ComponentType::UInt: |
590 | 0 | ret = LOCAL_GL_UNSIGNED_INT; |
591 | 0 | break; |
592 | 0 | case webgl::ComponentType::NormInt: |
593 | 0 | ret = LOCAL_GL_SIGNED_NORMALIZED; |
594 | 0 | break; |
595 | 0 | case webgl::ComponentType::NormUInt: |
596 | 0 | ret = LOCAL_GL_UNSIGNED_NORMALIZED; |
597 | 0 | break; |
598 | 0 | case webgl::ComponentType::Float: |
599 | 0 | ret = LOCAL_GL_FLOAT; |
600 | 0 | break; |
601 | 0 | default: |
602 | 0 | MOZ_ASSERT(false, "No matched component type"); |
603 | 0 | break; |
604 | 0 | } |
605 | 0 | break; |
606 | 0 |
|
607 | 0 | default: |
608 | 0 | MOZ_ASSERT(false, "Missing case."); |
609 | 0 | break; |
610 | 0 | } |
611 | 0 |
|
612 | 0 | return JS::Int32Value(ret); |
613 | 0 | } |
614 | | |
615 | | //////////////////////////////////////////////////////////////////////////////// |
616 | | //////////////////////////////////////////////////////////////////////////////// |
617 | | // WebGLFramebuffer |
618 | | |
619 | | WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo) |
620 | | : WebGLRefCountedObject(webgl) |
621 | | , mGLName(fbo) |
622 | | , mNumFBStatusInvals(0) |
623 | | #ifdef ANDROID |
624 | | , mIsFB(false) |
625 | | #endif |
626 | | , mDepthAttachment(this, LOCAL_GL_DEPTH_ATTACHMENT) |
627 | | , mStencilAttachment(this, LOCAL_GL_STENCIL_ATTACHMENT) |
628 | | , mDepthStencilAttachment(this, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) |
629 | 0 | { |
630 | 0 | mContext->mFramebuffers.insertBack(this); |
631 | 0 |
|
632 | 0 | size_t i = 0; |
633 | 0 | for (auto& cur : mColorAttachments) { |
634 | 0 | new (&cur) WebGLFBAttachPoint(this, LOCAL_GL_COLOR_ATTACHMENT0 + i); |
635 | 0 | i++; |
636 | 0 | } |
637 | 0 |
|
638 | 0 | mColorDrawBuffers.push_back(&mColorAttachments[0]); |
639 | 0 | mColorReadBuffer = &mColorAttachments[0]; |
640 | 0 | } |
641 | | |
642 | | void |
643 | | WebGLFramebuffer::Delete() |
644 | 0 | { |
645 | 0 | InvalidateFramebufferStatus(); |
646 | 0 |
|
647 | 0 | mDepthAttachment.Clear(); |
648 | 0 | mStencilAttachment.Clear(); |
649 | 0 | mDepthStencilAttachment.Clear(); |
650 | 0 |
|
651 | 0 | for (auto& cur : mColorAttachments) { |
652 | 0 | cur.Clear(); |
653 | 0 | } |
654 | 0 |
|
655 | 0 | mContext->gl->fDeleteFramebuffers(1, &mGLName); |
656 | 0 |
|
657 | 0 | LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers); |
658 | 0 |
|
659 | | #ifdef ANDROID |
660 | | mIsFB = false; |
661 | | #endif |
662 | | } |
663 | | |
664 | | //// |
665 | | |
666 | | Maybe<WebGLFBAttachPoint*> |
667 | | WebGLFramebuffer::GetColorAttachPoint(GLenum attachPoint) |
668 | 0 | { |
669 | 0 | if (attachPoint == LOCAL_GL_NONE) |
670 | 0 | return Some<WebGLFBAttachPoint*>(nullptr); |
671 | 0 | |
672 | 0 | if (attachPoint < LOCAL_GL_COLOR_ATTACHMENT0) |
673 | 0 | return Nothing(); |
674 | 0 | |
675 | 0 | const size_t colorId = attachPoint - LOCAL_GL_COLOR_ATTACHMENT0; |
676 | 0 |
|
677 | 0 | MOZ_ASSERT(mContext->mGLMaxColorAttachments <= kMaxColorAttachments); |
678 | 0 | if (colorId >= mContext->mGLMaxColorAttachments) |
679 | 0 | return Nothing(); |
680 | 0 | |
681 | 0 | return Some(&mColorAttachments[colorId]); |
682 | 0 | } |
683 | | |
684 | | Maybe<WebGLFBAttachPoint*> |
685 | | WebGLFramebuffer::GetAttachPoint(GLenum attachPoint) |
686 | 0 | { |
687 | 0 | switch (attachPoint) { |
688 | 0 | case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: |
689 | 0 | return Some(&mDepthStencilAttachment); |
690 | 0 |
|
691 | 0 | case LOCAL_GL_DEPTH_ATTACHMENT: |
692 | 0 | return Some(&mDepthAttachment); |
693 | 0 |
|
694 | 0 | case LOCAL_GL_STENCIL_ATTACHMENT: |
695 | 0 | return Some(&mStencilAttachment); |
696 | 0 |
|
697 | 0 | default: |
698 | 0 | return GetColorAttachPoint(attachPoint); |
699 | 0 | } |
700 | 0 | } |
701 | | |
702 | | #define FOR_EACH_ATTACHMENT(X) \ |
703 | 0 | X(mDepthAttachment); \ |
704 | 0 | X(mStencilAttachment); \ |
705 | 0 | X(mDepthStencilAttachment); \ |
706 | 0 | \ |
707 | 0 | for (auto& cur : mColorAttachments) { \ |
708 | 0 | X(cur); \ |
709 | 0 | } |
710 | | |
711 | | void |
712 | | WebGLFramebuffer::DetachTexture(const WebGLTexture* tex) |
713 | 0 | { |
714 | 0 | const auto fnDetach = [&](WebGLFBAttachPoint& attach) { |
715 | 0 | if (attach.Texture() == tex) { |
716 | 0 | attach.Clear(); |
717 | 0 | } |
718 | 0 | }; |
719 | 0 |
|
720 | 0 | FOR_EACH_ATTACHMENT(fnDetach) |
721 | 0 | } |
722 | | |
723 | | void |
724 | | WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb) |
725 | 0 | { |
726 | 0 | const auto fnDetach = [&](WebGLFBAttachPoint& attach) { |
727 | 0 | if (attach.Renderbuffer() == rb) { |
728 | 0 | attach.Clear(); |
729 | 0 | } |
730 | 0 | }; |
731 | 0 |
|
732 | 0 | FOR_EACH_ATTACHMENT(fnDetach) |
733 | 0 | } |
734 | | |
735 | | //////////////////////////////////////////////////////////////////////////////// |
736 | | // Completeness |
737 | | |
738 | | bool |
739 | | WebGLFramebuffer::HasDuplicateAttachments() const |
740 | 0 | { |
741 | 0 | std::set<WebGLFBAttachPoint::Ordered> uniqueAttachSet; |
742 | 0 |
|
743 | 0 | for (const auto& attach : mColorAttachments) { |
744 | 0 | if (!attach.IsDefined()) |
745 | 0 | continue; |
746 | 0 | |
747 | 0 | const WebGLFBAttachPoint::Ordered ordered(attach); |
748 | 0 |
|
749 | 0 | const bool didInsert = uniqueAttachSet.insert(ordered).second; |
750 | 0 | if (!didInsert) |
751 | 0 | return true; |
752 | 0 | } |
753 | 0 |
|
754 | 0 | return false; |
755 | 0 | } |
756 | | |
757 | | bool |
758 | | WebGLFramebuffer::HasDefinedAttachments() const |
759 | 0 | { |
760 | 0 | bool hasAttachments = false; |
761 | 0 | const auto func = [&](const WebGLFBAttachPoint& attach) { |
762 | 0 | hasAttachments |= attach.IsDefined(); |
763 | 0 | }; |
764 | 0 |
|
765 | 0 | FOR_EACH_ATTACHMENT(func) |
766 | 0 | return hasAttachments; |
767 | 0 | } |
768 | | |
769 | | bool |
770 | | WebGLFramebuffer::HasIncompleteAttachments(nsCString* const out_info) const |
771 | 0 | { |
772 | 0 | bool hasIncomplete = false; |
773 | 0 | const auto func = [&](const WebGLFBAttachPoint& cur) { |
774 | 0 | if (!cur.IsDefined()) |
775 | 0 | return; // Not defined, so can't count as incomplete. |
776 | 0 | |
777 | 0 | hasIncomplete |= !cur.IsComplete(mContext, out_info); |
778 | 0 | }; |
779 | 0 |
|
780 | 0 | FOR_EACH_ATTACHMENT(func) |
781 | 0 | return hasIncomplete; |
782 | 0 | } |
783 | | |
784 | | bool |
785 | | WebGLFramebuffer::AllImageRectsMatch() const |
786 | 0 | { |
787 | 0 | MOZ_ASSERT(HasDefinedAttachments()); |
788 | 0 | DebugOnly<nsCString> fbStatusInfo; |
789 | 0 | MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo)); |
790 | 0 |
|
791 | 0 | bool needsInit = true; |
792 | 0 | uint32_t width = 0; |
793 | 0 | uint32_t height = 0; |
794 | 0 |
|
795 | 0 | bool hasMismatch = false; |
796 | 0 | const auto func = [&](const WebGLFBAttachPoint& attach) { |
797 | 0 | if (!attach.HasImage()) |
798 | 0 | return; |
799 | 0 | |
800 | 0 | uint32_t curWidth; |
801 | 0 | uint32_t curHeight; |
802 | 0 | attach.Size(&curWidth, &curHeight); |
803 | 0 |
|
804 | 0 | if (needsInit) { |
805 | 0 | needsInit = false; |
806 | 0 | width = curWidth; |
807 | 0 | height = curHeight; |
808 | 0 | return; |
809 | 0 | } |
810 | 0 | |
811 | 0 | hasMismatch |= (curWidth != width || |
812 | 0 | curHeight != height); |
813 | 0 | }; |
814 | 0 |
|
815 | 0 | FOR_EACH_ATTACHMENT(func) |
816 | 0 | return !hasMismatch; |
817 | 0 | } |
818 | | |
819 | | bool |
820 | | WebGLFramebuffer::AllImageSamplesMatch() const |
821 | 0 | { |
822 | 0 | MOZ_ASSERT(HasDefinedAttachments()); |
823 | 0 | DebugOnly<nsCString> fbStatusInfo; |
824 | 0 | MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo)); |
825 | 0 |
|
826 | 0 | bool needsInit = true; |
827 | 0 | uint32_t samples = 0; |
828 | 0 |
|
829 | 0 | bool hasMismatch = false; |
830 | 0 | const auto func = [&](const WebGLFBAttachPoint& attach) { |
831 | 0 | if (!attach.HasImage()) |
832 | 0 | return; |
833 | 0 | |
834 | 0 | const uint32_t curSamples = attach.Samples(); |
835 | 0 |
|
836 | 0 | if (needsInit) { |
837 | 0 | needsInit = false; |
838 | 0 | samples = curSamples; |
839 | 0 | return; |
840 | 0 | } |
841 | 0 | |
842 | 0 | hasMismatch |= (curSamples != samples); |
843 | 0 | }; |
844 | 0 |
|
845 | 0 | FOR_EACH_ATTACHMENT(func) |
846 | 0 | return !hasMismatch; |
847 | 0 | } |
848 | | |
849 | | #undef FOR_EACH_ATTACHMENT |
850 | | |
851 | | FBStatus |
852 | | WebGLFramebuffer::PrecheckFramebufferStatus(nsCString* const out_info) const |
853 | 0 | { |
854 | 0 | MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || |
855 | 0 | mContext->mBoundReadFramebuffer == this); |
856 | 0 |
|
857 | 0 | if (!HasDefinedAttachments()) |
858 | 0 | return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // No attachments |
859 | 0 | |
860 | 0 | if (HasIncompleteAttachments(out_info)) |
861 | 0 | return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; |
862 | 0 | |
863 | 0 | if (!AllImageRectsMatch()) |
864 | 0 | return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes |
865 | 0 | |
866 | 0 | if (!AllImageSamplesMatch()) |
867 | 0 | return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; // Inconsistent samples |
868 | 0 | |
869 | 0 | if (HasDuplicateAttachments()) |
870 | 0 | return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; |
871 | 0 | |
872 | 0 | if (mContext->IsWebGL2()) { |
873 | 0 | MOZ_ASSERT(!mDepthStencilAttachment.IsDefined()); |
874 | 0 | } else { |
875 | 0 | const auto depthOrStencilCount = int(mDepthAttachment.IsDefined()) + |
876 | 0 | int(mStencilAttachment.IsDefined()) + |
877 | 0 | int(mDepthStencilAttachment.IsDefined()); |
878 | 0 | if (depthOrStencilCount > 1) |
879 | 0 | return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; |
880 | 0 | } |
881 | 0 | |
882 | 0 | return LOCAL_GL_FRAMEBUFFER_COMPLETE; |
883 | 0 | } |
884 | | |
885 | | //////////////////////////////////////// |
886 | | // Validation |
887 | | |
888 | | bool |
889 | | WebGLFramebuffer::ValidateAndInitAttachments() const |
890 | 0 | { |
891 | 0 | MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || |
892 | 0 | mContext->mBoundReadFramebuffer == this); |
893 | 0 |
|
894 | 0 | const auto fbStatus = CheckFramebufferStatus(); |
895 | 0 | if (fbStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE) |
896 | 0 | return true; |
897 | 0 | |
898 | 0 | mContext->ErrorInvalidFramebufferOperation("Framebuffer must be complete."); |
899 | 0 | return false; |
900 | 0 | } |
901 | | |
902 | | bool |
903 | | WebGLFramebuffer::ValidateClearBufferType(GLenum buffer, |
904 | | uint32_t drawBuffer, GLenum funcType) const |
905 | 0 | { |
906 | 0 | if (buffer != LOCAL_GL_COLOR) |
907 | 0 | return true; |
908 | 0 | |
909 | 0 | const auto& attach = mColorAttachments[drawBuffer]; |
910 | 0 | if (!attach.IsDefined()) |
911 | 0 | return true; |
912 | 0 | |
913 | 0 | if (!count(mColorDrawBuffers.begin(), mColorDrawBuffers.end(), &attach)) |
914 | 0 | return true; // DRAW_BUFFERi set to NONE. |
915 | 0 | |
916 | 0 | GLenum attachType; |
917 | 0 | switch (attach.Format()->format->componentType) { |
918 | 0 | case webgl::ComponentType::Int: |
919 | 0 | attachType = LOCAL_GL_INT; |
920 | 0 | break; |
921 | 0 | case webgl::ComponentType::UInt: |
922 | 0 | attachType = LOCAL_GL_UNSIGNED_INT; |
923 | 0 | break; |
924 | 0 | default: |
925 | 0 | attachType = LOCAL_GL_FLOAT; |
926 | 0 | break; |
927 | 0 | } |
928 | 0 |
|
929 | 0 | if (attachType != funcType) { |
930 | 0 | mContext->ErrorInvalidOperation("This attachment is of type 0x%04x, but" |
931 | 0 | " this function is of type 0x%04x.", |
932 | 0 | attachType, funcType); |
933 | 0 | return false; |
934 | 0 | } |
935 | 0 | |
936 | 0 | return true; |
937 | 0 | } |
938 | | |
939 | | bool |
940 | | WebGLFramebuffer::ValidateForColorRead(const webgl::FormatUsageInfo** const out_format, |
941 | | uint32_t* const out_width, |
942 | | uint32_t* const out_height) const |
943 | 0 | { |
944 | 0 | if (!mColorReadBuffer) { |
945 | 0 | mContext->ErrorInvalidOperation("READ_BUFFER must not be NONE."); |
946 | 0 | return false; |
947 | 0 | } |
948 | 0 | |
949 | 0 | if (!mColorReadBuffer->IsDefined()) { |
950 | 0 | mContext->ErrorInvalidOperation("The READ_BUFFER attachment is not defined."); |
951 | 0 | return false; |
952 | 0 | } |
953 | 0 | |
954 | 0 | if (mColorReadBuffer->Samples()) { |
955 | 0 | mContext->ErrorInvalidOperation("The READ_BUFFER attachment is multisampled."); |
956 | 0 | return false; |
957 | 0 | } |
958 | 0 | |
959 | 0 | *out_format = mColorReadBuffer->Format(); |
960 | 0 | mColorReadBuffer->Size(out_width, out_height); |
961 | 0 | return true; |
962 | 0 | } |
963 | | |
964 | | //////////////////////////////////////////////////////////////////////////////// |
965 | | // Resolution and caching |
966 | | |
967 | | void |
968 | | WebGLFramebuffer::ResolveAttachments() const |
969 | 0 | { |
970 | 0 | const auto& gl = mContext->gl; |
971 | 0 |
|
972 | 0 | //// |
973 | 0 | // Nuke attachment points. |
974 | 0 |
|
975 | 0 | for (uint32_t i = 0; i < mContext->mGLMaxColorAttachments; i++) { |
976 | 0 | const GLenum attachEnum = LOCAL_GL_COLOR_ATTACHMENT0 + i; |
977 | 0 | gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachEnum, |
978 | 0 | LOCAL_GL_RENDERBUFFER, 0); |
979 | 0 | } |
980 | 0 |
|
981 | 0 | gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, |
982 | 0 | LOCAL_GL_RENDERBUFFER, 0); |
983 | 0 | gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, |
984 | 0 | LOCAL_GL_RENDERBUFFER, 0); |
985 | 0 |
|
986 | 0 | //// |
987 | 0 |
|
988 | 0 | for (const auto& attach : mColorAttachments) { |
989 | 0 | attach.Resolve(gl); |
990 | 0 | } |
991 | 0 |
|
992 | 0 | mDepthAttachment.Resolve(gl); |
993 | 0 | mStencilAttachment.Resolve(gl); |
994 | 0 | mDepthStencilAttachment.Resolve(gl); |
995 | 0 | } |
996 | | |
997 | | bool |
998 | | WebGLFramebuffer::ResolveAttachmentData() const |
999 | 0 | { |
1000 | 0 | ////// |
1001 | 0 | // Check if we need to initialize anything |
1002 | 0 |
|
1003 | 0 | const auto fnIs3D = [&](const WebGLFBAttachPoint& attach) { |
1004 | 0 | const auto& tex = attach.Texture(); |
1005 | 0 | if (!tex) |
1006 | 0 | return false; |
1007 | 0 | |
1008 | 0 | const auto& info = tex->ImageInfoAt(attach.ImageTarget(), attach.MipLevel()); |
1009 | 0 | if (info.mDepth == 1) |
1010 | 0 | return false; |
1011 | 0 | |
1012 | 0 | return true; |
1013 | 0 | }; |
1014 | 0 |
|
1015 | 0 | uint32_t clearBits = 0; |
1016 | 0 | std::vector<const WebGLFBAttachPoint*> attachmentsToClear; |
1017 | 0 | std::vector<const WebGLFBAttachPoint*> colorAttachmentsToClear; |
1018 | 0 | std::vector<const WebGLFBAttachPoint*> tex3DAttachmentsToInit; |
1019 | 0 |
|
1020 | 0 | const auto fnGather = [&](const WebGLFBAttachPoint& attach, GLenum attachClearBits) { |
1021 | 0 | if (!attach.HasUninitializedImageData()) |
1022 | 0 | return false; |
1023 | 0 | |
1024 | 0 | if (fnIs3D(attach)) { |
1025 | 0 | tex3DAttachmentsToInit.push_back(&attach); |
1026 | 0 | return false; |
1027 | 0 | } |
1028 | 0 | |
1029 | 0 | clearBits |= attachClearBits; |
1030 | 0 | attachmentsToClear.push_back(&attach); |
1031 | 0 | return true; |
1032 | 0 | }; |
1033 | 0 |
|
1034 | 0 | ////// |
1035 | 0 |
|
1036 | 0 | for (auto& cur : mColorDrawBuffers) { |
1037 | 0 | if (fnGather(*cur, LOCAL_GL_COLOR_BUFFER_BIT)) { |
1038 | 0 | colorAttachmentsToClear.push_back(cur); |
1039 | 0 | } |
1040 | 0 | } |
1041 | 0 |
|
1042 | 0 | fnGather(mDepthAttachment, LOCAL_GL_DEPTH_BUFFER_BIT); |
1043 | 0 | fnGather(mStencilAttachment, LOCAL_GL_STENCIL_BUFFER_BIT); |
1044 | 0 | fnGather(mDepthStencilAttachment, LOCAL_GL_DEPTH_BUFFER_BIT | |
1045 | 0 | LOCAL_GL_STENCIL_BUFFER_BIT); |
1046 | 0 |
|
1047 | 0 | ////// |
1048 | 0 |
|
1049 | 0 | for (const auto& attach : tex3DAttachmentsToInit) { |
1050 | 0 | const auto& tex = attach->Texture(); |
1051 | 0 | if (!tex->InitializeImageData(attach->ImageTarget(), |
1052 | 0 | attach->MipLevel())) |
1053 | 0 | { |
1054 | 0 | return false; |
1055 | 0 | } |
1056 | 0 | } |
1057 | 0 |
|
1058 | 0 | if (clearBits) { |
1059 | 0 | const auto fnDrawBuffers = [&](const std::vector<const WebGLFBAttachPoint*>& src) |
1060 | 0 | { |
1061 | 0 | std::vector<GLenum> enumList; |
1062 | 0 |
|
1063 | 0 | for (const auto& cur : src) { |
1064 | 0 | const auto& attachEnum = cur->mAttachmentPoint; |
1065 | 0 | const GLenum attachId = attachEnum - LOCAL_GL_COLOR_ATTACHMENT0; |
1066 | 0 |
|
1067 | 0 | while (enumList.size() < attachId) { |
1068 | 0 | enumList.push_back(LOCAL_GL_NONE); |
1069 | 0 | } |
1070 | 0 | enumList.push_back(attachEnum); |
1071 | 0 | } |
1072 | 0 |
|
1073 | 0 | mContext->gl->fDrawBuffers(enumList.size(), enumList.data()); |
1074 | 0 | }; |
1075 | 0 |
|
1076 | 0 | //// |
1077 | 0 | // Clear |
1078 | 0 |
|
1079 | 0 | const bool hasDrawBuffers = mContext->HasDrawBuffers(); |
1080 | 0 | if (hasDrawBuffers) { |
1081 | 0 | fnDrawBuffers(colorAttachmentsToClear); |
1082 | 0 | } |
1083 | 0 |
|
1084 | 0 | { |
1085 | 0 | gl::ScopedBindFramebuffer autoBind(mContext->gl, mGLName); |
1086 | 0 |
|
1087 | 0 | mContext->ForceClearFramebufferWithDefaultValues(clearBits, false); |
1088 | 0 | } |
1089 | 0 |
|
1090 | 0 | if (hasDrawBuffers) { |
1091 | 0 | RefreshDrawBuffers(); |
1092 | 0 | } |
1093 | 0 |
|
1094 | 0 | // Mark initialized. |
1095 | 0 | for (const auto& cur : attachmentsToClear) { |
1096 | 0 | cur->SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); |
1097 | 0 | } |
1098 | 0 | } |
1099 | 0 |
|
1100 | 0 | return true; |
1101 | 0 | } |
1102 | | |
1103 | | WebGLFramebuffer::ResolvedData::ResolvedData(const WebGLFramebuffer& parent) |
1104 | 0 | { |
1105 | 0 |
|
1106 | 0 | texDrawBuffers.reserve(parent.mColorDrawBuffers.size() + 2); // +2 for depth+stencil. |
1107 | 0 |
|
1108 | 0 | const auto fnCommon = [&](const WebGLFBAttachPoint& attach) { |
1109 | 0 | if (!attach.IsDefined()) |
1110 | 0 | return false; |
1111 | 0 | |
1112 | 0 | if (attach.Texture()) { |
1113 | 0 | texDrawBuffers.push_back(&attach); |
1114 | 0 | } |
1115 | 0 | return true; |
1116 | 0 | }; |
1117 | 0 |
|
1118 | 0 | //// |
1119 | 0 |
|
1120 | 0 | const auto fnDepthStencil = [&](const WebGLFBAttachPoint& attach) { |
1121 | 0 | if (!fnCommon(attach)) |
1122 | 0 | return; |
1123 | 0 | |
1124 | 0 | drawSet.insert(WebGLFBAttachPoint::Ordered(attach)); |
1125 | 0 | readSet.insert(WebGLFBAttachPoint::Ordered(attach)); |
1126 | 0 | }; |
1127 | 0 |
|
1128 | 0 | fnDepthStencil(parent.mDepthAttachment); |
1129 | 0 | fnDepthStencil(parent.mStencilAttachment); |
1130 | 0 | fnDepthStencil(parent.mDepthStencilAttachment); |
1131 | 0 |
|
1132 | 0 | //// |
1133 | 0 |
|
1134 | 0 | for (const auto& pAttach : parent.mColorDrawBuffers) { |
1135 | 0 | const auto& attach = *pAttach; |
1136 | 0 | if (!fnCommon(attach)) |
1137 | 0 | return; |
1138 | 0 | |
1139 | 0 | drawSet.insert(WebGLFBAttachPoint::Ordered(attach)); |
1140 | 0 | } |
1141 | 0 |
|
1142 | 0 | if (parent.mColorReadBuffer) { |
1143 | 0 | const auto& attach = *parent.mColorReadBuffer; |
1144 | 0 | if (!fnCommon(attach)) |
1145 | 0 | return; |
1146 | 0 | |
1147 | 0 | readSet.insert(WebGLFBAttachPoint::Ordered(attach)); |
1148 | 0 | } |
1149 | 0 | } |
1150 | | |
1151 | | void |
1152 | | WebGLFramebuffer::InvalidateFramebufferStatus() |
1153 | 0 | { |
1154 | 0 | if (mResolvedCompleteData) { |
1155 | 0 | mNumFBStatusInvals++; |
1156 | 0 | if (mNumFBStatusInvals > mContext->mMaxAcceptableFBStatusInvals) { |
1157 | 0 | mContext->GeneratePerfWarning("FB was invalidated after being complete %u" |
1158 | 0 | " times.", |
1159 | 0 | uint32_t(mNumFBStatusInvals)); |
1160 | 0 | } |
1161 | 0 | } |
1162 | 0 |
|
1163 | 0 | mResolvedCompleteData = nullptr; |
1164 | 0 | } |
1165 | | |
1166 | | void |
1167 | | WebGLFramebuffer::RefreshResolvedData() |
1168 | 0 | { |
1169 | 0 | if (mResolvedCompleteData) { |
1170 | 0 | mResolvedCompleteData.reset(new ResolvedData(*this)); |
1171 | 0 | } |
1172 | 0 | } |
1173 | | |
1174 | | //////////////////////////////////////////////////////////////////////////////// |
1175 | | // Entrypoints |
1176 | | |
1177 | | FBStatus |
1178 | | WebGLFramebuffer::CheckFramebufferStatus() const |
1179 | 0 | { |
1180 | 0 | if (IsResolvedComplete()) |
1181 | 0 | return LOCAL_GL_FRAMEBUFFER_COMPLETE; |
1182 | 0 | |
1183 | 0 | // Ok, let's try to resolve it! |
1184 | 0 | |
1185 | 0 | nsCString statusInfo; |
1186 | 0 | FBStatus ret = PrecheckFramebufferStatus(&statusInfo); |
1187 | 0 | do { |
1188 | 0 | if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE) |
1189 | 0 | break; |
1190 | 0 | |
1191 | 0 | // Looks good on our end. Let's ask the driver. |
1192 | 0 | gl::GLContext* const gl = mContext->gl; |
1193 | 0 |
|
1194 | 0 | const ScopedFBRebinder autoFB(mContext); |
1195 | 0 | gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGLName); |
1196 | 0 |
|
1197 | 0 | //// |
1198 | 0 |
|
1199 | 0 | ResolveAttachments(); // OK, attach everything properly! |
1200 | 0 | RefreshDrawBuffers(); |
1201 | 0 | RefreshReadBuffer(); |
1202 | 0 |
|
1203 | 0 | ret = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); |
1204 | 0 |
|
1205 | 0 | //// |
1206 | 0 |
|
1207 | 0 | if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE) { |
1208 | 0 | const nsPrintfCString text("Bad status according to the driver: 0x%04x", |
1209 | 0 | ret.get()); |
1210 | 0 | statusInfo = text; |
1211 | 0 | break; |
1212 | 0 | } |
1213 | 0 | |
1214 | 0 | if (!ResolveAttachmentData()) { |
1215 | 0 | ret = LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; |
1216 | 0 | statusInfo.AssignLiteral("Failed to lazily-initialize attachment data."); |
1217 | 0 | break; |
1218 | 0 | } |
1219 | 0 |
|
1220 | 0 | mResolvedCompleteData.reset(new ResolvedData(*this)); |
1221 | 0 | return LOCAL_GL_FRAMEBUFFER_COMPLETE; |
1222 | 0 | } while (false); |
1223 | 0 |
|
1224 | 0 | MOZ_ASSERT(ret != LOCAL_GL_FRAMEBUFFER_COMPLETE); |
1225 | 0 | mContext->GenerateWarning("Framebuffer not complete. (status: 0x%04x) %s", |
1226 | 0 | ret.get(), statusInfo.BeginReading()); |
1227 | 0 | return ret; |
1228 | 0 | } |
1229 | | |
1230 | | //// |
1231 | | |
1232 | | void |
1233 | | WebGLFramebuffer::RefreshDrawBuffers() const |
1234 | 0 | { |
1235 | 0 | const auto& gl = mContext->gl; |
1236 | 0 | if (!gl->IsSupported(gl::GLFeature::draw_buffers)) |
1237 | 0 | return; |
1238 | 0 | |
1239 | 0 | // Prior to GL4.1, having a no-image FB attachment that's selected by DrawBuffers |
1240 | 0 | // yields a framebuffer status of FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER. |
1241 | 0 | // We could workaround this only on affected versions, but it's easier be |
1242 | 0 | // unconditional. |
1243 | 0 | std::vector<GLenum> driverBuffers(mContext->mGLMaxDrawBuffers, LOCAL_GL_NONE); |
1244 | 0 | for (const auto& attach : mColorDrawBuffers) { |
1245 | 0 | if (attach->HasImage()) { |
1246 | 0 | const uint32_t index = attach->mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0; |
1247 | 0 | driverBuffers[index] = attach->mAttachmentPoint; |
1248 | 0 | } |
1249 | 0 | } |
1250 | 0 |
|
1251 | 0 | gl->fDrawBuffers(driverBuffers.size(), driverBuffers.data()); |
1252 | 0 | } |
1253 | | |
1254 | | void |
1255 | | WebGLFramebuffer::RefreshReadBuffer() const |
1256 | 0 | { |
1257 | 0 | const auto& gl = mContext->gl; |
1258 | 0 | if (!gl->IsSupported(gl::GLFeature::read_buffer)) |
1259 | 0 | return; |
1260 | 0 | |
1261 | 0 | // Prior to GL4.1, having a no-image FB attachment that's selected by ReadBuffer |
1262 | 0 | // yields a framebuffer status of FRAMEBUFFER_INCOMPLETE_READ_BUFFER. |
1263 | 0 | // We could workaround this only on affected versions, but it's easier be |
1264 | 0 | // unconditional. |
1265 | 0 | GLenum driverBuffer = LOCAL_GL_NONE; |
1266 | 0 | if (mColorReadBuffer && mColorReadBuffer->HasImage()) { |
1267 | 0 | driverBuffer = mColorReadBuffer->mAttachmentPoint; |
1268 | 0 | } |
1269 | 0 |
|
1270 | 0 | gl->fReadBuffer(driverBuffer); |
1271 | 0 | } |
1272 | | |
1273 | | //// |
1274 | | |
1275 | | void |
1276 | | WebGLFramebuffer::DrawBuffers(const dom::Sequence<GLenum>& buffers) |
1277 | 0 | { |
1278 | 0 | if (buffers.Length() > mContext->mGLMaxDrawBuffers) { |
1279 | 0 | // "An INVALID_VALUE error is generated if `n` is greater than MAX_DRAW_BUFFERS." |
1280 | 0 | mContext->ErrorInvalidValue("`buffers` must have a length <=" |
1281 | 0 | " MAX_DRAW_BUFFERS."); |
1282 | 0 | return; |
1283 | 0 | } |
1284 | 0 | |
1285 | 0 | std::vector<const WebGLFBAttachPoint*> newColorDrawBuffers; |
1286 | 0 | newColorDrawBuffers.reserve(buffers.Length()); |
1287 | 0 |
|
1288 | 0 | for (size_t i = 0; i < buffers.Length(); i++) { |
1289 | 0 | // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed in |
1290 | 0 | // bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of order, |
1291 | 0 | // BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to the value of |
1292 | 0 | // MAX_COLOR_ATTACHMENTS, will generate the error INVALID_OPERATION. |
1293 | 0 |
|
1294 | 0 | // WEBGL_draw_buffers: |
1295 | 0 | // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or |
1296 | 0 | // equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter." |
1297 | 0 | // This means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't |
1298 | 0 | // be larger than MaxColorAttachments. |
1299 | 0 | const auto& cur = buffers[i]; |
1300 | 0 | if (cur == LOCAL_GL_COLOR_ATTACHMENT0 + i) { |
1301 | 0 | const auto& attach = mColorAttachments[i]; |
1302 | 0 | newColorDrawBuffers.push_back(&attach); |
1303 | 0 | } else if (cur != LOCAL_GL_NONE) { |
1304 | 0 | const bool isColorEnum = (cur >= LOCAL_GL_COLOR_ATTACHMENT0 && |
1305 | 0 | cur < mContext->LastColorAttachmentEnum()); |
1306 | 0 | if (cur != LOCAL_GL_BACK && |
1307 | 0 | !isColorEnum) |
1308 | 0 | { |
1309 | 0 | mContext->ErrorInvalidEnum("Unexpected enum in buffers."); |
1310 | 0 | return; |
1311 | 0 | } |
1312 | 0 | |
1313 | 0 | mContext->ErrorInvalidOperation("`buffers[i]` must be NONE or" |
1314 | 0 | " COLOR_ATTACHMENTi."); |
1315 | 0 | return; |
1316 | 0 | } |
1317 | 0 | } |
1318 | 0 |
|
1319 | 0 | //// |
1320 | 0 |
|
1321 | 0 | mColorDrawBuffers.swap(newColorDrawBuffers); |
1322 | 0 | RefreshDrawBuffers(); // Calls glDrawBuffers. |
1323 | 0 | RefreshResolvedData(); |
1324 | 0 | } |
1325 | | |
1326 | | void |
1327 | | WebGLFramebuffer::ReadBuffer(GLenum attachPoint) |
1328 | 0 | { |
1329 | 0 | const auto& maybeAttach = GetColorAttachPoint(attachPoint); |
1330 | 0 | if (!maybeAttach) { |
1331 | 0 | const char text[] = "`mode` must be a COLOR_ATTACHMENTi, for 0 <= i <" |
1332 | 0 | " MAX_DRAW_BUFFERS."; |
1333 | 0 | if (attachPoint == LOCAL_GL_BACK) { |
1334 | 0 | mContext->ErrorInvalidOperation(text); |
1335 | 0 | } else { |
1336 | 0 | mContext->ErrorInvalidEnum(text); |
1337 | 0 | } |
1338 | 0 | return; |
1339 | 0 | } |
1340 | 0 | const auto& attach = maybeAttach.value(); // Might be nullptr. |
1341 | 0 |
|
1342 | 0 | //// |
1343 | 0 |
|
1344 | 0 | mColorReadBuffer = attach; |
1345 | 0 | RefreshReadBuffer(); // Calls glReadBuffer. |
1346 | 0 | RefreshResolvedData(); |
1347 | 0 | } |
1348 | | |
1349 | | //// |
1350 | | |
1351 | | void |
1352 | | WebGLFramebuffer::FramebufferRenderbuffer(GLenum attachEnum, |
1353 | | GLenum rbtarget, WebGLRenderbuffer* rb) |
1354 | 0 | { |
1355 | 0 | MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || |
1356 | 0 | mContext->mBoundReadFramebuffer == this); |
1357 | 0 |
|
1358 | 0 | // `attachment` |
1359 | 0 | const auto maybeAttach = GetAttachPoint(attachEnum); |
1360 | 0 | if (!maybeAttach || !maybeAttach.value()) { |
1361 | 0 | mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum); |
1362 | 0 | return; |
1363 | 0 | } |
1364 | 0 | const auto& attach = maybeAttach.value(); |
1365 | 0 |
|
1366 | 0 | // `rbTarget` |
1367 | 0 | if (rbtarget != LOCAL_GL_RENDERBUFFER) { |
1368 | 0 | mContext->ErrorInvalidEnumInfo("rbtarget", rbtarget); |
1369 | 0 | return; |
1370 | 0 | } |
1371 | 0 | |
1372 | 0 | // `rb` |
1373 | 0 | if (rb) { |
1374 | 0 | if (!mContext->ValidateObject("rb", *rb)) |
1375 | 0 | return; |
1376 | 0 | |
1377 | 0 | if (!rb->mHasBeenBound) { |
1378 | 0 | mContext->ErrorInvalidOperation("bindRenderbuffer must be called before" |
1379 | 0 | " attachment to %04x", |
1380 | 0 | attachEnum); |
1381 | 0 | return; |
1382 | 0 | } |
1383 | 0 | } |
1384 | 0 | |
1385 | 0 | // End of validation. |
1386 | 0 | |
1387 | 0 | if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { |
1388 | 0 | mDepthAttachment.SetRenderbuffer(rb); |
1389 | 0 | mStencilAttachment.SetRenderbuffer(rb); |
1390 | 0 | } else { |
1391 | 0 | attach->SetRenderbuffer(rb); |
1392 | 0 | } |
1393 | 0 |
|
1394 | 0 | InvalidateFramebufferStatus(); |
1395 | 0 | } |
1396 | | |
1397 | | void |
1398 | | WebGLFramebuffer::FramebufferTexture2D(GLenum attachEnum, |
1399 | | GLenum texImageTarget, WebGLTexture* tex, |
1400 | | GLint level) |
1401 | 0 | { |
1402 | 0 | MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || |
1403 | 0 | mContext->mBoundReadFramebuffer == this); |
1404 | 0 |
|
1405 | 0 | // `attachment` |
1406 | 0 | const auto maybeAttach = GetAttachPoint(attachEnum); |
1407 | 0 | if (!maybeAttach || !maybeAttach.value()) { |
1408 | 0 | mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum); |
1409 | 0 | return; |
1410 | 0 | } |
1411 | 0 | const auto& attach = maybeAttach.value(); |
1412 | 0 |
|
1413 | 0 | // `texImageTarget` |
1414 | 0 | if (texImageTarget != LOCAL_GL_TEXTURE_2D && |
1415 | 0 | (texImageTarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X || |
1416 | 0 | texImageTarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)) |
1417 | 0 | { |
1418 | 0 | mContext->ErrorInvalidEnumInfo("texImageTarget", |
1419 | 0 | texImageTarget); |
1420 | 0 | return; |
1421 | 0 | } |
1422 | 0 | |
1423 | 0 | // `texture` |
1424 | 0 | if (tex) { |
1425 | 0 | if (!mContext->ValidateObject("texture", *tex)) |
1426 | 0 | return; |
1427 | 0 | |
1428 | 0 | if (!tex->HasEverBeenBound()) { |
1429 | 0 | mContext->ErrorInvalidOperation("`texture` has never been bound."); |
1430 | 0 | return; |
1431 | 0 | } |
1432 | 0 | |
1433 | 0 | const TexTarget destTexTarget = TexImageTargetToTexTarget(texImageTarget); |
1434 | 0 | if (tex->Target() != destTexTarget) { |
1435 | 0 | mContext->ErrorInvalidOperation("Mismatched texture and texture target."); |
1436 | 0 | return; |
1437 | 0 | } |
1438 | 0 | } |
1439 | 0 | |
1440 | 0 | // `level` |
1441 | 0 | if (level < 0) |
1442 | 0 | return mContext->ErrorInvalidValue("`level` must not be negative."); |
1443 | 0 | |
1444 | 0 | if (mContext->IsWebGL2()) { |
1445 | 0 | /* GLES 3.0.4 p208: |
1446 | 0 | * If textarget is one of TEXTURE_CUBE_MAP_POSITIVE_X, |
1447 | 0 | * TEXTURE_CUBE_MAP_POSITIVE_Y, TEXTURE_CUBE_MAP_POSITIVE_Z, |
1448 | 0 | * TEXTURE_CUBE_MAP_NEGATIVE_X, TEXTURE_CUBE_MAP_NEGATIVE_Y, |
1449 | 0 | * or TEXTURE_CUBE_MAP_NEGATIVE_Z, then level must be greater |
1450 | 0 | * than or equal to zero and less than or equal to log2 of the |
1451 | 0 | * value of MAX_CUBE_MAP_TEXTURE_SIZE. If textarget is TEXTURE_2D, |
1452 | 0 | * level must be greater than or equal to zero and no larger than |
1453 | 0 | * log2 of the value of MAX_TEXTURE_SIZE. Otherwise, an |
1454 | 0 | * INVALID_VALUE error is generated. |
1455 | 0 | */ |
1456 | 0 |
|
1457 | 0 | if (texImageTarget == LOCAL_GL_TEXTURE_2D) { |
1458 | 0 | if (uint32_t(level) > FloorLog2(mContext->mGLMaxTextureSize)) |
1459 | 0 | return mContext->ErrorInvalidValue("`level` is too large."); |
1460 | 0 | } else { |
1461 | 0 | MOZ_ASSERT(texImageTarget >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X && |
1462 | 0 | texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z); |
1463 | 0 |
|
1464 | 0 | if (uint32_t(level) > FloorLog2(mContext->mGLMaxCubeMapTextureSize)) |
1465 | 0 | return mContext->ErrorInvalidValue("`level` is too large."); |
1466 | 0 | } |
1467 | 0 | } else if (level != 0) { |
1468 | 0 | return mContext->ErrorInvalidValue("`level` must be 0."); |
1469 | 0 | } |
1470 | 0 | |
1471 | 0 | // End of validation. |
1472 | 0 | |
1473 | 0 | if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { |
1474 | 0 | mDepthAttachment.SetTexImage(tex, texImageTarget, level); |
1475 | 0 | mStencilAttachment.SetTexImage(tex, texImageTarget, level); |
1476 | 0 | } else { |
1477 | 0 | attach->SetTexImage(tex, texImageTarget, level); |
1478 | 0 | } |
1479 | 0 |
|
1480 | 0 | InvalidateFramebufferStatus(); |
1481 | 0 | } |
1482 | | |
1483 | | void |
1484 | | WebGLFramebuffer::FramebufferTextureLayer(GLenum attachEnum, |
1485 | | WebGLTexture* tex, GLint level, GLint layer) |
1486 | 0 | { |
1487 | 0 | MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || |
1488 | 0 | mContext->mBoundReadFramebuffer == this); |
1489 | 0 |
|
1490 | 0 | // `attachment` |
1491 | 0 | const auto maybeAttach = GetAttachPoint(attachEnum); |
1492 | 0 | if (!maybeAttach || !maybeAttach.value()) { |
1493 | 0 | mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum); |
1494 | 0 | return; |
1495 | 0 | } |
1496 | 0 | const auto& attach = maybeAttach.value(); |
1497 | 0 |
|
1498 | 0 | // `level`, `layer` |
1499 | 0 | if (layer < 0) |
1500 | 0 | return mContext->ErrorInvalidValue("`layer` must be >= 0."); |
1501 | 0 | |
1502 | 0 | if (level < 0) |
1503 | 0 | return mContext->ErrorInvalidValue("`level` must be >= 0."); |
1504 | 0 | |
1505 | 0 | // `texture` |
1506 | 0 | GLenum texImageTarget = LOCAL_GL_TEXTURE_3D; |
1507 | 0 | if (tex) { |
1508 | 0 | if (!mContext->ValidateObject("texture", *tex)) |
1509 | 0 | return; |
1510 | 0 | |
1511 | 0 | if (!tex->HasEverBeenBound()) { |
1512 | 0 | mContext->ErrorInvalidOperation("`texture` has never been bound."); |
1513 | 0 | return; |
1514 | 0 | } |
1515 | 0 | |
1516 | 0 | texImageTarget = tex->Target().get(); |
1517 | 0 | switch (texImageTarget) { |
1518 | 0 | case LOCAL_GL_TEXTURE_3D: |
1519 | 0 | if (uint32_t(layer) >= mContext->mGLMax3DTextureSize) { |
1520 | 0 | mContext->ErrorInvalidValue("`layer` must be < %s.", |
1521 | 0 | "MAX_3D_TEXTURE_SIZE"); |
1522 | 0 | return; |
1523 | 0 | } |
1524 | 0 | |
1525 | 0 | if (uint32_t(level) > FloorLog2(mContext->mGLMax3DTextureSize)) { |
1526 | 0 | mContext->ErrorInvalidValue("`level` must be <= log2(%s).", |
1527 | 0 | "MAX_3D_TEXTURE_SIZE"); |
1528 | 0 | return; |
1529 | 0 | } |
1530 | 0 | break; |
1531 | 0 |
|
1532 | 0 | case LOCAL_GL_TEXTURE_2D_ARRAY: |
1533 | 0 | if (uint32_t(layer) >= mContext->mGLMaxArrayTextureLayers) { |
1534 | 0 | mContext->ErrorInvalidValue("`layer` must be < %s.", |
1535 | 0 | "MAX_ARRAY_TEXTURE_LAYERS"); |
1536 | 0 | return; |
1537 | 0 | } |
1538 | 0 | |
1539 | 0 | if (uint32_t(level) > FloorLog2(mContext->mGLMaxTextureSize)) { |
1540 | 0 | mContext->ErrorInvalidValue("`level` must be <= log2(%s).", |
1541 | 0 | "MAX_TEXTURE_SIZE"); |
1542 | 0 | return; |
1543 | 0 | } |
1544 | 0 | break; |
1545 | 0 |
|
1546 | 0 | default: |
1547 | 0 | mContext->ErrorInvalidOperation("`texture` must be a TEXTURE_3D or" |
1548 | 0 | " TEXTURE_2D_ARRAY."); |
1549 | 0 | return; |
1550 | 0 | } |
1551 | 0 | } |
1552 | 0 | |
1553 | 0 | // End of validation. |
1554 | 0 | |
1555 | 0 | if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { |
1556 | 0 | mDepthAttachment.SetTexImage(tex, texImageTarget, level, layer); |
1557 | 0 | mStencilAttachment.SetTexImage(tex, texImageTarget, level, layer); |
1558 | 0 | } else { |
1559 | 0 | attach->SetTexImage(tex, texImageTarget, level, layer); |
1560 | 0 | } |
1561 | 0 |
|
1562 | 0 | InvalidateFramebufferStatus(); |
1563 | 0 | } |
1564 | | |
1565 | | JS::Value |
1566 | | WebGLFramebuffer::GetAttachmentParameter(JSContext* cx, |
1567 | | GLenum target, GLenum attachEnum, GLenum pname, |
1568 | | ErrorResult* const out_error) |
1569 | 0 | { |
1570 | 0 | const auto maybeAttach = GetAttachPoint(attachEnum); |
1571 | 0 | if (!maybeAttach || attachEnum == LOCAL_GL_NONE) { |
1572 | 0 | mContext->ErrorInvalidEnum("Can only query COLOR_ATTACHMENTi," |
1573 | 0 | " DEPTH_ATTACHMENT, DEPTH_STENCIL_ATTACHMENT, or" |
1574 | 0 | " STENCIL_ATTACHMENT for a framebuffer."); |
1575 | 0 | return JS::NullValue(); |
1576 | 0 | } |
1577 | 0 | auto attach = maybeAttach.value(); |
1578 | 0 |
|
1579 | 0 | if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { |
1580 | 0 | // There are a couple special rules for this one. |
1581 | 0 |
|
1582 | 0 | if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE) { |
1583 | 0 | mContext->ErrorInvalidOperation("Querying" |
1584 | 0 | " FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE" |
1585 | 0 | " against DEPTH_STENCIL_ATTACHMENT is an" |
1586 | 0 | " error."); |
1587 | 0 | return JS::NullValue(); |
1588 | 0 | } |
1589 | 0 | |
1590 | 0 | if (mDepthAttachment.Renderbuffer() != mStencilAttachment.Renderbuffer() || |
1591 | 0 | mDepthAttachment.Texture() != mStencilAttachment.Texture()) |
1592 | 0 | { |
1593 | 0 | mContext->ErrorInvalidOperation("DEPTH_ATTACHMENT and STENCIL_ATTACHMENT" |
1594 | 0 | " have different objects bound."); |
1595 | 0 | return JS::NullValue(); |
1596 | 0 | } |
1597 | 0 | |
1598 | 0 | attach = &mDepthAttachment; |
1599 | 0 | } |
1600 | 0 |
|
1601 | 0 | return attach->GetParameter(mContext, cx, target, attachEnum, pname, |
1602 | 0 | out_error); |
1603 | 0 | } |
1604 | | |
1605 | | //////////////////// |
1606 | | |
1607 | | static void |
1608 | | GetBackbufferFormats(const WebGLContext* webgl, |
1609 | | const webgl::FormatInfo** const out_color, |
1610 | | const webgl::FormatInfo** const out_depth, |
1611 | | const webgl::FormatInfo** const out_stencil) |
1612 | 0 | { |
1613 | 0 | const auto& options = webgl->Options(); |
1614 | 0 |
|
1615 | 0 | const auto effFormat = (options.alpha ? webgl::EffectiveFormat::RGBA8 |
1616 | 0 | : webgl::EffectiveFormat::RGB8); |
1617 | 0 | *out_color = webgl::GetFormat(effFormat); |
1618 | 0 |
|
1619 | 0 | *out_depth = nullptr; |
1620 | 0 | *out_stencil = nullptr; |
1621 | 0 | if (options.depth && options.stencil) { |
1622 | 0 | *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8); |
1623 | 0 | *out_stencil = *out_depth; |
1624 | 0 | } else { |
1625 | 0 | if (options.depth) { |
1626 | 0 | *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16); |
1627 | 0 | } |
1628 | 0 | if (options.stencil) { |
1629 | 0 | *out_stencil = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8); |
1630 | 0 | } |
1631 | 0 | } |
1632 | 0 | } |
1633 | | |
1634 | | /*static*/ void |
1635 | | WebGLFramebuffer::BlitFramebuffer(WebGLContext* webgl, |
1636 | | GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, |
1637 | | GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, |
1638 | | GLbitfield mask, GLenum filter) |
1639 | 0 | { |
1640 | 0 | const auto& gl = webgl->gl; |
1641 | 0 |
|
1642 | 0 | const auto& srcFB = webgl->mBoundReadFramebuffer; |
1643 | 0 | const auto& dstFB = webgl->mBoundDrawFramebuffer; |
1644 | 0 |
|
1645 | 0 | //// |
1646 | 0 | // Collect data |
1647 | 0 |
|
1648 | 0 | const auto fnGetDepthAndStencilAttach = [](const WebGLFramebuffer* fb, |
1649 | 0 | const WebGLFBAttachPoint** const out_depth, |
1650 | 0 | const WebGLFBAttachPoint** const out_stencil) |
1651 | 0 | { |
1652 | 0 | *out_depth = nullptr; |
1653 | 0 | *out_stencil = nullptr; |
1654 | 0 |
|
1655 | 0 | if (!fb) |
1656 | 0 | return; |
1657 | 0 | |
1658 | 0 | if (fb->mDepthStencilAttachment.IsDefined()) { |
1659 | 0 | *out_depth = *out_stencil = &fb->mDepthStencilAttachment; |
1660 | 0 | return; |
1661 | 0 | } |
1662 | 0 | if (fb->mDepthAttachment.IsDefined()) { |
1663 | 0 | *out_depth = &fb->mDepthAttachment; |
1664 | 0 | } |
1665 | 0 | if (fb->mStencilAttachment.IsDefined()) { |
1666 | 0 | *out_stencil = &fb->mStencilAttachment; |
1667 | 0 | } |
1668 | 0 | }; |
1669 | 0 |
|
1670 | 0 | const WebGLFBAttachPoint* srcDepthAttach; |
1671 | 0 | const WebGLFBAttachPoint* srcStencilAttach; |
1672 | 0 | fnGetDepthAndStencilAttach(srcFB, &srcDepthAttach, &srcStencilAttach); |
1673 | 0 | const WebGLFBAttachPoint* dstDepthAttach; |
1674 | 0 | const WebGLFBAttachPoint* dstStencilAttach; |
1675 | 0 | fnGetDepthAndStencilAttach(dstFB, &dstDepthAttach, &dstStencilAttach); |
1676 | 0 |
|
1677 | 0 | //// |
1678 | 0 |
|
1679 | 0 | const auto fnGetFormat = [](const WebGLFBAttachPoint* cur, |
1680 | 0 | bool* const out_hasSamples) -> const webgl::FormatInfo* |
1681 | 0 | { |
1682 | 0 | if (!cur || !cur->IsDefined()) |
1683 | 0 | return nullptr; |
1684 | 0 | |
1685 | 0 | *out_hasSamples |= bool(cur->Samples()); |
1686 | 0 | return cur->Format()->format; |
1687 | 0 | }; |
1688 | 0 |
|
1689 | 0 | const auto fnNarrowComponentType = [&](const webgl::FormatInfo* format) { |
1690 | 0 | switch (format->componentType) { |
1691 | 0 | case webgl::ComponentType::NormInt: |
1692 | 0 | case webgl::ComponentType::NormUInt: |
1693 | 0 | return webgl::ComponentType::Float; |
1694 | 0 |
|
1695 | 0 | default: |
1696 | 0 | return format->componentType; |
1697 | 0 | } |
1698 | 0 | }; |
1699 | 0 |
|
1700 | 0 | bool srcHasSamples; |
1701 | 0 | const webgl::FormatInfo* srcColorFormat; |
1702 | 0 | webgl::ComponentType srcColorType = webgl::ComponentType::None; |
1703 | 0 | const webgl::FormatInfo* srcDepthFormat; |
1704 | 0 | const webgl::FormatInfo* srcStencilFormat; |
1705 | 0 |
|
1706 | 0 | if (srcFB) { |
1707 | 0 | srcHasSamples = false; |
1708 | 0 | srcColorFormat = fnGetFormat(srcFB->mColorReadBuffer, &srcHasSamples); |
1709 | 0 | srcDepthFormat = fnGetFormat(srcDepthAttach, &srcHasSamples); |
1710 | 0 | srcStencilFormat = fnGetFormat(srcStencilAttach, &srcHasSamples); |
1711 | 0 | } else { |
1712 | 0 | srcHasSamples = false; // Always false. |
1713 | 0 |
|
1714 | 0 | GetBackbufferFormats(webgl, &srcColorFormat, &srcDepthFormat, &srcStencilFormat); |
1715 | 0 | } |
1716 | 0 |
|
1717 | 0 | if (srcColorFormat) { |
1718 | 0 | srcColorType = fnNarrowComponentType(srcColorFormat); |
1719 | 0 | } |
1720 | 0 |
|
1721 | 0 | //// |
1722 | 0 |
|
1723 | 0 | bool dstHasSamples; |
1724 | 0 | const webgl::FormatInfo* dstDepthFormat; |
1725 | 0 | const webgl::FormatInfo* dstStencilFormat; |
1726 | 0 | bool dstHasColor = false; |
1727 | 0 | bool colorFormatsMatch = true; |
1728 | 0 | bool colorTypesMatch = true; |
1729 | 0 |
|
1730 | 0 | const auto fnCheckColorFormat = [&](const webgl::FormatInfo* dstFormat) { |
1731 | 0 | MOZ_ASSERT(dstFormat->r || dstFormat->g || dstFormat->b || dstFormat->a); |
1732 | 0 | dstHasColor = true; |
1733 | 0 | colorFormatsMatch &= (dstFormat == srcColorFormat); |
1734 | 0 | colorTypesMatch &= ( fnNarrowComponentType(dstFormat) == srcColorType ); |
1735 | 0 | }; |
1736 | 0 |
|
1737 | 0 | if (dstFB) { |
1738 | 0 | dstHasSamples = false; |
1739 | 0 |
|
1740 | 0 | for (const auto& cur : dstFB->mColorDrawBuffers) { |
1741 | 0 | const auto& format = fnGetFormat(cur, &dstHasSamples); |
1742 | 0 | if (!format) |
1743 | 0 | continue; |
1744 | 0 | |
1745 | 0 | fnCheckColorFormat(format); |
1746 | 0 | } |
1747 | 0 |
|
1748 | 0 | dstDepthFormat = fnGetFormat(dstDepthAttach, &dstHasSamples); |
1749 | 0 | dstStencilFormat = fnGetFormat(dstStencilAttach, &dstHasSamples); |
1750 | 0 | } else { |
1751 | 0 | dstHasSamples = bool(gl->Screen()->Samples()); |
1752 | 0 |
|
1753 | 0 | const webgl::FormatInfo* dstColorFormat; |
1754 | 0 | GetBackbufferFormats(webgl, &dstColorFormat, &dstDepthFormat, &dstStencilFormat); |
1755 | 0 |
|
1756 | 0 | fnCheckColorFormat(dstColorFormat); |
1757 | 0 | } |
1758 | 0 |
|
1759 | 0 | //// |
1760 | 0 | // Clear unused buffer bits |
1761 | 0 |
|
1762 | 0 | if (mask & LOCAL_GL_COLOR_BUFFER_BIT && |
1763 | 0 | !srcColorFormat && !dstHasColor) |
1764 | 0 | { |
1765 | 0 | mask ^= LOCAL_GL_COLOR_BUFFER_BIT; |
1766 | 0 | } |
1767 | 0 |
|
1768 | 0 | if (mask & LOCAL_GL_DEPTH_BUFFER_BIT && |
1769 | 0 | !srcDepthFormat && !dstDepthFormat) |
1770 | 0 | { |
1771 | 0 | mask ^= LOCAL_GL_DEPTH_BUFFER_BIT; |
1772 | 0 | } |
1773 | 0 |
|
1774 | 0 | if (mask & LOCAL_GL_STENCIL_BUFFER_BIT && |
1775 | 0 | !srcStencilFormat && !dstStencilFormat) |
1776 | 0 | { |
1777 | 0 | mask ^= LOCAL_GL_STENCIL_BUFFER_BIT; |
1778 | 0 | } |
1779 | 0 |
|
1780 | 0 | //// |
1781 | 0 | // Validation |
1782 | 0 |
|
1783 | 0 | if (mask & LOCAL_GL_COLOR_BUFFER_BIT) { |
1784 | 0 | if (srcColorFormat && filter == LOCAL_GL_LINEAR) { |
1785 | 0 | const auto& type = srcColorFormat->componentType; |
1786 | 0 | if (type == webgl::ComponentType::Int || |
1787 | 0 | type == webgl::ComponentType::UInt) |
1788 | 0 | { |
1789 | 0 | webgl->ErrorInvalidOperation("`filter` is LINEAR and READ_BUFFER" |
1790 | 0 | " contains integer data."); |
1791 | 0 | return; |
1792 | 0 | } |
1793 | 0 | } |
1794 | 0 | |
1795 | 0 | if (!colorTypesMatch) { |
1796 | 0 | webgl->ErrorInvalidOperation("Color component types (fixed/float/uint/" |
1797 | 0 | "int) must match."); |
1798 | 0 | return; |
1799 | 0 | } |
1800 | 0 | } |
1801 | 0 | |
1802 | 0 | const GLbitfield depthAndStencilBits = LOCAL_GL_DEPTH_BUFFER_BIT | |
1803 | 0 | LOCAL_GL_STENCIL_BUFFER_BIT; |
1804 | 0 | if (bool(mask & depthAndStencilBits) && |
1805 | 0 | filter != LOCAL_GL_NEAREST) |
1806 | 0 | { |
1807 | 0 | webgl->ErrorInvalidOperation("DEPTH_BUFFER_BIT and STENCIL_BUFFER_BIT can" |
1808 | 0 | " only be used with NEAREST filtering."); |
1809 | 0 | return; |
1810 | 0 | } |
1811 | 0 | |
1812 | 0 | /* GLES 3.0.4, p199: |
1813 | 0 | * Calling BlitFramebuffer will result in an INVALID_OPERATION error if |
1814 | 0 | * mask includes DEPTH_BUFFER_BIT or STENCIL_BUFFER_BIT, and the source |
1815 | 0 | * and destination depth and stencil buffer formats do not match. |
1816 | 0 | * |
1817 | 0 | * jgilbert: The wording is such that if only DEPTH_BUFFER_BIT is specified, |
1818 | 0 | * the stencil formats must match. This seems wrong. It could be a spec bug, |
1819 | 0 | * or I could be missing an interaction in one of the earlier paragraphs. |
1820 | 0 | */ |
1821 | 0 | if (mask & LOCAL_GL_DEPTH_BUFFER_BIT && |
1822 | 0 | dstDepthFormat && dstDepthFormat != srcDepthFormat) |
1823 | 0 | { |
1824 | 0 | webgl->ErrorInvalidOperation("Depth buffer formats must match if selected."); |
1825 | 0 | return; |
1826 | 0 | } |
1827 | 0 | |
1828 | 0 | if (mask & LOCAL_GL_STENCIL_BUFFER_BIT && |
1829 | 0 | dstStencilFormat && dstStencilFormat != srcStencilFormat) |
1830 | 0 | { |
1831 | 0 | webgl->ErrorInvalidOperation("Stencil buffer formats must match if selected."); |
1832 | 0 | return; |
1833 | 0 | } |
1834 | 0 | |
1835 | 0 | //// |
1836 | 0 | |
1837 | 0 | if (dstHasSamples) { |
1838 | 0 | webgl->ErrorInvalidOperation("DRAW_FRAMEBUFFER may not have multiple" |
1839 | 0 | " samples."); |
1840 | 0 | return; |
1841 | 0 | } |
1842 | 0 | |
1843 | 0 | if (srcHasSamples) { |
1844 | 0 | if (mask & LOCAL_GL_COLOR_BUFFER_BIT && |
1845 | 0 | dstHasColor && !colorFormatsMatch) |
1846 | 0 | { |
1847 | 0 | webgl->ErrorInvalidOperation("Color buffer formats must match if" |
1848 | 0 | " selected, when reading from a multisampled" |
1849 | 0 | " source."); |
1850 | 0 | return; |
1851 | 0 | } |
1852 | 0 | |
1853 | 0 | if (dstX0 != srcX0 || |
1854 | 0 | dstX1 != srcX1 || |
1855 | 0 | dstY0 != srcY0 || |
1856 | 0 | dstY1 != srcY1) |
1857 | 0 | { |
1858 | 0 | webgl->ErrorInvalidOperation("If the source is multisampled, then the" |
1859 | 0 | " source and dest regions must match exactly."); |
1860 | 0 | return; |
1861 | 0 | } |
1862 | 0 | } |
1863 | 0 | |
1864 | 0 | //// |
1865 | 0 | // Check for feedback |
1866 | 0 | |
1867 | 0 | if (srcFB && dstFB) { |
1868 | 0 | const WebGLFBAttachPoint* feedback = nullptr; |
1869 | 0 |
|
1870 | 0 | if (mask & LOCAL_GL_COLOR_BUFFER_BIT) { |
1871 | 0 | MOZ_ASSERT(srcFB->mColorReadBuffer->IsDefined()); |
1872 | 0 | for (const auto& cur : dstFB->mColorDrawBuffers) { |
1873 | 0 | if (srcFB->mColorReadBuffer->IsEquivalentForFeedback(*cur)) { |
1874 | 0 | feedback = cur; |
1875 | 0 | break; |
1876 | 0 | } |
1877 | 0 | } |
1878 | 0 | } |
1879 | 0 |
|
1880 | 0 | if (mask & LOCAL_GL_DEPTH_BUFFER_BIT && |
1881 | 0 | srcDepthAttach->IsEquivalentForFeedback(*dstDepthAttach)) |
1882 | 0 | { |
1883 | 0 | feedback = dstDepthAttach; |
1884 | 0 | } |
1885 | 0 |
|
1886 | 0 | if (mask & LOCAL_GL_STENCIL_BUFFER_BIT && |
1887 | 0 | srcStencilAttach->IsEquivalentForFeedback(*dstStencilAttach)) |
1888 | 0 | { |
1889 | 0 | feedback = dstStencilAttach; |
1890 | 0 | } |
1891 | 0 |
|
1892 | 0 | if (feedback) { |
1893 | 0 | webgl->ErrorInvalidOperation("Feedback detected into DRAW_FRAMEBUFFER's" |
1894 | 0 | " 0x%04x attachment.", |
1895 | 0 | feedback->mAttachmentPoint); |
1896 | 0 | return; |
1897 | 0 | } |
1898 | 0 | } else if (!srcFB && !dstFB) { |
1899 | 0 | webgl->ErrorInvalidOperation("Feedback with default framebuffer."); |
1900 | 0 | return; |
1901 | 0 | } |
1902 | 0 | |
1903 | 0 | //// |
1904 | 0 | |
1905 | 0 | const ScopedDrawCallWrapper wrapper(*webgl); |
1906 | 0 | gl->fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, |
1907 | 0 | dstX0, dstY0, dstX1, dstY1, |
1908 | 0 | mask, filter); |
1909 | 0 | } |
1910 | | |
1911 | | //////////////////////////////////////////////////////////////////////////////// |
1912 | | // Goop. |
1913 | | |
1914 | | JSObject* |
1915 | | WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) |
1916 | 0 | { |
1917 | 0 | return dom::WebGLFramebuffer_Binding::Wrap(cx, this, givenProto); |
1918 | 0 | } |
1919 | | |
1920 | | inline void |
1921 | | ImplCycleCollectionUnlink(mozilla::WebGLFBAttachPoint& field) |
1922 | 0 | { |
1923 | 0 | field.Unlink(); |
1924 | 0 | } |
1925 | | |
1926 | | inline void |
1927 | | ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback, |
1928 | | const mozilla::WebGLFBAttachPoint& field, |
1929 | | const char* name, |
1930 | | uint32_t flags = 0) |
1931 | 0 | { |
1932 | 0 | CycleCollectionNoteChild(callback, field.Texture(), name, flags); |
1933 | 0 | CycleCollectionNoteChild(callback, field.Renderbuffer(), name, flags); |
1934 | 0 | } |
1935 | | |
1936 | | template<typename C> |
1937 | | inline void |
1938 | | ImplCycleCollectionUnlink(C& field) |
1939 | 0 | { |
1940 | 0 | for (auto& cur : field) { |
1941 | 0 | cur.Unlink(); |
1942 | 0 | } |
1943 | 0 | } |
1944 | | |
1945 | | template<typename C> |
1946 | | inline void |
1947 | | ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback, |
1948 | | const C& field, |
1949 | | const char* name, |
1950 | | uint32_t flags = 0) |
1951 | 0 | { |
1952 | 0 | for (auto& cur : field) { |
1953 | 0 | ImplCycleCollectionTraverse(callback, cur, name, flags); |
1954 | 0 | } |
1955 | 0 | } |
1956 | | |
1957 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLFramebuffer, |
1958 | | mDepthAttachment, |
1959 | | mStencilAttachment, |
1960 | | mDepthStencilAttachment, |
1961 | | mColorAttachments) |
1962 | | |
1963 | | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLFramebuffer, AddRef) |
1964 | | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLFramebuffer, Release) |
1965 | | |
1966 | | } // namespace mozilla |