Can I use a tuple to force a certain memory layout?

Many APIs like OpenGL take arrays where the atomic unit is multiple
elements long. For example, a buffer of coordinates laid out like

:[Float] = [ x1, y1, z1, x2, y2, z2, ... , xn, yn, zn ]

I want to be able to define *in Swift* (i.e., without creating and
importing a Objective C module) a struct that preserves the layout, so that
I can do withMemoryRebound(to:capacity:_) or something similar and treat
the buffer as

struct Point
{
    let x:Float,
        y:Float,
        z:Float
}

:[Point] = [ point1, point2, ... , pointn ]

The memory layout of the struct isn’t guaranteed, but will the layout be
guaranteed to be in declaration order if I use a tuple inside the struct
instead?

struct Point
{
    let _point:(x:Float, y:Float, z:Float)

    var x:Float
    {
        return self._point.x
    }

    var y:Float
    {
        return self._point.y
    }

    var z:Float
    {
        return self._point.z
    }
}

This is an ugly workaround, but I can’t really think of any alternatives
that don’t involve “import something from Objective C”. I am aware that the
implementation of structs currently lays them out in declaration order, but
I’m looking for something that’s actually defined in the language.

IIRC, Swift doesn't have a way to get a guaranteed memory layout yet, other than defining the type in C (or Obj-C) and importing it.

- Dave Sweeris

···

On Jul 19, 2017, at 20:33, Taylor Swift via swift-users <swift-users@swift.org> wrote:

Many APIs like OpenGL take arrays where the atomic unit is multiple elements long. For example, a buffer of coordinates laid out like

:[Float] = [ x1, y1, z1, x2, y2, z2, ... , xn, yn, zn ]

I want to be able to define in Swift (i.e., without creating and importing a Objective C module) a struct that preserves the layout, so that I can do withMemoryRebound(to:capacity:_) or something similar and treat the buffer as

struct Point
{
    let x:Float,
        y:Float,
        z:Float
}

:[Point] = [ point1, point2, ... , pointn ]

The memory layout of the struct isn’t guaranteed, but will the layout be guaranteed to be in declaration order if I use a tuple inside the struct instead?

struct Point
{
    let _point:(x:Float, y:Float, z:Float)

    var x:Float
    {
        return self._point.x
    }

    var y:Float
    {
        return self._point.y
    }

    var z:Float
    {
        return self._point.z
    }
}

This is an ugly workaround, but I can’t really think of any alternatives that don’t involve “import something from Objective C”. I am aware that the implementation of structs currently lays them out in declaration order, but I’m looking for something that’s actually defined in the language.

When you can (legally) observe it, tuples in Swift have guaranteed standard C-style layout.

John McCall confirms this here: [swift-dev] Guarantees of Tuples as Fixed Sized (stack allocated) Arrays

···

On 20 Jul 2017, at 4:33 am, Taylor Swift via swift-users <swift-users@swift.org> wrote:

Many APIs like OpenGL take arrays where the atomic unit is multiple elements long. For example, a buffer of coordinates laid out like

:[Float] = [ x1, y1, z1, x2, y2, z2, ... , xn, yn, zn ]

I want to be able to define in Swift (i.e., without creating and importing a Objective C module) a struct that preserves the layout, so that I can do withMemoryRebound(to:capacity:_) or something similar and treat the buffer as

struct Point
{
    let x:Float,
        y:Float,
        z:Float
}

:[Point] = [ point1, point2, ... , pointn ]

The memory layout of the struct isn’t guaranteed, but will the layout be guaranteed to be in declaration order if I use a tuple inside the struct instead?

struct Point
{
    let _point:(x:Float, y:Float, z:Float)

    var x:Float
    {
        return self._point.x
    }

    var y:Float
    {
        return self._point.y
    }

    var z:Float
    {
        return self._point.z
    }
}

This is an ugly workaround, but I can’t really think of any alternatives that don’t involve “import something from Objective C”. I am aware that the implementation of structs currently lays them out in declaration order, but I’m looking for something that’s actually defined in the language.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Does addressof count as legally observing it?

        var buffers:(GL.UInt, GL.UInt) = (0, 0)
        glGenBuffers(n: 2, buffers: &buffers.0)

Also, I assume Swift performs a swizzle if the tuple is defined in a
separate module from where the pointer to it is constructed?

···

On Thu, Jul 20, 2017 at 4:59 AM, Johannes Weiß <johannesweiss@apple.com> wrote:

When you can (legally) observe it, tuples in Swift have guaranteed
standard C-style layout.

John McCall confirms this here: https://lists.swift.org/
pipermail/swift-dev/Week-of-Mon-20170424/004481.html

> On 20 Jul 2017, at 4:33 am, Taylor Swift via swift-users < > swift-users@swift.org> wrote:
>
> Many APIs like OpenGL take arrays where the atomic unit is multiple
elements long. For example, a buffer of coordinates laid out like
>
> :[Float] = [ x1, y1, z1, x2, y2, z2, ... , xn, yn, zn ]
>
> I want to be able to define in Swift (i.e., without creating and
importing a Objective C module) a struct that preserves the layout, so that
I can do withMemoryRebound(to:capacity:_) or something similar and treat
the buffer as
>
> struct Point
> {
> let x:Float,
> y:Float,
> z:Float
> }
>
> :[Point] = [ point1, point2, ... , pointn ]
>
> The memory layout of the struct isn’t guaranteed, but will the layout be
guaranteed to be in declaration order if I use a tuple inside the struct
instead?
>
> struct Point
> {
> let _point:(x:Float, y:Float, z:Float)
>
> var x:Float
> {
> return self._point.x
> }
>
> var y:Float
> {
> return self._point.y
> }
>
> var z:Float
> {
> return self._point.z
> }
> }
>
> This is an ugly workaround, but I can’t really think of any alternatives
that don’t involve “import something from Objective C”. I am aware that the
implementation of structs currently lays them out in declaration order, but
I’m looking for something that’s actually defined in the language.
>
>
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org
> https://lists.swift.org/mailman/listinfo/swift-users

Hi,

Does addressof count as legally observing it?

        var buffers:(GL.UInt, GL.UInt) = (0, 0)
        glGenBuffers(n: 2, buffers: &buffers.0)

Also, I assume Swift performs a swizzle if the tuple is defined in a separate module from where the pointer to it is constructed?

yes, that's legal assuming the called function doesn't store the pointer and read/write it later.

···

On 20 Jul 2017, at 5:41 pm, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Thu, Jul 20, 2017 at 4:59 AM, Johannes Weiß <johannesweiss@apple.com> wrote:
When you can (legally) observe it, tuples in Swift have guaranteed standard C-style layout.

John McCall confirms this here: [swift-dev] Guarantees of Tuples as Fixed Sized (stack allocated) Arrays

> On 20 Jul 2017, at 4:33 am, Taylor Swift via swift-users <swift-users@swift.org> wrote:
>
> Many APIs like OpenGL take arrays where the atomic unit is multiple elements long. For example, a buffer of coordinates laid out like
>
> :[Float] = [ x1, y1, z1, x2, y2, z2, ... , xn, yn, zn ]
>
> I want to be able to define in Swift (i.e., without creating and importing a Objective C module) a struct that preserves the layout, so that I can do withMemoryRebound(to:capacity:_) or something similar and treat the buffer as
>
> struct Point
> {
> let x:Float,
> y:Float,
> z:Float
> }
>
> :[Point] = [ point1, point2, ... , pointn ]
>
> The memory layout of the struct isn’t guaranteed, but will the layout be guaranteed to be in declaration order if I use a tuple inside the struct instead?
>
> struct Point
> {
> let _point:(x:Float, y:Float, z:Float)
>
> var x:Float
> {
> return self._point.x
> }
>
> var y:Float
> {
> return self._point.y
> }
>
> var z:Float
> {
> return self._point.z
> }
> }
>
> This is an ugly workaround, but I can’t really think of any alternatives that don’t involve “import something from Objective C”. I am aware that the implementation of structs currently lays them out in declaration order, but I’m looking for something that’s actually defined in the language.
>
>
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org
> https://lists.swift.org/mailman/listinfo/swift-users

This does not seem to be the case…

    var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
    glGenBuffers(n: 2, buffers: &buffers.VBO)
    print(buffers)
    // > (VBO: 4, EBO: 0)

    var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
    glGenBuffers(n: 1, buffers: &buffers.VBO)
    glGenBuffers(n: 1, buffers: &buffers.EBO)
    print(buffers)
    // > (VBO: 4, EBO: 5)

···

On Thu, Jul 20, 2017 at 1:18 PM, Johannes Weiß <johannesweiss@apple.com> wrote:

Hi,

> On 20 Jul 2017, at 5:41 pm, Taylor Swift <kelvin13ma@gmail.com> wrote:
>
> Does addressof count as legally observing it?
>
> var buffers:(GL.UInt, GL.UInt) = (0, 0)
> glGenBuffers(n: 2, buffers: &buffers.0)
>
> Also, I assume Swift performs a swizzle if the tuple is defined in a
separate module from where the pointer to it is constructed?

yes, that's legal assuming the called function doesn't store the pointer
and read/write it later.

>
> On Thu, Jul 20, 2017 at 4:59 AM, Johannes Weiß <johannesweiss@apple.com> > wrote:
> When you can (legally) observe it, tuples in Swift have guaranteed
standard C-style layout.
>
> John McCall confirms this here: https://lists.swift.org/
pipermail/swift-dev/Week-of-Mon-20170424/004481.html
>
> > On 20 Jul 2017, at 4:33 am, Taylor Swift via swift-users < > swift-users@swift.org> wrote:
> >
> > Many APIs like OpenGL take arrays where the atomic unit is multiple
elements long. For example, a buffer of coordinates laid out like
> >
> > :[Float] = [ x1, y1, z1, x2, y2, z2, ... , xn, yn, zn ]
> >
> > I want to be able to define in Swift (i.e., without creating and
importing a Objective C module) a struct that preserves the layout, so that
I can do withMemoryRebound(to:capacity:_) or something similar and treat
the buffer as
> >
> > struct Point
> > {
> > let x:Float,
> > y:Float,
> > z:Float
> > }
> >
> > :[Point] = [ point1, point2, ... , pointn ]
> >
> > The memory layout of the struct isn’t guaranteed, but will the layout
be guaranteed to be in declaration order if I use a tuple inside the struct
instead?
> >
> > struct Point
> > {
> > let _point:(x:Float, y:Float, z:Float)
> >
> > var x:Float
> > {
> > return self._point.x
> > }
> >
> > var y:Float
> > {
> > return self._point.y
> > }
> >
> > var z:Float
> > {
> > return self._point.z
> > }
> > }
> >
> > This is an ugly workaround, but I can’t really think of any
alternatives that don’t involve “import something from Objective C”. I am
aware that the implementation of structs currently lays them out in
declaration order, but I’m looking for something that’s actually defined in
the language.
> >
> >
> > _______________________________________________
> > swift-users mailing list
> > swift-users@swift.org
> > https://lists.swift.org/mailman/listinfo/swift-users
>
>

Okay, apparently layout is only guaranteed if the reference is to the tuple
itself, not a member of the tuple. Don’t know if this is a bug or intended
behavior. The above code works when written as

        var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
        withUnsafeMutablePointer(to: &buffers)
        {
            $0.withMemoryRebound(to: UInt32.self, capacity: 2)
            {
                glGenBuffers(n: 2, buffers: $0)
            }
        }

···

On Thu, Jul 20, 2017 at 3:01 PM, Taylor Swift via swift-users < swift-users@swift.org> wrote:

This does not seem to be the case…

    var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
    glGenBuffers(n: 2, buffers: &buffers.VBO)
    print(buffers)
    // > (VBO: 4, EBO: 0)

    var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
    glGenBuffers(n: 1, buffers: &buffers.VBO)
    glGenBuffers(n: 1, buffers: &buffers.EBO)
    print(buffers)
    // > (VBO: 4, EBO: 5)

On Thu, Jul 20, 2017 at 1:18 PM, Johannes Weiß <johannesweiss@apple.com> > wrote:

Hi,

> On 20 Jul 2017, at 5:41 pm, Taylor Swift <kelvin13ma@gmail.com> wrote:
>
> Does addressof count as legally observing it?
>
> var buffers:(GL.UInt, GL.UInt) = (0, 0)
> glGenBuffers(n: 2, buffers: &buffers.0)
>
> Also, I assume Swift performs a swizzle if the tuple is defined in a
separate module from where the pointer to it is constructed?

yes, that's legal assuming the called function doesn't store the pointer
and read/write it later.

>
> On Thu, Jul 20, 2017 at 4:59 AM, Johannes Weiß <johannesweiss@apple.com> >> wrote:
> When you can (legally) observe it, tuples in Swift have guaranteed
standard C-style layout.
>
> John McCall confirms this here: https://lists.swift.org/piperm
ail/swift-dev/Week-of-Mon-20170424/004481.html
>
> > On 20 Jul 2017, at 4:33 am, Taylor Swift via swift-users < >> swift-users@swift.org> wrote:
> >
> > Many APIs like OpenGL take arrays where the atomic unit is multiple
elements long. For example, a buffer of coordinates laid out like
> >
> > :[Float] = [ x1, y1, z1, x2, y2, z2, ... , xn, yn, zn ]
> >
> > I want to be able to define in Swift (i.e., without creating and
importing a Objective C module) a struct that preserves the layout, so that
I can do withMemoryRebound(to:capacity:_) or something similar and treat
the buffer as
> >
> > struct Point
> > {
> > let x:Float,
> > y:Float,
> > z:Float
> > }
> >
> > :[Point] = [ point1, point2, ... , pointn ]
> >
> > The memory layout of the struct isn’t guaranteed, but will the layout
be guaranteed to be in declaration order if I use a tuple inside the struct
instead?
> >
> > struct Point
> > {
> > let _point:(x:Float, y:Float, z:Float)
> >
> > var x:Float
> > {
> > return self._point.x
> > }
> >
> > var y:Float
> > {
> > return self._point.y
> > }
> >
> > var z:Float
> > {
> > return self._point.z
> > }
> > }
> >
> > This is an ugly workaround, but I can’t really think of any
alternatives that don’t involve “import something from Objective C”. I am
aware that the implementation of structs currently lays them out in
declaration order, but I’m looking for something that’s actually defined in
the language.
> >
> >
> > _______________________________________________
> > swift-users mailing list
> > swift-users@swift.org
> > https://lists.swift.org/mailman/listinfo/swift-users
>
>

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Hi,

This does not seem to be the case…

    var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
    glGenBuffers(n: 2, buffers: &buffers.VBO)

this is definitely illegal as you're writing 2 GL.UInts and you're giving it a pointer to only one. You're only legally observing the address of buffers.VBO and not buffers.EBO.

    print(buffers)
    // > (VBO: 4, EBO: 0)

    var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
    glGenBuffers(n: 1, buffers: &buffers.VBO)
    glGenBuffers(n: 1, buffers: &buffers.EBO)

these uses seem legal.

-- Johannes

···

On 20 Jul 2017, at 7:54 pm, Taylor Swift <kelvin13ma@gmail.com> wrote:

    print(buffers)
    // > (VBO: 4, EBO: 5)

On Thu, Jul 20, 2017 at 1:18 PM, Johannes Weiß <johannesweiss@apple.com> wrote:
Hi,

> On 20 Jul 2017, at 5:41 pm, Taylor Swift <kelvin13ma@gmail.com> wrote:
>
> Does addressof count as legally observing it?
>
> var buffers:(GL.UInt, GL.UInt) = (0, 0)
> glGenBuffers(n: 2, buffers: &buffers.0)
>
> Also, I assume Swift performs a swizzle if the tuple is defined in a separate module from where the pointer to it is constructed?

yes, that's legal assuming the called function doesn't store the pointer and read/write it later.

>
> On Thu, Jul 20, 2017 at 4:59 AM, Johannes Weiß <johannesweiss@apple.com> wrote:
> When you can (legally) observe it, tuples in Swift have guaranteed standard C-style layout.
>
> John McCall confirms this here: [swift-dev] Guarantees of Tuples as Fixed Sized (stack allocated) Arrays
>
> > On 20 Jul 2017, at 4:33 am, Taylor Swift via swift-users <swift-users@swift.org> wrote:
> >
> > Many APIs like OpenGL take arrays where the atomic unit is multiple elements long. For example, a buffer of coordinates laid out like
> >
> > :[Float] = [ x1, y1, z1, x2, y2, z2, ... , xn, yn, zn ]
> >
> > I want to be able to define in Swift (i.e., without creating and importing a Objective C module) a struct that preserves the layout, so that I can do withMemoryRebound(to:capacity:_) or something similar and treat the buffer as
> >
> > struct Point
> > {
> > let x:Float,
> > y:Float,
> > z:Float
> > }
> >
> > :[Point] = [ point1, point2, ... , pointn ]
> >
> > The memory layout of the struct isn’t guaranteed, but will the layout be guaranteed to be in declaration order if I use a tuple inside the struct instead?
> >
> > struct Point
> > {
> > let _point:(x:Float, y:Float, z:Float)
> >
> > var x:Float
> > {
> > return self._point.x
> > }
> >
> > var y:Float
> > {
> > return self._point.y
> > }
> >
> > var z:Float
> > {
> > return self._point.z
> > }
> > }
> >
> > This is an ugly workaround, but I can’t really think of any alternatives that don’t involve “import something from Objective C”. I am aware that the implementation of structs currently lays them out in declaration order, but I’m looking for something that’s actually defined in the language.
> >
> >
> > _______________________________________________
> > swift-users mailing list
> > swift-users@swift.org
> > https://lists.swift.org/mailman/listinfo/swift-users
>
>

this is legal now as you're now observing the whole tuple rather than just one value. That means they're now guaranteed to be in standard C layout.

In other words, Swift could have the tuple in memory like this

···

On 21 Jul 2017, at 1:45 am, Taylor Swift <kelvin13ma@gmail.com> wrote:

Okay, apparently layout is only guaranteed if the reference is to the tuple itself, not a member of the tuple. Don’t know if this is a bug or intended behavior. The above code works when written as

        var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
        withUnsafeMutablePointer(to: &buffers)
        {
            $0.withMemoryRebound(to: UInt32.self, capacity: 2)
            {
                glGenBuffers(n: 2, buffers: $0)
            }
        }

+-----+----------+-----+

VBO | whatever | EBO |

+-----+----------+-----+
   ^ ^ ^
   > > >
   a b c

when you now get a pointer to &buffers.VBO, you'll get the pointer 'a' and as you see, you'll not be guaranteed to have EBO right next to it.

It you however request a pointer to `buffers` as a whole, the Swift compiler will do whatever is necessary to give you a compound view in standard C layout. Ie. it might copy it into

+-----+-----+

VBO | EBO |

+-----+-----+

and after the call returns put the VBO and EBO values back where they're supposed to be.

Does that make sense?

-- Johannes

On Thu, Jul 20, 2017 at 3:01 PM, Taylor Swift via swift-users <swift-users@swift.org> wrote:
This does not seem to be the case…

    var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
    glGenBuffers(n: 2, buffers: &buffers.VBO)
    print(buffers)
    // > (VBO: 4, EBO: 0)

    var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
    glGenBuffers(n: 1, buffers: &buffers.VBO)
    glGenBuffers(n: 1, buffers: &buffers.EBO)
    print(buffers)
    // > (VBO: 4, EBO: 5)

On Thu, Jul 20, 2017 at 1:18 PM, Johannes Weiß <johannesweiss@apple.com> wrote:
Hi,

> On 20 Jul 2017, at 5:41 pm, Taylor Swift <kelvin13ma@gmail.com> wrote:
>
> Does addressof count as legally observing it?
>
> var buffers:(GL.UInt, GL.UInt) = (0, 0)
> glGenBuffers(n: 2, buffers: &buffers.0)
>
> Also, I assume Swift performs a swizzle if the tuple is defined in a separate module from where the pointer to it is constructed?

yes, that's legal assuming the called function doesn't store the pointer and read/write it later.

>
> On Thu, Jul 20, 2017 at 4:59 AM, Johannes Weiß <johannesweiss@apple.com> wrote:
> When you can (legally) observe it, tuples in Swift have guaranteed standard C-style layout.
>
> John McCall confirms this here: [swift-dev] Guarantees of Tuples as Fixed Sized (stack allocated) Arrays
>
> > On 20 Jul 2017, at 4:33 am, Taylor Swift via swift-users <swift-users@swift.org> wrote:
> >
> > Many APIs like OpenGL take arrays where the atomic unit is multiple elements long. For example, a buffer of coordinates laid out like
> >
> > :[Float] = [ x1, y1, z1, x2, y2, z2, ... , xn, yn, zn ]
> >
> > I want to be able to define in Swift (i.e., without creating and importing a Objective C module) a struct that preserves the layout, so that I can do withMemoryRebound(to:capacity:_) or something similar and treat the buffer as
> >
> > struct Point
> > {
> > let x:Float,
> > y:Float,
> > z:Float
> > }
> >
> > :[Point] = [ point1, point2, ... , pointn ]
> >
> > The memory layout of the struct isn’t guaranteed, but will the layout be guaranteed to be in declaration order if I use a tuple inside the struct instead?
> >
> > struct Point
> > {
> > let _point:(x:Float, y:Float, z:Float)
> >
> > var x:Float
> > {
> > return self._point.x
> > }
> >
> > var y:Float
> > {
> > return self._point.y
> > }
> >
> > var z:Float
> > {
> > return self._point.z
> > }
> > }
> >
> > This is an ugly workaround, but I can’t really think of any alternatives that don’t involve “import something from Objective C”. I am aware that the implementation of structs currently lays them out in declaration order, but I’m looking for something that’s actually defined in the language.
> >
> >
> > _______________________________________________
> > swift-users mailing list
> > swift-users@swift.org
> > https://lists.swift.org/mailman/listinfo/swift-users
>
>

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

So... It seems to me that shuffling things around like that would have some overhead. What are the performance implications here? Is it just a matter of profiling our code twice -- once with the type defined in Swift and once with it defined in C? I mean presumably the Swift compiler wouldn't interject "stuff" between the elements without a reason, but OTOH having smaller types means that more of them can fit in cache.

Is this an area where the best answer is "do what works now, and ask again when we get to Swift 5/6/etc"?

- Dave Sweeris

···

On Jul 21, 2017, at 07:55, Johannes Weiß via swift-users <swift-users@swift.org> wrote:

On 21 Jul 2017, at 1:45 am, Taylor Swift <kelvin13ma@gmail.com> wrote:

Okay, apparently layout is only guaranteed if the reference is to the tuple itself, not a member of the tuple. Don’t know if this is a bug or intended behavior. The above code works when written as

       var buffers:(VBO:GL.UInt, EBO:GL.UInt) = (0, 0)
       withUnsafeMutablePointer(to: &buffers)
       {
           $0.withMemoryRebound(to: UInt32.self, capacity: 2)
           {
               glGenBuffers(n: 2, buffers: $0)
           }
       }

this is legal now as you're now observing the whole tuple rather than just one value. That means they're now guaranteed to be in standard C layout.

In other words, Swift could have the tuple in memory like this

+-----+----------+-----+
> VBO | whatever | EBO |
+-----+----------+-----+
  ^ ^ ^
  > > >
  a b c

when you now get a pointer to &buffers.VBO, you'll get the pointer 'a' and as you see, you'll not be guaranteed to have EBO right next to it.

It you however request a pointer to `buffers` as a whole, the Swift compiler will do whatever is necessary to give you a compound view in standard C layout. Ie. it might copy it into

+-----+-----+
> VBO | EBO |
+-----+-----+

and after the call returns put the VBO and EBO values back where they're supposed to be.

Does that make sense?