LCOV - code coverage report
Current view: top level - dom/canvas - WebGLBuffer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 194 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 22 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "WebGLBuffer.h"
       7             : 
       8             : #include "GLContext.h"
       9             : #include "mozilla/dom/WebGLRenderingContextBinding.h"
      10             : #include "WebGLContext.h"
      11             : 
      12             : namespace mozilla {
      13             : 
      14           0 : WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
      15             :     : WebGLRefCountedObject(webgl)
      16             :     , mGLName(buf)
      17             :     , mContent(Kind::Undefined)
      18             :     , mUsage(LOCAL_GL_STATIC_DRAW)
      19             :     , mByteLength(0)
      20             :     , mTFBindCount(0)
      21           0 :     , mNonTFBindCount(0)
      22             : {
      23           0 :     mContext->mBuffers.insertBack(this);
      24           0 : }
      25             : 
      26           0 : WebGLBuffer::~WebGLBuffer()
      27             : {
      28           0 :     DeleteOnce();
      29           0 : }
      30             : 
      31             : void
      32           0 : WebGLBuffer::SetContentAfterBind(GLenum target)
      33             : {
      34           0 :     if (mContent != Kind::Undefined)
      35           0 :         return;
      36             : 
      37           0 :     switch (target) {
      38             :     case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
      39           0 :         mContent = Kind::ElementArray;
      40           0 :         break;
      41             : 
      42             :     case LOCAL_GL_ARRAY_BUFFER:
      43             :     case LOCAL_GL_PIXEL_PACK_BUFFER:
      44             :     case LOCAL_GL_PIXEL_UNPACK_BUFFER:
      45             :     case LOCAL_GL_UNIFORM_BUFFER:
      46             :     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
      47             :     case LOCAL_GL_COPY_READ_BUFFER:
      48             :     case LOCAL_GL_COPY_WRITE_BUFFER:
      49           0 :         mContent = Kind::OtherData;
      50           0 :         break;
      51             : 
      52             :     default:
      53           0 :         MOZ_CRASH("GFX: invalid target");
      54             :     }
      55             : }
      56             : 
      57             : void
      58           0 : WebGLBuffer::Delete()
      59             : {
      60           0 :     mContext->MakeContextCurrent();
      61           0 :     mContext->gl->fDeleteBuffers(1, &mGLName);
      62           0 :     mByteLength = 0;
      63           0 :     mIndexCache = nullptr;
      64           0 :     mIndexRanges.clear();
      65           0 :     LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
      66           0 : }
      67             : 
      68             : ////////////////////////////////////////
      69             : 
      70             : static bool
      71           0 : ValidateBufferUsageEnum(WebGLContext* webgl, const char* funcName, GLenum usage)
      72             : {
      73           0 :     switch (usage) {
      74             :     case LOCAL_GL_STREAM_DRAW:
      75             :     case LOCAL_GL_STATIC_DRAW:
      76             :     case LOCAL_GL_DYNAMIC_DRAW:
      77           0 :         return true;
      78             : 
      79             :     case LOCAL_GL_DYNAMIC_COPY:
      80             :     case LOCAL_GL_DYNAMIC_READ:
      81             :     case LOCAL_GL_STATIC_COPY:
      82             :     case LOCAL_GL_STATIC_READ:
      83             :     case LOCAL_GL_STREAM_COPY:
      84             :     case LOCAL_GL_STREAM_READ:
      85           0 :         if (MOZ_LIKELY(webgl->IsWebGL2()))
      86           0 :             return true;
      87           0 :         break;
      88             : 
      89             :     default:
      90           0 :         break;
      91             :     }
      92             : 
      93           0 :     webgl->ErrorInvalidEnum("%s: Invalid `usage`: 0x%04x", funcName, usage);
      94           0 :     return false;
      95             : }
      96             : 
      97             : void
      98           0 : WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usage)
      99             : {
     100           0 :     const char funcName[] = "bufferData";
     101             : 
     102             :     // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
     103             :     // is like intptr_t.
     104           0 :     if (!CheckedInt<GLsizeiptr>(size).isValid())
     105           0 :         return mContext->ErrorOutOfMemory("%s: bad size", funcName);
     106             : 
     107           0 :     if (!ValidateBufferUsageEnum(mContext, funcName, usage))
     108           0 :         return;
     109             : 
     110             : #ifdef XP_MACOSX
     111             :     // bug 790879
     112             :     if (mContext->gl->WorkAroundDriverBugs() &&
     113             :         size > INT32_MAX)
     114             :     {
     115             :         mContext->ErrorOutOfMemory("%s: Allocation size too large.", funcName);
     116             :         return;
     117             :     }
     118             : #endif
     119             : 
     120           0 :     const void* uploadData = data;
     121             : 
     122           0 :     UniqueBuffer newIndexCache;
     123           0 :     if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER &&
     124           0 :         mContext->mNeedsIndexValidation)
     125             :     {
     126           0 :         newIndexCache = malloc(size);
     127           0 :         if (!newIndexCache) {
     128           0 :             mContext->ErrorOutOfMemory("%s: Failed to alloc index cache.", funcName);
     129           0 :             return;
     130             :         }
     131           0 :         memcpy(newIndexCache.get(), data, size);
     132           0 :         uploadData = newIndexCache.get();
     133             :     }
     134             : 
     135           0 :     const auto& gl = mContext->gl;
     136           0 :     gl->MakeCurrent();
     137           0 :     const ScopedLazyBind lazyBind(gl, target, this);
     138             : 
     139           0 :     const bool sizeChanges = (size != ByteLength());
     140           0 :     if (sizeChanges) {
     141           0 :         mContext->InvalidateBufferFetching();
     142             : 
     143           0 :         gl::GLContext::LocalErrorScope errorScope(*gl);
     144           0 :         gl->fBufferData(target, size, uploadData, usage);
     145           0 :         const auto error = errorScope.GetError();
     146             : 
     147           0 :         if (error) {
     148           0 :             MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY);
     149           0 :             mContext->ErrorOutOfMemory("%s: Error from driver: 0x%04x", funcName, error);
     150           0 :             return;
     151             :         }
     152             :     } else {
     153           0 :         gl->fBufferData(target, size, uploadData, usage);
     154             :     }
     155             : 
     156           0 :     mUsage = usage;
     157           0 :     mByteLength = size;
     158           0 :     mIndexCache = Move(newIndexCache);
     159             : 
     160           0 :     if (mIndexCache) {
     161           0 :         if (mIndexRanges.size()) {
     162           0 :             mContext->GeneratePerfWarning("[%p] Invalidating %u ranges.", this,
     163           0 :                                           uint32_t(mIndexRanges.size()));
     164           0 :             mIndexRanges.clear();
     165             :         }
     166             :     }
     167             : }
     168             : 
     169             : void
     170           0 : WebGLBuffer::BufferSubData(GLenum target, size_t dstByteOffset, size_t dataLen,
     171             :                            const void* data) const
     172             : {
     173           0 :     const char funcName[] = "bufferSubData";
     174             : 
     175           0 :     if (!ValidateRange(funcName, dstByteOffset, dataLen))
     176           0 :         return;
     177             : 
     178           0 :     if (!CheckedInt<GLintptr>(dataLen).isValid())
     179           0 :         return mContext->ErrorOutOfMemory("%s: Size too large.", funcName);
     180             : 
     181             :     ////
     182             : 
     183           0 :     const void* uploadData = data;
     184           0 :     if (mIndexCache) {
     185           0 :         const auto cachedDataBegin = (uint8_t*)mIndexCache.get() + dstByteOffset;
     186           0 :         memcpy(cachedDataBegin, data, dataLen);
     187           0 :         uploadData = cachedDataBegin;
     188             : 
     189           0 :         InvalidateCacheRange(dstByteOffset, dataLen);
     190             :     }
     191             : 
     192             :     ////
     193             : 
     194           0 :     const auto& gl = mContext->gl;
     195           0 :     gl->MakeCurrent();
     196           0 :     const ScopedLazyBind lazyBind(gl, target, this);
     197             : 
     198           0 :     gl->fBufferSubData(target, dstByteOffset, dataLen, uploadData);
     199             : }
     200             : 
     201             : bool
     202           0 : WebGLBuffer::ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const
     203             : {
     204           0 :     auto availLength = mByteLength;
     205           0 :     if (byteOffset > availLength) {
     206           0 :         mContext->ErrorInvalidValue("%s: Offset passes the end of the buffer.", funcName);
     207           0 :         return false;
     208             :     }
     209           0 :     availLength -= byteOffset;
     210             : 
     211           0 :     if (byteLen > availLength) {
     212           0 :         mContext->ErrorInvalidValue("%s: Offset+size passes the end of the buffer.",
     213           0 :                                     funcName);
     214           0 :         return false;
     215             :     }
     216             : 
     217           0 :     return true;
     218             : }
     219             : 
     220             : ////////////////////////////////////////
     221             : 
     222             : static uint8_t
     223           0 : IndexByteSizeByType(GLenum type)
     224             : {
     225           0 :     switch (type) {
     226           0 :     case LOCAL_GL_UNSIGNED_BYTE:  return 1;
     227           0 :     case LOCAL_GL_UNSIGNED_SHORT: return 2;
     228           0 :     case LOCAL_GL_UNSIGNED_INT:   return 4;
     229             :     default:
     230           0 :         MOZ_CRASH();
     231             :     }
     232             : }
     233             : 
     234             : void
     235           0 : WebGLBuffer::InvalidateCacheRange(size_t byteOffset, size_t byteLength) const
     236             : {
     237           0 :     MOZ_ASSERT(mIndexCache);
     238             : 
     239           0 :     std::vector<IndexRange> invalids;
     240           0 :     const size_t updateBegin = byteOffset;
     241           0 :     const size_t updateEnd = updateBegin + byteLength;
     242           0 :     for (const auto& cur : mIndexRanges) {
     243           0 :         const auto& range = cur.first;
     244           0 :         const auto& indexByteSize = IndexByteSizeByType(range.type);
     245           0 :         const size_t rangeBegin = range.first * indexByteSize;
     246           0 :         const size_t rangeEnd = rangeBegin + range.count*indexByteSize;
     247           0 :         if (rangeBegin >= updateEnd || rangeEnd <= updateBegin)
     248           0 :             continue;
     249           0 :         invalids.push_back(range);
     250             :     }
     251             : 
     252           0 :     if (invalids.size()) {
     253           0 :         mContext->GeneratePerfWarning("[%p] Invalidating %u/%u ranges.", this,
     254           0 :                                       uint32_t(invalids.size()),
     255           0 :                                       uint32_t(mIndexRanges.size()));
     256             : 
     257           0 :         for (const auto& cur : invalids) {
     258           0 :             mIndexRanges.erase(cur);
     259             :         }
     260             :     }
     261           0 : }
     262             : 
     263             : size_t
     264           0 : WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     265             : {
     266           0 :     size_t size = mallocSizeOf(this);
     267           0 :     if (mIndexCache) {
     268           0 :         size += mByteLength;
     269             :     }
     270           0 :     return size;
     271             : }
     272             : 
     273             : template<typename T>
     274             : static size_t
     275           0 : MaxForRange(const void* data, size_t first, size_t count, const uint32_t ignoredVal)
     276             : {
     277           0 :     const T ignoredTVal(ignoredVal);
     278           0 :     T ret = 0;
     279             : 
     280           0 :     auto itr = (const T*)data + first;
     281           0 :     const auto end = itr + count;
     282             : 
     283           0 :     for (; itr != end; ++itr) {
     284           0 :         const auto& val = *itr;
     285           0 :         if (val <= ret)
     286           0 :             continue;
     287             : 
     288           0 :         if (val == ignoredTVal)
     289           0 :             continue;
     290             : 
     291           0 :         ret = val;
     292             :     }
     293             : 
     294           0 :     return size_t(ret);
     295             : }
     296             : 
     297             : const uint32_t kMaxIndexRanges = 256;
     298             : 
     299             : bool
     300           0 : WebGLBuffer::ValidateIndexedFetch(GLenum type, uint32_t numFetchable, size_t first,
     301             :                                   size_t count) const
     302             : {
     303           0 :     if (!mIndexCache)
     304           0 :         return true;
     305             : 
     306           0 :     if (!count)
     307           0 :         return true;
     308             : 
     309           0 :     const IndexRange range = { type, first, count };
     310           0 :     auto res = mIndexRanges.insert({ range, size_t(0) });
     311           0 :     if (mIndexRanges.size() > kMaxIndexRanges) {
     312           0 :         mContext->GeneratePerfWarning("[%p] Clearing mIndexRanges after exceeding %u.",
     313           0 :                                       this, kMaxIndexRanges);
     314           0 :         mIndexRanges.clear();
     315           0 :         res = mIndexRanges.insert({ range, size_t(0) });
     316             :     }
     317             : 
     318           0 :     const auto& itr = res.first;
     319           0 :     const auto& didInsert = res.second;
     320             : 
     321           0 :     auto& maxFetchIndex = itr->second;
     322           0 :     if (didInsert) {
     323           0 :         const auto& data = mIndexCache.get();
     324           0 :         const uint32_t ignoreVal = (mContext->IsWebGL2() ? UINT32_MAX : 0);
     325             : 
     326           0 :         switch (type) {
     327             :         case LOCAL_GL_UNSIGNED_BYTE:
     328           0 :             maxFetchIndex = MaxForRange<uint8_t>(data, first, count, ignoreVal);
     329           0 :             break;
     330             :         case LOCAL_GL_UNSIGNED_SHORT:
     331           0 :             maxFetchIndex = MaxForRange<uint16_t>(data, first, count, ignoreVal);
     332           0 :             break;
     333             :         case LOCAL_GL_UNSIGNED_INT:
     334           0 :             maxFetchIndex = MaxForRange<uint32_t>(data, first, count, ignoreVal);
     335           0 :             break;
     336             :         default:
     337           0 :             MOZ_CRASH();
     338             :         }
     339             : 
     340           0 :         mContext->GeneratePerfWarning("[%p] New range #%u: (0x%04x, %u, %u): %u", this,
     341           0 :                                       uint32_t(mIndexRanges.size()), type,
     342             :                                       uint32_t(first), uint32_t(count),
     343           0 :                                       uint32_t(maxFetchIndex));
     344             :     }
     345             : 
     346           0 :     return maxFetchIndex < numFetchable;
     347             : }
     348             : 
     349             : ////
     350             : 
     351             : bool
     352           0 : WebGLBuffer::ValidateCanBindToTarget(const char* funcName, GLenum target)
     353             : {
     354             :     /* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
     355             :      *
     356             :      * In the WebGL 2 API, buffers have their WebGL buffer type
     357             :      * initially set to undefined. Calling bindBuffer, bindBufferRange
     358             :      * or bindBufferBase with the target argument set to any buffer
     359             :      * binding point except COPY_READ_BUFFER or COPY_WRITE_BUFFER will
     360             :      * then set the WebGL buffer type of the buffer being bound
     361             :      * according to the table above.
     362             :      *
     363             :      * Any call to one of these functions which attempts to bind a
     364             :      * WebGLBuffer that has the element array WebGL buffer type to a
     365             :      * binding point that falls under other data, or bind a
     366             :      * WebGLBuffer which has the other data WebGL buffer type to
     367             :      * ELEMENT_ARRAY_BUFFER will generate an INVALID_OPERATION error,
     368             :      * and the state of the binding point will remain untouched.
     369             :      */
     370             : 
     371           0 :     if (mContent == WebGLBuffer::Kind::Undefined)
     372           0 :         return true;
     373             : 
     374           0 :     switch (target) {
     375             :     case LOCAL_GL_COPY_READ_BUFFER:
     376             :     case LOCAL_GL_COPY_WRITE_BUFFER:
     377           0 :         return true;
     378             : 
     379             :     case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
     380           0 :         if (mContent == WebGLBuffer::Kind::ElementArray)
     381           0 :             return true;
     382           0 :         break;
     383             : 
     384             :     case LOCAL_GL_ARRAY_BUFFER:
     385             :     case LOCAL_GL_PIXEL_PACK_BUFFER:
     386             :     case LOCAL_GL_PIXEL_UNPACK_BUFFER:
     387             :     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
     388             :     case LOCAL_GL_UNIFORM_BUFFER:
     389           0 :         if (mContent == WebGLBuffer::Kind::OtherData)
     390           0 :             return true;
     391           0 :         break;
     392             : 
     393             :     default:
     394           0 :         MOZ_CRASH();
     395             :     }
     396             : 
     397           0 :     const auto dataType = (mContent == WebGLBuffer::Kind::OtherData) ? "other"
     398           0 :                                                                      : "element";
     399           0 :     mContext->ErrorInvalidOperation("%s: Buffer already contains %s data.", funcName,
     400           0 :                                     dataType);
     401           0 :     return false;
     402             : }
     403             : 
     404             : JSObject*
     405           0 : WebGLBuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
     406             : {
     407           0 :     return dom::WebGLBufferBinding::Wrap(cx, this, givenProto);
     408             : }
     409             : 
     410           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLBuffer)
     411             : 
     412           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLBuffer, AddRef)
     413           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLBuffer, Release)
     414             : 
     415             : } // namespace mozilla

Generated by: LCOV version 1.13