IndexDataManager.cpp (13015B)
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> 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 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 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. 115 IndexDataManager::IndexDataManager(BufferFactoryD3D *factory) 116 : mFactory(factory), mStreamingBufferShort(), mStreamingBufferInt() 117 {} 118 119 IndexDataManager::~IndexDataManager() {} 120 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. 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 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 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 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