From b4f8f08b706f38e759be5c58361716c348e56165 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 3 Sep 2017 18:23:54 -0300 Subject: [PATCH] Enable (when available) and improve performance of persistently mapped buffer path for the vertex/index buffers used with automatic batching. --HG-- branch : minor --- CMakeLists.txt | 4 +- .../xcode/liblove.xcodeproj/project.pbxproj | 20 +- src/modules/graphics/Graphics.cpp | 4 +- src/modules/graphics/StreamBuffer.h | 3 + .../opengl/{BufferSync.cpp => FenceSync.cpp} | 55 ++++- .../opengl/{BufferSync.h => FenceSync.h} | 16 ++ src/modules/graphics/opengl/Graphics.cpp | 4 + src/modules/graphics/opengl/StreamBuffer.cpp | 201 ++++++++++++------ 8 files changed, 230 insertions(+), 77 deletions(-) rename src/modules/graphics/opengl/{BufferSync.cpp => FenceSync.cpp} (73%) rename src/modules/graphics/opengl/{BufferSync.h => FenceSync.h} (92%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fbfd3b905..ff88ee892 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -542,10 +542,10 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT set(LOVE_SRC_MODULE_GRAPHICS_OPENGL src/modules/graphics/opengl/Buffer.cpp src/modules/graphics/opengl/Buffer.h - src/modules/graphics/opengl/BufferSync.cpp - src/modules/graphics/opengl/BufferSync.h src/modules/graphics/opengl/Canvas.cpp src/modules/graphics/opengl/Canvas.h + src/modules/graphics/opengl/FenceSync.cpp + src/modules/graphics/opengl/FenceSync.h src/modules/graphics/opengl/Graphics.cpp src/modules/graphics/opengl/Graphics.h src/modules/graphics/opengl/Image.cpp diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 7b175fdb9..159035a3a 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -844,9 +844,9 @@ FA27B3C11B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3B91B4985BF008A9DCE /* wrap_VideoStream.cpp */; }; FA27B3C21B4985BF008A9DCE /* wrap_VideoStream.h in Headers */ = {isa = PBXBuildFile; fileRef = FA27B3BA1B4985BF008A9DCE /* wrap_VideoStream.h */; }; FA27B3C91B498623008A9DCE /* Theora.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA27B3C81B498623008A9DCE /* Theora.framework */; }; - FA28EBD51E352DB5003446F4 /* BufferSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA28EBD31E352DB5003446F4 /* BufferSync.cpp */; }; - FA28EBD61E352DB5003446F4 /* BufferSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA28EBD31E352DB5003446F4 /* BufferSync.cpp */; }; - FA28EBD71E352DB5003446F4 /* BufferSync.h in Headers */ = {isa = PBXBuildFile; fileRef = FA28EBD41E352DB5003446F4 /* BufferSync.h */; }; + FA28EBD51E352DB5003446F4 /* FenceSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA28EBD31E352DB5003446F4 /* FenceSync.cpp */; }; + FA28EBD61E352DB5003446F4 /* FenceSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA28EBD31E352DB5003446F4 /* FenceSync.cpp */; }; + FA28EBD71E352DB5003446F4 /* FenceSync.h in Headers */ = {isa = PBXBuildFile; fileRef = FA28EBD41E352DB5003446F4 /* FenceSync.h */; }; FA29C0051E12355B00268CD8 /* StreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */; }; FA29C0061E12355B00268CD8 /* StreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */; }; FA2AF6741DAD64970032B62C /* vertex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA2AF6731DAD64970032B62C /* vertex.cpp */; }; @@ -1738,8 +1738,8 @@ FA27B3C81B498623008A9DCE /* Theora.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Theora.framework; path = /Library/Frameworks/Theora.framework; sourceTree = ""; }; FA283EDC1B27CFAA00C70067 /* nogame.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = nogame.lua; sourceTree = ""; }; FA283EDD1B27CFAA00C70067 /* nogame.lua.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nogame.lua.h; sourceTree = ""; }; - FA28EBD31E352DB5003446F4 /* BufferSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BufferSync.cpp; sourceTree = ""; }; - FA28EBD41E352DB5003446F4 /* BufferSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BufferSync.h; sourceTree = ""; }; + FA28EBD31E352DB5003446F4 /* FenceSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FenceSync.cpp; sourceTree = ""; }; + FA28EBD41E352DB5003446F4 /* FenceSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FenceSync.h; sourceTree = ""; }; FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StreamBuffer.cpp; sourceTree = ""; }; FA2AF6711DAC76FF0032B62C /* vertex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = vertex.h; sourceTree = ""; }; FA2AF6721DAD62710032B62C /* StreamBuffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StreamBuffer.h; sourceTree = ""; }; @@ -2733,10 +2733,10 @@ children = ( FA0B7BA41A95902C000E1D17 /* Buffer.cpp */, FA0B7BA51A95902C000E1D17 /* Buffer.h */, - FA28EBD31E352DB5003446F4 /* BufferSync.cpp */, - FA28EBD41E352DB5003446F4 /* BufferSync.h */, FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */, FA0B7B8E1A95902C000E1D17 /* Canvas.h */, + FA28EBD31E352DB5003446F4 /* FenceSync.cpp */, + FA28EBD41E352DB5003446F4 /* FenceSync.h */, FA0B7B911A95902C000E1D17 /* Graphics.cpp */, FA0B7B921A95902C000E1D17 /* Graphics.h */, FA0B7B931A95902C000E1D17 /* Image.cpp */, @@ -3837,7 +3837,7 @@ FA0B7DDE1A95902C000E1D17 /* wrap_BezierCurve.h in Headers */, FA0B7DED1A95902C000E1D17 /* Cursor.h in Headers */, FA0B7E501A95902C000E1D17 /* wrap_Fixture.h in Headers */, - FA28EBD71E352DB5003446F4 /* BufferSync.h in Headers */, + FA28EBD71E352DB5003446F4 /* FenceSync.h in Headers */, FADF542C1E3DAADA00012CC0 /* wrap_Mesh.h in Headers */, FAA3A9B01B7D465A00CED060 /* android.h in Headers */, 217DFBDE1D9F6D490055D849 /* compat.h in Headers */, @@ -4253,7 +4253,7 @@ FA57FB991AE1993600F2AD6D /* noise1234.cpp in Sources */, FA0B7E221A95902C000E1D17 /* PolygonShape.cpp in Sources */, FA0B7A651A958EA3000E1D17 /* b2Fixture.cpp in Sources */, - FA28EBD61E352DB5003446F4 /* BufferSync.cpp in Sources */, + FA28EBD61E352DB5003446F4 /* FenceSync.cpp in Sources */, FA0B7D521A95902C000E1D17 /* Text.cpp in Sources */, FA0B7DA31A95902C000E1D17 /* PKMHandler.cpp in Sources */, FA0B7AB21A958EA3000E1D17 /* b2Rope.cpp in Sources */, @@ -4613,7 +4613,7 @@ FA0B7EA61A95902C000E1D17 /* wrap_Decoder.cpp in Sources */, 217DFBF21D9F6D490055D849 /* mime.c in Sources */, 217DFBDF1D9F6D490055D849 /* except.c in Sources */, - FA28EBD51E352DB5003446F4 /* BufferSync.cpp in Sources */, + FA28EBD51E352DB5003446F4 /* FenceSync.cpp in Sources */, FA0B7E1B1A95902C000E1D17 /* MouseJoint.cpp in Sources */, FA0B7CF41A95902C000E1D17 /* File.cpp in Sources */, FA0B7E331A95902C000E1D17 /* WeldJoint.cpp in Sources */, diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 12e43992e..3ad6ad82e 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -798,7 +798,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawCommand & if (state.vbMap[i].data != nullptr && datasize > state.vbMap[i].size) shouldflush = true; - if (datasize > state.vb[i]->getSize()) + if (datasize > state.vb[i]->getUsableSize()) { buffersizes[i] = std::max(datasize, state.vb[i]->getSize() * 2); shouldresize = true; @@ -814,7 +814,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawCommand & if (state.indexBufferMap.data != nullptr && datasize > state.indexBufferMap.size) shouldflush = true; - if (datasize > state.indexBuffer->getSize()) + if (datasize > state.indexBuffer->getUsableSize()) { buffersizes[2] = std::max(datasize, state.indexBuffer->getSize() * 2); shouldresize = true; diff --git a/src/modules/graphics/StreamBuffer.h b/src/modules/graphics/StreamBuffer.h index 15364656b..dbcc2a49a 100644 --- a/src/modules/graphics/StreamBuffer.h +++ b/src/modules/graphics/StreamBuffer.h @@ -54,10 +54,13 @@ public: size_t getSize() const { return bufferSize; } BufferType getMode() const { return mode; } + virtual size_t getUsableSize() const = 0; virtual MapInfo map(size_t minsize) = 0; virtual size_t unmap(size_t usedsize) = 0; virtual void markUsed(size_t usedsize) = 0; + virtual void nextFrame() {} + virtual ptrdiff_t getHandle() const = 0; protected: diff --git a/src/modules/graphics/opengl/BufferSync.cpp b/src/modules/graphics/opengl/FenceSync.cpp similarity index 73% rename from src/modules/graphics/opengl/BufferSync.cpp rename to src/modules/graphics/opengl/FenceSync.cpp index 4ccbc78a8..889a978af 100644 --- a/src/modules/graphics/opengl/BufferSync.cpp +++ b/src/modules/graphics/opengl/FenceSync.cpp @@ -19,7 +19,7 @@ **/ -#include "BufferSync.h" +#include "FenceSync.h" namespace love { @@ -28,6 +28,59 @@ namespace graphics namespace opengl { +FenceSync::~FenceSync() +{ + cleanup(); +} + +bool FenceSync::fence() +{ + bool wasActive = sync != 0; + + if (wasActive) + cleanup(); + + sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + return !wasActive; +} + +bool FenceSync::cpuWait() +{ + if (sync == 0) + return false; + + GLbitfield flags = 0; + GLuint64 duration = 0; + + while (true) + { + GLenum status = glClientWaitSync(sync, flags, duration); + + if (status == GL_ALREADY_SIGNALED || status == GL_CONDITION_SATISFIED) + break; + + if (status == GL_WAIT_FAILED) + break; + + flags = GL_SYNC_FLUSH_COMMANDS_BIT; + duration = 1000000000; // 1 second in nanoseconds. + } + + cleanup(); + + return true; +} + +void FenceSync::cleanup() +{ + if (sync != 0) + { + glDeleteSync(sync); + sync = 0; + } +} + BufferSync::~BufferSync() { cleanup(); diff --git a/src/modules/graphics/opengl/BufferSync.h b/src/modules/graphics/opengl/FenceSync.h similarity index 92% rename from src/modules/graphics/opengl/BufferSync.h rename to src/modules/graphics/opengl/FenceSync.h index d12801ede..8106e9936 100644 --- a/src/modules/graphics/opengl/BufferSync.h +++ b/src/modules/graphics/opengl/FenceSync.h @@ -34,6 +34,22 @@ namespace graphics namespace opengl { +class FenceSync +{ +public: + + ~FenceSync(); + + bool fence(); + bool cpuWait(); + void cleanup(); + +private: + + GLsync sync; + +}; // FenceSync + class BufferSync { public: diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index c8c15d29c..6c2579a41 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -1004,6 +1004,10 @@ void Graphics::present(void *screenshotCallbackData) glBindRenderbuffer(GL_RENDERBUFFER, info.info.uikit.colorbuffer); #endif + for (StreamBuffer *buffer : streamBufferState.vb) + buffer->nextFrame(); + streamBufferState.indexBuffer->nextFrame(); + auto window = getInstance(M_WINDOW); if (window != nullptr) window->swapBuffers(); diff --git a/src/modules/graphics/opengl/StreamBuffer.cpp b/src/modules/graphics/opengl/StreamBuffer.cpp index 74f32a0a8..c92dafba3 100644 --- a/src/modules/graphics/opengl/StreamBuffer.cpp +++ b/src/modules/graphics/opengl/StreamBuffer.cpp @@ -18,13 +18,15 @@ * 3. This notice may not be removed or altered from any source distribution. **/ +#include "common/config.h" #include "StreamBuffer.h" #include "OpenGL.h" -#include "BufferSync.h" +#include "FenceSync.h" #include "graphics/Volatile.h" #include "common/Exception.h" #include +#include namespace love { @@ -33,7 +35,8 @@ namespace graphics namespace opengl { -static int BUFFER_FRAMES = 3; +static const int BUFFER_FRAMES = 3; +static const int MAX_SYNCS_PER_FRAME = 4; class StreamBufferClientMemory final : public love::graphics::StreamBuffer { @@ -58,6 +61,11 @@ public: delete[] data; } + size_t getUsableSize() const override + { + return bufferSize; + } + MapInfo map(size_t /*minsize*/) override { return MapInfo(data, bufferSize); @@ -87,6 +95,7 @@ public: , glMode(OpenGL::getGLBufferType(mode)) , data(nullptr) , offset(0) + , frameOffset(0) { try { @@ -106,11 +115,17 @@ public: delete[] data; } + size_t getUsableSize() const override + { + return bufferSize - frameOffset; + } + MapInfo map(size_t minsize) override { if (offset + minsize > bufferSize) { offset = 0; + frameOffset = 0; gl.bindBuffer(mode, vbo); glBufferData(glMode, bufferSize, nullptr, GL_STREAM_DRAW); } @@ -128,6 +143,12 @@ public: void markUsed(size_t usedsize) override { offset += usedsize; + frameOffset += usedsize; + } + + void nextFrame() override + { + frameOffset = 0; } ptrdiff_t getHandle() const override { return vbo; } @@ -142,6 +163,7 @@ public: glBufferData(glMode, bufferSize, nullptr, GL_STREAM_DRAW); offset = 0; + frameOffset = 0; return true; } @@ -163,17 +185,69 @@ protected: uint8 *data; size_t offset; + size_t frameOffset; }; // StreamBufferSubDataOrphan -class StreamBufferMapSync final : public love::graphics::StreamBuffer, public Volatile +class StreamBufferSync : public love::graphics::StreamBuffer +{ +public: + + StreamBufferSync(BufferType type, size_t size) + : love::graphics::StreamBuffer(type, size) + , syncSize((size + MAX_SYNCS_PER_FRAME - 1) / MAX_SYNCS_PER_FRAME) + , frameIndex(0) + , frameGPUReadOffset(0) + , syncs() + {} + + virtual ~StreamBufferSync() {} + + void nextFrame() override + { + getCurrentSync()->fence(); + + frameIndex = (frameIndex + 1) % BUFFER_FRAMES; + frameGPUReadOffset = 0; + } + + void markUsed(size_t usedsize) override + { + int firstSyncIndex = frameGPUReadOffset / syncSize; + int lastSyncIndex = std::min((frameGPUReadOffset + usedsize), bufferSize - 1) / syncSize; + + // Insert fences for all sync buckets completely filled by this section + // of the data. The last bucket before the end of the frame will also be + // handled by nextFrame(). + for (int i = firstSyncIndex; i < lastSyncIndex; i++) + syncs[frameIndex * MAX_SYNCS_PER_FRAME + i].fence(); + + frameGPUReadOffset += usedsize; + } + +protected: + + const size_t syncSize; + + int frameIndex; + size_t frameGPUReadOffset; + + FenceSync syncs[MAX_SYNCS_PER_FRAME * BUFFER_FRAMES]; + + FenceSync *getCurrentSync() + { + return &syncs[frameIndex * MAX_SYNCS_PER_FRAME + frameGPUReadOffset / syncSize]; + } + +}; // StreamBufferSync + +class StreamBufferMapSync final : public StreamBufferSync, public Volatile { public: StreamBufferMapSync(BufferType type, size_t size) - : love::graphics::StreamBuffer(type, size) + : StreamBufferSync(type, size) , vbo(0) - , gpuReadOffset(0) , glMode(OpenGL::getGLBufferType(mode)) { loadVolatile(); @@ -184,21 +258,32 @@ public: unloadVolatile(); } - MapInfo map(size_t minsize) override + size_t getUsableSize() const override + { + return bufferSize - frameGPUReadOffset; + } + + MapInfo map(size_t /*minsize*/) override { gl.bindBuffer(mode, vbo); - if (gpuReadOffset + minsize > bufferSize * BUFFER_FRAMES) - gpuReadOffset = 0; - MapInfo info; - info.size = bufferSize - (gpuReadOffset % bufferSize); + info.size = bufferSize - frameGPUReadOffset; - sync.wait(gpuReadOffset, info.size); + int firstSyncIndex = frameGPUReadOffset / syncSize; + int lastSyncIndex = (bufferSize - 1) / syncSize; + + // We're mapping the full range of space left in the buffer, so we + // need to wait on all of it... + // FIXME: is it even worth it to have multiple sync objects per frame? + for (int i = firstSyncIndex; i <= lastSyncIndex; i++) + syncs[frameIndex * MAX_SYNCS_PER_FRAME + i].cpuWait(); GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_UNSYNCHRONIZED_BIT; - info.data = (uint8 *) glMapBufferRange(glMode, gpuReadOffset, info.size, flags); + size_t mapoffset = (frameIndex * bufferSize) + frameGPUReadOffset; + info.data = (uint8 *) glMapBufferRange(glMode, mapoffset, info.size, flags); + return info; } @@ -208,13 +293,7 @@ public: glFlushMappedBufferRange(glMode, 0, usedsize); glUnmapBuffer(glMode); - return gpuReadOffset; - } - - void markUsed(size_t usedsize) override - { - sync.lock(gpuReadOffset, usedsize); - gpuReadOffset += usedsize; + return (frameIndex * bufferSize) + frameGPUReadOffset; } ptrdiff_t getHandle() const override { return vbo; } @@ -228,40 +307,38 @@ public: gl.bindBuffer(mode, vbo); glBufferData(glMode, bufferSize * BUFFER_FRAMES, nullptr, GL_STREAM_DRAW); - gpuReadOffset = 0; + frameGPUReadOffset = 0; + frameIndex = 0; return true; } void unloadVolatile() override { - if (vbo == 0) - return; + if (vbo != 0) + { + gl.deleteBuffer(vbo); + vbo = 0; + } - gl.deleteBuffer(vbo); - vbo = 0; - - sync.cleanup(); + for (FenceSync &sync : syncs) + sync.cleanup(); } private: GLuint vbo; - size_t gpuReadOffset; GLenum glMode; - BufferSync sync; - }; // StreamBufferMapSync -class StreamBufferPersistentMapSync final : public love::graphics::StreamBuffer, public Volatile +class StreamBufferPersistentMapSync final : public StreamBufferSync, public Volatile { public: StreamBufferPersistentMapSync(BufferType type, size_t size) - : love::graphics::StreamBuffer(type, size) + : StreamBufferSync(type, size) , vbo(0) - , gpuReadOffset(0) , glMode(OpenGL::getGLBufferType(mode)) , data(nullptr) { @@ -273,32 +350,37 @@ public: unloadVolatile(); } - MapInfo map(size_t minsize) override + size_t getUsableSize() const override { - if (gpuReadOffset + minsize > bufferSize * BUFFER_FRAMES) - gpuReadOffset = 0; + return bufferSize - frameGPUReadOffset; + } + MapInfo map(size_t /*minsize*/) override + { MapInfo info; - info.size = bufferSize - (gpuReadOffset % bufferSize); - info.data = data + gpuReadOffset; + info.size = bufferSize - frameGPUReadOffset; + info.data = data + (frameIndex * bufferSize) + frameGPUReadOffset; - sync.wait(gpuReadOffset, info.size); + int firstSyncIndex = frameGPUReadOffset / syncSize; + int lastSyncIndex = (bufferSize - 1) / syncSize; + + // We're mapping the full range of space left in the buffer, so we + // need to wait on all of it... + // FIXME: is it even worth it to have multiple sync objects per frame? + for (int i = firstSyncIndex; i <= lastSyncIndex; i++) + syncs[frameIndex * MAX_SYNCS_PER_FRAME + i].cpuWait(); return info; } size_t unmap(size_t usedsize) override { + size_t offset = (frameIndex * bufferSize) + frameGPUReadOffset; + gl.bindBuffer(mode, vbo); - glFlushMappedBufferRange(glMode, gpuReadOffset, usedsize); + glFlushMappedBufferRange(glMode, offset, usedsize); - return gpuReadOffset; - } - - void markUsed(size_t usedsize) override - { - sync.lock(gpuReadOffset, usedsize); - gpuReadOffset += usedsize; + return offset; } ptrdiff_t getHandle() const override { return vbo; } @@ -317,46 +399,41 @@ public: glBufferStorage(glMode, bufferSize * BUFFER_FRAMES, nullptr, storageflags); data = (uint8 *) glMapBufferRange(glMode, 0, bufferSize * BUFFER_FRAMES, mapflags); - gpuReadOffset = 0; + frameGPUReadOffset = 0; + frameIndex = 0; return true; } void unloadVolatile() override { - if (vbo == 0) - return; + if (vbo != 0) + { + gl.bindBuffer(mode, vbo); + glUnmapBuffer(glMode); + gl.deleteBuffer(vbo); + vbo = 0; + } - gl.bindBuffer(mode, vbo); - glUnmapBuffer(glMode); - gl.deleteBuffer(vbo); - vbo = 0; - - sync.cleanup(); + for (FenceSync &sync : syncs) + sync.cleanup(); } private: GLuint vbo; - size_t gpuReadOffset; GLenum glMode; uint8 *data; - BufferSync sync; - }; // StreamBufferPersistentMapSync love::graphics::StreamBuffer *CreateStreamBuffer(BufferType mode, size_t size) { if (gl.isCoreProfile()) { - // FIXME: This is disabled until more efficient manual syncing can be - // implemented. -#if 0 if (GLAD_VERSION_4_4 || GLAD_ARB_buffer_storage) return new StreamBufferPersistentMapSync(mode, size); else -#endif return new StreamBufferSubDataOrphan(mode, size); } else