• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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