gl::Error BufferGL::getIndexRange(GLenum type, size_t offset, size_t count, bool primitiveRestartEnabled, gl::IndexRange *outRange) { ASSERT(!mIsMapped); if (mShadowBufferData) { *outRange = gl::ComputeIndexRange(type, mShadowCopy.data() + offset, count, primitiveRestartEnabled); } else { mStateManager->bindBuffer(DestBufferOperationTarget, mBufferID); const gl::Type &typeInfo = gl::GetTypeInfo(type); const uint8_t *bufferData = MapBufferRangeWithFallback( mFunctions, DestBufferOperationTarget, offset, count * typeInfo.bytes, GL_MAP_READ_BIT); *outRange = gl::ComputeIndexRange(type, bufferData, count, primitiveRestartEnabled); mFunctions->unmapBuffer(DestBufferOperationTarget); } return gl::Error(GL_NO_ERROR); }
gl::Error VertexArrayGL::streamAttributes(const gl::AttributesMask &activeAttributesMask, GLsizei instanceCount, const gl::IndexRange &indexRange) const { // Sync the vertex attribute state and track what data needs to be streamed size_t streamingDataSize = 0; size_t maxAttributeDataSize = 0; computeStreamingAttributeSizes(activeAttributesMask, instanceCount, indexRange, &streamingDataSize, &maxAttributeDataSize); if (streamingDataSize == 0) { return gl::NoError(); } if (mStreamingArrayBuffer == 0) { mFunctions->genBuffers(1, &mStreamingArrayBuffer); mStreamingArrayBufferSize = 0; } // If first is greater than zero, a slack space needs to be left at the beginning of the buffer // so that the same 'first' argument can be passed into the draw call. const size_t bufferEmptySpace = maxAttributeDataSize * indexRange.start; const size_t requiredBufferSize = streamingDataSize + bufferEmptySpace; mStateManager->bindBuffer(gl::BufferBinding::Array, mStreamingArrayBuffer); if (requiredBufferSize > mStreamingArrayBufferSize) { mFunctions->bufferData(GL_ARRAY_BUFFER, requiredBufferSize, nullptr, GL_DYNAMIC_DRAW); mStreamingArrayBufferSize = requiredBufferSize; } // Unmapping a buffer can return GL_FALSE to indicate that the system has corrupted the data // somehow (such as by a screen change), retry writing the data a few times and return // OUT_OF_MEMORY if that fails. GLboolean unmapResult = GL_FALSE; size_t unmapRetryAttempts = 5; while (unmapResult != GL_TRUE && --unmapRetryAttempts > 0) { uint8_t *bufferPointer = MapBufferRangeWithFallback(mFunctions, GL_ARRAY_BUFFER, 0, requiredBufferSize, GL_MAP_WRITE_BIT); size_t curBufferOffset = bufferEmptySpace; const auto &attribs = mState.getVertexAttributes(); const auto &bindings = mState.getVertexBindings(); gl::AttributesMask attribsToStream = (mAttributesNeedStreaming & activeAttributesMask); for (auto idx : attribsToStream) { const auto &attrib = attribs[idx]; ASSERT(IsVertexAttribPointerSupported(idx, attrib)); const auto &binding = bindings[attrib.bindingIndex]; ASSERT(AttributeNeedsStreaming(attrib, binding)); GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor()); const size_t streamedVertexCount = ComputeVertexBindingElementCount( adjustedDivisor, indexRange.vertexCount(), instanceCount); const size_t sourceStride = ComputeVertexAttributeStride(attrib, binding); const size_t destStride = ComputeVertexAttributeTypeSize(attrib); // Vertices do not apply the 'start' offset when the divisor is non-zero even when doing // a non-instanced draw call const size_t firstIndex = adjustedDivisor == 0 ? indexRange.start : 0; // Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state. // https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt const uint8_t *inputPointer = reinterpret_cast<const uint8_t *>(attrib.pointer); // Pack the data when copying it, user could have supplied a very large stride that // would cause the buffer to be much larger than needed. if (destStride == sourceStride) { // Can copy in one go, the data is packed memcpy(bufferPointer + curBufferOffset, inputPointer + (sourceStride * firstIndex), destStride * streamedVertexCount); } else { // Copy each vertex individually for (size_t vertexIdx = 0; vertexIdx < streamedVertexCount; vertexIdx++) { uint8_t *out = bufferPointer + curBufferOffset + (destStride * vertexIdx); const uint8_t *in = inputPointer + sourceStride * (vertexIdx + firstIndex); memcpy(out, in, destStride); } } // Compute where the 0-index vertex would be. const size_t vertexStartOffset = curBufferOffset - (firstIndex * destStride); callVertexAttribPointer(static_cast<GLuint>(idx), attrib, static_cast<GLsizei>(destStride), static_cast<GLintptr>(vertexStartOffset)); curBufferOffset += destStride * streamedVertexCount; } unmapResult = mFunctions->unmapBuffer(GL_ARRAY_BUFFER); } if (unmapResult != GL_TRUE) { return gl::OutOfMemory() << "Failed to unmap the client data streaming buffer."; } return gl::NoError(); }