void VertexArrayGL::computeStreamingAttributeSizes(const gl::AttributesMask &activeAttributesMask, GLsizei instanceCount, const gl::IndexRange &indexRange, size_t *outStreamingDataSize, size_t *outMaxAttributeDataSize) const { *outStreamingDataSize = 0; *outMaxAttributeDataSize = 0; ASSERT(mAttributesNeedStreaming.any()); const auto &attribs = mState.getVertexAttributes(); const auto &bindings = mState.getVertexBindings(); gl::AttributesMask attribsToStream = (mAttributesNeedStreaming & activeAttributesMask); for (auto idx : attribsToStream) { const auto &attrib = attribs[idx]; const auto &binding = bindings[attrib.bindingIndex]; ASSERT(AttributeNeedsStreaming(attrib, binding)); // If streaming is going to be required, compute the size of the required buffer // and how much slack space at the beginning of the buffer will be required by determining // the attribute with the largest data size. size_t typeSize = ComputeVertexAttributeTypeSize(attrib); GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor()); *outStreamingDataSize += typeSize * ComputeVertexBindingElementCount(adjustedDivisor, indexRange.vertexCount(), instanceCount); *outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize); } }
void VertexArrayGL::computeStreamingAttributeSizes(const gl::AttributesMask &activeAttributesMask, GLsizei instanceCount, const gl::IndexRange &indexRange, size_t *outStreamingDataSize, size_t *outMaxAttributeDataSize) const { *outStreamingDataSize = 0; *outMaxAttributeDataSize = 0; ASSERT(mAttributesNeedStreaming.any()); const auto &attribs = mData.getVertexAttributes(); for (unsigned int idx : angle::IterateBitSet(mAttributesNeedStreaming & activeAttributesMask)) { const auto &attrib = attribs[idx]; ASSERT(AttributeNeedsStreaming(attrib)); // If streaming is going to be required, compute the size of the required buffer // and how much slack space at the beginning of the buffer will be required by determining // the attribute with the largest data size. size_t typeSize = ComputeVertexAttributeTypeSize(attrib); *outStreamingDataSize += typeSize * ComputeVertexAttributeElementCount( attrib, indexRange.vertexCount(), instanceCount); *outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize); } }
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(); }
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::Error(GL_NO_ERROR); } 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_ARRAY_BUFFER, 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 = reinterpret_cast<uint8_t*>(mFunctions->mapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); size_t curBufferOffset = bufferEmptySpace; const auto &attribs = mData.getVertexAttributes(); for (unsigned int idx : angle::IterateBitSet(mAttributesNeedStreaming & activeAttributesMask)) { const auto &attrib = attribs[idx]; ASSERT(AttributeNeedsStreaming(attrib)); const size_t streamedVertexCount = ComputeVertexAttributeElementCount(attrib, indexRange.vertexCount(), instanceCount); const size_t sourceStride = ComputeVertexAttributeStride(attrib); const size_t destStride = ComputeVertexAttributeTypeSize(attrib); 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 * indexRange.start), 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 + indexRange.start); memcpy(out, in, destStride); } } // Compute where the 0-index vertex would be. const size_t vertexStartOffset = curBufferOffset - (indexRange.start * destStride); if (attrib.pureInteger) { ASSERT(!attrib.normalized); mFunctions->vertexAttribIPointer( idx, attrib.size, attrib.type, static_cast<GLsizei>(destStride), reinterpret_cast<const GLvoid *>(vertexStartOffset)); } else { mFunctions->vertexAttribPointer( idx, attrib.size, attrib.type, attrib.normalized, static_cast<GLsizei>(destStride), reinterpret_cast<const GLvoid *>(vertexStartOffset)); } curBufferOffset += destStride * streamedVertexCount; // Mark the applied attribute as dirty by setting an invalid size so that if it doesn't // need to be streamed later, there is no chance that the caching will skip it. mAppliedAttributes[idx].size = static_cast<GLuint>(-1); } unmapResult = mFunctions->unmapBuffer(GL_ARRAY_BUFFER); } if (unmapResult != GL_TRUE) { return Error(GL_OUT_OF_MEMORY, "Failed to unmap the client data streaming buffer."); } return Error(GL_NO_ERROR); }