From 6b3781b97fc2f62e0c168b43c42664ee558dd3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 7 Oct 2012 23:56:32 +0200 Subject: [PATCH] Tracking Buffer state. Reduces OpenGL call count if subsequently binding the same Buffer into the same Target. --- src/Buffer.cpp | 50 ++++++++++++++++++++---- src/Buffer.h | 15 +++---- src/CMakeLists.txt | 1 + src/Implementation/BufferState.cpp | 63 ++++++++++++++++++++++++++++++ src/Implementation/BufferState.h | 43 ++++++++++++++++++++ src/Implementation/State.cpp | 5 ++- src/Implementation/State.h | 4 +- 7 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 src/Implementation/BufferState.cpp create mode 100644 src/Implementation/BufferState.h diff --git a/src/Buffer.cpp b/src/Buffer.cpp index e5486e0db..5ecefd2e8 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -19,6 +19,8 @@ #include "Context.h" #include "Extensions.h" +#include "Implementation/State.h" +#include "Implementation/BufferState.h" namespace Magnum { @@ -36,10 +38,46 @@ void Buffer::initializeContextBasedFunctionality(Context* context) { } } +Buffer::~Buffer() { + GLuint* bindings = Context::current()->state()->buffer->bindings; + + /* Remove all current bindings from the state */ + for(std::size_t i = 1; i != Implementation::BufferState::TargetCount; ++i) + if(bindings[i] == _id) bindings[i] = 0; + + glDeleteBuffers(1, &_id); +} + +void Buffer::bind(Target target, GLuint id) { + GLuint& bound = Context::current()->state()->buffer->bindings[Implementation::BufferState::indexForTarget(target)]; + + /* Already bound, nothing to do */ + if(bound == id) return; + + /* Bind the buffer otherwise */ + bound = id; + glBindBuffer(static_cast(target), id); +} + +Buffer::Target Buffer::bindInternal(Target hint) { + GLuint* bindings = Context::current()->state()->buffer->bindings; + GLuint& hintBinding = bindings[Implementation::BufferState::indexForTarget(hint)]; + + /* Shortcut - if already bound to hint, return */ + if(hintBinding == _id) return hint; + + /* Return first target in which the buffer is bound */ + for(std::size_t i = 1; i != Implementation::BufferState::TargetCount; ++i) + if(bindings[i] == _id) return Implementation::BufferState::targetForIndex[i]; + + /* Bind the buffer to hint target otherwise */ + hintBinding = _id; + glBindBuffer(static_cast(hint), _id); + return hint; +} + void Buffer::copyImplementationDefault(Buffer* read, Buffer* write, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { - read->bind(Target::CopyRead); - write->bind(Target::CopyWrite); - glCopyBufferSubData(static_cast(Target::CopyRead), static_cast(Target::CopyWrite), readOffset, writeOffset, size); + glCopyBufferSubData(static_cast(read->bindInternal(Target::CopyRead)), static_cast(write->bindInternal(Target::CopyWrite)), readOffset, writeOffset, size); } void Buffer::copyImplementationDSA(Buffer* read, Buffer* write, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { @@ -47,8 +85,7 @@ void Buffer::copyImplementationDSA(Buffer* read, Buffer* write, GLintptr readOff } void Buffer::setDataImplementationDefault(GLsizeiptr size, const GLvoid* data, Buffer::Usage usage) { - bind(); - glBufferData(static_cast(_defaultTarget), size, data, static_cast(usage)); + glBufferData(static_cast(bindInternal(_defaultTarget)), size, data, static_cast(usage)); } void Buffer::setDataImplementationDSA(GLsizeiptr size, const GLvoid* data, Buffer::Usage usage) { @@ -56,8 +93,7 @@ void Buffer::setDataImplementationDSA(GLsizeiptr size, const GLvoid* data, Buffe } void Buffer::setSubDataImplementationDefault(GLintptr offset, GLsizeiptr size, const GLvoid* data) { - bind(); - glBufferSubData(static_cast(_defaultTarget), offset, size, data); + glBufferSubData(static_cast(bindInternal(_defaultTarget)), offset, size, data); } void Buffer::setSubDataImplementationDSA(GLintptr offset, GLsizeiptr size, const GLvoid* data) { diff --git a/src/Buffer.h b/src/Buffer.h index f14583c0c..e4747aa69 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -236,9 +236,7 @@ class MAGNUM_EXPORT Buffer { * * @see @fn_gl{BindBuffer} */ - inline static void unbind(Target target) { - glBindBuffer(static_cast(target), 0); - } + inline static void unbind(Target target) { bind(target, 0); } /** * @brief Copy one buffer to another @@ -277,9 +275,7 @@ class MAGNUM_EXPORT Buffer { * Deletes associated OpenGL buffer. * @see @fn_gl{DeleteBuffers} */ - inline virtual ~Buffer() { - glDeleteBuffers(1, &_id); - } + virtual ~Buffer(); /** @brief Default bind type */ inline Target defaultTarget() const { return _defaultTarget; } @@ -301,9 +297,7 @@ class MAGNUM_EXPORT Buffer { * * @see @fn_gl{BindBuffer} */ - inline void bind(Target target) { - glBindBuffer(static_cast(target), _id); - } + inline void bind(Target target) { bind(target, _id); } /** * @brief Set buffer data @@ -390,6 +384,9 @@ class MAGNUM_EXPORT Buffer { private: static void initializeContextBasedFunctionality(Context* context); + static void bind(Target hint, GLuint id); + Target bindInternal(Target hint); + typedef void(*CopyImplementation)(Buffer*, Buffer*, GLintptr, GLintptr, GLsizeiptr); static void MAGNUM_LOCAL copyImplementationDefault(Buffer* read, Buffer* write, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); static void MAGNUM_LOCAL copyImplementationDSA(Buffer* read, Buffer* write, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6f1d45eaa..b3a5f3c80 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -37,6 +37,7 @@ set(Magnum_SRCS Timeline.cpp TypeTraits.cpp + Implementation/BufferState.cpp Implementation/State.cpp Trade/AbstractImporter.cpp diff --git a/src/Implementation/BufferState.cpp b/src/Implementation/BufferState.cpp new file mode 100644 index 000000000..b8ef09cd4 --- /dev/null +++ b/src/Implementation/BufferState.cpp @@ -0,0 +1,63 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "BufferState.h" + +#include + +namespace Magnum { namespace Implementation { + +const Buffer::Target BufferState::targetForIndex[] = { + Buffer::Target::Array, + Buffer::Target::CopyRead, + Buffer::Target::CopyWrite, + Buffer::Target::ElementArray, + Buffer::Target::PixelPack, + Buffer::Target::PixelUnpack, + Buffer::Target::TransformFeedback, + Buffer::Target::Uniform, + #ifndef MAGNUM_TARGET_GLES + Buffer::Target::AtomicCounter, + Buffer::Target::DispatchIndirect, + Buffer::Target::DrawIndirect, + Buffer::Target::ShaderStorage, + Buffer::Target::Texture + #endif +}; + +std::size_t BufferState::indexForTarget(Buffer::Target target) { + switch(target) { + case Buffer::Target::Array: return 1; + case Buffer::Target::CopyRead: return 2; + case Buffer::Target::CopyWrite: return 3; + case Buffer::Target::ElementArray: return 4; + case Buffer::Target::PixelPack: return 5; + case Buffer::Target::PixelUnpack: return 6; + case Buffer::Target::TransformFeedback: return 7; + case Buffer::Target::Uniform: return 8; + #ifndef MAGNUM_TARGET_GLES + case Buffer::Target::AtomicCounter: return 9; + case Buffer::Target::DispatchIndirect: return 10; + case Buffer::Target::DrawIndirect: return 11; + case Buffer::Target::ShaderStorage: return 12; + case Buffer::Target::Texture: return 13; + #endif + } + + CORRADE_ASSERT(false, "Unknown Buffer target", 0); + return 0; +} + +}} diff --git a/src/Implementation/BufferState.h b/src/Implementation/BufferState.h new file mode 100644 index 000000000..3ea1e551b --- /dev/null +++ b/src/Implementation/BufferState.h @@ -0,0 +1,43 @@ +#ifndef Magnum_Implementation_BufferState_h +#define Magnum_Implementation_BufferState_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "Magnum.h" + +#include "Buffer.h" + +namespace Magnum { namespace Implementation { + +struct BufferState { + #ifndef MAGNUM_TARGET_GLES + static const std::size_t TargetCount = 13+1; + #else + static const std::size_t TargetCount = 8+1; + #endif + + /* Target <-> index mapping */ + static std::size_t indexForTarget(Buffer::Target target); + static const Buffer::Target targetForIndex[TargetCount-1]; + + inline constexpr BufferState(): bindings() {} + + /* Currently bound buffer for all targets */ + GLuint bindings[TargetCount]; +}; + +}} + +#endif diff --git a/src/Implementation/State.cpp b/src/Implementation/State.cpp index 2fb89de2d..daf8b4cab 100644 --- a/src/Implementation/State.cpp +++ b/src/Implementation/State.cpp @@ -15,11 +15,14 @@ #include "State.h" +#include "BufferState.h" + namespace Magnum { namespace Implementation { -State::State() {} +State::State(): buffer(new BufferState) {} State::~State() { + delete buffer; } }} diff --git a/src/Implementation/State.h b/src/Implementation/State.h index 8a070d47d..a3438b028 100644 --- a/src/Implementation/State.h +++ b/src/Implementation/State.h @@ -19,11 +19,13 @@ namespace Magnum { namespace Implementation { -class BufferState; +struct BufferState; struct State { State(); ~State(); + + BufferState* const buffer; }; }}