content/canvas/src/WebGLContextDraw.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "WebGLContext.h"
michael@0 7
michael@0 8 #include "GLContext.h"
michael@0 9 #include "mozilla/CheckedInt.h"
michael@0 10 #include "WebGLBuffer.h"
michael@0 11 #include "WebGLContextUtils.h"
michael@0 12 #include "WebGLFramebuffer.h"
michael@0 13 #include "WebGLProgram.h"
michael@0 14 #include "WebGLRenderbuffer.h"
michael@0 15 #include "WebGLShader.h"
michael@0 16 #include "WebGLTexture.h"
michael@0 17 #include "WebGLUniformInfo.h"
michael@0 18 #include "WebGLVertexArray.h"
michael@0 19 #include "WebGLVertexAttribData.h"
michael@0 20
michael@0 21 using namespace mozilla;
michael@0 22 using namespace mozilla::dom;
michael@0 23 using namespace mozilla::gl;
michael@0 24
michael@0 25 // For a Tegra workaround.
michael@0 26 static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
michael@0 27
michael@0 28 bool
michael@0 29 WebGLContext::DrawInstanced_check(const char* info)
michael@0 30 {
michael@0 31 // This restriction was removed in GLES3, so WebGL2 shouldn't have it.
michael@0 32 if (!IsWebGL2() &&
michael@0 33 IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays) &&
michael@0 34 !mBufferFetchingHasPerVertex)
michael@0 35 {
michael@0 36 /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt
michael@0 37 * If all of the enabled vertex attribute arrays that are bound to active
michael@0 38 * generic attributes in the program have a non-zero divisor, the draw
michael@0 39 * call should return INVALID_OPERATION.
michael@0 40 *
michael@0 41 * NB: This also appears to apply to NV_instanced_arrays, though the
michael@0 42 * INVALID_OPERATION emission is not explicitly stated.
michael@0 43 * ARB_instanced_arrays does not have this restriction.
michael@0 44 */
michael@0 45 ErrorInvalidOperation("%s: at least one vertex attribute divisor should be 0", info);
michael@0 46 return false;
michael@0 47 }
michael@0 48
michael@0 49 return true;
michael@0 50 }
michael@0 51
michael@0 52 bool WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcount, const char* info)
michael@0 53 {
michael@0 54 if (first < 0 || count < 0) {
michael@0 55 ErrorInvalidValue("%s: negative first or count", info);
michael@0 56 return false;
michael@0 57 }
michael@0 58
michael@0 59 if (primcount < 0) {
michael@0 60 ErrorInvalidValue("%s: negative primcount", info);
michael@0 61 return false;
michael@0 62 }
michael@0 63
michael@0 64 if (!ValidateStencilParamsForDrawCall()) {
michael@0 65 return false;
michael@0 66 }
michael@0 67
michael@0 68 // If count is 0, there's nothing to do.
michael@0 69 if (count == 0 || primcount == 0) {
michael@0 70 return false;
michael@0 71 }
michael@0 72
michael@0 73 // Any checks below this depend on a program being available.
michael@0 74 if (!mCurrentProgram) {
michael@0 75 ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
michael@0 76 return false;
michael@0 77 }
michael@0 78
michael@0 79 if (!ValidateBufferFetching(info)) {
michael@0 80 return false;
michael@0 81 }
michael@0 82
michael@0 83 CheckedInt<GLsizei> checked_firstPlusCount = CheckedInt<GLsizei>(first) + count;
michael@0 84
michael@0 85 if (!checked_firstPlusCount.isValid()) {
michael@0 86 ErrorInvalidOperation("%s: overflow in first+count", info);
michael@0 87 return false;
michael@0 88 }
michael@0 89
michael@0 90 if (uint32_t(checked_firstPlusCount.value()) > mMaxFetchedVertices) {
michael@0 91 ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient size for given first and count", info);
michael@0 92 return false;
michael@0 93 }
michael@0 94
michael@0 95 if (uint32_t(primcount) > mMaxFetchedInstances) {
michael@0 96 ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
michael@0 97 return false;
michael@0 98 }
michael@0 99
michael@0 100 MakeContextCurrent();
michael@0 101
michael@0 102 if (mBoundFramebuffer) {
michael@0 103 if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
michael@0 104 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
michael@0 105 return false;
michael@0 106 }
michael@0 107 } else {
michael@0 108 ClearBackbufferIfNeeded();
michael@0 109 }
michael@0 110
michael@0 111 if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
michael@0 112 return false;
michael@0 113 }
michael@0 114 BindFakeBlackTextures();
michael@0 115
michael@0 116 return true;
michael@0 117 }
michael@0 118
michael@0 119 void
michael@0 120 WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count)
michael@0 121 {
michael@0 122 if (IsContextLost())
michael@0 123 return;
michael@0 124
michael@0 125 if (!ValidateDrawModeEnum(mode, "drawArrays: mode"))
michael@0 126 return;
michael@0 127
michael@0 128 if (!DrawArrays_check(first, count, 1, "drawArrays"))
michael@0 129 return;
michael@0 130
michael@0 131 SetupContextLossTimer();
michael@0 132 gl->fDrawArrays(mode, first, count);
michael@0 133
michael@0 134 Draw_cleanup();
michael@0 135 }
michael@0 136
michael@0 137 void
michael@0 138 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
michael@0 139 {
michael@0 140 if (IsContextLost())
michael@0 141 return;
michael@0 142
michael@0 143 if (!ValidateDrawModeEnum(mode, "drawArraysInstanced: mode"))
michael@0 144 return;
michael@0 145
michael@0 146 if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced"))
michael@0 147 return;
michael@0 148
michael@0 149 if (!DrawInstanced_check("drawArraysInstanced"))
michael@0 150 return;
michael@0 151
michael@0 152 SetupContextLossTimer();
michael@0 153 gl->fDrawArraysInstanced(mode, first, count, primcount);
michael@0 154
michael@0 155 Draw_cleanup();
michael@0 156 }
michael@0 157
michael@0 158 bool
michael@0 159 WebGLContext::DrawElements_check(GLsizei count, GLenum type,
michael@0 160 WebGLintptr byteOffset, GLsizei primcount,
michael@0 161 const char* info, GLuint* out_upperBound)
michael@0 162 {
michael@0 163 if (count < 0 || byteOffset < 0) {
michael@0 164 ErrorInvalidValue("%s: negative count or offset", info);
michael@0 165 return false;
michael@0 166 }
michael@0 167
michael@0 168 if (primcount < 0) {
michael@0 169 ErrorInvalidValue("%s: negative primcount", info);
michael@0 170 return false;
michael@0 171 }
michael@0 172
michael@0 173 if (!ValidateStencilParamsForDrawCall()) {
michael@0 174 return false;
michael@0 175 }
michael@0 176
michael@0 177 // If count is 0, there's nothing to do.
michael@0 178 if (count == 0 || primcount == 0) {
michael@0 179 return false;
michael@0 180 }
michael@0 181
michael@0 182 CheckedUint32 checked_byteCount;
michael@0 183
michael@0 184 GLsizei first = 0;
michael@0 185
michael@0 186 if (type == LOCAL_GL_UNSIGNED_SHORT) {
michael@0 187 checked_byteCount = 2 * CheckedUint32(count);
michael@0 188 if (byteOffset % 2 != 0) {
michael@0 189 ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)", info);
michael@0 190 return false;
michael@0 191 }
michael@0 192 first = byteOffset / 2;
michael@0 193 }
michael@0 194 else if (type == LOCAL_GL_UNSIGNED_BYTE) {
michael@0 195 checked_byteCount = count;
michael@0 196 first = byteOffset;
michael@0 197 }
michael@0 198 else if (type == LOCAL_GL_UNSIGNED_INT && IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
michael@0 199 checked_byteCount = 4 * CheckedUint32(count);
michael@0 200 if (byteOffset % 4 != 0) {
michael@0 201 ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_INT (must be a multiple of 4)", info);
michael@0 202 return false;
michael@0 203 }
michael@0 204 first = byteOffset / 4;
michael@0 205 }
michael@0 206 else {
michael@0 207 ErrorInvalidEnum("%s: type must be UNSIGNED_SHORT or UNSIGNED_BYTE", info);
michael@0 208 return false;
michael@0 209 }
michael@0 210
michael@0 211 if (!checked_byteCount.isValid()) {
michael@0 212 ErrorInvalidValue("%s: overflow in byteCount", info);
michael@0 213 return false;
michael@0 214 }
michael@0 215
michael@0 216 // Any checks below this depend on a program being available.
michael@0 217 if (!mCurrentProgram) {
michael@0 218 ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
michael@0 219 return false;
michael@0 220 }
michael@0 221
michael@0 222 if (!mBoundVertexArray->mBoundElementArrayBuffer) {
michael@0 223 ErrorInvalidOperation("%s: must have element array buffer binding", info);
michael@0 224 return false;
michael@0 225 }
michael@0 226
michael@0 227 WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mBoundElementArrayBuffer;
michael@0 228
michael@0 229 if (!elemArrayBuffer.ByteLength()) {
michael@0 230 ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info);
michael@0 231 return false;
michael@0 232 }
michael@0 233
michael@0 234 CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset;
michael@0 235
michael@0 236 if (!checked_neededByteCount.isValid()) {
michael@0 237 ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info);
michael@0 238 return false;
michael@0 239 }
michael@0 240
michael@0 241 if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) {
michael@0 242 ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info);
michael@0 243 return false;
michael@0 244 }
michael@0 245
michael@0 246 if (!ValidateBufferFetching(info))
michael@0 247 return false;
michael@0 248
michael@0 249 if (!mMaxFetchedVertices ||
michael@0 250 !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound))
michael@0 251 {
michael@0 252 ErrorInvalidOperation(
michael@0 253 "%s: bound vertex attribute buffers do not have sufficient "
michael@0 254 "size for given indices from the bound element array", info);
michael@0 255 return false;
michael@0 256 }
michael@0 257
michael@0 258 if (uint32_t(primcount) > mMaxFetchedInstances) {
michael@0 259 ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
michael@0 260 return false;
michael@0 261 }
michael@0 262
michael@0 263 MakeContextCurrent();
michael@0 264
michael@0 265 if (mBoundFramebuffer) {
michael@0 266 if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
michael@0 267 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
michael@0 268 return false;
michael@0 269 }
michael@0 270 } else {
michael@0 271 ClearBackbufferIfNeeded();
michael@0 272 }
michael@0 273
michael@0 274 if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
michael@0 275 return false;
michael@0 276 }
michael@0 277 BindFakeBlackTextures();
michael@0 278
michael@0 279 return true;
michael@0 280 }
michael@0 281
michael@0 282 void
michael@0 283 WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
michael@0 284 WebGLintptr byteOffset)
michael@0 285 {
michael@0 286 if (IsContextLost())
michael@0 287 return;
michael@0 288
michael@0 289 if (!ValidateDrawModeEnum(mode, "drawElements: mode"))
michael@0 290 return;
michael@0 291
michael@0 292 GLuint upperBound = UINT_MAX;
michael@0 293 if (!DrawElements_check(count, type, byteOffset, 1, "drawElements",
michael@0 294 &upperBound))
michael@0 295 {
michael@0 296 return;
michael@0 297 }
michael@0 298
michael@0 299 SetupContextLossTimer();
michael@0 300
michael@0 301 if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
michael@0 302 gl->fDrawRangeElements(mode, 0, upperBound,
michael@0 303 count, type, reinterpret_cast<GLvoid*>(byteOffset));
michael@0 304 } else {
michael@0 305 gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset));
michael@0 306 }
michael@0 307
michael@0 308 Draw_cleanup();
michael@0 309 }
michael@0 310
michael@0 311 void
michael@0 312 WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
michael@0 313 WebGLintptr byteOffset, GLsizei primcount)
michael@0 314 {
michael@0 315 if (IsContextLost())
michael@0 316 return;
michael@0 317
michael@0 318 if (!ValidateDrawModeEnum(mode, "drawElementsInstanced: mode"))
michael@0 319 return;
michael@0 320
michael@0 321 if (!DrawElements_check(count, type, byteOffset, primcount, "drawElementsInstanced"))
michael@0 322 return;
michael@0 323
michael@0 324 if (!DrawInstanced_check("drawElementsInstanced"))
michael@0 325 return;
michael@0 326
michael@0 327 SetupContextLossTimer();
michael@0 328 gl->fDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset), primcount);
michael@0 329
michael@0 330 Draw_cleanup();
michael@0 331 }
michael@0 332
michael@0 333 void WebGLContext::Draw_cleanup()
michael@0 334 {
michael@0 335 UndoFakeVertexAttrib0();
michael@0 336 UnbindFakeBlackTextures();
michael@0 337
michael@0 338 if (!mBoundFramebuffer) {
michael@0 339 Invalidate();
michael@0 340 mShouldPresent = true;
michael@0 341 MOZ_ASSERT(!mBackbufferNeedsClear);
michael@0 342 }
michael@0 343
michael@0 344 if (gl->WorkAroundDriverBugs()) {
michael@0 345 if (gl->Renderer() == gl::GLRenderer::Tegra) {
michael@0 346 mDrawCallsSinceLastFlush++;
michael@0 347
michael@0 348 if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
michael@0 349 gl->fFlush();
michael@0 350 mDrawCallsSinceLastFlush = 0;
michael@0 351 }
michael@0 352 }
michael@0 353 }
michael@0 354
michael@0 355 // Let's check the viewport
michael@0 356 const WebGLRectangleObject* rect = CurValidFBRectObject();
michael@0 357 if (rect) {
michael@0 358 if (mViewportWidth > rect->Width() ||
michael@0 359 mViewportHeight > rect->Height())
michael@0 360 {
michael@0 361 if (!mAlreadyWarnedAboutViewportLargerThanDest) {
michael@0 362 GenerateWarning("Drawing to a destination rect smaller than the viewport rect. "
michael@0 363 "(This warning will only be given once)");
michael@0 364 mAlreadyWarnedAboutViewportLargerThanDest = true;
michael@0 365 }
michael@0 366 }
michael@0 367 }
michael@0 368 }
michael@0 369
michael@0 370 /*
michael@0 371 * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
michael@0 372 * that will be legal to be read from bound VBOs.
michael@0 373 */
michael@0 374
michael@0 375 bool
michael@0 376 WebGLContext::ValidateBufferFetching(const char *info)
michael@0 377 {
michael@0 378 #ifdef DEBUG
michael@0 379 GLint currentProgram = 0;
michael@0 380 MakeContextCurrent();
michael@0 381 gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram);
michael@0 382 MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->GLName(),
michael@0 383 "WebGL: current program doesn't agree with GL state");
michael@0 384 #endif
michael@0 385
michael@0 386 if (mBufferFetchingIsVerified) {
michael@0 387 return true;
michael@0 388 }
michael@0 389
michael@0 390 bool hasPerVertex = false;
michael@0 391 uint32_t maxVertices = UINT32_MAX;
michael@0 392 uint32_t maxInstances = UINT32_MAX;
michael@0 393 uint32_t attribs = mBoundVertexArray->mAttribs.Length();
michael@0 394
michael@0 395 for (uint32_t i = 0; i < attribs; ++i) {
michael@0 396 const WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[i];
michael@0 397
michael@0 398 // If the attrib array isn't enabled, there's nothing to check;
michael@0 399 // it's a static value.
michael@0 400 if (!vd.enabled)
michael@0 401 continue;
michael@0 402
michael@0 403 if (vd.buf == nullptr) {
michael@0 404 ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i);
michael@0 405 return false;
michael@0 406 }
michael@0 407
michael@0 408 // If the attrib is not in use, then we don't have to validate
michael@0 409 // it, just need to make sure that the binding is non-null.
michael@0 410 if (!mCurrentProgram->IsAttribInUse(i))
michael@0 411 continue;
michael@0 412
michael@0 413 // the base offset
michael@0 414 CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset;
michael@0 415 CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size;
michael@0 416
michael@0 417 if (!checked_byteLength.isValid() ||
michael@0 418 !checked_sizeOfLastElement.isValid())
michael@0 419 {
michael@0 420 ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
michael@0 421 return false;
michael@0 422 }
michael@0 423
michael@0 424 if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
michael@0 425 maxVertices = 0;
michael@0 426 maxInstances = 0;
michael@0 427 break;
michael@0 428 }
michael@0 429
michael@0 430 CheckedUint32 checked_maxAllowedCount = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
michael@0 431
michael@0 432 if (!checked_maxAllowedCount.isValid()) {
michael@0 433 ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
michael@0 434 return false;
michael@0 435 }
michael@0 436
michael@0 437 if (vd.divisor == 0) {
michael@0 438 maxVertices = std::min(maxVertices, checked_maxAllowedCount.value());
michael@0 439 hasPerVertex = true;
michael@0 440 } else {
michael@0 441 maxInstances = std::min(maxInstances, checked_maxAllowedCount.value() / vd.divisor);
michael@0 442 }
michael@0 443 }
michael@0 444
michael@0 445 mBufferFetchingIsVerified = true;
michael@0 446 mBufferFetchingHasPerVertex = hasPerVertex;
michael@0 447 mMaxFetchedVertices = maxVertices;
michael@0 448 mMaxFetchedInstances = maxInstances;
michael@0 449
michael@0 450 return true;
michael@0 451 }
michael@0 452
michael@0 453 WebGLVertexAttrib0Status
michael@0 454 WebGLContext::WhatDoesVertexAttrib0Need()
michael@0 455 {
michael@0 456 MOZ_ASSERT(mCurrentProgram);
michael@0 457
michael@0 458 // work around Mac OSX crash, see bug 631420
michael@0 459 #ifdef XP_MACOSX
michael@0 460 if (gl->WorkAroundDriverBugs() &&
michael@0 461 mBoundVertexArray->IsAttribArrayEnabled(0) &&
michael@0 462 !mCurrentProgram->IsAttribInUse(0))
michael@0 463 {
michael@0 464 return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
michael@0 465 }
michael@0 466 #endif
michael@0 467
michael@0 468 if (MOZ_LIKELY(gl->IsGLES() ||
michael@0 469 mBoundVertexArray->IsAttribArrayEnabled(0)))
michael@0 470 {
michael@0 471 return WebGLVertexAttrib0Status::Default;
michael@0 472 }
michael@0 473
michael@0 474 return mCurrentProgram->IsAttribInUse(0)
michael@0 475 ? WebGLVertexAttrib0Status::EmulatedInitializedArray
michael@0 476 : WebGLVertexAttrib0Status::EmulatedUninitializedArray;
michael@0 477 }
michael@0 478
michael@0 479 bool
michael@0 480 WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
michael@0 481 {
michael@0 482 WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
michael@0 483
michael@0 484 if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
michael@0 485 return true;
michael@0 486
michael@0 487 if (!mAlreadyWarnedAboutFakeVertexAttrib0) {
michael@0 488 GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser "
michael@0 489 "to do expensive emulation work when running on desktop OpenGL "
michael@0 490 "platforms, for example on Mac. It is preferable to always draw "
michael@0 491 "with vertex attrib 0 array enabled, by using bindAttribLocation "
michael@0 492 "to bind some always-used attribute to location 0.");
michael@0 493 mAlreadyWarnedAboutFakeVertexAttrib0 = true;
michael@0 494 }
michael@0 495
michael@0 496 CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(GLfloat);
michael@0 497
michael@0 498 if (!checked_dataSize.isValid()) {
michael@0 499 ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
michael@0 500 "with %d vertices. Try reducing the number of vertices.", vertexCount);
michael@0 501 return false;
michael@0 502 }
michael@0 503
michael@0 504 GLuint dataSize = checked_dataSize.value();
michael@0 505
michael@0 506 if (!mFakeVertexAttrib0BufferObject) {
michael@0 507 gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject);
michael@0 508 }
michael@0 509
michael@0 510 // if the VBO status is already exactly what we need, or if the only difference is that it's initialized and
michael@0 511 // we don't need it to be, then consider it OK
michael@0 512 bool vertexAttrib0BufferStatusOK =
michael@0 513 mFakeVertexAttrib0BufferStatus == whatDoesAttrib0Need ||
michael@0 514 (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray &&
michael@0 515 whatDoesAttrib0Need == WebGLVertexAttrib0Status::EmulatedUninitializedArray);
michael@0 516
michael@0 517 if (!vertexAttrib0BufferStatusOK ||
michael@0 518 mFakeVertexAttrib0BufferObjectSize < dataSize ||
michael@0 519 mFakeVertexAttrib0BufferObjectVector[0] != mVertexAttrib0Vector[0] ||
michael@0 520 mFakeVertexAttrib0BufferObjectVector[1] != mVertexAttrib0Vector[1] ||
michael@0 521 mFakeVertexAttrib0BufferObjectVector[2] != mVertexAttrib0Vector[2] ||
michael@0 522 mFakeVertexAttrib0BufferObjectVector[3] != mVertexAttrib0Vector[3])
michael@0 523 {
michael@0 524 mFakeVertexAttrib0BufferStatus = whatDoesAttrib0Need;
michael@0 525 mFakeVertexAttrib0BufferObjectSize = dataSize;
michael@0 526 mFakeVertexAttrib0BufferObjectVector[0] = mVertexAttrib0Vector[0];
michael@0 527 mFakeVertexAttrib0BufferObjectVector[1] = mVertexAttrib0Vector[1];
michael@0 528 mFakeVertexAttrib0BufferObjectVector[2] = mVertexAttrib0Vector[2];
michael@0 529 mFakeVertexAttrib0BufferObjectVector[3] = mVertexAttrib0Vector[3];
michael@0 530
michael@0 531 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
michael@0 532
michael@0 533 GetAndFlushUnderlyingGLErrors();
michael@0 534
michael@0 535 if (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray) {
michael@0 536 nsAutoArrayPtr<GLfloat> array(new GLfloat[4 * vertexCount]);
michael@0 537 for(size_t i = 0; i < vertexCount; ++i) {
michael@0 538 array[4 * i + 0] = mVertexAttrib0Vector[0];
michael@0 539 array[4 * i + 1] = mVertexAttrib0Vector[1];
michael@0 540 array[4 * i + 2] = mVertexAttrib0Vector[2];
michael@0 541 array[4 * i + 3] = mVertexAttrib0Vector[3];
michael@0 542 }
michael@0 543 gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array, LOCAL_GL_DYNAMIC_DRAW);
michael@0 544 } else {
michael@0 545 gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
michael@0 546 }
michael@0 547 GLenum error = GetAndFlushUnderlyingGLErrors();
michael@0 548
michael@0 549 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
michael@0 550
michael@0 551 // note that we do this error checking and early return AFTER having restored the buffer binding above
michael@0 552 if (error) {
michael@0 553 ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation "
michael@0 554 "with %d vertices. Try reducing the number of vertices.", vertexCount);
michael@0 555 return false;
michael@0 556 }
michael@0 557 }
michael@0 558
michael@0 559 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
michael@0 560 gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0);
michael@0 561
michael@0 562 return true;
michael@0 563 }
michael@0 564
michael@0 565 void
michael@0 566 WebGLContext::UndoFakeVertexAttrib0()
michael@0 567 {
michael@0 568 WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
michael@0 569
michael@0 570 if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
michael@0 571 return;
michael@0 572
michael@0 573 if (mBoundVertexArray->HasAttrib(0) && mBoundVertexArray->mAttribs[0].buf) {
michael@0 574 const WebGLVertexAttribData& attrib0 = mBoundVertexArray->mAttribs[0];
michael@0 575 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.buf->GLName());
michael@0 576 gl->fVertexAttribPointer(0,
michael@0 577 attrib0.size,
michael@0 578 attrib0.type,
michael@0 579 attrib0.normalized,
michael@0 580 attrib0.stride,
michael@0 581 reinterpret_cast<const GLvoid *>(attrib0.byteOffset));
michael@0 582 } else {
michael@0 583 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
michael@0 584 }
michael@0 585
michael@0 586 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
michael@0 587 }
michael@0 588
michael@0 589 WebGLContextFakeBlackStatus
michael@0 590 WebGLContext::ResolvedFakeBlackStatus()
michael@0 591 {
michael@0 592 // handle this case first, it's the generic case
michael@0 593 if (MOZ_LIKELY(mFakeBlackStatus == WebGLContextFakeBlackStatus::NotNeeded))
michael@0 594 return mFakeBlackStatus;
michael@0 595
michael@0 596 if (mFakeBlackStatus == WebGLContextFakeBlackStatus::Needed)
michael@0 597 return mFakeBlackStatus;
michael@0 598
michael@0 599 for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
michael@0 600 if ((mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) ||
michael@0 601 (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded))
michael@0 602 {
michael@0 603 mFakeBlackStatus = WebGLContextFakeBlackStatus::Needed;
michael@0 604 return mFakeBlackStatus;
michael@0 605 }
michael@0 606 }
michael@0 607
michael@0 608 // we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
michael@0 609 // that means that we do NOT need it.
michael@0 610 mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
michael@0 611 return mFakeBlackStatus;
michael@0 612 }
michael@0 613
michael@0 614 void
michael@0 615 WebGLContext::BindFakeBlackTexturesHelper(
michael@0 616 GLenum target,
michael@0 617 const nsTArray<WebGLRefPtr<WebGLTexture> > & boundTexturesArray,
michael@0 618 ScopedDeletePtr<FakeBlackTexture> & opaqueTextureScopedPtr,
michael@0 619 ScopedDeletePtr<FakeBlackTexture> & transparentTextureScopedPtr)
michael@0 620 {
michael@0 621 for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
michael@0 622 if (!boundTexturesArray[i]) {
michael@0 623 continue;
michael@0 624 }
michael@0 625
michael@0 626 WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus();
michael@0 627 MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown);
michael@0 628
michael@0 629 if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) {
michael@0 630 continue;
michael@0 631 }
michael@0 632
michael@0 633 bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData &&
michael@0 634 FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().WebGLFormat());
michael@0 635 ScopedDeletePtr<FakeBlackTexture>&
michael@0 636 blackTexturePtr = alpha
michael@0 637 ? transparentTextureScopedPtr
michael@0 638 : opaqueTextureScopedPtr;
michael@0 639
michael@0 640 if (!blackTexturePtr) {
michael@0 641 GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
michael@0 642 blackTexturePtr
michael@0 643 = new FakeBlackTexture(gl, target, format);
michael@0 644 }
michael@0 645
michael@0 646 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
michael@0 647 gl->fBindTexture(target,
michael@0 648 blackTexturePtr->GLName());
michael@0 649 }
michael@0 650 }
michael@0 651
michael@0 652 void
michael@0 653 WebGLContext::BindFakeBlackTextures()
michael@0 654 {
michael@0 655 // this is the generic case: try to return early
michael@0 656 if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
michael@0 657 return;
michael@0 658
michael@0 659 BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_2D,
michael@0 660 mBound2DTextures,
michael@0 661 mBlackOpaqueTexture2D,
michael@0 662 mBlackTransparentTexture2D);
michael@0 663 BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_CUBE_MAP,
michael@0 664 mBoundCubeMapTextures,
michael@0 665 mBlackOpaqueTextureCubeMap,
michael@0 666 mBlackTransparentTextureCubeMap);
michael@0 667 }
michael@0 668
michael@0 669 void
michael@0 670 WebGLContext::UnbindFakeBlackTextures()
michael@0 671 {
michael@0 672 // this is the generic case: try to return early
michael@0 673 if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
michael@0 674 return;
michael@0 675
michael@0 676 for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
michael@0 677 if (mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
michael@0 678 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
michael@0 679 gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBound2DTextures[i]->GLName());
michael@0 680 }
michael@0 681 if (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
michael@0 682 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
michael@0 683 gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->GLName());
michael@0 684 }
michael@0 685 }
michael@0 686
michael@0 687 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
michael@0 688 }
michael@0 689
michael@0 690 WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext *gl, GLenum target, GLenum format)
michael@0 691 : mGL(gl)
michael@0 692 , mGLName(0)
michael@0 693 {
michael@0 694 MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D || target == LOCAL_GL_TEXTURE_CUBE_MAP);
michael@0 695 MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA);
michael@0 696
michael@0 697 mGL->MakeCurrent();
michael@0 698 GLuint formerBinding = 0;
michael@0 699 gl->GetUIntegerv(target == LOCAL_GL_TEXTURE_2D
michael@0 700 ? LOCAL_GL_TEXTURE_BINDING_2D
michael@0 701 : LOCAL_GL_TEXTURE_BINDING_CUBE_MAP,
michael@0 702 &formerBinding);
michael@0 703 gl->fGenTextures(1, &mGLName);
michael@0 704 gl->fBindTexture(target, mGLName);
michael@0 705
michael@0 706 // we allocate our zeros on the heap, and we overallocate (16 bytes instead of 4)
michael@0 707 // to minimize the risk of running into a driver bug in texImage2D, as it is
michael@0 708 // a bit unusual maybe to create 1x1 textures, and the stack may not have the alignment
michael@0 709 // that texImage2D expects.
michael@0 710 void* zeros = calloc(1, 16);
michael@0 711 if (target == LOCAL_GL_TEXTURE_2D) {
michael@0 712 gl->fTexImage2D(target, 0, format, 1, 1,
michael@0 713 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros);
michael@0 714 } else {
michael@0 715 for (GLuint i = 0; i < 6; ++i) {
michael@0 716 gl->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, 1, 1,
michael@0 717 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros);
michael@0 718 }
michael@0 719 }
michael@0 720 free(zeros);
michael@0 721
michael@0 722 gl->fBindTexture(target, formerBinding);
michael@0 723 }
michael@0 724
michael@0 725 WebGLContext::FakeBlackTexture::~FakeBlackTexture()
michael@0 726 {
michael@0 727 if (mGL) {
michael@0 728 mGL->MakeCurrent();
michael@0 729 mGL->fDeleteTextures(1, &mGLName);
michael@0 730 }
michael@0 731 }

mercurial