diff --git a/CMakeLists.txt b/CMakeLists.txt index 409bc6d4..624a5946 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ set(SOURCES "station.cpp" "gl/shader.cpp" "gl/shader_mvp.cpp" +"gl/vao.cpp" ) set (PREFIX "") diff --git a/gl/bindable.h b/gl/bindable.h new file mode 100644 index 00000000..0d37c772 --- /dev/null +++ b/gl/bindable.h @@ -0,0 +1,27 @@ +#pragma once + +namespace gl +{ + template + class bindable + { + private: + static bindable* active; + + public: + void bind() + { + if (active == this) + return; + active = this; + T::bind(*static_cast(active)); + } + void unbind() + { + active = nullptr; + T::bind(0); + } + }; + + template bindable* bindable::active; +} diff --git a/gl/shader.cpp b/gl/shader.cpp index 07d86f71..30330005 100644 --- a/gl/shader.cpp +++ b/gl/shader.cpp @@ -99,7 +99,7 @@ gl::program::~program() glDeleteProgram(*this); } -void gl::program::bind() +void gl::program::bind(GLuint i) { - glUseProgram(*this); + glUseProgram(i); } diff --git a/gl/shader.h b/gl/shader.h index fa13fcbf..ab0b3f13 100644 --- a/gl/shader.h +++ b/gl/shader.h @@ -5,6 +5,7 @@ #include #include "object.h" +#include "bindable.h" namespace gl { @@ -15,14 +16,15 @@ namespace gl ~shader(); }; - class program : public object + class program : public object, public bindable { public: program(); program(std::vector>); ~program(); - void bind(); + using bindable::bind; + static void bind(GLuint i); void attach(const shader &); void link(); diff --git a/gl/shader_mvp.cpp b/gl/shader_mvp.cpp index f518a498..edb7cf3c 100644 --- a/gl/shader_mvp.cpp +++ b/gl/shader_mvp.cpp @@ -10,6 +10,10 @@ void gl::program_mvp::init() void gl::program_mvp::set_mv(const glm::mat4 &m) { + if (last_mv == m) + return; + last_mv = m; + if (mvn_uniform != -1) { glm::mat3 mvn = glm::mat3(glm::transpose(glm::inverse(m))); @@ -20,6 +24,10 @@ void gl::program_mvp::set_mv(const glm::mat4 &m) void gl::program_mvp::set_p(const glm::mat4 &m) { + if (last_p == m) + return; + last_p = m; + glUniformMatrix4fv(p_uniform, 1, GL_FALSE, glm::value_ptr(m)); } diff --git a/gl/shader_mvp.h b/gl/shader_mvp.h index 7e309fc7..7043b061 100644 --- a/gl/shader_mvp.h +++ b/gl/shader_mvp.h @@ -10,6 +10,9 @@ namespace gl GLint mvn_uniform; GLuint p_uniform; + glm::mat4 last_mv; + glm::mat4 last_p; + public: using program::program; diff --git a/gl/vao.cpp b/gl/vao.cpp new file mode 100644 index 00000000..a07ec47b --- /dev/null +++ b/gl/vao.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "vao.h" + +gl::vao::vao() +{ + glGenVertexArrays(1, *this); +} + +gl::vao::~vao() +{ + glDeleteVertexArrays(1, *this); +} + +void gl::vao::setup_attrib(int attrib, int size, int type, int stride, int offset) +{ + bind(); + glVertexAttribPointer(attrib, size, type, GL_FALSE, stride, reinterpret_cast(offset)); + glEnableVertexAttribArray(attrib); +} + +void gl::vao::bind(GLuint i) +{ + glBindVertexArray(i); +} diff --git a/gl/vao.h b/gl/vao.h new file mode 100644 index 00000000..fd9a60b3 --- /dev/null +++ b/gl/vao.h @@ -0,0 +1,19 @@ +#pragma once + +#include "object.h" +#include "bindable.h" + +namespace gl +{ + class vao : public object, public bindable + { + public: + vao(); + ~vao(); + + void setup_attrib(int attrib, int size, int type, int stride, int offset); + + using bindable::bind; + static void bind(GLuint i); + }; +} diff --git a/openglgeometrybank.cpp b/openglgeometrybank.cpp index 8dba5843..5f60de99 100644 --- a/openglgeometrybank.cpp +++ b/openglgeometrybank.cpp @@ -118,12 +118,6 @@ geometry_bank::vertices( gfx::geometry_handle const &Geometry ) const { return geometry_bank::chunk( Geometry ).vertices; } -// opengl vbo-based variant of the geometry bank - -GLuint opengl_vbogeometrybank::m_activebuffer { 0 }; // buffer bound currently on the opengl end, if any -unsigned int opengl_vbogeometrybank::m_activestreams { gfx::stream::none }; // currently enabled data type pointers -std::vector opengl_vbogeometrybank::m_activetexturearrays; // currently enabled texture coord arrays - // create() subclass details void opengl_vbogeometrybank::create_( gfx::geometry_handle const &Geometry ) { @@ -173,7 +167,8 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream if( datasize == 0 ) { return; } // try to set up the buffer we need ::glGenBuffers( 1, &m_buffer ); - bind_buffer(); + glBindBuffer( GL_ARRAY_BUFFER, m_buffer ); + // NOTE: we're using static_draw since it's generally true for all we have implemented at the moment // TODO: allow to specify usage hint at the object creation, and pass it here ::glBufferData( @@ -181,6 +176,9 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream datasize * sizeof( gfx::basic_vertex ), nullptr, GL_STATIC_DRAW ); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + if( ::glGetError() == GL_OUT_OF_MEMORY ) { // TBD: throw a bad_alloc? ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" ); @@ -189,34 +187,45 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream } m_buffercapacity = datasize; } - // actual draw procedure starts here - // setup... - if( m_activebuffer != m_buffer ) { - bind_buffer(); + + if (!m_vao) + { + m_vao = std::make_unique(); + glBindBuffer( GL_ARRAY_BUFFER, m_buffer ); + + if( Streams & gfx::stream::position ) + m_vao->setup_attrib(0, 3, GL_FLOAT, sizeof(basic_vertex), 0 * sizeof(GL_FLOAT)); + + // NOTE: normal and color streams share the data, making them effectively mutually exclusive + if( Streams & gfx::stream::normal ) + m_vao->setup_attrib(1, 3, GL_FLOAT, sizeof(basic_vertex), 3 * sizeof(GL_FLOAT)); + else if( Streams & gfx::stream::color ) + m_vao->setup_attrib(1, 3, GL_FLOAT, sizeof(basic_vertex), 3 * sizeof(GL_FLOAT)); + + if( Streams & gfx::stream::texture ) + m_vao->setup_attrib(2, 2, GL_FLOAT, sizeof(basic_vertex), 6 * sizeof(GL_FLOAT)); + + glBindBuffer(GL_ARRAY_BUFFER, 0); } + + // actual draw procedure starts here auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; auto const &chunk = gfx::geometry_bank::chunk( Geometry ); if( false == chunkrecord.is_good ) { + glBindBuffer( GL_ARRAY_BUFFER, m_buffer ); // we may potentially need to upload new buffer data before we can draw it ::glBufferSubData( GL_ARRAY_BUFFER, chunkrecord.offset * sizeof( gfx::basic_vertex ), chunkrecord.size * sizeof( gfx::basic_vertex ), chunk.vertices.data() ); + glBindBuffer(GL_ARRAY_BUFFER, 0); chunkrecord.is_good = true; } - if( m_activestreams != Streams ) { - bind_streams( Units, Streams ); - } + // ...render... + m_vao->bind(); ::glDrawArrays( chunk.type, chunkrecord.offset, chunkrecord.size ); - // ...post-render cleanup -/* - ::glDisableClientState( GL_VERTEX_ARRAY ); - ::glDisableClientState( GL_NORMAL_ARRAY ); - ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); m_activebuffer = 0; -*/ } // release () subclass details @@ -226,24 +235,13 @@ opengl_vbogeometrybank::release_() { delete_buffer(); } -void -opengl_vbogeometrybank::bind_buffer() { - - ::glBindBuffer( GL_ARRAY_BUFFER, m_buffer ); - m_activebuffer = m_buffer; - m_activestreams = gfx::stream::none; -} - void opengl_vbogeometrybank::delete_buffer() { if( m_buffer != 0 ) { + m_vao.reset(nullptr); ::glDeleteBuffers( 1, &m_buffer ); - if( m_activebuffer == m_buffer ) { - m_activebuffer = 0; - release_streams(); - } m_buffer = 0; m_buffercapacity = 0; // NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well @@ -251,52 +249,6 @@ opengl_vbogeometrybank::delete_buffer() { } } -void -opengl_vbogeometrybank::bind_streams( gfx::stream_units const &Units, unsigned int const Streams ) { - - if( Streams & gfx::stream::position ) { - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GL_FLOAT), (void*)0); - glEnableVertexAttribArray(0); - } - else { - glDisableVertexAttribArray(0); - } - // NOTE: normal and color streams share the data, making them effectively mutually exclusive - if( Streams & gfx::stream::normal ) { - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GL_FLOAT), (void*)(3 * sizeof(GL_FLOAT))); - glEnableVertexAttribArray(1); - } - else { - glDisableVertexAttribArray(1); - } - if( Streams & gfx::stream::color ) { - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GL_FLOAT), (void*)(3 * sizeof(GL_FLOAT))); - glEnableVertexAttribArray(1); - } - if( Streams & gfx::stream::texture ) { - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GL_FLOAT), (void*)(6 * sizeof(GL_FLOAT))); - glEnableVertexAttribArray(2); - m_activetexturearrays = Units.texture; - } - else { - glDisableVertexAttribArray(2); - m_activetexturearrays.clear(); // NOTE: we're simplifying here, since we always toggle the same texture coord sets - } - - m_activestreams = Streams; -} - -void -opengl_vbogeometrybank::release_streams() { - - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(2); - - m_activestreams = gfx::stream::none; - m_activetexturearrays.clear(); -} - // opengl display list based variant of the geometry bank // create() subclass details diff --git a/openglgeometrybank.h b/openglgeometrybank.h index a928cec4..4be67b0a 100644 --- a/openglgeometrybank.h +++ b/openglgeometrybank.h @@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/. #include "GL/wglew.h" #endif #include "ResourceManager.h" +#include "gl/vao.h" namespace gfx { @@ -169,9 +170,7 @@ public: // methods: static void - reset() { - m_activebuffer = 0; - m_activestreams = gfx::stream::none; } + reset() { } private: // types: @@ -196,22 +195,12 @@ private: // release() subclass details void release_(); - void - bind_buffer(); void delete_buffer(); - static - void - bind_streams( gfx::stream_units const &Units, unsigned int const Streams ); - static - void - release_streams(); // members: - static GLuint m_activebuffer; // buffer bound currently on the opengl end, if any - static unsigned int m_activestreams; - static std::vector m_activetexturearrays; GLuint m_buffer { 0 }; // id of the buffer holding data on the opengl end + std::unique_ptr m_vao; std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order diff --git a/renderer.cpp b/renderer.cpp index f8f46298..8ba86bf0 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -166,10 +166,6 @@ opengl_renderer::Init( GLFWwindow *Window ) { shader = std::make_unique(std::vector>({vert, frag})); shader->init(); - GLuint vao; - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - return true; } @@ -323,7 +319,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } } - glUseProgram(0); + shader->unbind(); if( m_environmentcubetexturesupport ) { // restore default texture matrix for reflections cube map diff --git a/skydome.cpp b/skydome.cpp index ba1d1034..6195241d 100644 --- a/skydome.cpp +++ b/skydome.cpp @@ -126,14 +126,18 @@ void CSkyDome::Render() { } if( m_vertexbuffer == -1 ) { + m_vao = std::make_unique(); + // build the buffers ::glGenBuffers( 1, &m_vertexbuffer ); ::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer ); ::glBufferData( GL_ARRAY_BUFFER, m_vertices.size() * sizeof( glm::vec3 ), m_vertices.data(), GL_STATIC_DRAW ); + m_vao->setup_attrib(0, 3, GL_FLOAT, sizeof(glm::vec3), 0); ::glGenBuffers( 1, &m_coloursbuffer ); ::glBindBuffer( GL_ARRAY_BUFFER, m_coloursbuffer ); ::glBufferData( GL_ARRAY_BUFFER, m_colours.size() * sizeof( glm::vec3 ), m_colours.data(), GL_DYNAMIC_DRAW ); + m_vao->setup_attrib(1, 3, GL_FLOAT, sizeof(glm::vec3), 0); ::glGenBuffers( 1, &m_indexbuffer ); ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer ); @@ -143,20 +147,10 @@ void CSkyDome::Render() { m_shader->bind(); m_shader->copy_gl_mvp(); - - glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), 0); - glEnableVertexAttribArray(0); - glBindBuffer(GL_ARRAY_BUFFER, m_coloursbuffer); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), 0); - glEnableVertexAttribArray(1); - glDisableVertexAttribArray(2); - glBindBuffer(GL_ARRAY_BUFFER, 0); + m_vao->bind(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer); - ::glDrawElements( GL_TRIANGLES, static_cast( m_indices.size() ), GL_UNSIGNED_SHORT, reinterpret_cast( 0 ) ); - - glUseProgram(0); + glDrawElements( GL_TRIANGLES, static_cast( m_indices.size() ), GL_UNSIGNED_SHORT, reinterpret_cast( 0 ) ); } bool CSkyDome::SetSunPosition( glm::vec3 const &Direction ) { diff --git a/skydome.h b/skydome.h index 6214df4d..e1a81394 100644 --- a/skydome.h +++ b/skydome.h @@ -1,6 +1,7 @@ #pragma once #include "gl/shader_mvp.h" +#include "gl/vao.h" #include // sky gradient based on "A practical analytic model for daylight" @@ -65,4 +66,5 @@ private: float PerezFunctionO2( float Perezcoeffs[ 5 ], const float Icostheta, const float Gamma, const float Cosgamma2, const float Zenithval ); std::unique_ptr m_shader; + std::unique_ptr m_vao; };