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 es1
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_SHORT)
56 {
57 memcpy(output, input, count * sizeof(GLushort));
58 }
59 else UNREACHABLE(type);
60 }
61
62 template<class IndexType>
computeRange(const IndexType * indices,GLsizei count,GLuint * minIndex,GLuint * maxIndex)63 void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
64 {
65 *minIndex = indices[0];
66 *maxIndex = indices[0];
67
68 for(GLsizei i = 0; i < count; i++)
69 {
70 if(*minIndex > indices[i]) *minIndex = indices[i];
71 if(*maxIndex < indices[i]) *maxIndex = indices[i];
72 }
73 }
74
computeRange(GLenum type,const void * indices,GLsizei count,GLuint * minIndex,GLuint * maxIndex)75 void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
76 {
77 if(type == GL_UNSIGNED_BYTE)
78 {
79 computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex);
80 }
81 else if(type == GL_UNSIGNED_SHORT)
82 {
83 computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex);
84 }
85 else UNREACHABLE(type);
86 }
87
prepareIndexData(GLenum type,GLsizei count,Buffer * buffer,const void * indices,TranslatedIndexData * translated)88 GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated)
89 {
90 if(!mStreamingBuffer)
91 {
92 return GL_OUT_OF_MEMORY;
93 }
94
95 intptr_t offset = reinterpret_cast<intptr_t>(indices);
96
97 if(buffer != NULL)
98 {
99 if(typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size()))
100 {
101 return GL_INVALID_OPERATION;
102 }
103
104 indices = static_cast<const GLubyte*>(buffer->data()) + offset;
105 }
106
107 StreamingIndexBuffer *streamingBuffer = mStreamingBuffer;
108
109 sw::Resource *staticBuffer = buffer ? buffer->getResource() : NULL;
110
111 if(staticBuffer)
112 {
113 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
114
115 translated->indexBuffer = staticBuffer;
116 translated->indexOffset = offset;
117 }
118 else
119 {
120 unsigned int streamOffset = 0;
121 int convertCount = count;
122
123 streamingBuffer->reserveSpace(convertCount * typeSize(type), type);
124 void *output = streamingBuffer->map(typeSize(type) * convertCount, &streamOffset);
125
126 if(output == NULL)
127 {
128 ERR("Failed to map index buffer.");
129 return GL_OUT_OF_MEMORY;
130 }
131
132 copyIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output);
133 streamingBuffer->unmap();
134
135 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
136
137 translated->indexBuffer = streamingBuffer->getResource();
138 translated->indexOffset = streamOffset;
139 }
140
141 return GL_NO_ERROR;
142 }
143
typeSize(GLenum type)144 std::size_t IndexDataManager::typeSize(GLenum type)
145 {
146 switch(type)
147 {
148 case GL_UNSIGNED_SHORT: return sizeof(GLushort);
149 case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
150 default: UNREACHABLE(type); return sizeof(GLushort);
151 }
152 }
153
StreamingIndexBuffer(unsigned int initialSize)154 StreamingIndexBuffer::StreamingIndexBuffer(unsigned int initialSize) : mIndexBuffer(nullptr), mBufferSize(initialSize)
155 {
156 if(initialSize > 0)
157 {
158 mIndexBuffer = new sw::Resource(initialSize + 16);
159
160 if(!mIndexBuffer)
161 {
162 ERR("Out of memory allocating an index buffer of size %u.", initialSize);
163 }
164 }
165
166 mWritePosition = 0;
167 }
168
~StreamingIndexBuffer()169 StreamingIndexBuffer::~StreamingIndexBuffer()
170 {
171 if(mIndexBuffer)
172 {
173 mIndexBuffer->destruct();
174 }
175 }
176
map(unsigned int requiredSpace,unsigned int * offset)177 void *StreamingIndexBuffer::map(unsigned int requiredSpace, unsigned int *offset)
178 {
179 void *mapPtr = NULL;
180
181 if(mIndexBuffer)
182 {
183 mapPtr = (char*)mIndexBuffer->lock(sw::PUBLIC) + mWritePosition;
184
185 if(!mapPtr)
186 {
187 ERR(" Lock failed");
188 return NULL;
189 }
190
191 *offset = mWritePosition;
192 mWritePosition += requiredSpace;
193 }
194
195 return mapPtr;
196 }
197
unmap()198 void StreamingIndexBuffer::unmap()
199 {
200 if(mIndexBuffer)
201 {
202 mIndexBuffer->unlock();
203 }
204 }
205
reserveSpace(unsigned int requiredSpace,GLenum type)206 void StreamingIndexBuffer::reserveSpace(unsigned int requiredSpace, GLenum type)
207 {
208 if(requiredSpace > mBufferSize)
209 {
210 if(mIndexBuffer)
211 {
212 mIndexBuffer->destruct();
213 mIndexBuffer = 0;
214 }
215
216 mBufferSize = std::max(requiredSpace, 2 * mBufferSize);
217
218 mIndexBuffer = new sw::Resource(mBufferSize + 16);
219
220 if(!mIndexBuffer)
221 {
222 ERR("Out of memory allocating an index buffer of size %u.", mBufferSize);
223 }
224
225 mWritePosition = 0;
226 }
227 else if(mWritePosition + requiredSpace > mBufferSize) // Recycle
228 {
229 if(mIndexBuffer)
230 {
231 mIndexBuffer->destruct();
232 mIndexBuffer = new sw::Resource(mBufferSize + 16);
233 }
234
235 mWritePosition = 0;
236 }
237 }
238
getResource() const239 sw::Resource *StreamingIndexBuffer::getResource() const
240 {
241 return mIndexBuffer;
242 }
243
244 }
245