• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2002 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 // IndexDataManager.cpp: Defines the IndexDataManager, a class that
8 // runs the Buffer translation process for index buffers.
9 
10 #include "libANGLE/renderer/d3d/IndexDataManager.h"
11 
12 #include "common/utilities.h"
13 #include "libANGLE/Buffer.h"
14 #include "libANGLE/Context.h"
15 #include "libANGLE/VertexArray.h"
16 #include "libANGLE/formatutils.h"
17 #include "libANGLE/renderer/d3d/BufferD3D.h"
18 #include "libANGLE/renderer/d3d/ContextD3D.h"
19 #include "libANGLE/renderer/d3d/IndexBuffer.h"
20 
21 namespace rx
22 {
23 
24 namespace
25 {
26 
27 template <typename InputT, typename DestT>
ConvertIndexArray(const void * input,gl::DrawElementsType sourceType,void * output,gl::DrawElementsType destinationType,GLsizei count,bool usePrimitiveRestartFixedIndex)28 void ConvertIndexArray(const void *input,
29                        gl::DrawElementsType sourceType,
30                        void *output,
31                        gl::DrawElementsType destinationType,
32                        GLsizei count,
33                        bool usePrimitiveRestartFixedIndex)
34 {
35     const InputT *in = static_cast<const InputT *>(input);
36     DestT *out       = static_cast<DestT *>(output);
37 
38     if (usePrimitiveRestartFixedIndex)
39     {
40         InputT srcRestartIndex = static_cast<InputT>(gl::GetPrimitiveRestartIndex(sourceType));
41         DestT destRestartIndex = static_cast<DestT>(gl::GetPrimitiveRestartIndex(destinationType));
42         for (GLsizei i = 0; i < count; i++)
43         {
44             out[i] = (in[i] == srcRestartIndex ? destRestartIndex : static_cast<DestT>(in[i]));
45         }
46     }
47     else
48     {
49         for (GLsizei i = 0; i < count; i++)
50         {
51             out[i] = static_cast<DestT>(in[i]);
52         }
53     }
54 }
55 
ConvertIndices(gl::DrawElementsType sourceType,gl::DrawElementsType destinationType,const void * input,GLsizei count,void * output,bool usePrimitiveRestartFixedIndex)56 void ConvertIndices(gl::DrawElementsType sourceType,
57                     gl::DrawElementsType destinationType,
58                     const void *input,
59                     GLsizei count,
60                     void *output,
61                     bool usePrimitiveRestartFixedIndex)
62 {
63     if (sourceType == destinationType)
64     {
65         const GLuint dstTypeSize = gl::GetDrawElementsTypeSize(destinationType);
66         memcpy(output, input, count * dstTypeSize);
67         return;
68     }
69 
70     if (sourceType == gl::DrawElementsType::UnsignedByte)
71     {
72         ASSERT(destinationType == gl::DrawElementsType::UnsignedShort);
73         ConvertIndexArray<GLubyte, GLushort>(input, sourceType, output, destinationType, count,
74                                              usePrimitiveRestartFixedIndex);
75     }
76     else if (sourceType == gl::DrawElementsType::UnsignedShort)
77     {
78         ASSERT(destinationType == gl::DrawElementsType::UnsignedInt);
79         ConvertIndexArray<GLushort, GLuint>(input, sourceType, output, destinationType, count,
80                                             usePrimitiveRestartFixedIndex);
81     }
82     else
83         UNREACHABLE();
84 }
85 
StreamInIndexBuffer(const gl::Context * context,IndexBufferInterface * buffer,const void * data,unsigned int count,gl::DrawElementsType srcType,gl::DrawElementsType dstType,bool usePrimitiveRestartFixedIndex,unsigned int * offset)86 angle::Result StreamInIndexBuffer(const gl::Context *context,
87                                   IndexBufferInterface *buffer,
88                                   const void *data,
89                                   unsigned int count,
90                                   gl::DrawElementsType srcType,
91                                   gl::DrawElementsType dstType,
92                                   bool usePrimitiveRestartFixedIndex,
93                                   unsigned int *offset)
94 {
95     const GLuint dstTypeBytesShift = gl::GetDrawElementsTypeShift(dstType);
96 
97     bool check = (count > (std::numeric_limits<unsigned int>::max() >> dstTypeBytesShift));
98     ANGLE_CHECK(GetImplAs<ContextD3D>(context), !check,
99                 "Reserving indices exceeds the maximum buffer size.", GL_OUT_OF_MEMORY);
100 
101     unsigned int bufferSizeRequired = count << dstTypeBytesShift;
102     ANGLE_TRY(buffer->reserveBufferSpace(context, bufferSizeRequired, dstType));
103 
104     void *output = nullptr;
105     ANGLE_TRY(buffer->mapBuffer(context, bufferSizeRequired, &output, offset));
106 
107     ConvertIndices(srcType, dstType, data, count, output, usePrimitiveRestartFixedIndex);
108 
109     ANGLE_TRY(buffer->unmapBuffer(context));
110     return angle::Result::Continue;
111 }
112 }  // anonymous namespace
113 
114 // IndexDataManager implementation.
IndexDataManager(BufferFactoryD3D * factory)115 IndexDataManager::IndexDataManager(BufferFactoryD3D *factory)
116     : mFactory(factory), mStreamingBufferShort(), mStreamingBufferInt()
117 {}
118 
~IndexDataManager()119 IndexDataManager::~IndexDataManager() {}
120 
deinitialize()121 void IndexDataManager::deinitialize()
122 {
123     mStreamingBufferShort.reset();
124     mStreamingBufferInt.reset();
125 }
126 
127 // This function translates a GL-style indices into DX-style indices, with their description
128 // returned in translated.
129 // GL can specify vertex data in immediate mode (pointer to CPU array of indices), which is not
130 // possible in DX and requires streaming (Case 1). If the GL indices are specified with a buffer
131 // (Case 2), in a format supported by DX (subcase a) then all is good.
132 // When we have a buffer with an unsupported format (subcase b) then we need to do some translation:
133 // we will start by falling back to streaming, and after a while will start using a static
134 // translated copy of the index buffer.
prepareIndexData(const gl::Context * context,gl::DrawElementsType srcType,gl::DrawElementsType dstType,GLsizei count,gl::Buffer * glBuffer,const void * indices,TranslatedIndexData * translated)135 angle::Result IndexDataManager::prepareIndexData(const gl::Context *context,
136                                                  gl::DrawElementsType srcType,
137                                                  gl::DrawElementsType dstType,
138                                                  GLsizei count,
139                                                  gl::Buffer *glBuffer,
140                                                  const void *indices,
141                                                  TranslatedIndexData *translated)
142 {
143     GLuint srcTypeBytes = gl::GetDrawElementsTypeSize(srcType);
144     GLuint srcTypeShift = gl::GetDrawElementsTypeShift(srcType);
145     GLuint dstTypeShift = gl::GetDrawElementsTypeShift(dstType);
146 
147     BufferD3D *buffer = glBuffer ? GetImplAs<BufferD3D>(glBuffer) : nullptr;
148 
149     translated->indexType                 = dstType;
150     translated->srcIndexData.srcBuffer    = buffer;
151     translated->srcIndexData.srcIndices   = indices;
152     translated->srcIndexData.srcIndexType = srcType;
153     translated->srcIndexData.srcCount     = count;
154 
155     // Context can be nullptr in perf tests.
156     bool primitiveRestartFixedIndexEnabled =
157         context ? context->getState().isPrimitiveRestartEnabled() : false;
158 
159     // Case 1: the indices are passed by pointer, which forces the streaming of index data
160     if (glBuffer == nullptr)
161     {
162         translated->storage = nullptr;
163         return streamIndexData(context, indices, count, srcType, dstType,
164                                primitiveRestartFixedIndexEnabled, translated);
165     }
166 
167     // Case 2: the indices are already in a buffer
168     unsigned int offset = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(indices));
169     ASSERT(srcTypeBytes * static_cast<unsigned int>(count) + offset <= buffer->getSize());
170 
171     bool offsetAligned = IsOffsetAligned(srcType, offset);
172 
173     // Case 2a: the buffer can be used directly
174     if (offsetAligned && buffer->supportsDirectBinding() && dstType == srcType)
175     {
176         translated->storage     = buffer;
177         translated->indexBuffer = nullptr;
178         translated->serial      = buffer->getSerial();
179         translated->startIndex  = (offset >> srcTypeShift);
180         translated->startOffset = offset;
181         return angle::Result::Continue;
182     }
183 
184     translated->storage = nullptr;
185 
186     // Case 2b: use a static translated copy or fall back to streaming
187     StaticIndexBufferInterface *staticBuffer = buffer->getStaticIndexBuffer();
188 
189     bool staticBufferInitialized = staticBuffer && staticBuffer->getBufferSize() != 0;
190     bool staticBufferUsable =
191         staticBuffer && offsetAligned && staticBuffer->getIndexType() == dstType;
192 
193     if (staticBufferInitialized && !staticBufferUsable)
194     {
195         buffer->invalidateStaticData(context);
196         staticBuffer = nullptr;
197     }
198 
199     if (staticBuffer == nullptr || !offsetAligned)
200     {
201         const uint8_t *bufferData = nullptr;
202         ANGLE_TRY(buffer->getData(context, &bufferData));
203         ASSERT(bufferData != nullptr);
204 
205         ANGLE_TRY(streamIndexData(context, bufferData + offset, count, srcType, dstType,
206                                   primitiveRestartFixedIndexEnabled, translated));
207         buffer->promoteStaticUsage(context, count << srcTypeShift);
208     }
209     else
210     {
211         if (!staticBufferInitialized)
212         {
213             const uint8_t *bufferData = nullptr;
214             ANGLE_TRY(buffer->getData(context, &bufferData));
215             ASSERT(bufferData != nullptr);
216 
217             unsigned int convertCount =
218                 static_cast<unsigned int>(buffer->getSize()) >> srcTypeShift;
219             ANGLE_TRY(StreamInIndexBuffer(context, staticBuffer, bufferData, convertCount, srcType,
220                                           dstType, primitiveRestartFixedIndexEnabled, nullptr));
221         }
222         ASSERT(offsetAligned && staticBuffer->getIndexType() == dstType);
223 
224         translated->indexBuffer = staticBuffer->getIndexBuffer();
225         translated->serial      = staticBuffer->getSerial();
226         translated->startIndex  = (offset >> srcTypeShift);
227         translated->startOffset = (offset >> srcTypeShift) << dstTypeShift;
228     }
229 
230     return angle::Result::Continue;
231 }
232 
streamIndexData(const gl::Context * context,const void * data,unsigned int count,gl::DrawElementsType srcType,gl::DrawElementsType dstType,bool usePrimitiveRestartFixedIndex,TranslatedIndexData * translated)233 angle::Result IndexDataManager::streamIndexData(const gl::Context *context,
234                                                 const void *data,
235                                                 unsigned int count,
236                                                 gl::DrawElementsType srcType,
237                                                 gl::DrawElementsType dstType,
238                                                 bool usePrimitiveRestartFixedIndex,
239                                                 TranslatedIndexData *translated)
240 {
241     const GLuint dstTypeShift = gl::GetDrawElementsTypeShift(dstType);
242 
243     IndexBufferInterface *indexBuffer = nullptr;
244     ANGLE_TRY(getStreamingIndexBuffer(context, dstType, &indexBuffer));
245     ASSERT(indexBuffer != nullptr);
246 
247     unsigned int offset;
248     ANGLE_TRY(StreamInIndexBuffer(context, indexBuffer, data, count, srcType, dstType,
249                                   usePrimitiveRestartFixedIndex, &offset));
250 
251     translated->indexBuffer = indexBuffer->getIndexBuffer();
252     translated->serial      = indexBuffer->getSerial();
253     translated->startIndex  = (offset >> dstTypeShift);
254     translated->startOffset = offset;
255 
256     return angle::Result::Continue;
257 }
258 
getStreamingIndexBuffer(const gl::Context * context,gl::DrawElementsType destinationIndexType,IndexBufferInterface ** outBuffer)259 angle::Result IndexDataManager::getStreamingIndexBuffer(const gl::Context *context,
260                                                         gl::DrawElementsType destinationIndexType,
261                                                         IndexBufferInterface **outBuffer)
262 {
263     ASSERT(outBuffer);
264     ASSERT(destinationIndexType == gl::DrawElementsType::UnsignedShort ||
265            destinationIndexType == gl::DrawElementsType::UnsignedInt);
266 
267     auto &streamingBuffer = (destinationIndexType == gl::DrawElementsType::UnsignedInt)
268                                 ? mStreamingBufferInt
269                                 : mStreamingBufferShort;
270 
271     if (!streamingBuffer)
272     {
273         StreamingBuffer newBuffer(new StreamingIndexBufferInterface(mFactory));
274         ANGLE_TRY(newBuffer->reserveBufferSpace(context, INITIAL_INDEX_BUFFER_SIZE,
275                                                 destinationIndexType));
276         streamingBuffer = std::move(newBuffer);
277     }
278 
279     *outBuffer = streamingBuffer.get();
280     return angle::Result::Continue;
281 }
282 
GetIndexTranslationDestType(const gl::Context * context,GLsizei indexCount,gl::DrawElementsType indexType,const void * indices,bool usePrimitiveRestartWorkaround,gl::DrawElementsType * destTypeOut)283 angle::Result GetIndexTranslationDestType(const gl::Context *context,
284                                           GLsizei indexCount,
285                                           gl::DrawElementsType indexType,
286                                           const void *indices,
287                                           bool usePrimitiveRestartWorkaround,
288                                           gl::DrawElementsType *destTypeOut)
289 {
290     // Avoid D3D11's primitive restart index value
291     // see http://msdn.microsoft.com/en-us/library/windows/desktop/bb205124(v=vs.85).aspx
292     if (usePrimitiveRestartWorkaround)
293     {
294         // Conservatively assume we need to translate the indices for draw indirect.
295         // This is a bit of a trick. We assume the count for an indirect draw is zero.
296         if (indexCount == 0)
297         {
298             *destTypeOut = gl::DrawElementsType::UnsignedInt;
299             return angle::Result::Continue;
300         }
301 
302         gl::IndexRange indexRange;
303         ANGLE_TRY(context->getState().getVertexArray()->getIndexRange(
304             context, indexType, indexCount, indices, &indexRange));
305         if (indexRange.end == gl::GetPrimitiveRestartIndex(indexType))
306         {
307             *destTypeOut = gl::DrawElementsType::UnsignedInt;
308             return angle::Result::Continue;
309         }
310     }
311 
312     *destTypeOut = (indexType == gl::DrawElementsType::UnsignedInt)
313                        ? gl::DrawElementsType::UnsignedInt
314                        : gl::DrawElementsType::UnsignedShort;
315     return angle::Result::Continue;
316 }
317 
318 }  // namespace rx
319