gl::Error VertexDataManager::storeDynamicAttribs(
    std::vector<TranslatedAttribute> *translatedAttribs,
    const gl::AttributesMask &dynamicAttribsMask,
    GLint start,
    GLsizei count,
    GLsizei instances)
{
    // Reserve the required space for the dynamic buffers.
    for (auto attribIndex : angle::IterateBitSet(dynamicAttribsMask))
    {
        const auto &dynamicAttrib = (*translatedAttribs)[attribIndex];
        gl::Error error = reserveSpaceForAttrib(dynamicAttrib, count, instances);
        if (error.isError())
        {
            return error;
        }
    }

    // Store dynamic attributes
    for (auto attribIndex : angle::IterateBitSet(dynamicAttribsMask))
    {
        auto *dynamicAttrib = &(*translatedAttribs)[attribIndex];
        gl::Error error = storeDynamicAttrib(dynamicAttrib, start, count, instances);
        if (error.isError())
        {
            unmapStreamingBuffer();
            return error;
        }
    }

    unmapStreamingBuffer();

    // Promote static usage of dynamic buffers.
    for (auto attribIndex : angle::IterateBitSet(dynamicAttribsMask))
    {
        auto *dynamicAttrib = &(*translatedAttribs)[attribIndex];
        gl::Buffer *buffer = dynamicAttrib->attribute->buffer.get();
        if (buffer)
        {
            BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer);
            size_t typeSize = ComputeVertexAttributeTypeSize(*dynamicAttrib->attribute);
            bufferD3D->promoteStaticUsage(count * static_cast<int>(typeSize));
        }
    }

    return gl::Error(GL_NO_ERROR);
}
angle::Result VertexDataManager::storeDynamicAttribs(
    const gl::Context *context,
    std::vector<TranslatedAttribute> *translatedAttribs,
    const gl::AttributesMask &dynamicAttribsMask,
    GLint start,
    size_t count,
    GLsizei instances)
{
    // Instantiating this class will ensure the streaming buffer is never left mapped.
    class StreamingBufferUnmapper final : NonCopyable
    {
      public:
        StreamingBufferUnmapper(StreamingVertexBufferInterface *streamingBuffer)
            : mStreamingBuffer(streamingBuffer)
        {
            ASSERT(mStreamingBuffer);
        }
        ~StreamingBufferUnmapper() { mStreamingBuffer->getVertexBuffer()->hintUnmapResource(); }

      private:
        StreamingVertexBufferInterface *mStreamingBuffer;
    };

    // Will trigger unmapping on return.
    StreamingBufferUnmapper localUnmapper(&mStreamingBuffer);

    // Reserve the required space for the dynamic buffers.
    for (auto attribIndex : dynamicAttribsMask)
    {
        const auto &dynamicAttrib = (*translatedAttribs)[attribIndex];
        ANGLE_TRY(reserveSpaceForAttrib(context, dynamicAttrib, start, count, instances));
    }

    // Store dynamic attributes
    for (auto attribIndex : dynamicAttribsMask)
    {
        auto *dynamicAttrib = &(*translatedAttribs)[attribIndex];
        ANGLE_TRY(storeDynamicAttrib(context, dynamicAttrib, start, count, instances));
    }

    return angle::Result::Continue();
}