1 /**
2 * Copyright (c) 2006-2012 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty.  In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 *    claim that you wrote the original software. If you use this software
14 *    in a product, an acknowledgment in the product documentation would be
15 *    appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 *    misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20 
21 #define GL_GLEXT_PROTOTYPES
22 #include "VertexBuffer.h"
23 
24 #include "common/Exception.h"
25 #include <common/config.h>
26 
27 #include <cstdlib>
28 #include <cstring>
29 
30 namespace love
31 {
32 namespace graphics
33 {
34 namespace opengl
35 {
36 	// VertexBuffer
37 
Create(size_t size,GLenum target,GLenum usage)38 	VertexBuffer *VertexBuffer::Create(size_t size, GLenum target, GLenum usage)
39 	{
40 		try
41 		{
42 			// Try to create a VBO.
43 			return new VBO(size, target, usage);
44 		}
45 		catch (const love::Exception &)
46 		{
47 			// VBO not supported ... create regular array.
48 			return new VertexArray(size, target, usage);
49 		}
50 	}
51 
VertexBuffer(size_t size,GLenum target,GLenum usage)52 	VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage)
53 		: size(size)
54 		, target(target)
55 		, usage(usage)
56 	{
57 	}
58 
~VertexBuffer()59 	VertexBuffer::~VertexBuffer()
60 	{
61 	}
62 
63 	// VertexArray
64 
VertexArray(size_t size,GLenum target,GLenum usage)65 	VertexArray::VertexArray(size_t size, GLenum target, GLenum usage)
66 		: VertexBuffer(size, target, usage)
67 		, buf(new char[size])
68 	{
69 	}
70 
~VertexArray()71 	VertexArray::~VertexArray()
72 	{
73 		delete [] buf;
74 	}
75 
map()76 	void *VertexArray::map()
77 	{
78 		return buf;
79 	}
80 
unmap()81 	void VertexArray::unmap()
82 	{
83 	}
84 
bind()85 	void VertexArray::bind()
86 	{
87 	}
88 
unbind()89 	void VertexArray::unbind()
90 	{
91 	}
92 
fill(size_t offset,size_t size,const void * data)93 	void VertexArray::fill(size_t offset, size_t size, const void *data)
94 	{
95 		memcpy(buf + offset, data, size);
96 	}
97 
getPointer(size_t offset) const98 	const void *VertexArray::getPointer(size_t offset) const
99 	{
100 		return buf + offset;
101 	}
102 
103 	// VBO
104 
VBO(size_t size,GLenum target,GLenum usage)105 	VBO::VBO(size_t size, GLenum target, GLenum usage)
106 		: VertexBuffer(size, target, usage)
107 		, vbo(0)
108 		, buffer_copy(0)
109 		, mapped(0)
110 	{
111 		if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5))
112 			throw love::Exception("Not supported");
113 
114 		bool ok = load(false);
115 
116 		if (!ok)
117 			throw love::Exception("Could not load VBO.");
118 	}
119 
~VBO()120 	VBO::~VBO()
121 	{
122 		if (vbo != 0)
123 			unload(false);
124 	}
125 
map()126 	void *VBO::map()
127 	{
128 		// mapping twice could result in memory leaks
129 		if (mapped)
130 			throw love::Exception("VBO is already mapped!");
131 
132 		mapped = malloc(getSize());
133 		if (!mapped)
134 			throw love::Exception("Out of memory (oh the humanity!)");
135 		glGetBufferSubDataARB(getTarget(), 0, getSize(), mapped);
136 
137 		return mapped;
138 	}
139 
unmap()140 	void VBO::unmap()
141 	{
142 		glBufferSubDataARB(getTarget(), 0, getSize(), mapped);
143 		free(mapped);
144 		mapped = 0;
145 	}
146 
bind()147 	void VBO::bind()
148 	{
149 		glBindBufferARB(getTarget(), vbo);
150 	}
151 
unbind()152 	void VBO::unbind()
153 	{
154 		glBindBufferARB(getTarget(), 0);
155 	}
156 
fill(size_t offset,size_t size,const void * data)157 	void VBO::fill(size_t offset, size_t size, const void *data)
158 	{
159 		if (mapped)
160 			memcpy(static_cast<char*>(mapped) + offset, data, size);
161 		else
162 			glBufferSubDataARB(getTarget(), offset, size, data);
163 	}
164 
getPointer(size_t offset) const165 	const void *VBO::getPointer(size_t offset) const
166 	{
167 		return reinterpret_cast<const void*>(offset);
168 	}
169 
loadVolatile()170 	bool VBO::loadVolatile()
171 	{
172 		return load(true);
173 	}
174 
unloadVolatile()175 	void VBO::unloadVolatile()
176 	{
177 		unload(true);
178 	}
179 
load(bool restore)180 	bool VBO::load(bool restore)
181 	{
182 		glGenBuffersARB(1, &vbo);
183 
184 		VertexBuffer::Bind bind(*this);
185 
186 		// Copy the old buffer only if 'restore' was requested.
187 		const GLvoid *src = restore ? buffer_copy : 0;
188 
189 		while (GL_NO_ERROR != glGetError())
190 			/* clear error messages */;
191 
192 		// Note that if 'src' is '0', no data will be copied.
193 		glBufferDataARB(getTarget(), getSize(), src, getUsage());
194 		GLenum err = glGetError();
195 
196 		// Clean up buffer_copy, if it exists.
197 		delete[] buffer_copy;
198 		buffer_copy = 0;
199 
200 		return (GL_NO_ERROR == err);
201 	}
202 
unload(bool save)203 	void VBO::unload(bool save)
204 	{
205 		// Clean up buffer_copy, if it exists.
206 		delete[] buffer_copy;
207 		buffer_copy = 0;
208 
209 		// Save data before unloading.
210 		if (save)
211 		{
212 			VertexBuffer::Bind bind(*this);
213 
214 			GLint size;
215 			glGetBufferParameterivARB(getTarget(), GL_BUFFER_SIZE, &size);
216 
217 			const char *src = static_cast<char *>(map());
218 
219 			if (src)
220 			{
221 				buffer_copy = new char[size];
222 				memcpy(buffer_copy, src, size);
223 				unmap();
224 			}
225 		}
226 
227 		glDeleteBuffers(1, &vbo);
228 		vbo = 0;
229 	}
230 
231 } // opengl
232 } // graphics
233 } // love
234