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