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 es2
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,GLuint start,GLuint end,GLsizei count,Buffer * buffer,const void * indices,TranslatedIndexData * translated)96 GLenum IndexDataManager::prepareIndexData(GLenum type, GLuint start, GLuint end, 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
105 if(buffer != NULL)
106 {
107 if(typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size()))
108 {
109 return GL_INVALID_OPERATION;
110 }
111
112 indices = static_cast<const GLubyte*>(buffer->data()) + offset;
113 }
114
115 StreamingIndexBuffer *streamingBuffer = mStreamingBuffer;
116
117 sw::Resource *staticBuffer = buffer ? buffer->getResource() : NULL;
118
119 if(staticBuffer)
120 {
121 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
122
123 translated->indexBuffer = staticBuffer;
124 translated->indexOffset = offset;
125 }
126 else
127 {
128 unsigned int streamOffset = 0;
129 int convertCount = count;
130
131 streamingBuffer->reserveSpace(convertCount * typeSize(type), type);
132 void *output = streamingBuffer->map(typeSize(type) * convertCount, &streamOffset);
133
134 if(output == NULL)
135 {
136 ERR("Failed to map index buffer.");
137 return GL_OUT_OF_MEMORY;
138 }
139
140 copyIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output);
141 streamingBuffer->unmap();
142
143 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
144
145 translated->indexBuffer = streamingBuffer->getResource();
146 translated->indexOffset = streamOffset;
147 }
148
149 if(translated->minIndex < start || translated->maxIndex > end)
150 {
151 ERR("glDrawRangeElements: out of range access. Range provided: [%d -> %d]. Range used: [%d -> %d].", start, end, translated->minIndex, translated->maxIndex);
152 }
153
154 return GL_NO_ERROR;
155 }
156
typeSize(GLenum type)157 std::size_t IndexDataManager::typeSize(GLenum type)
158 {
159 switch(type)
160 {
161 case GL_UNSIGNED_INT: return sizeof(GLuint);
162 case GL_UNSIGNED_SHORT: return sizeof(GLushort);
163 case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
164 default: UNREACHABLE(type); return sizeof(GLushort);
165 }
166 }
167
StreamingIndexBuffer(unsigned int initialSize)168 StreamingIndexBuffer::StreamingIndexBuffer(unsigned int initialSize) : mIndexBuffer(NULL), mBufferSize(initialSize)
169 {
170 if(initialSize > 0)
171 {
172 mIndexBuffer = new sw::Resource(initialSize + 16);
173
174 if(!mIndexBuffer)
175 {
176 ERR("Out of memory allocating an index buffer of size %u.", initialSize);
177 }
178 }
179
180 mWritePosition = 0;
181 }
182
~StreamingIndexBuffer()183 StreamingIndexBuffer::~StreamingIndexBuffer()
184 {
185 if(mIndexBuffer)
186 {
187 mIndexBuffer->destruct();
188 }
189 }
190
map(unsigned int requiredSpace,unsigned int * offset)191 void *StreamingIndexBuffer::map(unsigned int requiredSpace, unsigned int *offset)
192 {
193 void *mapPtr = NULL;
194
195 if(mIndexBuffer)
196 {
197 mapPtr = (char*)mIndexBuffer->lock(sw::PUBLIC) + mWritePosition;
198
199 if(!mapPtr)
200 {
201 ERR(" Lock failed");
202 return NULL;
203 }
204
205 *offset = mWritePosition;
206 mWritePosition += requiredSpace;
207 }
208
209 return mapPtr;
210 }
211
unmap()212 void StreamingIndexBuffer::unmap()
213 {
214 if(mIndexBuffer)
215 {
216 mIndexBuffer->unlock();
217 }
218 }
219
reserveSpace(unsigned int requiredSpace,GLenum type)220 void StreamingIndexBuffer::reserveSpace(unsigned int requiredSpace, GLenum type)
221 {
222 if(requiredSpace > mBufferSize)
223 {
224 if(mIndexBuffer)
225 {
226 mIndexBuffer->destruct();
227 mIndexBuffer = 0;
228 }
229
230 mBufferSize = std::max(requiredSpace, 2 * mBufferSize);
231
232 mIndexBuffer = new sw::Resource(mBufferSize + 16);
233
234 if(!mIndexBuffer)
235 {
236 ERR("Out of memory allocating an index buffer of size %u.", mBufferSize);
237 }
238
239 mWritePosition = 0;
240 }
241 else if(mWritePosition + requiredSpace > mBufferSize) // Recycle
242 {
243 if(mIndexBuffer)
244 {
245 mIndexBuffer->destruct();
246 mIndexBuffer = new sw::Resource(mBufferSize + 16);
247 }
248
249 mWritePosition = 0;
250 }
251 }
252
getResource() const253 sw::Resource *StreamingIndexBuffer::getResource() const
254 {
255 return mIndexBuffer;
256 }
257
258 }
259