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