/** * Copyright (c) 2006-2012 LOVE Development Team * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. **/ #include "VertexBuffer.h" #include "common/Exception.h" #include #include #include namespace love { namespace graphics { namespace opengl { // VertexBuffer VertexBuffer *VertexBuffer::Create(size_t size, GLenum target, GLenum usage) { try { // Try to create a VBO. return new VBO(size, target, usage); } catch (const love::Exception &) { // VBO not supported ... create regular array. return new VertexArray(size, target, usage); } } VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage) : size(size) , target(target) , usage(usage) { } VertexBuffer::~VertexBuffer() { } // VertexArray VertexArray::VertexArray(size_t size, GLenum target, GLenum usage) : VertexBuffer(size, target, usage) , buf(new char[size]) { } VertexArray::~VertexArray() { delete [] buf; } void *VertexArray::map() { return buf; } void VertexArray::unmap() { } void VertexArray::bind() { } void VertexArray::unbind() { } void VertexArray::fill(size_t offset, size_t size, const void *data) { memcpy(buf + offset, data, size); } const void *VertexArray::getPointer(size_t offset) const { return buf + offset; } // VBO VBO::VBO(size_t size, GLenum target, GLenum usage) : VertexBuffer(size, target, usage) , vbo(0) , buffer_copy(0) , mapped(0) { if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5)) throw love::Exception("Not supported"); bool ok = load(false); if (!ok) throw love::Exception("Could not load VBO."); } VBO::~VBO() { if (vbo != 0) unload(false); } void *VBO::map() { // mapping twice could result in memory leaks if (mapped) throw love::Exception("VBO is already mapped!"); mapped = malloc(getSize()); if (!mapped) throw love::Exception("Out of memory (oh the humanity!)"); glGetBufferSubDataARB(getTarget(), 0, getSize(), mapped); return mapped; } void VBO::unmap() { glBufferSubDataARB(getTarget(), 0, getSize(), mapped); free(mapped); mapped = 0; } void VBO::bind() { glBindBufferARB(getTarget(), vbo); } void VBO::unbind() { glBindBufferARB(getTarget(), 0); } void VBO::fill(size_t offset, size_t size, const void *data) { if (mapped) memcpy(static_cast(mapped) + offset, data, size); else glBufferSubDataARB(getTarget(), offset, size, data); } const void *VBO::getPointer(size_t offset) const { return reinterpret_cast(offset); } bool VBO::loadVolatile() { return load(true); } void VBO::unloadVolatile() { unload(true); } bool VBO::load(bool restore) { glGenBuffersARB(1, &vbo); VertexBuffer::Bind bind(*this); // Copy the old buffer only if 'restore' was requested. const GLvoid *src = restore ? buffer_copy : 0; while (GL_NO_ERROR != glGetError()) /* clear error messages */; // Note that if 'src' is '0', no data will be copied. glBufferDataARB(getTarget(), getSize(), src, getUsage()); GLenum err = glGetError(); // Clean up buffer_copy, if it exists. delete[] buffer_copy; buffer_copy = 0; return (GL_NO_ERROR == err); } void VBO::unload(bool save) { // Clean up buffer_copy, if it exists. delete[] buffer_copy; buffer_copy = 0; // Save data before unloading. if (save) { VertexBuffer::Bind bind(*this); GLint size; glGetBufferParameterivARB(getTarget(), GL_BUFFER_SIZE, &size); const char *src = static_cast(map()); if (src) { buffer_copy = new char[size]; memcpy(buffer_copy, src, size); unmap(); } } glDeleteBuffers(1, &vbo); vbo = 0; } } // opengl } // graphics } // love