WebGL2ContextBuffers.cpp (6010B)
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 "ClientWebGLContext.h" 7 #include "GLContext.h" 8 #include "WebGL2Context.h" 9 #include "WebGLBuffer.h" 10 #include "WebGLTransformFeedback.h" 11 12 namespace mozilla { 13 14 // ------------------------------------------------------------------------- 15 // Buffer objects 16 17 void WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget, 18 uint64_t readOffset, uint64_t writeOffset, 19 uint64_t size) const { 20 const FuncScope funcScope(*this, "copyBufferSubData"); 21 if (IsContextLost()) return; 22 23 const auto& readBuffer = ValidateBufferSelection(readTarget); 24 if (!readBuffer) return; 25 26 const auto& writeBuffer = ValidateBufferSelection(writeTarget); 27 if (!writeBuffer) return; 28 29 if (!CheckedInt<GLintptr>(readOffset).isValid() || 30 !CheckedInt<GLintptr>(writeOffset).isValid() || 31 !CheckedInt<GLsizeiptr>(size).isValid()) 32 return ErrorOutOfMemory("offset or size too large for platform."); 33 34 const auto fnValidateOffsetSize = [&](const char* info, WebGLintptr offset, 35 const WebGLBuffer* buffer) { 36 const auto neededBytes = CheckedInt<uint64_t>(offset) + size; 37 if (!neededBytes.isValid() || neededBytes.value() > buffer->ByteLength()) { 38 ErrorInvalidValue("Invalid %s range.", info); 39 return false; 40 } 41 return true; 42 }; 43 44 if (!fnValidateOffsetSize("read", readOffset, readBuffer) || 45 !fnValidateOffsetSize("write", writeOffset, writeBuffer)) { 46 return; 47 } 48 49 if (readBuffer == writeBuffer) { 50 const bool separate = 51 (readOffset + size <= writeOffset || writeOffset + size <= readOffset); 52 if (!separate) { 53 ErrorInvalidValue( 54 "Ranges [readOffset, readOffset + size) and" 55 " [writeOffset, writeOffset + size) overlap."); 56 return; 57 } 58 } 59 60 const auto& readType = readBuffer->Content(); 61 const auto& writeType = writeBuffer->Content(); 62 MOZ_ASSERT(readType != WebGLBuffer::Kind::Undefined); 63 MOZ_ASSERT(writeType != WebGLBuffer::Kind::Undefined); 64 if (writeType != readType) { 65 ErrorInvalidOperation( 66 "Can't copy %s data to %s data.", 67 (readType == WebGLBuffer::Kind::OtherData) ? "other" : "element", 68 (writeType == WebGLBuffer::Kind::OtherData) ? "other" : "element"); 69 return; 70 } 71 72 const ScopedLazyBind readBind(gl, readTarget, readBuffer); 73 const ScopedLazyBind writeBind(gl, writeTarget, writeBuffer); 74 gl->fCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, 75 size); 76 77 writeBuffer->ResetLastUpdateFenceId(); 78 } 79 80 bool WebGL2Context::GetBufferSubData(GLenum target, uint64_t srcByteOffset, 81 const Range<uint8_t>& dest, 82 uint64_t numRows, uint64_t rowDataWidth, 83 uint64_t srcStride, 84 uint64_t destStride) const { 85 const FuncScope funcScope(*this, "getBufferSubData"); 86 if (IsContextLost()) return false; 87 88 const auto& buffer = ValidateBufferSelection(target); 89 if (!buffer) return false; 90 91 uint64_t srcLen = 92 numRows > 0 ? srcStride * (numRows - 1) + rowDataWidth : dest.length(); 93 uint64_t destLen = 94 numRows > 0 ? destStride * (numRows - 1) + rowDataWidth : dest.length(); 95 if (!buffer->ValidateRange(srcByteOffset, srcLen)) return false; 96 if (rowDataWidth > srcStride || rowDataWidth > destStride || 97 destLen > dest.length()) { 98 ErrorInvalidValue("Destination is outside buffer."); 99 return false; 100 } 101 102 //// 103 104 if (!CheckedInt<GLintptr>(srcByteOffset).isValid() || 105 !CheckedInt<GLsizeiptr>(srcLen).isValid()) { 106 ErrorOutOfMemory("Offset or size too large for platform."); 107 return false; 108 } 109 const GLsizeiptr glByteLen(srcLen); 110 111 //// 112 113 switch (buffer->mUsage) { 114 case LOCAL_GL_STATIC_READ: 115 case LOCAL_GL_STREAM_READ: 116 case LOCAL_GL_DYNAMIC_READ: 117 if (mCompletedFenceId < buffer->mLastUpdateFenceId) { 118 GenerateWarning( 119 "Reading from a buffer without checking for previous" 120 " command completion likely causes pipeline stalls." 121 " Please use FenceSync."); 122 } 123 break; 124 default: 125 GenerateWarning( 126 "Reading from a buffer with usage other than *_READ" 127 " causes pipeline stalls. Copy through a STREAM_READ buffer."); 128 break; 129 } 130 131 //// 132 133 const ScopedLazyBind readBind(gl, target, buffer); 134 135 if (srcLen) { 136 const bool isTF = (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER); 137 GLenum mapTarget = target; 138 if (isTF) { 139 gl->fBindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, mEmptyTFO); 140 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, buffer->mGLName); 141 mapTarget = LOCAL_GL_ARRAY_BUFFER; 142 } 143 144 const void* mappedBytes = gl->fMapBufferRange( 145 mapTarget, srcByteOffset, glByteLen, LOCAL_GL_MAP_READ_BIT); 146 if (numRows > 0 && (destStride != srcStride || rowDataWidth != srcStride)) { 147 const uint8_t* srcRow = (const uint8_t*)mappedBytes; 148 uint8_t* destRow = dest.begin().get(); 149 while (numRows > 0) { 150 memcpy(destRow, srcRow, rowDataWidth); 151 srcRow += srcStride; 152 destRow += destStride; 153 --numRows; 154 } 155 } else { 156 memcpy(dest.begin().get(), mappedBytes, srcLen); 157 } 158 gl->fUnmapBuffer(mapTarget); 159 160 if (isTF) { 161 const GLuint vbo = (mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0); 162 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, vbo); 163 const GLuint tfo = 164 (mBoundTransformFeedback ? mBoundTransformFeedback->mGLName : 0); 165 gl->fBindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, tfo); 166 } 167 } 168 return true; 169 } 170 171 } // namespace mozilla