1# Introduction 2 3Since Direct3D 9 only supports buffers that either contain vertex or index data, 4and OpenGL buffers can contain both, ANGLE waits till a draw call is issued to 5determine which resources to create/update. The generic implementation 'streams' 6the data into global vertex and index buffers. This streaming buffer 7implementation works in all circumstances, but does not offer optimal 8performance. When buffer data isn't updated, there's no reason to copy the data 9again. For these cases a 'static' buffer implementation is used. 10 11The OpenGL ES 2.0 glBufferData() function allows to specify a usage hint 12parameter (GL\_STREAM\_DRAW, GL\_DYNAMIC\_DRAW or GL\_STATIC\_DRAW). Both 13GL\_STREAM\_DRAW and GL\_DYNAMIC\_DRAW use the streaming buffer implementation. 14With the GL\_STATIC\_DRAW hint, ANGLE will attempt to use the static buffer 15implementation. If you update the buffer data after it has already been used in 16a draw call, it falls back to the streaming buffer implementation, because 17updating static ones would involve creating new ones, which is slower than 18updating streaming ones (more on this later). 19 20Because some applications use GL\_STREAM\_DRAW or GL\_DYNAMIC\_DRAW even when 21the data is not or very infrequently updated, ANGLE also has a heuristic to 22promote buffers to use the static implementation. 23 24# Streaming buffers 25 26The streaming buffers implementation uses one Context-global vertex buffer 27(VertexDataManager::mStreamingBuffer) and two index buffers 28(IndexDataManager::mStreamingBufferShort and 29IndexDataManager::mStreamingBufferInt). The streaming behavior is achieved by 30writing new data behind previously written data (i.e. without overwriting old 31data). Direct3D 9 allows to efficiently update vertex and index buffers when 32you're not reading or overwriting anything (it won't stall waiting for the GPU 33finish using it). 34 35When the end of these streaming buffers is reached, they are 'recycled' by 36discarding their content. D3D9 will still keep a copy of the data that's in use, 37so this recycling efficiently renames the driver level buffers. ANGLE can then 38write new data to the beginning of the vertex or index buffer. 39 40The ArrayVertexBuffer::mWritePosition variable holds the current end position of 41the last data that was written. StreamingVertexBuffer::reserveRequiredSpace() 42allocates space to write the data, and StreamingVertexBuffer::map() actually 43locks the D3D buffer and updates the write position. Similar for index buffers. 44 45# Static buffers 46 47Each GL buffer object can have a corresponding static vertex or index buffer 48(Buffer::mVertexBuffer and Buffer::mIndexBuffer). When a GL buffer with static 49usage is used in a draw call for the first time, all of its data is converted to 50a D3D vertex or index buffer, based on the attribute or index formats 51respectively. If a subsequent draw call uses different formats, the static 52buffer is invalidated (deleted) and the streaming buffer implementation is used 53for this buffer object instead. So for optimal performance it's important to 54store only a single format of vertices or indices in a buffer. This is highly 55typical, and even when in some cases it falls back to the streaming buffer 56implementation the performance isn't bad at all. 57 58The StreamingVertexBuffer and StaticVertexBuffer classes share a common base 59class, ArrayVertexBuffer. StaticVertexBuffer also has access to the write 60position, but it's used only for the initial conversion of the data. So the 61interfaces of both classes are not that different. Static buffers have an exact 62size though, and can't be changed afterwards (streaming buffers can grow to 63handle draw calls which use more data, and avoid excessive recycling). 64StaticVertexBuffer has a lookupAttribute() method to retrieve the location of a 65certain attribute (this is also used to verify that the formats haven't changed, 66which would result in invalidating the static buffer). The descriptions of all 67the attribute formats a static buffer contains are stored in the 68StaticVertexBuffer::mCache vector. 69 70StaticIndexBuffer also caches information about what's stored in them, namely 71the minimum and maximum value for certain ranges of indices. This information is 72required by the Direct3D 9 draw calls, and is also used to know the range of 73vertices that need to be copied to the streaming vertex buffer in case it needs 74to be used (e.g. it is not uncommon to have a buffer with static vertex position 75data and a buffer with streaming texture coordinate data for skinning). 76 77# Constant attributes 78 79Aside from using array buffers to feed attribute data to the vertex shader, 80OpenGL also supports attributes which remain constant for all vertices used in a 81draw call. Direct3D 9 doesn't have a similar concept, at least not explicitly. 82 83Constant attributes are implemented using separate (static) vertex buffers, 84and uses a stride of 0 to ensure that every vertex retrieves the same data. 85Using a stride of 0 is not possible with streaming buffers because on some 86hardware it is incompatible with the D3DUSAGE\_DYNAMIC flag. We found that with 87static usage, all hardware tested so far can handle stride 0 fine. 88 89This functionality was implemented in a ConstantVertexBuffer class, and it 90integrates nicely with the rest of the static buffer implementation. 91 92# Line loops 93 94Direct3D 9 does not support the 'line loop' primitive type directly. This is 95implemented by drawing the 'closing' line segment separately, constructing a 96tiny temporary index buffer connecting the last and first vertex. 97 98# Putting it all together 99 100glDrawElements() calls IndexDataManager::prepareIndexData() to retrieve a 101Direct3D index buffer containing the necessary data. If an element array is used 102(i.e. a buffer object), it has static usage, and it hasn't been invalidated, the 103GL buffer's static D3D index buffer will be returned. Else the updated streaming 104index buffer is returned, as well as the index offset (write position) where the 105new data is located. When prepareIndexData() does find a static index buffer, 106but it's empty, it means the GL buffer's data hasn't been converted and stored 107in the D3D index buffer yet. So in the convertIndices() call it will convert the 108entire buffer. prepareIndexData() will also look up the min/max value of a range 109of indices, or computes it when not already in the static buffer or when a 110streaming buffer is used. 111 112Similarly, both glDrawElements() and glDrawArrays() both call 113VertexDataManager::prepareVertexData() to retrieve a set of Direct3D vertex 114buffers and their translated format and offset information. It's implementation 115is more complicated than prepareIndexData() because buffer objects can contain 116multiple vertex attributes, and multiple buffers can be used as input to the 117vertex shader. So first it accumulates how much storage space is required for 118each of the buffers in use. For all static non-empty buffers in use, it 119determines whether the stored attributes still match what is required by the 120draw call, and invalidates them if not (at which point more space is allocated 121in the streaming buffer). Converting the GL buffer object's data into D3D 122compatible vertex formats is still done by specialized template functions. 123