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
IndexDataManager()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
~IndexDataManager()44 IndexDataManager::~IndexDataManager()
45 {
46 delete mStreamingBuffer;
47 }
48
copyIndices(GLenum type,const void * input,GLsizei count,void * output)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>
computeRange(const IndexType * indices,GLsizei count,GLuint * minIndex,GLuint * maxIndex)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
computeRange(GLenum type,const void * indices,GLsizei count,GLuint * minIndex,GLuint * maxIndex)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
prepareIndexData(GLenum type,GLsizei count,Buffer * buffer,const void * indices,TranslatedIndexData * translated)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
typeSize(GLenum type)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
StreamingIndexBuffer(unsigned int initialSize)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
~StreamingIndexBuffer()187 StreamingIndexBuffer::~StreamingIndexBuffer()
188 {
189 if(mIndexBuffer)
190 {
191 mIndexBuffer->destruct();
192 }
193 }
194
map(unsigned int requiredSpace,unsigned int * offset)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
unmap()216 void StreamingIndexBuffer::unmap()
217 {
218 if(mIndexBuffer)
219 {
220 mIndexBuffer->unlock();
221 }
222 }
223
reserveSpace(unsigned int requiredSpace,GLenum type)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
getResource() const257 sw::Resource *StreamingIndexBuffer::getResource() const
258 {
259 return mIndexBuffer;
260 }
261
262 }
263