WebGL2ContextFramebuffers.cpp (7733B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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 "GLContext.h" 7 #include "GLScreenBuffer.h" 8 #include "WebGL2Context.h" 9 #include "WebGLContextUtils.h" 10 #include "WebGLFormats.h" 11 #include "WebGLFramebuffer.h" 12 #include "mozilla/CheckedInt.h" 13 14 namespace mozilla { 15 16 void WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, 17 GLint srcY1, GLint dstX0, GLint dstY0, 18 GLint dstX1, GLint dstY1, GLbitfield mask, 19 GLenum filter) { 20 const FuncScope funcScope(*this, "blitFramebuffer"); 21 if (IsContextLost()) return; 22 23 const GLbitfield validBits = LOCAL_GL_COLOR_BUFFER_BIT | 24 LOCAL_GL_DEPTH_BUFFER_BIT | 25 LOCAL_GL_STENCIL_BUFFER_BIT; 26 if ((mask | validBits) != validBits) { 27 ErrorInvalidValue("Invalid bit set in mask."); 28 return; 29 } 30 31 switch (filter) { 32 case LOCAL_GL_NEAREST: 33 case LOCAL_GL_LINEAR: 34 break; 35 default: 36 ErrorInvalidEnumInfo("filter", filter); 37 return; 38 } 39 40 // -- 41 42 const auto fnLikelyOverflow = [](GLint p0, GLint p1) { 43 auto checked = CheckedInt<GLint>(p1) - p0; 44 checked = -checked; // And check the negation! 45 return !checked.isValid(); 46 }; 47 48 if (fnLikelyOverflow(srcX0, srcX1) || fnLikelyOverflow(srcY0, srcY1) || 49 fnLikelyOverflow(dstX0, dstX1) || fnLikelyOverflow(dstY0, dstY1)) { 50 ErrorInvalidValue("Likely-to-overflow large ranges are forbidden."); 51 return; 52 } 53 54 // -- 55 56 if (!ValidateAndInitFB(mBoundReadFramebuffer) || 57 !ValidateAndInitFB(mBoundDrawFramebuffer)) { 58 return; 59 } 60 61 DoBindFB(mBoundReadFramebuffer, LOCAL_GL_READ_FRAMEBUFFER); 62 DoBindFB(mBoundDrawFramebuffer, LOCAL_GL_DRAW_FRAMEBUFFER); 63 64 WebGLFramebuffer::BlitFramebuffer(this, srcX0, srcY0, srcX1, srcY1, dstX0, 65 dstY0, dstX1, dstY1, mask, filter); 66 } 67 68 //// 69 70 static bool ValidateBackbufferAttachmentEnum(WebGLContext* webgl, 71 GLenum attachment) { 72 switch (attachment) { 73 case LOCAL_GL_COLOR: 74 case LOCAL_GL_DEPTH: 75 case LOCAL_GL_STENCIL: 76 return true; 77 78 default: 79 webgl->ErrorInvalidEnumInfo("attachment", attachment); 80 return false; 81 } 82 } 83 84 static bool ValidateFramebufferAttachmentEnum(WebGLContext* webgl, 85 GLenum attachment) { 86 switch (attachment) { 87 case LOCAL_GL_DEPTH_ATTACHMENT: 88 case LOCAL_GL_STENCIL_ATTACHMENT: 89 case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: 90 return true; 91 } 92 93 if (attachment < LOCAL_GL_COLOR_ATTACHMENT0) { 94 webgl->ErrorInvalidEnumInfo("attachment", attachment); 95 return false; 96 } 97 98 if (attachment > webgl->LastColorAttachmentEnum()) { 99 // That these errors have different types is ridiculous. 100 webgl->ErrorInvalidOperation("Too-large LOCAL_GL_COLOR_ATTACHMENTn."); 101 return false; 102 } 103 104 return true; 105 } 106 107 bool WebGLContext::ValidateInvalidateFramebuffer( 108 GLenum target, const Span<const GLenum>& attachments, 109 std::vector<GLenum>* const scopedVector, 110 GLsizei* const out_glNumAttachments, 111 const GLenum** const out_glAttachments) { 112 if (IsContextLost()) return false; 113 114 if (!ValidateFramebufferTarget(target)) return false; 115 116 const WebGLFramebuffer* fb; 117 bool isDefaultFB = false; 118 switch (target) { 119 case LOCAL_GL_FRAMEBUFFER: 120 case LOCAL_GL_DRAW_FRAMEBUFFER: 121 fb = mBoundDrawFramebuffer; 122 break; 123 124 case LOCAL_GL_READ_FRAMEBUFFER: 125 fb = mBoundReadFramebuffer; 126 break; 127 128 default: 129 MOZ_CRASH("GFX: Bad target."); 130 } 131 132 if (fb) { 133 const auto fbStatus = fb->CheckFramebufferStatus(); 134 if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE) 135 return false; // Not an error, but don't run forward to driver either. 136 } else { 137 if (!EnsureDefaultFB()) return false; 138 } 139 DoBindFB(fb, target); 140 141 *out_glNumAttachments = AutoAssertCast(attachments.size()); 142 *out_glAttachments = attachments.data(); 143 144 if (fb) { 145 for (const auto& attachment : attachments) { 146 if (!ValidateFramebufferAttachmentEnum(this, attachment)) return false; 147 } 148 } else { 149 for (const auto& attachment : attachments) { 150 if (!ValidateBackbufferAttachmentEnum(this, attachment)) return false; 151 } 152 153 if (!isDefaultFB) { 154 MOZ_ASSERT(scopedVector->empty()); 155 scopedVector->reserve(attachments.size()); 156 for (const auto& attachment : attachments) { 157 switch (attachment) { 158 case LOCAL_GL_COLOR: 159 scopedVector->push_back(LOCAL_GL_COLOR_ATTACHMENT0); 160 break; 161 162 case LOCAL_GL_DEPTH: 163 scopedVector->push_back(LOCAL_GL_DEPTH_ATTACHMENT); 164 break; 165 166 case LOCAL_GL_STENCIL: 167 scopedVector->push_back(LOCAL_GL_STENCIL_ATTACHMENT); 168 break; 169 170 default: 171 MOZ_CRASH(); 172 } 173 } 174 *out_glNumAttachments = AutoAssertCast(scopedVector->size()); 175 *out_glAttachments = scopedVector->data(); 176 } 177 } 178 179 //// 180 181 return true; 182 } 183 184 void WebGL2Context::InvalidateFramebuffer( 185 GLenum target, const Span<const GLenum>& attachments) { 186 const FuncScope funcScope(*this, "invalidateFramebuffer"); 187 188 std::vector<GLenum> scopedVector; 189 GLsizei glNumAttachments; 190 const GLenum* glAttachments; 191 if (!ValidateInvalidateFramebuffer(target, attachments, &scopedVector, 192 &glNumAttachments, &glAttachments)) { 193 return; 194 } 195 196 //// 197 198 // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer. 199 const bool useFBInvalidation = 200 (mAllowFBInvalidation && 201 gl->IsSupported(gl::GLFeature::invalidate_framebuffer)); 202 if (useFBInvalidation) { 203 gl->fInvalidateFramebuffer(target, glNumAttachments, glAttachments); 204 return; 205 } 206 207 // Use clear instead? 208 // No-op for now. 209 } 210 211 void WebGL2Context::InvalidateSubFramebuffer( 212 GLenum target, const Span<const GLenum>& attachments, GLint x, GLint y, 213 GLsizei width, GLsizei height) { 214 const FuncScope funcScope(*this, "invalidateSubFramebuffer"); 215 216 std::vector<GLenum> scopedVector; 217 GLsizei glNumAttachments; 218 const GLenum* glAttachments; 219 if (!ValidateInvalidateFramebuffer(target, attachments, &scopedVector, 220 &glNumAttachments, &glAttachments)) { 221 return; 222 } 223 224 if (!ValidateNonNegative("width", width) || 225 !ValidateNonNegative("height", height)) { 226 return; 227 } 228 229 //// 230 231 // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer. 232 const bool useFBInvalidation = 233 (mAllowFBInvalidation && 234 gl->IsSupported(gl::GLFeature::invalidate_framebuffer)); 235 if (useFBInvalidation) { 236 gl->fInvalidateSubFramebuffer(target, glNumAttachments, glAttachments, x, y, 237 width, height); 238 return; 239 } 240 241 // Use clear instead? 242 // No-op for now. 243 } 244 245 void WebGL2Context::ReadBuffer(GLenum mode) { 246 const FuncScope funcScope(*this, "readBuffer"); 247 if (IsContextLost()) return; 248 249 if (mBoundReadFramebuffer) { 250 mBoundReadFramebuffer->ReadBuffer(mode); 251 return; 252 } 253 254 // Operating on the default framebuffer. 255 if (mode != LOCAL_GL_NONE && mode != LOCAL_GL_BACK) { 256 nsCString enumName; 257 EnumName(mode, &enumName); 258 ErrorInvalidOperation( 259 "If READ_FRAMEBUFFER is null, `mode` must be BACK or" 260 " NONE. Was %s.", 261 enumName.BeginReading()); 262 return; 263 } 264 265 mDefaultFB_ReadBuffer = mode; 266 } 267 268 } // namespace mozilla