|
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 "WebGLContext.h" |
|
7 #include "WebGLFramebuffer.h" |
|
8 #include "WebGLExtensions.h" |
|
9 #include "WebGLRenderbuffer.h" |
|
10 #include "WebGLTexture.h" |
|
11 #include "mozilla/dom/WebGLRenderingContextBinding.h" |
|
12 #include "WebGLTexture.h" |
|
13 #include "WebGLRenderbuffer.h" |
|
14 #include "GLContext.h" |
|
15 #include "WebGLContextUtils.h" |
|
16 |
|
17 using namespace mozilla; |
|
18 using namespace mozilla::gl; |
|
19 |
|
20 JSObject* |
|
21 WebGLFramebuffer::WrapObject(JSContext* cx) |
|
22 { |
|
23 return dom::WebGLFramebufferBinding::Wrap(cx, this); |
|
24 } |
|
25 |
|
26 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* context) |
|
27 : WebGLContextBoundObject(context) |
|
28 , mStatus(0) |
|
29 , mHasEverBeenBound(false) |
|
30 , mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT) |
|
31 , mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT) |
|
32 , mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) |
|
33 { |
|
34 SetIsDOMBinding(); |
|
35 mContext->MakeContextCurrent(); |
|
36 mContext->gl->fGenFramebuffers(1, &mGLName); |
|
37 mContext->mFramebuffers.insertBack(this); |
|
38 |
|
39 mColorAttachments.SetLength(1); |
|
40 mColorAttachments[0].mAttachmentPoint = LOCAL_GL_COLOR_ATTACHMENT0; |
|
41 } |
|
42 |
|
43 bool |
|
44 WebGLFramebuffer::Attachment::IsDeleteRequested() const |
|
45 { |
|
46 return Texture() ? Texture()->IsDeleteRequested() |
|
47 : Renderbuffer() ? Renderbuffer()->IsDeleteRequested() |
|
48 : false; |
|
49 } |
|
50 |
|
51 bool |
|
52 WebGLFramebuffer::Attachment::HasAlpha() const |
|
53 { |
|
54 MOZ_ASSERT(HasImage()); |
|
55 |
|
56 GLenum format = 0; |
|
57 if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) |
|
58 format = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLFormat(); |
|
59 else if (Renderbuffer()) |
|
60 format = Renderbuffer()->InternalFormat(); |
|
61 return FormatHasAlpha(format); |
|
62 } |
|
63 |
|
64 bool |
|
65 WebGLFramebuffer::Attachment::IsReadableFloat() const |
|
66 { |
|
67 if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) { |
|
68 GLenum type = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLType(); |
|
69 switch (type) { |
|
70 case LOCAL_GL_FLOAT: |
|
71 case LOCAL_GL_HALF_FLOAT_OES: |
|
72 return true; |
|
73 } |
|
74 return false; |
|
75 } |
|
76 |
|
77 if (Renderbuffer()) { |
|
78 GLenum format = Renderbuffer()->InternalFormat(); |
|
79 switch (format) { |
|
80 case LOCAL_GL_RGB16F: |
|
81 case LOCAL_GL_RGBA16F: |
|
82 case LOCAL_GL_RGB32F: |
|
83 case LOCAL_GL_RGBA32F: |
|
84 return true; |
|
85 } |
|
86 return false; |
|
87 } |
|
88 |
|
89 MOZ_ASSERT(false, "Should not get here."); |
|
90 return false; |
|
91 } |
|
92 |
|
93 void |
|
94 WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture* tex, GLenum target, GLint level) |
|
95 { |
|
96 mTexturePtr = tex; |
|
97 mRenderbufferPtr = nullptr; |
|
98 mTexImageTarget = target; |
|
99 mTexImageLevel = level; |
|
100 |
|
101 mNeedsFinalize = true; |
|
102 } |
|
103 |
|
104 void |
|
105 WebGLFramebuffer::Attachment::SetRenderbuffer(WebGLRenderbuffer* rb) |
|
106 { |
|
107 mTexturePtr = nullptr; |
|
108 mRenderbufferPtr = rb; |
|
109 |
|
110 mNeedsFinalize = true; |
|
111 } |
|
112 |
|
113 bool |
|
114 WebGLFramebuffer::Attachment::HasUninitializedImageData() const |
|
115 { |
|
116 if (!HasImage()) |
|
117 return false; |
|
118 |
|
119 if (Renderbuffer()) { |
|
120 return Renderbuffer()->HasUninitializedImageData(); |
|
121 } |
|
122 |
|
123 if (Texture()) { |
|
124 MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); |
|
125 return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).HasUninitializedImageData(); |
|
126 } |
|
127 |
|
128 MOZ_ASSERT(false, "Should not get here."); |
|
129 return false; |
|
130 } |
|
131 |
|
132 void |
|
133 WebGLFramebuffer::Attachment::SetImageDataStatus(WebGLImageDataStatus newStatus) |
|
134 { |
|
135 if (!HasImage()) |
|
136 return; |
|
137 |
|
138 if (Renderbuffer()) { |
|
139 Renderbuffer()->SetImageDataStatus(newStatus); |
|
140 return; |
|
141 } |
|
142 |
|
143 if (Texture()) { |
|
144 Texture()->SetImageDataStatus(mTexImageTarget, mTexImageLevel, newStatus); |
|
145 return; |
|
146 } |
|
147 |
|
148 MOZ_ASSERT(false, "Should not get here."); |
|
149 } |
|
150 |
|
151 bool |
|
152 WebGLFramebuffer::Attachment::HasImage() const |
|
153 { |
|
154 if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) |
|
155 return true; |
|
156 |
|
157 if (Renderbuffer()) |
|
158 return true; |
|
159 |
|
160 return false; |
|
161 } |
|
162 |
|
163 const WebGLRectangleObject& |
|
164 WebGLFramebuffer::Attachment::RectangleObject() const |
|
165 { |
|
166 MOZ_ASSERT(HasImage(), "Make sure it has an image before requesting the rectangle."); |
|
167 |
|
168 if (Texture()) { |
|
169 MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); |
|
170 return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel); |
|
171 } |
|
172 |
|
173 if (Renderbuffer()) { |
|
174 return *Renderbuffer(); |
|
175 } |
|
176 |
|
177 MOZ_CRASH("Should not get here."); |
|
178 } |
|
179 |
|
180 /* The following IsValidFBOTextureXXX functions check the internal |
|
181 format that is used by GL or GL ES texture formats. This |
|
182 corresponds to the state that is stored in |
|
183 WebGLTexture::ImageInfo::InternalFormat()*/ |
|
184 static inline bool |
|
185 IsValidFBOTextureColorFormat(GLenum internalFormat) |
|
186 { |
|
187 /* These formats are internal formats for each texture -- the actual |
|
188 * low level format, which we might have to do conversions for when |
|
189 * running against desktop GL (e.g. GL_RGBA + GL_FLOAT -> GL_RGBA32F). |
|
190 * |
|
191 * This function just handles all of them whether desktop GL or ES. |
|
192 */ |
|
193 |
|
194 return ( |
|
195 /* linear 8-bit formats */ |
|
196 internalFormat == LOCAL_GL_ALPHA || |
|
197 internalFormat == LOCAL_GL_LUMINANCE || |
|
198 internalFormat == LOCAL_GL_LUMINANCE_ALPHA || |
|
199 internalFormat == LOCAL_GL_RGB || |
|
200 internalFormat == LOCAL_GL_RGBA || |
|
201 /* sRGB 8-bit formats */ |
|
202 internalFormat == LOCAL_GL_SRGB_EXT || |
|
203 internalFormat == LOCAL_GL_SRGB_ALPHA_EXT || |
|
204 /* linear float32 formats */ |
|
205 internalFormat == LOCAL_GL_ALPHA32F_ARB || |
|
206 internalFormat == LOCAL_GL_LUMINANCE32F_ARB || |
|
207 internalFormat == LOCAL_GL_LUMINANCE_ALPHA32F_ARB || |
|
208 internalFormat == LOCAL_GL_RGB32F_ARB || |
|
209 internalFormat == LOCAL_GL_RGBA32F_ARB || |
|
210 /* texture_half_float formats */ |
|
211 internalFormat == LOCAL_GL_ALPHA16F_ARB || |
|
212 internalFormat == LOCAL_GL_LUMINANCE16F_ARB || |
|
213 internalFormat == LOCAL_GL_LUMINANCE_ALPHA16F_ARB || |
|
214 internalFormat == LOCAL_GL_RGB16F_ARB || |
|
215 internalFormat == LOCAL_GL_RGBA16F_ARB |
|
216 ); |
|
217 } |
|
218 |
|
219 static inline bool |
|
220 IsValidFBOTextureDepthFormat(GLenum internalFormat) |
|
221 { |
|
222 return ( |
|
223 internalFormat == LOCAL_GL_DEPTH_COMPONENT || |
|
224 internalFormat == LOCAL_GL_DEPTH_COMPONENT16 || |
|
225 internalFormat == LOCAL_GL_DEPTH_COMPONENT32); |
|
226 } |
|
227 |
|
228 static inline bool |
|
229 IsValidFBOTextureDepthStencilFormat(GLenum internalFormat) |
|
230 { |
|
231 return ( |
|
232 internalFormat == LOCAL_GL_DEPTH_STENCIL || |
|
233 internalFormat == LOCAL_GL_DEPTH24_STENCIL8); |
|
234 } |
|
235 |
|
236 /* The following IsValidFBORenderbufferXXX functions check the internal |
|
237 format that is stored by WebGLRenderbuffer::InternalFormat(). Valid |
|
238 values can be found in WebGLContext::RenderbufferStorage. */ |
|
239 static inline bool |
|
240 IsValidFBORenderbufferColorFormat(GLenum internalFormat) |
|
241 { |
|
242 return ( |
|
243 internalFormat == LOCAL_GL_RGB565 || |
|
244 internalFormat == LOCAL_GL_RGB5_A1 || |
|
245 internalFormat == LOCAL_GL_RGBA4 || |
|
246 internalFormat == LOCAL_GL_SRGB8_ALPHA8_EXT); |
|
247 } |
|
248 |
|
249 static inline bool |
|
250 IsValidFBORenderbufferDepthFormat(GLenum internalFormat) |
|
251 { |
|
252 return internalFormat == LOCAL_GL_DEPTH_COMPONENT16; |
|
253 } |
|
254 |
|
255 static inline bool |
|
256 IsValidFBORenderbufferDepthStencilFormat(GLenum internalFormat) |
|
257 { |
|
258 return internalFormat == LOCAL_GL_DEPTH_STENCIL; |
|
259 } |
|
260 |
|
261 static inline bool |
|
262 IsValidFBORenderbufferStencilFormat(GLenum internalFormat) |
|
263 { |
|
264 return internalFormat == LOCAL_GL_STENCIL_INDEX8; |
|
265 } |
|
266 |
|
267 bool |
|
268 WebGLFramebuffer::Attachment::IsComplete() const |
|
269 { |
|
270 if (!HasImage()) |
|
271 return false; |
|
272 |
|
273 const WebGLRectangleObject& rect = RectangleObject(); |
|
274 |
|
275 if (!rect.Width() || |
|
276 !rect.Height()) |
|
277 { |
|
278 return false; |
|
279 } |
|
280 |
|
281 if (Texture()) { |
|
282 MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); |
|
283 const WebGLTexture::ImageInfo& imageInfo = |
|
284 Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel); |
|
285 GLenum webGLFormat = imageInfo.WebGLFormat(); |
|
286 |
|
287 if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) |
|
288 return IsValidFBOTextureDepthFormat(webGLFormat); |
|
289 |
|
290 if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) |
|
291 return IsValidFBOTextureDepthStencilFormat(webGLFormat); |
|
292 |
|
293 if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 && |
|
294 mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + |
|
295 WebGLContext::sMaxColorAttachments)) |
|
296 { |
|
297 return IsValidFBOTextureColorFormat(webGLFormat); |
|
298 } |
|
299 MOZ_ASSERT(false, "Invalid WebGL attachment point?"); |
|
300 return false; |
|
301 } |
|
302 |
|
303 if (Renderbuffer()) { |
|
304 GLenum internalFormat = Renderbuffer()->InternalFormat(); |
|
305 |
|
306 if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) |
|
307 return IsValidFBORenderbufferDepthFormat(internalFormat); |
|
308 |
|
309 if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) |
|
310 return IsValidFBORenderbufferStencilFormat(internalFormat); |
|
311 |
|
312 if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) |
|
313 return IsValidFBORenderbufferDepthStencilFormat(internalFormat); |
|
314 |
|
315 if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 && |
|
316 mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + |
|
317 WebGLContext::sMaxColorAttachments)) |
|
318 { |
|
319 return IsValidFBORenderbufferColorFormat(internalFormat); |
|
320 } |
|
321 MOZ_ASSERT(false, "Invalid WebGL attachment point?"); |
|
322 return false; |
|
323 } |
|
324 |
|
325 MOZ_ASSERT(false, "Should not get here."); |
|
326 return false; |
|
327 } |
|
328 |
|
329 void |
|
330 WebGLFramebuffer::Attachment::FinalizeAttachment(GLContext* gl, GLenum attachmentLoc) const |
|
331 { |
|
332 if (!mNeedsFinalize) |
|
333 return; |
|
334 |
|
335 mNeedsFinalize = false; |
|
336 |
|
337 if (!HasImage()) { |
|
338 if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { |
|
339 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, |
|
340 LOCAL_GL_RENDERBUFFER, 0); |
|
341 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, |
|
342 LOCAL_GL_RENDERBUFFER, 0); |
|
343 } else { |
|
344 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachmentLoc, |
|
345 LOCAL_GL_RENDERBUFFER, 0); |
|
346 } |
|
347 |
|
348 return; |
|
349 } |
|
350 MOZ_ASSERT(HasImage()); |
|
351 |
|
352 if (Texture()) { |
|
353 MOZ_ASSERT(gl == Texture()->Context()->gl); |
|
354 |
|
355 if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { |
|
356 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, |
|
357 TexImageTarget(), Texture()->GLName(), TexImageLevel()); |
|
358 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, |
|
359 TexImageTarget(), Texture()->GLName(), TexImageLevel()); |
|
360 } else { |
|
361 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachmentLoc, |
|
362 TexImageTarget(), Texture()->GLName(), TexImageLevel()); |
|
363 } |
|
364 return; |
|
365 } |
|
366 |
|
367 if (Renderbuffer()) { |
|
368 Renderbuffer()->FramebufferRenderbuffer(attachmentLoc); |
|
369 return; |
|
370 } |
|
371 |
|
372 MOZ_ASSERT(false, "Should not get here."); |
|
373 } |
|
374 |
|
375 void |
|
376 WebGLFramebuffer::Delete() |
|
377 { |
|
378 DetachAllAttachments(); |
|
379 mColorAttachments.Clear(); |
|
380 mDepthAttachment.Reset(); |
|
381 mStencilAttachment.Reset(); |
|
382 mDepthStencilAttachment.Reset(); |
|
383 |
|
384 mContext->MakeContextCurrent(); |
|
385 mContext->gl->fDeleteFramebuffers(1, &mGLName); |
|
386 LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers); |
|
387 } |
|
388 |
|
389 void |
|
390 WebGLFramebuffer::DetachAttachment(WebGLFramebuffer::Attachment& attachment) |
|
391 { |
|
392 if (attachment.Texture()) |
|
393 attachment.Texture()->DetachFrom(this, attachment.mAttachmentPoint); |
|
394 |
|
395 if (attachment.Renderbuffer()) |
|
396 attachment.Renderbuffer()->DetachFrom(this, attachment.mAttachmentPoint); |
|
397 } |
|
398 |
|
399 void |
|
400 WebGLFramebuffer::DetachAllAttachments() |
|
401 { |
|
402 size_t count = mColorAttachments.Length(); |
|
403 for (size_t i = 0; i < count; i++) { |
|
404 DetachAttachment(mColorAttachments[i]); |
|
405 } |
|
406 |
|
407 DetachAttachment(mDepthAttachment); |
|
408 DetachAttachment(mStencilAttachment); |
|
409 DetachAttachment(mDepthStencilAttachment); |
|
410 } |
|
411 |
|
412 void |
|
413 WebGLFramebuffer::FramebufferRenderbuffer(GLenum target, |
|
414 GLenum attachment, |
|
415 GLenum rbtarget, |
|
416 WebGLRenderbuffer* wrb) |
|
417 { |
|
418 MOZ_ASSERT(mContext->mBoundFramebuffer == this); |
|
419 |
|
420 if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", wrb)) |
|
421 return; |
|
422 |
|
423 if (target != LOCAL_GL_FRAMEBUFFER) |
|
424 return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target); |
|
425 |
|
426 if (rbtarget != LOCAL_GL_RENDERBUFFER) |
|
427 return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget); |
|
428 |
|
429 /* Get the requested attachment. If result is NULL, attachment is |
|
430 * invalid and an error is generated. |
|
431 * |
|
432 * Don't use GetAttachment(...) here because it opt builds it |
|
433 * returns mColorAttachment[0] for invalid attachment, which we |
|
434 * really don't want to mess with. |
|
435 */ |
|
436 Attachment* a = GetAttachmentOrNull(attachment); |
|
437 if (!a) |
|
438 return; // Error generated internally to GetAttachmentOrNull. |
|
439 |
|
440 /* Invalidate cached framebuffer status and inform texture of it's |
|
441 * new attachment |
|
442 */ |
|
443 mStatus = 0; |
|
444 // Detach current |
|
445 if (a->Texture()) |
|
446 a->Texture()->DetachFrom(this, attachment); |
|
447 else if (a->Renderbuffer()) |
|
448 a->Renderbuffer()->DetachFrom(this, attachment); |
|
449 |
|
450 // Attach new |
|
451 if (wrb) |
|
452 wrb->AttachTo(this, attachment); |
|
453 |
|
454 a->SetRenderbuffer(wrb); |
|
455 } |
|
456 |
|
457 void |
|
458 WebGLFramebuffer::FramebufferTexture2D(GLenum target, |
|
459 GLenum attachment, |
|
460 GLenum textarget, |
|
461 WebGLTexture* wtex, |
|
462 GLint level) |
|
463 { |
|
464 MOZ_ASSERT(mContext->mBoundFramebuffer == this); |
|
465 |
|
466 if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", wtex)) |
|
467 return; |
|
468 |
|
469 if (target != LOCAL_GL_FRAMEBUFFER) |
|
470 return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: target", target); |
|
471 |
|
472 if (textarget != LOCAL_GL_TEXTURE_2D && |
|
473 (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X || |
|
474 textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)) |
|
475 { |
|
476 return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget); |
|
477 } |
|
478 |
|
479 if (wtex) { |
|
480 bool isTexture2D = wtex->Target() == LOCAL_GL_TEXTURE_2D; |
|
481 bool isTexTarget2D = textarget == LOCAL_GL_TEXTURE_2D; |
|
482 if (isTexture2D != isTexTarget2D) { |
|
483 return mContext->ErrorInvalidOperation("framebufferTexture2D: mismatched texture and texture target"); |
|
484 } |
|
485 } |
|
486 |
|
487 if (level != 0) |
|
488 return mContext->ErrorInvalidValue("framebufferTexture2D: level must be 0"); |
|
489 |
|
490 /* Get the requested attachment. If result is NULL, attachment is |
|
491 * invalid and an error is generated. |
|
492 * |
|
493 * Don't use GetAttachment(...) here because it opt builds it |
|
494 * returns mColorAttachment[0] for invalid attachment, which we |
|
495 * really don't want to mess with. |
|
496 */ |
|
497 Attachment* a = GetAttachmentOrNull(attachment); |
|
498 if (!a) |
|
499 return; // Error generated internally to GetAttachmentOrNull. |
|
500 |
|
501 /* Invalidate cached framebuffer status and inform texture of it's |
|
502 * new attachment |
|
503 */ |
|
504 mStatus = 0; |
|
505 // Detach current |
|
506 if (a->Texture()) |
|
507 a->Texture()->DetachFrom(this, attachment); |
|
508 else if (a->Renderbuffer()) |
|
509 a->Renderbuffer()->DetachFrom(this, attachment); |
|
510 |
|
511 // Attach new |
|
512 if (wtex) |
|
513 wtex->AttachTo(this, attachment); |
|
514 |
|
515 a->SetTexImage(wtex, textarget, level); |
|
516 } |
|
517 |
|
518 WebGLFramebuffer::Attachment* |
|
519 WebGLFramebuffer::GetAttachmentOrNull(GLenum attachment) |
|
520 { |
|
521 if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) |
|
522 return &mDepthStencilAttachment; |
|
523 |
|
524 if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) |
|
525 return &mDepthAttachment; |
|
526 |
|
527 if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) |
|
528 return &mStencilAttachment; |
|
529 |
|
530 if (!CheckColorAttachmentNumber(attachment, "getAttachmentOrNull")) |
|
531 return nullptr; |
|
532 |
|
533 size_t colorAttachmentId = attachment - LOCAL_GL_COLOR_ATTACHMENT0; |
|
534 EnsureColorAttachments(colorAttachmentId); |
|
535 |
|
536 return &mColorAttachments[colorAttachmentId]; |
|
537 } |
|
538 |
|
539 const WebGLFramebuffer::Attachment& |
|
540 WebGLFramebuffer::GetAttachment(GLenum attachment) const |
|
541 { |
|
542 if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) |
|
543 return mDepthStencilAttachment; |
|
544 if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) |
|
545 return mDepthAttachment; |
|
546 if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) |
|
547 return mStencilAttachment; |
|
548 |
|
549 if (!CheckColorAttachmentNumber(attachment, "getAttachment")) { |
|
550 MOZ_ASSERT(false); |
|
551 return mColorAttachments[0]; |
|
552 } |
|
553 |
|
554 size_t colorAttachmentId = attachment - LOCAL_GL_COLOR_ATTACHMENT0; |
|
555 if (colorAttachmentId >= mColorAttachments.Length()) { |
|
556 MOZ_ASSERT(false); |
|
557 return mColorAttachments[0]; |
|
558 } |
|
559 |
|
560 return mColorAttachments[colorAttachmentId]; |
|
561 } |
|
562 |
|
563 void |
|
564 WebGLFramebuffer::DetachTexture(const WebGLTexture* tex) |
|
565 { |
|
566 size_t count = mColorAttachments.Length(); |
|
567 for (size_t i = 0; i < count; i++) { |
|
568 if (mColorAttachments[i].Texture() == tex) { |
|
569 FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0+i, LOCAL_GL_TEXTURE_2D, nullptr, 0); |
|
570 // a texture might be attached more that once while editing the framebuffer |
|
571 } |
|
572 } |
|
573 |
|
574 if (mDepthAttachment.Texture() == tex) |
|
575 FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); |
|
576 if (mStencilAttachment.Texture() == tex) |
|
577 FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); |
|
578 if (mDepthStencilAttachment.Texture() == tex) |
|
579 FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); |
|
580 } |
|
581 |
|
582 void |
|
583 WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb) |
|
584 { |
|
585 size_t count = mColorAttachments.Length(); |
|
586 for (size_t i = 0; i < count; i++) { |
|
587 if (mColorAttachments[i].Renderbuffer() == rb) { |
|
588 FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0+i, LOCAL_GL_RENDERBUFFER, nullptr); |
|
589 // a renderbuffer might be attached more that once while editing the framebuffer |
|
590 } |
|
591 } |
|
592 |
|
593 if (mDepthAttachment.Renderbuffer() == rb) |
|
594 FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); |
|
595 if (mStencilAttachment.Renderbuffer() == rb) |
|
596 FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); |
|
597 if (mDepthStencilAttachment.Renderbuffer() == rb) |
|
598 FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); |
|
599 } |
|
600 |
|
601 bool |
|
602 WebGLFramebuffer::HasDefinedAttachments() const |
|
603 { |
|
604 bool hasAttachments = false; |
|
605 |
|
606 size_t count = mColorAttachments.Length(); |
|
607 for (size_t i = 0; i < count; i++) { |
|
608 hasAttachments |= mColorAttachments[i].IsDefined(); |
|
609 } |
|
610 |
|
611 hasAttachments |= mDepthAttachment.IsDefined(); |
|
612 hasAttachments |= mStencilAttachment.IsDefined(); |
|
613 hasAttachments |= mDepthStencilAttachment.IsDefined(); |
|
614 |
|
615 return hasAttachments; |
|
616 } |
|
617 |
|
618 |
|
619 static bool |
|
620 IsIncomplete(const WebGLFramebuffer::Attachment& cur) |
|
621 { |
|
622 return cur.IsDefined() && !cur.IsComplete(); |
|
623 } |
|
624 |
|
625 bool |
|
626 WebGLFramebuffer::HasIncompleteAttachments() const |
|
627 { |
|
628 bool hasIncomplete = false; |
|
629 |
|
630 size_t count = mColorAttachments.Length(); |
|
631 for (size_t i = 0; i < count; i++) { |
|
632 hasIncomplete |= IsIncomplete(mColorAttachments[i]); |
|
633 } |
|
634 |
|
635 hasIncomplete |= IsIncomplete(mDepthAttachment); |
|
636 hasIncomplete |= IsIncomplete(mStencilAttachment); |
|
637 hasIncomplete |= IsIncomplete(mDepthStencilAttachment); |
|
638 |
|
639 return hasIncomplete; |
|
640 } |
|
641 |
|
642 |
|
643 const WebGLRectangleObject& |
|
644 WebGLFramebuffer::GetAnyRectObject() const |
|
645 { |
|
646 MOZ_ASSERT(HasDefinedAttachments()); |
|
647 |
|
648 size_t count = mColorAttachments.Length(); |
|
649 for (size_t i = 0; i < count; i++) { |
|
650 if (mColorAttachments[i].HasImage()) |
|
651 return mColorAttachments[i].RectangleObject(); |
|
652 } |
|
653 |
|
654 if (mDepthAttachment.HasImage()) |
|
655 return mDepthAttachment.RectangleObject(); |
|
656 |
|
657 if (mStencilAttachment.HasImage()) |
|
658 return mStencilAttachment.RectangleObject(); |
|
659 |
|
660 if (mDepthStencilAttachment.HasImage()) |
|
661 return mDepthStencilAttachment.RectangleObject(); |
|
662 |
|
663 MOZ_CRASH("Should not get here."); |
|
664 } |
|
665 |
|
666 |
|
667 static bool |
|
668 RectsMatch(const WebGLFramebuffer::Attachment& attachment, |
|
669 const WebGLRectangleObject& rect) |
|
670 { |
|
671 return attachment.RectangleObject().HasSameDimensionsAs(rect); |
|
672 } |
|
673 |
|
674 bool |
|
675 WebGLFramebuffer::AllImageRectsMatch() const |
|
676 { |
|
677 MOZ_ASSERT(HasDefinedAttachments()); |
|
678 MOZ_ASSERT(!HasIncompleteAttachments()); |
|
679 |
|
680 const WebGLRectangleObject& rect = GetAnyRectObject(); |
|
681 |
|
682 // Alright, we have *a* rect, let's check all the others. |
|
683 bool imageRectsMatch = true; |
|
684 |
|
685 size_t count = mColorAttachments.Length(); |
|
686 for (size_t i = 0; i < count; i++) { |
|
687 if (mColorAttachments[i].HasImage()) |
|
688 imageRectsMatch &= RectsMatch(mColorAttachments[i], rect); |
|
689 } |
|
690 |
|
691 if (mDepthAttachment.HasImage()) |
|
692 imageRectsMatch &= RectsMatch(mDepthAttachment, rect); |
|
693 |
|
694 if (mStencilAttachment.HasImage()) |
|
695 imageRectsMatch &= RectsMatch(mStencilAttachment, rect); |
|
696 |
|
697 if (mDepthStencilAttachment.HasImage()) |
|
698 imageRectsMatch &= RectsMatch(mDepthStencilAttachment, rect); |
|
699 |
|
700 return imageRectsMatch; |
|
701 } |
|
702 |
|
703 |
|
704 const WebGLRectangleObject& |
|
705 WebGLFramebuffer::RectangleObject() const |
|
706 { |
|
707 // If we're using this as the RectObj of an FB, we need to be sure the FB |
|
708 // has a consistent rect. |
|
709 MOZ_ASSERT(AllImageRectsMatch(), "Did you mean `GetAnyRectObject`?"); |
|
710 return GetAnyRectObject(); |
|
711 } |
|
712 |
|
713 GLenum |
|
714 WebGLFramebuffer::PrecheckFramebufferStatus() const |
|
715 { |
|
716 MOZ_ASSERT(mContext->mBoundFramebuffer == this); |
|
717 |
|
718 if (!HasDefinedAttachments()) |
|
719 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // No attachments |
|
720 |
|
721 if (HasIncompleteAttachments()) |
|
722 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; |
|
723 |
|
724 if (!AllImageRectsMatch()) |
|
725 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes |
|
726 |
|
727 if (HasDepthStencilConflict()) |
|
728 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; |
|
729 |
|
730 return LOCAL_GL_FRAMEBUFFER_COMPLETE; |
|
731 } |
|
732 |
|
733 GLenum |
|
734 WebGLFramebuffer::CheckFramebufferStatus() const |
|
735 { |
|
736 if (mStatus != 0) |
|
737 return mStatus; |
|
738 |
|
739 mStatus = PrecheckFramebufferStatus(); |
|
740 if (mStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE) |
|
741 return mStatus; |
|
742 |
|
743 // Looks good on our end. Let's ask the driver. |
|
744 mContext->MakeContextCurrent(); |
|
745 |
|
746 // Ok, attach our chosen flavor of {DEPTH, STENCIL, DEPTH_STENCIL}. |
|
747 FinalizeAttachments(); |
|
748 |
|
749 mStatus = mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); |
|
750 return mStatus; |
|
751 } |
|
752 |
|
753 bool |
|
754 WebGLFramebuffer::HasCompletePlanes(GLbitfield mask) |
|
755 { |
|
756 if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE) |
|
757 return false; |
|
758 |
|
759 MOZ_ASSERT(mContext->mBoundFramebuffer == this); |
|
760 bool hasPlanes = true; |
|
761 if (mask & LOCAL_GL_COLOR_BUFFER_BIT) { |
|
762 hasPlanes &= ColorAttachmentCount() && |
|
763 ColorAttachment(0).IsDefined(); |
|
764 } |
|
765 |
|
766 if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) { |
|
767 hasPlanes &= DepthAttachment().IsDefined() || |
|
768 DepthStencilAttachment().IsDefined(); |
|
769 } |
|
770 |
|
771 if (mask & LOCAL_GL_STENCIL_BUFFER_BIT) { |
|
772 hasPlanes &= StencilAttachment().IsDefined() || |
|
773 DepthStencilAttachment().IsDefined(); |
|
774 } |
|
775 |
|
776 return hasPlanes; |
|
777 } |
|
778 |
|
779 bool |
|
780 WebGLFramebuffer::CheckAndInitializeAttachments() |
|
781 { |
|
782 MOZ_ASSERT(mContext->mBoundFramebuffer == this); |
|
783 |
|
784 if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE) |
|
785 return false; |
|
786 |
|
787 // Cool! We've checked out ok. Just need to initialize. |
|
788 size_t colorAttachmentCount = mColorAttachments.Length(); |
|
789 |
|
790 // Check if we need to initialize anything |
|
791 { |
|
792 bool hasUninitializedAttachments = false; |
|
793 |
|
794 for (size_t i = 0; i < colorAttachmentCount; i++) { |
|
795 if (mColorAttachments[i].HasImage()) |
|
796 hasUninitializedAttachments |= mColorAttachments[i].HasUninitializedImageData(); |
|
797 } |
|
798 |
|
799 if (mDepthAttachment.HasImage()) |
|
800 hasUninitializedAttachments |= mDepthAttachment.HasUninitializedImageData(); |
|
801 if (mStencilAttachment.HasImage()) |
|
802 hasUninitializedAttachments |= mStencilAttachment.HasUninitializedImageData(); |
|
803 if (mDepthStencilAttachment.HasImage()) |
|
804 hasUninitializedAttachments |= mDepthStencilAttachment.HasUninitializedImageData(); |
|
805 |
|
806 if (!hasUninitializedAttachments) |
|
807 return true; |
|
808 } |
|
809 |
|
810 // Get buffer-bit-mask and color-attachment-mask-list |
|
811 uint32_t mask = 0; |
|
812 bool colorAttachmentsMask[WebGLContext::sMaxColorAttachments] = { false }; |
|
813 MOZ_ASSERT(colorAttachmentCount <= WebGLContext::sMaxColorAttachments); |
|
814 |
|
815 for (size_t i = 0; i < colorAttachmentCount; i++) { |
|
816 if (mColorAttachments[i].HasUninitializedImageData()) { |
|
817 colorAttachmentsMask[i] = true; |
|
818 mask |= LOCAL_GL_COLOR_BUFFER_BIT; |
|
819 } |
|
820 } |
|
821 |
|
822 if (mDepthAttachment.HasUninitializedImageData() || |
|
823 mDepthStencilAttachment.HasUninitializedImageData()) |
|
824 { |
|
825 mask |= LOCAL_GL_DEPTH_BUFFER_BIT; |
|
826 } |
|
827 |
|
828 if (mStencilAttachment.HasUninitializedImageData() || |
|
829 mDepthStencilAttachment.HasUninitializedImageData()) |
|
830 { |
|
831 mask |= LOCAL_GL_STENCIL_BUFFER_BIT; |
|
832 } |
|
833 |
|
834 // Clear! |
|
835 mContext->ForceClearFramebufferWithDefaultValues(mask, colorAttachmentsMask); |
|
836 |
|
837 // Mark all the uninitialized images as initialized. |
|
838 for (size_t i = 0; i < colorAttachmentCount; i++) { |
|
839 if (mColorAttachments[i].HasUninitializedImageData()) |
|
840 mColorAttachments[i].SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); |
|
841 } |
|
842 |
|
843 if (mDepthAttachment.HasUninitializedImageData()) |
|
844 mDepthAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); |
|
845 if (mStencilAttachment.HasUninitializedImageData()) |
|
846 mStencilAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); |
|
847 if (mDepthStencilAttachment.HasUninitializedImageData()) |
|
848 mDepthStencilAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); |
|
849 |
|
850 return true; |
|
851 } |
|
852 |
|
853 bool WebGLFramebuffer::CheckColorAttachmentNumber(GLenum attachment, const char* functionName) const |
|
854 { |
|
855 const char* const errorFormating = "%s: attachment: invalid enum value 0x%x"; |
|
856 |
|
857 if (mContext->IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { |
|
858 if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 || |
|
859 attachment >= GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mGLMaxColorAttachments)) |
|
860 { |
|
861 mContext->ErrorInvalidEnum(errorFormating, functionName, attachment); |
|
862 return false; |
|
863 } |
|
864 } else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) { |
|
865 if (attachment > LOCAL_GL_COLOR_ATTACHMENT0 && |
|
866 attachment <= LOCAL_GL_COLOR_ATTACHMENT15) |
|
867 { |
|
868 mContext->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x. " |
|
869 "Try the WEBGL_draw_buffers extension if supported.", functionName, attachment); |
|
870 return false; |
|
871 } else { |
|
872 mContext->ErrorInvalidEnum(errorFormating, functionName, attachment); |
|
873 return false; |
|
874 } |
|
875 } |
|
876 |
|
877 return true; |
|
878 } |
|
879 |
|
880 void WebGLFramebuffer::EnsureColorAttachments(size_t colorAttachmentId) |
|
881 { |
|
882 MOZ_ASSERT(colorAttachmentId < WebGLContext::sMaxColorAttachments); |
|
883 |
|
884 size_t currentAttachmentCount = mColorAttachments.Length(); |
|
885 if (colorAttachmentId < currentAttachmentCount) |
|
886 return; |
|
887 |
|
888 mColorAttachments.SetLength(colorAttachmentId + 1); |
|
889 |
|
890 for (size_t i = colorAttachmentId; i >= currentAttachmentCount; i--) { |
|
891 mColorAttachments[i].mAttachmentPoint = LOCAL_GL_COLOR_ATTACHMENT0 + i; |
|
892 } |
|
893 } |
|
894 |
|
895 void |
|
896 WebGLFramebuffer::NotifyAttachableChanged() const |
|
897 { |
|
898 // Attachment has changed, so invalidate cached status |
|
899 mStatus = 0; |
|
900 } |
|
901 |
|
902 static void |
|
903 FinalizeDrawAndReadBuffers(GLContext* aGL, bool aColorBufferDefined) |
|
904 { |
|
905 MOZ_ASSERT(aGL, "Expected a valid GLContext ptr."); |
|
906 // GLES don't support DrawBuffer()/ReadBuffer. |
|
907 // According to http://www.opengl.org/wiki/Framebuffer_Object |
|
908 // |
|
909 // Each draw buffers must either specify color attachment points that have images |
|
910 // attached or must be GL_NONE. (GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER when false). |
|
911 // |
|
912 // If the read buffer is set, then it must specify an attachment point that has an |
|
913 // image attached. (GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER when false). |
|
914 // |
|
915 // Note that this test is not performed if OpenGL 4.2 or ARB_ES2_compatibility is |
|
916 // available. |
|
917 if (aGL->IsGLES() || |
|
918 aGL->IsSupported(GLFeature::ES2_compatibility) || |
|
919 aGL->IsAtLeast(ContextProfile::OpenGL, 420)) |
|
920 { |
|
921 return; |
|
922 } |
|
923 |
|
924 // TODO(djg): Assert that fDrawBuffer/fReadBuffer is not NULL. |
|
925 GLenum colorBufferSource = aColorBufferDefined ? LOCAL_GL_COLOR_ATTACHMENT0 : LOCAL_GL_NONE; |
|
926 aGL->fDrawBuffer(colorBufferSource); |
|
927 aGL->fReadBuffer(colorBufferSource); |
|
928 } |
|
929 |
|
930 void |
|
931 WebGLFramebuffer::FinalizeAttachments() const |
|
932 { |
|
933 GLContext* gl = mContext->gl; |
|
934 |
|
935 size_t count = ColorAttachmentCount(); |
|
936 for (size_t i = 0; i < count; i++) { |
|
937 ColorAttachment(i).FinalizeAttachment(gl, LOCAL_GL_COLOR_ATTACHMENT0 + i); |
|
938 } |
|
939 |
|
940 DepthAttachment().FinalizeAttachment(gl, LOCAL_GL_DEPTH_ATTACHMENT); |
|
941 StencilAttachment().FinalizeAttachment(gl, LOCAL_GL_STENCIL_ATTACHMENT); |
|
942 DepthStencilAttachment().FinalizeAttachment(gl, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT); |
|
943 |
|
944 FinalizeDrawAndReadBuffers(gl, ColorAttachment(0).IsDefined()); |
|
945 } |
|
946 |
|
947 inline void |
|
948 ImplCycleCollectionUnlink(mozilla::WebGLFramebuffer::Attachment& aField) |
|
949 { |
|
950 aField.mTexturePtr = nullptr; |
|
951 aField.mRenderbufferPtr = nullptr; |
|
952 } |
|
953 |
|
954 inline void |
|
955 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, |
|
956 mozilla::WebGLFramebuffer::Attachment& aField, |
|
957 const char* aName, |
|
958 uint32_t aFlags = 0) |
|
959 { |
|
960 CycleCollectionNoteChild(aCallback, aField.mTexturePtr.get(), |
|
961 aName, aFlags); |
|
962 |
|
963 CycleCollectionNoteChild(aCallback, aField.mRenderbufferPtr.get(), |
|
964 aName, aFlags); |
|
965 } |
|
966 |
|
967 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_4(WebGLFramebuffer, |
|
968 mColorAttachments, |
|
969 mDepthAttachment, |
|
970 mStencilAttachment, |
|
971 mDepthStencilAttachment) |
|
972 |
|
973 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLFramebuffer, AddRef) |
|
974 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLFramebuffer, Release) |