1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // IndexDataManager.cpp: Defines the IndexDataManager, a class that 16 // runs the Buffer translation process for index buffers. 17 18 #include "IndexDataManager.h" 19 20 #include "Buffer.h" 21 #include "common/debug.h" 22 23 #include <string.h> 24 #include <algorithm> 25 26 namespace 27 { 28 enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) }; 29 } 30 31 namespace gl 32 { 33 34 IndexDataManager::IndexDataManager() 35 { 36 mStreamingBuffer = new StreamingIndexBuffer(INITIAL_INDEX_BUFFER_SIZE); 37 38 if(!mStreamingBuffer) 39 { 40 ERR("Failed to allocate the streaming index buffer."); 41 } 42 } 43 44 IndexDataManager::~IndexDataManager() 45 { 46 delete mStreamingBuffer; 47 } 48 49 void copyIndices(GLenum type, const void *input, GLsizei count, void *output) 50 { 51 if(type == GL_UNSIGNED_BYTE) 52 { 53 memcpy(output, input, count * sizeof(GLubyte)); 54 } 55 else if(type == GL_UNSIGNED_INT) 56 { 57 memcpy(output, input, count * sizeof(GLuint)); 58 } 59 else if(type == GL_UNSIGNED_SHORT) 60 { 61 memcpy(output, input, count * sizeof(GLushort)); 62 } 63 else UNREACHABLE(type); 64 } 65 66 template<class IndexType> 67 void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex) 68 { 69 *minIndex = indices[0]; 70 *maxIndex = indices[0]; 71 72 for(GLsizei i = 0; i < count; i++) 73 { 74 if(*minIndex > indices[i]) *minIndex = indices[i]; 75 if(*maxIndex < indices[i]) *maxIndex = indices[i]; 76 } 77 } 78 79 void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex) 80 { 81 if(type == GL_UNSIGNED_BYTE) 82 { 83 computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex); 84 } 85 else if(type == GL_UNSIGNED_INT) 86 { 87 computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex); 88 } 89 else if(type == GL_UNSIGNED_SHORT) 90 { 91 computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex); 92 } 93 else UNREACHABLE(type); 94 } 95 96 GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated) 97 { 98 if(!mStreamingBuffer) 99 { 100 return GL_OUT_OF_MEMORY; 101 } 102 103 intptr_t offset = reinterpret_cast<intptr_t>(indices); 104 bool alignedOffset = false; 105 106 if(buffer) 107 { 108 switch(type) 109 { 110 case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break; 111 case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break; 112 case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break; 113 default: UNREACHABLE(type); alignedOffset = false; 114 } 115 116 if(typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size())) 117 { 118 return GL_INVALID_OPERATION; 119 } 120 121 indices = static_cast<const GLubyte*>(buffer->data()) + offset; 122 } 123 124 StreamingIndexBuffer *streamingBuffer = mStreamingBuffer; 125 126 sw::Resource *staticBuffer = buffer ? buffer->getResource() : nullptr; 127 128 if(staticBuffer) 129 { 130 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); 131 132 translated->indexBuffer = staticBuffer; 133 translated->indexOffset = offset; 134 } 135 else 136 { 137 unsigned int streamOffset = 0; 138 int convertCount = count; 139 140 streamingBuffer->reserveSpace(convertCount * typeSize(type), type); 141 void *output = streamingBuffer->map(typeSize(type) * convertCount, &streamOffset); 142 143 if(!output) 144 { 145 ERR("Failed to map index buffer."); 146 return GL_OUT_OF_MEMORY; 147 } 148 149 copyIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output); 150 streamingBuffer->unmap(); 151 152 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); 153 154 translated->indexBuffer = streamingBuffer->getResource(); 155 translated->indexOffset = streamOffset; 156 } 157 158 return GL_NO_ERROR; 159 } 160 161 std::size_t IndexDataManager::typeSize(GLenum type) 162 { 163 switch(type) 164 { 165 case GL_UNSIGNED_INT: return sizeof(GLuint); 166 case GL_UNSIGNED_SHORT: return sizeof(GLushort); 167 case GL_UNSIGNED_BYTE: return sizeof(GLubyte); 168 default: UNREACHABLE(type); return sizeof(GLushort); 169 } 170 } 171 172 StreamingIndexBuffer::StreamingIndexBuffer(unsigned int initialSize) : mIndexBuffer(nullptr), mBufferSize(initialSize) 173 { 174 if(initialSize > 0) 175 { 176 mIndexBuffer = new sw::Resource(initialSize + 16); 177 178 if(!mIndexBuffer) 179 { 180 ERR("Out of memory allocating an index buffer of size %u.", initialSize); 181 } 182 } 183 184 mWritePosition = 0; 185 } 186 187 StreamingIndexBuffer::~StreamingIndexBuffer() 188 { 189 if(mIndexBuffer) 190 { 191 mIndexBuffer->destruct(); 192 } 193 } 194 195 void *StreamingIndexBuffer::map(unsigned int requiredSpace, unsigned int *offset) 196 { 197 void *mapPtr = nullptr; 198 199 if(mIndexBuffer) 200 { 201 mapPtr = (char*)mIndexBuffer->lock(sw::PUBLIC) + mWritePosition; 202 203 if(!mapPtr) 204 { 205 ERR(" Lock failed"); 206 return nullptr; 207 } 208 209 *offset = mWritePosition; 210 mWritePosition += requiredSpace; 211 } 212 213 return mapPtr; 214 } 215 216 void StreamingIndexBuffer::unmap() 217 { 218 if(mIndexBuffer) 219 { 220 mIndexBuffer->unlock(); 221 } 222 } 223 224 void StreamingIndexBuffer::reserveSpace(unsigned int requiredSpace, GLenum type) 225 { 226 if(requiredSpace > mBufferSize) 227 { 228 if(mIndexBuffer) 229 { 230 mIndexBuffer->destruct(); 231 mIndexBuffer = 0; 232 } 233 234 mBufferSize = std::max(requiredSpace, 2 * mBufferSize); 235 236 mIndexBuffer = new sw::Resource(mBufferSize + 16); 237 238 if(!mIndexBuffer) 239 { 240 ERR("Out of memory allocating an index buffer of size %u.", mBufferSize); 241 } 242 243 mWritePosition = 0; 244 } 245 else if(mWritePosition + requiredSpace > mBufferSize) // Recycle 246 { 247 if(mIndexBuffer) 248 { 249 mIndexBuffer->destruct(); 250 mIndexBuffer = new sw::Resource(mBufferSize + 16); 251 } 252 253 mWritePosition = 0; 254 } 255 } 256 257 sw::Resource *StreamingIndexBuffer::getResource() const 258 { 259 return mIndexBuffer; 260 } 261 262 } 263