1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/canvas/src/WebGLContextDraw.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,731 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "WebGLContext.h" 1.10 + 1.11 +#include "GLContext.h" 1.12 +#include "mozilla/CheckedInt.h" 1.13 +#include "WebGLBuffer.h" 1.14 +#include "WebGLContextUtils.h" 1.15 +#include "WebGLFramebuffer.h" 1.16 +#include "WebGLProgram.h" 1.17 +#include "WebGLRenderbuffer.h" 1.18 +#include "WebGLShader.h" 1.19 +#include "WebGLTexture.h" 1.20 +#include "WebGLUniformInfo.h" 1.21 +#include "WebGLVertexArray.h" 1.22 +#include "WebGLVertexAttribData.h" 1.23 + 1.24 +using namespace mozilla; 1.25 +using namespace mozilla::dom; 1.26 +using namespace mozilla::gl; 1.27 + 1.28 +// For a Tegra workaround. 1.29 +static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100; 1.30 + 1.31 +bool 1.32 +WebGLContext::DrawInstanced_check(const char* info) 1.33 +{ 1.34 + // This restriction was removed in GLES3, so WebGL2 shouldn't have it. 1.35 + if (!IsWebGL2() && 1.36 + IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays) && 1.37 + !mBufferFetchingHasPerVertex) 1.38 + { 1.39 + /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt 1.40 + * If all of the enabled vertex attribute arrays that are bound to active 1.41 + * generic attributes in the program have a non-zero divisor, the draw 1.42 + * call should return INVALID_OPERATION. 1.43 + * 1.44 + * NB: This also appears to apply to NV_instanced_arrays, though the 1.45 + * INVALID_OPERATION emission is not explicitly stated. 1.46 + * ARB_instanced_arrays does not have this restriction. 1.47 + */ 1.48 + ErrorInvalidOperation("%s: at least one vertex attribute divisor should be 0", info); 1.49 + return false; 1.50 + } 1.51 + 1.52 + return true; 1.53 +} 1.54 + 1.55 +bool WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcount, const char* info) 1.56 +{ 1.57 + if (first < 0 || count < 0) { 1.58 + ErrorInvalidValue("%s: negative first or count", info); 1.59 + return false; 1.60 + } 1.61 + 1.62 + if (primcount < 0) { 1.63 + ErrorInvalidValue("%s: negative primcount", info); 1.64 + return false; 1.65 + } 1.66 + 1.67 + if (!ValidateStencilParamsForDrawCall()) { 1.68 + return false; 1.69 + } 1.70 + 1.71 + // If count is 0, there's nothing to do. 1.72 + if (count == 0 || primcount == 0) { 1.73 + return false; 1.74 + } 1.75 + 1.76 + // Any checks below this depend on a program being available. 1.77 + if (!mCurrentProgram) { 1.78 + ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info); 1.79 + return false; 1.80 + } 1.81 + 1.82 + if (!ValidateBufferFetching(info)) { 1.83 + return false; 1.84 + } 1.85 + 1.86 + CheckedInt<GLsizei> checked_firstPlusCount = CheckedInt<GLsizei>(first) + count; 1.87 + 1.88 + if (!checked_firstPlusCount.isValid()) { 1.89 + ErrorInvalidOperation("%s: overflow in first+count", info); 1.90 + return false; 1.91 + } 1.92 + 1.93 + if (uint32_t(checked_firstPlusCount.value()) > mMaxFetchedVertices) { 1.94 + ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient size for given first and count", info); 1.95 + return false; 1.96 + } 1.97 + 1.98 + if (uint32_t(primcount) > mMaxFetchedInstances) { 1.99 + ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info); 1.100 + return false; 1.101 + } 1.102 + 1.103 + MakeContextCurrent(); 1.104 + 1.105 + if (mBoundFramebuffer) { 1.106 + if (!mBoundFramebuffer->CheckAndInitializeAttachments()) { 1.107 + ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info); 1.108 + return false; 1.109 + } 1.110 + } else { 1.111 + ClearBackbufferIfNeeded(); 1.112 + } 1.113 + 1.114 + if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) { 1.115 + return false; 1.116 + } 1.117 + BindFakeBlackTextures(); 1.118 + 1.119 + return true; 1.120 +} 1.121 + 1.122 +void 1.123 +WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count) 1.124 +{ 1.125 + if (IsContextLost()) 1.126 + return; 1.127 + 1.128 + if (!ValidateDrawModeEnum(mode, "drawArrays: mode")) 1.129 + return; 1.130 + 1.131 + if (!DrawArrays_check(first, count, 1, "drawArrays")) 1.132 + return; 1.133 + 1.134 + SetupContextLossTimer(); 1.135 + gl->fDrawArrays(mode, first, count); 1.136 + 1.137 + Draw_cleanup(); 1.138 +} 1.139 + 1.140 +void 1.141 +WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount) 1.142 +{ 1.143 + if (IsContextLost()) 1.144 + return; 1.145 + 1.146 + if (!ValidateDrawModeEnum(mode, "drawArraysInstanced: mode")) 1.147 + return; 1.148 + 1.149 + if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced")) 1.150 + return; 1.151 + 1.152 + if (!DrawInstanced_check("drawArraysInstanced")) 1.153 + return; 1.154 + 1.155 + SetupContextLossTimer(); 1.156 + gl->fDrawArraysInstanced(mode, first, count, primcount); 1.157 + 1.158 + Draw_cleanup(); 1.159 +} 1.160 + 1.161 +bool 1.162 +WebGLContext::DrawElements_check(GLsizei count, GLenum type, 1.163 + WebGLintptr byteOffset, GLsizei primcount, 1.164 + const char* info, GLuint* out_upperBound) 1.165 +{ 1.166 + if (count < 0 || byteOffset < 0) { 1.167 + ErrorInvalidValue("%s: negative count or offset", info); 1.168 + return false; 1.169 + } 1.170 + 1.171 + if (primcount < 0) { 1.172 + ErrorInvalidValue("%s: negative primcount", info); 1.173 + return false; 1.174 + } 1.175 + 1.176 + if (!ValidateStencilParamsForDrawCall()) { 1.177 + return false; 1.178 + } 1.179 + 1.180 + // If count is 0, there's nothing to do. 1.181 + if (count == 0 || primcount == 0) { 1.182 + return false; 1.183 + } 1.184 + 1.185 + CheckedUint32 checked_byteCount; 1.186 + 1.187 + GLsizei first = 0; 1.188 + 1.189 + if (type == LOCAL_GL_UNSIGNED_SHORT) { 1.190 + checked_byteCount = 2 * CheckedUint32(count); 1.191 + if (byteOffset % 2 != 0) { 1.192 + ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)", info); 1.193 + return false; 1.194 + } 1.195 + first = byteOffset / 2; 1.196 + } 1.197 + else if (type == LOCAL_GL_UNSIGNED_BYTE) { 1.198 + checked_byteCount = count; 1.199 + first = byteOffset; 1.200 + } 1.201 + else if (type == LOCAL_GL_UNSIGNED_INT && IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) { 1.202 + checked_byteCount = 4 * CheckedUint32(count); 1.203 + if (byteOffset % 4 != 0) { 1.204 + ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_INT (must be a multiple of 4)", info); 1.205 + return false; 1.206 + } 1.207 + first = byteOffset / 4; 1.208 + } 1.209 + else { 1.210 + ErrorInvalidEnum("%s: type must be UNSIGNED_SHORT or UNSIGNED_BYTE", info); 1.211 + return false; 1.212 + } 1.213 + 1.214 + if (!checked_byteCount.isValid()) { 1.215 + ErrorInvalidValue("%s: overflow in byteCount", info); 1.216 + return false; 1.217 + } 1.218 + 1.219 + // Any checks below this depend on a program being available. 1.220 + if (!mCurrentProgram) { 1.221 + ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info); 1.222 + return false; 1.223 + } 1.224 + 1.225 + if (!mBoundVertexArray->mBoundElementArrayBuffer) { 1.226 + ErrorInvalidOperation("%s: must have element array buffer binding", info); 1.227 + return false; 1.228 + } 1.229 + 1.230 + WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mBoundElementArrayBuffer; 1.231 + 1.232 + if (!elemArrayBuffer.ByteLength()) { 1.233 + ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info); 1.234 + return false; 1.235 + } 1.236 + 1.237 + CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset; 1.238 + 1.239 + if (!checked_neededByteCount.isValid()) { 1.240 + ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info); 1.241 + return false; 1.242 + } 1.243 + 1.244 + if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) { 1.245 + ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info); 1.246 + return false; 1.247 + } 1.248 + 1.249 + if (!ValidateBufferFetching(info)) 1.250 + return false; 1.251 + 1.252 + if (!mMaxFetchedVertices || 1.253 + !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound)) 1.254 + { 1.255 + ErrorInvalidOperation( 1.256 + "%s: bound vertex attribute buffers do not have sufficient " 1.257 + "size for given indices from the bound element array", info); 1.258 + return false; 1.259 + } 1.260 + 1.261 + if (uint32_t(primcount) > mMaxFetchedInstances) { 1.262 + ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info); 1.263 + return false; 1.264 + } 1.265 + 1.266 + MakeContextCurrent(); 1.267 + 1.268 + if (mBoundFramebuffer) { 1.269 + if (!mBoundFramebuffer->CheckAndInitializeAttachments()) { 1.270 + ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info); 1.271 + return false; 1.272 + } 1.273 + } else { 1.274 + ClearBackbufferIfNeeded(); 1.275 + } 1.276 + 1.277 + if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) { 1.278 + return false; 1.279 + } 1.280 + BindFakeBlackTextures(); 1.281 + 1.282 + return true; 1.283 +} 1.284 + 1.285 +void 1.286 +WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type, 1.287 + WebGLintptr byteOffset) 1.288 +{ 1.289 + if (IsContextLost()) 1.290 + return; 1.291 + 1.292 + if (!ValidateDrawModeEnum(mode, "drawElements: mode")) 1.293 + return; 1.294 + 1.295 + GLuint upperBound = UINT_MAX; 1.296 + if (!DrawElements_check(count, type, byteOffset, 1, "drawElements", 1.297 + &upperBound)) 1.298 + { 1.299 + return; 1.300 + } 1.301 + 1.302 + SetupContextLossTimer(); 1.303 + 1.304 + if (gl->IsSupported(gl::GLFeature::draw_range_elements)) { 1.305 + gl->fDrawRangeElements(mode, 0, upperBound, 1.306 + count, type, reinterpret_cast<GLvoid*>(byteOffset)); 1.307 + } else { 1.308 + gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset)); 1.309 + } 1.310 + 1.311 + Draw_cleanup(); 1.312 +} 1.313 + 1.314 +void 1.315 +WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, 1.316 + WebGLintptr byteOffset, GLsizei primcount) 1.317 +{ 1.318 + if (IsContextLost()) 1.319 + return; 1.320 + 1.321 + if (!ValidateDrawModeEnum(mode, "drawElementsInstanced: mode")) 1.322 + return; 1.323 + 1.324 + if (!DrawElements_check(count, type, byteOffset, primcount, "drawElementsInstanced")) 1.325 + return; 1.326 + 1.327 + if (!DrawInstanced_check("drawElementsInstanced")) 1.328 + return; 1.329 + 1.330 + SetupContextLossTimer(); 1.331 + gl->fDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset), primcount); 1.332 + 1.333 + Draw_cleanup(); 1.334 +} 1.335 + 1.336 +void WebGLContext::Draw_cleanup() 1.337 +{ 1.338 + UndoFakeVertexAttrib0(); 1.339 + UnbindFakeBlackTextures(); 1.340 + 1.341 + if (!mBoundFramebuffer) { 1.342 + Invalidate(); 1.343 + mShouldPresent = true; 1.344 + MOZ_ASSERT(!mBackbufferNeedsClear); 1.345 + } 1.346 + 1.347 + if (gl->WorkAroundDriverBugs()) { 1.348 + if (gl->Renderer() == gl::GLRenderer::Tegra) { 1.349 + mDrawCallsSinceLastFlush++; 1.350 + 1.351 + if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) { 1.352 + gl->fFlush(); 1.353 + mDrawCallsSinceLastFlush = 0; 1.354 + } 1.355 + } 1.356 + } 1.357 + 1.358 + // Let's check the viewport 1.359 + const WebGLRectangleObject* rect = CurValidFBRectObject(); 1.360 + if (rect) { 1.361 + if (mViewportWidth > rect->Width() || 1.362 + mViewportHeight > rect->Height()) 1.363 + { 1.364 + if (!mAlreadyWarnedAboutViewportLargerThanDest) { 1.365 + GenerateWarning("Drawing to a destination rect smaller than the viewport rect. " 1.366 + "(This warning will only be given once)"); 1.367 + mAlreadyWarnedAboutViewportLargerThanDest = true; 1.368 + } 1.369 + } 1.370 + } 1.371 +} 1.372 + 1.373 +/* 1.374 + * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount) 1.375 + * that will be legal to be read from bound VBOs. 1.376 + */ 1.377 + 1.378 +bool 1.379 +WebGLContext::ValidateBufferFetching(const char *info) 1.380 +{ 1.381 +#ifdef DEBUG 1.382 + GLint currentProgram = 0; 1.383 + MakeContextCurrent(); 1.384 + gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, ¤tProgram); 1.385 + MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->GLName(), 1.386 + "WebGL: current program doesn't agree with GL state"); 1.387 +#endif 1.388 + 1.389 + if (mBufferFetchingIsVerified) { 1.390 + return true; 1.391 + } 1.392 + 1.393 + bool hasPerVertex = false; 1.394 + uint32_t maxVertices = UINT32_MAX; 1.395 + uint32_t maxInstances = UINT32_MAX; 1.396 + uint32_t attribs = mBoundVertexArray->mAttribs.Length(); 1.397 + 1.398 + for (uint32_t i = 0; i < attribs; ++i) { 1.399 + const WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[i]; 1.400 + 1.401 + // If the attrib array isn't enabled, there's nothing to check; 1.402 + // it's a static value. 1.403 + if (!vd.enabled) 1.404 + continue; 1.405 + 1.406 + if (vd.buf == nullptr) { 1.407 + ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i); 1.408 + return false; 1.409 + } 1.410 + 1.411 + // If the attrib is not in use, then we don't have to validate 1.412 + // it, just need to make sure that the binding is non-null. 1.413 + if (!mCurrentProgram->IsAttribInUse(i)) 1.414 + continue; 1.415 + 1.416 + // the base offset 1.417 + CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset; 1.418 + CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size; 1.419 + 1.420 + if (!checked_byteLength.isValid() || 1.421 + !checked_sizeOfLastElement.isValid()) 1.422 + { 1.423 + ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i); 1.424 + return false; 1.425 + } 1.426 + 1.427 + if (checked_byteLength.value() < checked_sizeOfLastElement.value()) { 1.428 + maxVertices = 0; 1.429 + maxInstances = 0; 1.430 + break; 1.431 + } 1.432 + 1.433 + CheckedUint32 checked_maxAllowedCount = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1; 1.434 + 1.435 + if (!checked_maxAllowedCount.isValid()) { 1.436 + ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i); 1.437 + return false; 1.438 + } 1.439 + 1.440 + if (vd.divisor == 0) { 1.441 + maxVertices = std::min(maxVertices, checked_maxAllowedCount.value()); 1.442 + hasPerVertex = true; 1.443 + } else { 1.444 + maxInstances = std::min(maxInstances, checked_maxAllowedCount.value() / vd.divisor); 1.445 + } 1.446 + } 1.447 + 1.448 + mBufferFetchingIsVerified = true; 1.449 + mBufferFetchingHasPerVertex = hasPerVertex; 1.450 + mMaxFetchedVertices = maxVertices; 1.451 + mMaxFetchedInstances = maxInstances; 1.452 + 1.453 + return true; 1.454 +} 1.455 + 1.456 +WebGLVertexAttrib0Status 1.457 +WebGLContext::WhatDoesVertexAttrib0Need() 1.458 +{ 1.459 + MOZ_ASSERT(mCurrentProgram); 1.460 + 1.461 + // work around Mac OSX crash, see bug 631420 1.462 +#ifdef XP_MACOSX 1.463 + if (gl->WorkAroundDriverBugs() && 1.464 + mBoundVertexArray->IsAttribArrayEnabled(0) && 1.465 + !mCurrentProgram->IsAttribInUse(0)) 1.466 + { 1.467 + return WebGLVertexAttrib0Status::EmulatedUninitializedArray; 1.468 + } 1.469 +#endif 1.470 + 1.471 + if (MOZ_LIKELY(gl->IsGLES() || 1.472 + mBoundVertexArray->IsAttribArrayEnabled(0))) 1.473 + { 1.474 + return WebGLVertexAttrib0Status::Default; 1.475 + } 1.476 + 1.477 + return mCurrentProgram->IsAttribInUse(0) 1.478 + ? WebGLVertexAttrib0Status::EmulatedInitializedArray 1.479 + : WebGLVertexAttrib0Status::EmulatedUninitializedArray; 1.480 +} 1.481 + 1.482 +bool 1.483 +WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount) 1.484 +{ 1.485 + WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need(); 1.486 + 1.487 + if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default)) 1.488 + return true; 1.489 + 1.490 + if (!mAlreadyWarnedAboutFakeVertexAttrib0) { 1.491 + GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser " 1.492 + "to do expensive emulation work when running on desktop OpenGL " 1.493 + "platforms, for example on Mac. It is preferable to always draw " 1.494 + "with vertex attrib 0 array enabled, by using bindAttribLocation " 1.495 + "to bind some always-used attribute to location 0."); 1.496 + mAlreadyWarnedAboutFakeVertexAttrib0 = true; 1.497 + } 1.498 + 1.499 + CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(GLfloat); 1.500 + 1.501 + if (!checked_dataSize.isValid()) { 1.502 + ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation " 1.503 + "with %d vertices. Try reducing the number of vertices.", vertexCount); 1.504 + return false; 1.505 + } 1.506 + 1.507 + GLuint dataSize = checked_dataSize.value(); 1.508 + 1.509 + if (!mFakeVertexAttrib0BufferObject) { 1.510 + gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject); 1.511 + } 1.512 + 1.513 + // if the VBO status is already exactly what we need, or if the only difference is that it's initialized and 1.514 + // we don't need it to be, then consider it OK 1.515 + bool vertexAttrib0BufferStatusOK = 1.516 + mFakeVertexAttrib0BufferStatus == whatDoesAttrib0Need || 1.517 + (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray && 1.518 + whatDoesAttrib0Need == WebGLVertexAttrib0Status::EmulatedUninitializedArray); 1.519 + 1.520 + if (!vertexAttrib0BufferStatusOK || 1.521 + mFakeVertexAttrib0BufferObjectSize < dataSize || 1.522 + mFakeVertexAttrib0BufferObjectVector[0] != mVertexAttrib0Vector[0] || 1.523 + mFakeVertexAttrib0BufferObjectVector[1] != mVertexAttrib0Vector[1] || 1.524 + mFakeVertexAttrib0BufferObjectVector[2] != mVertexAttrib0Vector[2] || 1.525 + mFakeVertexAttrib0BufferObjectVector[3] != mVertexAttrib0Vector[3]) 1.526 + { 1.527 + mFakeVertexAttrib0BufferStatus = whatDoesAttrib0Need; 1.528 + mFakeVertexAttrib0BufferObjectSize = dataSize; 1.529 + mFakeVertexAttrib0BufferObjectVector[0] = mVertexAttrib0Vector[0]; 1.530 + mFakeVertexAttrib0BufferObjectVector[1] = mVertexAttrib0Vector[1]; 1.531 + mFakeVertexAttrib0BufferObjectVector[2] = mVertexAttrib0Vector[2]; 1.532 + mFakeVertexAttrib0BufferObjectVector[3] = mVertexAttrib0Vector[3]; 1.533 + 1.534 + gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject); 1.535 + 1.536 + GetAndFlushUnderlyingGLErrors(); 1.537 + 1.538 + if (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray) { 1.539 + nsAutoArrayPtr<GLfloat> array(new GLfloat[4 * vertexCount]); 1.540 + for(size_t i = 0; i < vertexCount; ++i) { 1.541 + array[4 * i + 0] = mVertexAttrib0Vector[0]; 1.542 + array[4 * i + 1] = mVertexAttrib0Vector[1]; 1.543 + array[4 * i + 2] = mVertexAttrib0Vector[2]; 1.544 + array[4 * i + 3] = mVertexAttrib0Vector[3]; 1.545 + } 1.546 + gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array, LOCAL_GL_DYNAMIC_DRAW); 1.547 + } else { 1.548 + gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW); 1.549 + } 1.550 + GLenum error = GetAndFlushUnderlyingGLErrors(); 1.551 + 1.552 + gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0); 1.553 + 1.554 + // note that we do this error checking and early return AFTER having restored the buffer binding above 1.555 + if (error) { 1.556 + ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation " 1.557 + "with %d vertices. Try reducing the number of vertices.", vertexCount); 1.558 + return false; 1.559 + } 1.560 + } 1.561 + 1.562 + gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject); 1.563 + gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0); 1.564 + 1.565 + return true; 1.566 +} 1.567 + 1.568 +void 1.569 +WebGLContext::UndoFakeVertexAttrib0() 1.570 +{ 1.571 + WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need(); 1.572 + 1.573 + if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default)) 1.574 + return; 1.575 + 1.576 + if (mBoundVertexArray->HasAttrib(0) && mBoundVertexArray->mAttribs[0].buf) { 1.577 + const WebGLVertexAttribData& attrib0 = mBoundVertexArray->mAttribs[0]; 1.578 + gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.buf->GLName()); 1.579 + gl->fVertexAttribPointer(0, 1.580 + attrib0.size, 1.581 + attrib0.type, 1.582 + attrib0.normalized, 1.583 + attrib0.stride, 1.584 + reinterpret_cast<const GLvoid *>(attrib0.byteOffset)); 1.585 + } else { 1.586 + gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0); 1.587 + } 1.588 + 1.589 + gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0); 1.590 +} 1.591 + 1.592 +WebGLContextFakeBlackStatus 1.593 +WebGLContext::ResolvedFakeBlackStatus() 1.594 +{ 1.595 + // handle this case first, it's the generic case 1.596 + if (MOZ_LIKELY(mFakeBlackStatus == WebGLContextFakeBlackStatus::NotNeeded)) 1.597 + return mFakeBlackStatus; 1.598 + 1.599 + if (mFakeBlackStatus == WebGLContextFakeBlackStatus::Needed) 1.600 + return mFakeBlackStatus; 1.601 + 1.602 + for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) { 1.603 + if ((mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) || 1.604 + (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded)) 1.605 + { 1.606 + mFakeBlackStatus = WebGLContextFakeBlackStatus::Needed; 1.607 + return mFakeBlackStatus; 1.608 + } 1.609 + } 1.610 + 1.611 + // we have exhausted all cases where we do need fakeblack, so if the status is still unknown, 1.612 + // that means that we do NOT need it. 1.613 + mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded; 1.614 + return mFakeBlackStatus; 1.615 +} 1.616 + 1.617 +void 1.618 +WebGLContext::BindFakeBlackTexturesHelper( 1.619 + GLenum target, 1.620 + const nsTArray<WebGLRefPtr<WebGLTexture> > & boundTexturesArray, 1.621 + ScopedDeletePtr<FakeBlackTexture> & opaqueTextureScopedPtr, 1.622 + ScopedDeletePtr<FakeBlackTexture> & transparentTextureScopedPtr) 1.623 +{ 1.624 + for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) { 1.625 + if (!boundTexturesArray[i]) { 1.626 + continue; 1.627 + } 1.628 + 1.629 + WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus(); 1.630 + MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown); 1.631 + 1.632 + if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) { 1.633 + continue; 1.634 + } 1.635 + 1.636 + bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData && 1.637 + FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().WebGLFormat()); 1.638 + ScopedDeletePtr<FakeBlackTexture>& 1.639 + blackTexturePtr = alpha 1.640 + ? transparentTextureScopedPtr 1.641 + : opaqueTextureScopedPtr; 1.642 + 1.643 + if (!blackTexturePtr) { 1.644 + GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB; 1.645 + blackTexturePtr 1.646 + = new FakeBlackTexture(gl, target, format); 1.647 + } 1.648 + 1.649 + gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i); 1.650 + gl->fBindTexture(target, 1.651 + blackTexturePtr->GLName()); 1.652 + } 1.653 +} 1.654 + 1.655 +void 1.656 +WebGLContext::BindFakeBlackTextures() 1.657 +{ 1.658 + // this is the generic case: try to return early 1.659 + if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded)) 1.660 + return; 1.661 + 1.662 + BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_2D, 1.663 + mBound2DTextures, 1.664 + mBlackOpaqueTexture2D, 1.665 + mBlackTransparentTexture2D); 1.666 + BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_CUBE_MAP, 1.667 + mBoundCubeMapTextures, 1.668 + mBlackOpaqueTextureCubeMap, 1.669 + mBlackTransparentTextureCubeMap); 1.670 +} 1.671 + 1.672 +void 1.673 +WebGLContext::UnbindFakeBlackTextures() 1.674 +{ 1.675 + // this is the generic case: try to return early 1.676 + if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded)) 1.677 + return; 1.678 + 1.679 + for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) { 1.680 + if (mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) { 1.681 + gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i); 1.682 + gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBound2DTextures[i]->GLName()); 1.683 + } 1.684 + if (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) { 1.685 + gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i); 1.686 + gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->GLName()); 1.687 + } 1.688 + } 1.689 + 1.690 + gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture); 1.691 +} 1.692 + 1.693 +WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext *gl, GLenum target, GLenum format) 1.694 + : mGL(gl) 1.695 + , mGLName(0) 1.696 +{ 1.697 + MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D || target == LOCAL_GL_TEXTURE_CUBE_MAP); 1.698 + MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA); 1.699 + 1.700 + mGL->MakeCurrent(); 1.701 + GLuint formerBinding = 0; 1.702 + gl->GetUIntegerv(target == LOCAL_GL_TEXTURE_2D 1.703 + ? LOCAL_GL_TEXTURE_BINDING_2D 1.704 + : LOCAL_GL_TEXTURE_BINDING_CUBE_MAP, 1.705 + &formerBinding); 1.706 + gl->fGenTextures(1, &mGLName); 1.707 + gl->fBindTexture(target, mGLName); 1.708 + 1.709 + // we allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) 1.710 + // to minimize the risk of running into a driver bug in texImage2D, as it is 1.711 + // a bit unusual maybe to create 1x1 textures, and the stack may not have the alignment 1.712 + // that texImage2D expects. 1.713 + void* zeros = calloc(1, 16); 1.714 + if (target == LOCAL_GL_TEXTURE_2D) { 1.715 + gl->fTexImage2D(target, 0, format, 1, 1, 1.716 + 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros); 1.717 + } else { 1.718 + for (GLuint i = 0; i < 6; ++i) { 1.719 + gl->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, 1, 1, 1.720 + 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros); 1.721 + } 1.722 + } 1.723 + free(zeros); 1.724 + 1.725 + gl->fBindTexture(target, formerBinding); 1.726 +} 1.727 + 1.728 +WebGLContext::FakeBlackTexture::~FakeBlackTexture() 1.729 +{ 1.730 + if (mGL) { 1.731 + mGL->MakeCurrent(); 1.732 + mGL->fDeleteTextures(1, &mGLName); 1.733 + } 1.734 +}