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