From 70e57a338f057b38825e5dc0c883d95fa155abd0 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sun, 20 Apr 2014 22:32:09 +0200 Subject: [PATCH] Optimized hardware buffers Former-commit-id: 59f96166fb259137b5e0445eb5faa0b912b0f29c --- src/Nazara/Renderer/HardwareBuffer.cpp | 85 +++++++------------------- src/Nazara/Renderer/OpenGL.cpp | 18 +++--- 2 files changed, 30 insertions(+), 73 deletions(-) diff --git a/src/Nazara/Renderer/HardwareBuffer.cpp b/src/Nazara/Renderer/HardwareBuffer.cpp index a35fba5de..f4f3b8c95 100644 --- a/src/Nazara/Renderer/HardwareBuffer.cpp +++ b/src/Nazara/Renderer/HardwareBuffer.cpp @@ -11,53 +11,6 @@ #include #include -namespace -{ - using LockRoutine = nzUInt8* (*)(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size); - - nzUInt8* LockBuffer(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size) - { - NazaraUnused(size); - - if (access == nzBufferAccess_DiscardAndWrite) - { - GLint bufSize; - glGetBufferParameteriv(NzOpenGL::BufferTargetBinding[type], GL_BUFFER_SIZE, &bufSize); - - GLint bufUsage; - glGetBufferParameteriv(NzOpenGL::BufferTargetBinding[type], GL_BUFFER_USAGE, &bufUsage); - - // On discard le buffer - glBufferData(NzOpenGL::BufferTargetBinding[type], bufSize, nullptr, bufUsage); - } - - void* ptr = glMapBuffer(NzOpenGL::BufferTarget[type], NzOpenGL::BufferLock[access]); - if (ptr) - return reinterpret_cast(ptr) + offset; - else - return nullptr; - } - - nzUInt8* LockBufferRange(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size) - { - return reinterpret_cast(glMapBufferRange(NzOpenGL::BufferTarget[type], offset, size, NzOpenGL::BufferLockRange[access])); - } - - nzUInt8* LockBufferFirstRun(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size); - - LockRoutine mapBuffer = LockBufferFirstRun; - - nzUInt8* LockBufferFirstRun(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size) - { - if (glMapBufferRange) - mapBuffer = LockBufferRange; - else - mapBuffer = LockBuffer; - - return mapBuffer(type, access, offset, size); - } -} - NzHardwareBuffer::NzHardwareBuffer(NzBuffer* parent, nzBufferType type) : m_type(type), m_parent(parent) @@ -98,17 +51,19 @@ bool NzHardwareBuffer::Fill(const void* data, unsigned int offset, unsigned int NzOpenGL::BindBuffer(m_type, m_buffer); - // http://www.opengl.org/wiki/Vertex_Specification_Best_Practices - if (forceDiscard) - glBufferData(NzOpenGL::BufferTarget[m_type], totalSize, nullptr, NzOpenGL::BufferUsage[m_parent->GetUsage()]); // Discard - // Il semblerait que glBuffer(Sub)Data soit plus performant que glMapBuffer(Range) en dessous d'un certain seuil // http://www.stevestreeting.com/2007/03/17/glmapbuffer-vs-glbuffersubdata-the-return/ if (size < 32*1024) + { + // http://www.opengl.org/wiki/Buffer_Object_Streaming + if (forceDiscard) + glBufferData(NzOpenGL::BufferTarget[m_type], totalSize, nullptr, NzOpenGL::BufferUsage[m_parent->GetUsage()]); // Discard + glBufferSubData(NzOpenGL::BufferTarget[m_type], offset, size, data); + } else { - nzUInt8* ptr = mapBuffer(m_type, (forceDiscard) ? nzBufferAccess_DiscardAndWrite : nzBufferAccess_WriteOnly, offset, size); + void* ptr = Map((forceDiscard) ? nzBufferAccess_DiscardAndWrite : nzBufferAccess_WriteOnly, offset, size); if (!ptr) { NazaraError("Failed to map buffer"); @@ -117,15 +72,7 @@ bool NzHardwareBuffer::Fill(const void* data, unsigned int offset, unsigned int std::memcpy(ptr, data, size); - if (glUnmapBuffer(NzOpenGL::BufferTarget[m_type]) != GL_TRUE) - { - // Une erreur rare est survenue, nous devons réinitialiser le buffer - NazaraError("Failed to unmap buffer, reinitialising content... (OpenGL error : 0x" + NzString::Number(glGetError(), 16) + ')'); - - glBufferData(NzOpenGL::BufferTarget[m_type], totalSize, nullptr, NzOpenGL::BufferUsage[m_parent->GetUsage()]); - - return false; - } + Unmap(); } return true; @@ -142,10 +89,20 @@ void* NzHardwareBuffer::Map(nzBufferAccess access, unsigned int offset, unsigned NzOpenGL::BindBuffer(m_type, m_buffer); - if (access == nzBufferAccess_DiscardAndWrite) - glBufferData(NzOpenGL::BufferTarget[m_type], m_parent->GetSize(), nullptr, NzOpenGL::BufferUsage[m_parent->GetUsage()]); // Discard + if (glMapBufferRange) + return glMapBufferRange(NzOpenGL::BufferTarget[m_type], offset, size, NzOpenGL::BufferLockRange[access]); + else + { + // http://www.opengl.org/wiki/Buffer_Object_Streaming + if (access == nzBufferAccess_DiscardAndWrite) + glBufferData(NzOpenGL::BufferTarget[m_type], m_parent->GetSize(), nullptr, NzOpenGL::BufferUsage[m_parent->GetUsage()]); // Discard - return mapBuffer(m_type, access, offset, size); + nzUInt8* ptr = static_cast(glMapBuffer(NzOpenGL::BufferTarget[m_type], NzOpenGL::BufferLock[access])); + if (ptr) + ptr += offset; + + return ptr; + } } bool NzHardwareBuffer::Unmap() diff --git a/src/Nazara/Renderer/OpenGL.cpp b/src/Nazara/Renderer/OpenGL.cpp index f5039acdc..5a3bb8a7a 100644 --- a/src/Nazara/Renderer/OpenGL.cpp +++ b/src/Nazara/Renderer/OpenGL.cpp @@ -1863,10 +1863,11 @@ static_assert(sizeof(NzOpenGL::BufferLock)/sizeof(GLenum) == nzBufferAccess_Max+ GLenum NzOpenGL::BufferLockRange[] = { - GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_WRITE_BIT, // nzBufferAccess_DiscardAndWrite - GL_MAP_READ_BIT, // nzBufferAccess_ReadOnly - GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, // nzBufferAccess_ReadWrite - GL_MAP_WRITE_BIT // nzBufferAccess_WriteOnly + // http://www.opengl.org/discussion_boards/showthread.php/170118-VBOs-strangely-slow?p=1198118#post1198118 + GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_WRITE_BIT, // nzBufferAccess_DiscardAndWrite + GL_MAP_READ_BIT, // nzBufferAccess_ReadOnly + GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, // nzBufferAccess_ReadWrite + GL_MAP_WRITE_BIT // nzBufferAccess_WriteOnly }; static_assert(sizeof(NzOpenGL::BufferLockRange)/sizeof(GLenum) == nzBufferAccess_Max+1, "Buffer lock range array is incomplete"); @@ -1889,11 +1890,10 @@ static_assert(sizeof(NzOpenGL::BufferTargetBinding)/sizeof(GLenum) == nzBufferTy GLenum NzOpenGL::BufferUsage[] = { - // J'ai choisi DYNAMIC à la place de STREAM car DYNAMIC semble plus adapté au profil "une mise à jour pour quelques rendus" - // Ce qui est je pense le scénario qui arrivera le plus souvent (Prévoir une option pour permettre d'utiliser le STREAM_DRAW ?) - // Source: http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=160839 - GL_DYNAMIC_DRAW, // nzBufferUsage_Dynamic - GL_STATIC_DRAW // nzBufferUsage_Static + // D'après la documentation, GL_STREAM_DRAW semble être plus adapté à notre cas (ratio modification/rendu 1:2-3) + // Source: http://www.opengl.org/sdk/docs/man/html/glBufferData.xhtml + GL_STREAM_DRAW, // nzBufferUsage_Dynamic + GL_STATIC_DRAW // nzBufferUsage_Static }; static_assert(sizeof(NzOpenGL::BufferUsage)/sizeof(GLenum) == nzBufferUsage_Max+1, "Buffer usage array is incomplete");