diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/video_core/rasterizer_interface.h | 3 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_buffer_cache.cpp | 189 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_buffer_cache.h | 115 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 40 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 1 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 4 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/utils.cpp | 17 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/utils.h | 14 |
8 files changed, 92 insertions, 291 deletions
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 5ee4f8e8e..2b7367568 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -47,6 +47,9 @@ public: /// and invalidated virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0; + /// Notify rasterizer that a frame is about to finish + virtual void TickFrame() = 0; + /// Attempt to use a faster method to perform a surface copy virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, const Tegra::Engines::Fermi2D::Regs::Surface& dst, diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index fb3aedd07..2a9b523f5 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -2,192 +2,57 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cstring> #include <memory> -#include <utility> -#include "common/alignment.h" +#include <glad/glad.h> + #include "common/assert.h" -#include "core/core.h" -#include "video_core/memory_manager.h" #include "video_core/renderer_opengl/gl_buffer_cache.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_resource_manager.h" namespace OpenGL { -namespace { +OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, + std::size_t stream_size) + : VideoCommon::BufferCache<OGLBuffer, GLuint, OGLStreamBuffer>{ + rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} {} -constexpr GLuint EmptyBuffer = 0; -constexpr GLintptr CachedBufferOffset = 0; +OGLBufferCache::~OGLBufferCache() = default; -OGLBuffer CreateBuffer(std::size_t size, GLenum usage) { +OGLBuffer OGLBufferCache::CreateBuffer(std::size_t size) { OGLBuffer buffer; buffer.Create(); - glNamedBufferData(buffer.handle, size, nullptr, usage); + glNamedBufferData(buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW); return buffer; } -} // Anonymous namespace - -CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, u8* host_ptr) - : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr} {} - -OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, std::size_t size) - : RasterizerCache{rasterizer}, system{system}, stream_buffer(size, true) {} - -OGLBufferCache::~OGLBufferCache() = default; - -void OGLBufferCache::Unregister(const std::shared_ptr<CachedBufferEntry>& entry) { - std::lock_guard lock{mutex}; - - if (entry->IsInternalized()) { - internalized_entries.erase(entry->GetCacheAddr()); - } - ReserveBuffer(entry); - RasterizerCache<std::shared_ptr<CachedBufferEntry>>::Unregister(entry); -} - -OGLBufferCache::BufferInfo OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, - std::size_t alignment, bool internalize, - bool is_written) { - std::lock_guard lock{mutex}; - - auto& memory_manager = system.GPU().MemoryManager(); - const auto host_ptr{memory_manager.GetPointer(gpu_addr)}; - const auto cache_addr{ToCacheAddr(host_ptr)}; - if (!host_ptr) { - return {EmptyBuffer, 0}; - } - - // Cache management is a big overhead, so only cache entries with a given size. - // TODO: Figure out which size is the best for given games. - if (!internalize && size < 0x800 && - internalized_entries.find(cache_addr) == internalized_entries.end()) { - return StreamBufferUpload(host_ptr, size, alignment); - } - - auto entry = TryGet(host_ptr); - if (!entry) { - return FixedBufferUpload(gpu_addr, host_ptr, size, internalize, is_written); - } - - if (entry->GetSize() < size) { - GrowBuffer(entry, size); - } - if (is_written) { - entry->MarkAsModified(true, *this); - } - return {entry->GetBuffer(), CachedBufferOffset}; -} - -OGLBufferCache::BufferInfo OGLBufferCache::UploadHostMemory(const void* raw_pointer, - std::size_t size, - std::size_t alignment) { - std::lock_guard lock{mutex}; - return StreamBufferUpload(raw_pointer, size, alignment); -} - -bool OGLBufferCache::Map(std::size_t max_size) { - const auto max_size_ = static_cast<GLsizeiptr>(max_size); - bool invalidate; - std::tie(buffer_ptr, buffer_offset_base, invalidate) = stream_buffer.Map(max_size_, 4); - buffer_offset = buffer_offset_base; - return invalidate; -} - -void OGLBufferCache::Unmap() { - stream_buffer.Unmap(buffer_offset - buffer_offset_base); -} - -void OGLBufferCache::FlushObjectInner(const std::shared_ptr<CachedBufferEntry>& entry) { - glGetNamedBufferSubData(entry->GetBuffer(), 0, entry->GetSize(), entry->GetWritableHostPtr()); -} - -OGLBufferCache::BufferInfo OGLBufferCache::StreamBufferUpload(const void* raw_pointer, - std::size_t size, - std::size_t alignment) { - AlignBuffer(alignment); - const GLintptr uploaded_offset = buffer_offset; - std::memcpy(buffer_ptr, raw_pointer, size); - - buffer_ptr += size; - buffer_offset += size; - return {stream_buffer.GetHandle(), uploaded_offset}; -} - -OGLBufferCache::BufferInfo OGLBufferCache::FixedBufferUpload(GPUVAddr gpu_addr, u8* host_ptr, - std::size_t size, bool internalize, - bool is_written) { - auto& memory_manager = system.GPU().MemoryManager(); - const auto cpu_addr = *memory_manager.GpuToCpuAddress(gpu_addr); - auto entry = GetUncachedBuffer(cpu_addr, host_ptr); - entry->SetSize(size); - entry->SetInternalState(internalize); - Register(entry); - - if (internalize) { - internalized_entries.emplace(ToCacheAddr(host_ptr)); - } - if (is_written) { - entry->MarkAsModified(true, *this); - } - - if (entry->GetCapacity() < size) { - entry->SetCapacity(CreateBuffer(size, GL_STATIC_DRAW), size); - } - glNamedBufferSubData(entry->GetBuffer(), 0, static_cast<GLintptr>(size), host_ptr); - return {entry->GetBuffer(), CachedBufferOffset}; -} - -void OGLBufferCache::GrowBuffer(std::shared_ptr<CachedBufferEntry>& entry, std::size_t new_size) { - const auto old_size = static_cast<GLintptr>(entry->GetSize()); - if (entry->GetCapacity() < new_size) { - const auto old_buffer = entry->GetBuffer(); - OGLBuffer new_buffer = CreateBuffer(new_size, GL_STATIC_COPY); - - // Copy bits from the old buffer to the new buffer. - glCopyNamedBufferSubData(old_buffer, new_buffer.handle, 0, 0, old_size); - entry->SetCapacity(std::move(new_buffer), new_size); - } - // Upload the new bits. - const auto size_diff = static_cast<GLintptr>(new_size - old_size); - glNamedBufferSubData(entry->GetBuffer(), old_size, size_diff, entry->GetHostPtr() + old_size); - - // Update entry's size in the object and in the cache. - entry->SetSize(new_size); - Unregister(entry); - Register(entry); +const GLuint* OGLBufferCache::ToHandle(const OGLBuffer& buffer) { + return &buffer.handle; } -std::shared_ptr<CachedBufferEntry> OGLBufferCache::GetUncachedBuffer(VAddr cpu_addr, u8* host_ptr) { - if (auto entry = TryGetReservedBuffer(host_ptr)) { - return entry; - } - return std::make_shared<CachedBufferEntry>(cpu_addr, host_ptr); +const GLuint* OGLBufferCache::GetEmptyBuffer(std::size_t) { + static const GLuint null_buffer = 0; + return &null_buffer; } -std::shared_ptr<CachedBufferEntry> OGLBufferCache::TryGetReservedBuffer(u8* host_ptr) { - const auto it = buffer_reserve.find(ToCacheAddr(host_ptr)); - if (it == buffer_reserve.end()) { - return {}; - } - auto& reserve = it->second; - auto entry = reserve.back(); - reserve.pop_back(); - return entry; +void OGLBufferCache::UploadBufferData(const OGLBuffer& buffer, std::size_t offset, std::size_t size, + const u8* data) { + glNamedBufferSubData(buffer.handle, static_cast<GLintptr>(offset), + static_cast<GLsizeiptr>(size), data); } -void OGLBufferCache::ReserveBuffer(std::shared_ptr<CachedBufferEntry> entry) { - buffer_reserve[entry->GetCacheAddr()].push_back(std::move(entry)); +void OGLBufferCache::DownloadBufferData(const OGLBuffer& buffer, std::size_t offset, + std::size_t size, u8* data) { + glGetNamedBufferSubData(buffer.handle, static_cast<GLintptr>(offset), + static_cast<GLsizeiptr>(size), data); } -void OGLBufferCache::AlignBuffer(std::size_t alignment) { - // Align the offset, not the mapped pointer - const GLintptr offset_aligned = - static_cast<GLintptr>(Common::AlignUp(static_cast<std::size_t>(buffer_offset), alignment)); - buffer_ptr += offset_aligned - buffer_offset; - buffer_offset = offset_aligned; +void OGLBufferCache::CopyBufferData(const OGLBuffer& src, const OGLBuffer& dst, + std::size_t src_offset, std::size_t dst_offset, + std::size_t size) { + glCopyNamedBufferSubData(src.handle, dst.handle, static_cast<GLintptr>(src_offset), + static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); } } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 19d643e41..3befdc6ab 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -4,15 +4,10 @@ #pragma once -#include <cstddef> -#include <map> #include <memory> -#include <tuple> -#include <unordered_set> -#include <utility> -#include <vector> #include "common/common_types.h" +#include "video_core/buffer_cache.h" #include "video_core/rasterizer_cache.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_stream_buffer.h" @@ -23,112 +18,30 @@ class System; namespace OpenGL { +class OGLStreamBuffer; class RasterizerOpenGL; -class CachedBufferEntry final : public RasterizerCacheObject { +class OGLBufferCache final : public VideoCommon::BufferCache<OGLBuffer, GLuint, OGLStreamBuffer> { public: - explicit CachedBufferEntry(VAddr cpu_addr, u8* host_ptr); - - VAddr GetCpuAddr() const override { - return cpu_addr; - } - - std::size_t GetSizeInBytes() const override { - return size; - } - - u8* GetWritableHostPtr() const { - return host_ptr; - } - - std::size_t GetSize() const { - return size; - } - - std::size_t GetCapacity() const { - return capacity; - } - - bool IsInternalized() const { - return is_internal; - } - - GLuint GetBuffer() const { - return buffer.handle; - } - - void SetSize(std::size_t new_size) { - size = new_size; - } - - void SetInternalState(bool is_internal_) { - is_internal = is_internal_; - } - - void SetCapacity(OGLBuffer&& new_buffer, std::size_t new_capacity) { - capacity = new_capacity; - buffer = std::move(new_buffer); - } - -private: - u8* host_ptr{}; - VAddr cpu_addr{}; - std::size_t size{}; - std::size_t capacity{}; - bool is_internal{}; - OGLBuffer buffer; -}; - -class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { - using BufferInfo = std::pair<GLuint, GLintptr>; - -public: - explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, std::size_t size); + explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, + std::size_t stream_size); ~OGLBufferCache(); - void Unregister(const std::shared_ptr<CachedBufferEntry>& entry) override; - - /// Uploads data from a guest GPU address. Returns the OpenGL buffer where it's located and its - /// offset. - BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, - bool internalize = false, bool is_written = false); - - /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset. - BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size, - std::size_t alignment = 4); - - bool Map(std::size_t max_size); - void Unmap(); - protected: - // We do not have to flush this cache as things in it are never modified by us. - void FlushObjectInner(const std::shared_ptr<CachedBufferEntry>& entry) override; - -private: - BufferInfo StreamBufferUpload(const void* raw_pointer, std::size_t size, std::size_t alignment); - - BufferInfo FixedBufferUpload(GPUVAddr gpu_addr, u8* host_ptr, std::size_t size, - bool internalize, bool is_written); - - void GrowBuffer(std::shared_ptr<CachedBufferEntry>& entry, std::size_t new_size); - - std::shared_ptr<CachedBufferEntry> GetUncachedBuffer(VAddr cpu_addr, u8* host_ptr); - - std::shared_ptr<CachedBufferEntry> TryGetReservedBuffer(u8* host_ptr); + OGLBuffer CreateBuffer(std::size_t size) override; - void ReserveBuffer(std::shared_ptr<CachedBufferEntry> entry); + const GLuint* ToHandle(const OGLBuffer& buffer) override; - void AlignBuffer(std::size_t alignment); + const GLuint* GetEmptyBuffer(std::size_t) override; - Core::System& system; + void UploadBufferData(const OGLBuffer& buffer, std::size_t offset, std::size_t size, + const u8* data) override; - u8* buffer_ptr = nullptr; - GLintptr buffer_offset = 0; - GLintptr buffer_offset_base = 0; + void DownloadBufferData(const OGLBuffer& buffer, std::size_t offset, std::size_t size, + u8* data) override; - OGLStreamBuffer stream_buffer; - std::unordered_set<CacheAddr> internalized_entries; - std::unordered_map<CacheAddr, std::vector<std::shared_ptr<CachedBufferEntry>>> buffer_reserve; + void CopyBufferData(const OGLBuffer& src, const OGLBuffer& dst, std::size_t src_offset, + std::size_t dst_offset, std::size_t size) override; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 35ba84235..b57d60856 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -198,7 +198,8 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) { const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); // Bind the vertex array to the buffer at the current offset. - glVertexArrayVertexBuffer(vao, index, vertex_buffer, vertex_buffer_offset, + // FIXME(Rodrigo): This dereferenced pointer might be invalidated in future uploads. + glVertexArrayVertexBuffer(vao, index, *vertex_buffer, vertex_buffer_offset, vertex_array.stride); if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) { @@ -221,7 +222,8 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer(GLuint vao) { const auto& regs = system.GPU().Maxwell3D().regs; const std::size_t size = CalculateIndexBufferSize(); const auto [buffer, offset] = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); - glVertexArrayElementBuffer(vao, buffer); + // FIXME(Rodrigo): This dereferenced pointer might be invalidated in future uploads. + glVertexArrayElementBuffer(vao, *buffer); return offset; } @@ -255,10 +257,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { BaseBindings base_bindings; std::array<bool, Maxwell::NumClipDistances> clip_distances{}; - // Prepare packed bindings - bind_ubo_pushbuffer.Setup(base_bindings.cbuf); - bind_ssbo_pushbuffer.Setup(base_bindings.gmem); - for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { const auto& shader_config = gpu.regs.shader_config[index]; const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; @@ -328,9 +326,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { base_bindings = next_bindings; } - bind_ubo_pushbuffer.Bind(); - bind_ssbo_pushbuffer.Bind(); - SyncClipEnabled(clip_distances); gpu.dirty_flags.shaders = false; @@ -644,11 +639,8 @@ void RasterizerOpenGL::DrawArrays() { buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + device.GetUniformBufferAlignment()); - const bool invalidate = buffer_cache.Map(buffer_size); - if (invalidate) { - // As all cached buffers are invalidated, we need to recheck their state. - gpu.dirty_flags.vertex_array.set(); - } + // Prepare the vertex array. + buffer_cache.Map(buffer_size); // Prepare vertex array format. const GLuint vao = SetupVertexFormat(); @@ -660,6 +652,10 @@ void RasterizerOpenGL::DrawArrays() { // Setup draw parameters. It will automatically choose what glDraw* method to use. const DrawParameters params = SetupDraw(index_buffer_offset); + // Prepare packed bindings. + bind_ubo_pushbuffer.Setup(0); + bind_ssbo_pushbuffer.Setup(0); + // Setup shaders and their used resources. texture_cache.GuardSamplers(true); SetupShaders(params.primitive_mode); @@ -667,7 +663,17 @@ void RasterizerOpenGL::DrawArrays() { ConfigureFramebuffers(state); - buffer_cache.Unmap(); + // Signal the buffer cache that we are not going to upload more things. + const bool invalidate = buffer_cache.Unmap(); + + // Now that we are no longer uploading data, we can safely bind the buffers to OpenGL. + bind_ubo_pushbuffer.Bind(); + bind_ssbo_pushbuffer.Bind(); + + if (invalidate) { + // As all cached buffers are invalidated, we need to recheck their state. + gpu.dirty_flags.vertex_array.set(); + } shader_program_manager->ApplyTo(state); state.Apply(); @@ -709,6 +715,10 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { InvalidateRegion(addr, size); } +void RasterizerOpenGL::TickFrame() { + buffer_cache.TickFrame(); +} + bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, const Tegra::Engines::Fermi2D::Regs::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index bc988727b..7067ad5b4 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -62,6 +62,7 @@ public: void FlushRegion(CacheAddr addr, u64 size) override; void InvalidateRegion(CacheAddr addr, u64 size) override; void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override; + void TickFrame() override; bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, const Tegra::Engines::Fermi2D::Regs::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) override; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index b142521ec..9ecdddb0d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -101,7 +101,6 @@ RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::Syst RendererOpenGL::~RendererOpenGL() = default; -/// Swap buffers (render frame) void RendererOpenGL::SwapBuffers( std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { @@ -130,6 +129,8 @@ void RendererOpenGL::SwapBuffers( DrawScreen(render_window.GetFramebufferLayout()); + rasterizer->TickFrame(); + render_window.SwapBuffers(); } @@ -262,7 +263,6 @@ void RendererOpenGL::CreateRasterizer() { if (rasterizer) { return; } - // Initialize sRGB Usage OpenGLState::ClearsRGBUsed(); rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info); } diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp index 68c36988d..22eefa1d7 100644 --- a/src/video_core/renderer_opengl/utils.cpp +++ b/src/video_core/renderer_opengl/utils.cpp @@ -19,23 +19,30 @@ BindBuffersRangePushBuffer::~BindBuffersRangePushBuffer() = default; void BindBuffersRangePushBuffer::Setup(GLuint first_) { first = first_; - buffers.clear(); + buffer_pointers.clear(); offsets.clear(); sizes.clear(); } -void BindBuffersRangePushBuffer::Push(GLuint buffer, GLintptr offset, GLsizeiptr size) { - buffers.push_back(buffer); +void BindBuffersRangePushBuffer::Push(const GLuint* buffer, GLintptr offset, GLsizeiptr size) { + buffer_pointers.push_back(buffer); offsets.push_back(offset); sizes.push_back(size); } -void BindBuffersRangePushBuffer::Bind() const { - const std::size_t count{buffers.size()}; +void BindBuffersRangePushBuffer::Bind() { + // Ensure sizes are valid. + const std::size_t count{buffer_pointers.size()}; DEBUG_ASSERT(count == offsets.size() && count == sizes.size()); if (count == 0) { return; } + + // Dereference buffers. + buffers.resize(count); + std::transform(buffer_pointers.begin(), buffer_pointers.end(), buffers.begin(), + [](const GLuint* pointer) { return *pointer; }); + glBindBuffersRange(target, first, static_cast<GLsizei>(count), buffers.data(), offsets.data(), sizes.data()); } diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h index 4a752f3b4..d2a3d25d9 100644 --- a/src/video_core/renderer_opengl/utils.h +++ b/src/video_core/renderer_opengl/utils.h @@ -11,20 +11,22 @@ namespace OpenGL { -class BindBuffersRangePushBuffer { +class BindBuffersRangePushBuffer final { public: - BindBuffersRangePushBuffer(GLenum target); + explicit BindBuffersRangePushBuffer(GLenum target); ~BindBuffersRangePushBuffer(); void Setup(GLuint first_); - void Push(GLuint buffer, GLintptr offset, GLsizeiptr size); + void Push(const GLuint* buffer, GLintptr offset, GLsizeiptr size); - void Bind() const; + void Bind(); private: - GLenum target; - GLuint first; + GLenum target{}; + GLuint first{}; + std::vector<const GLuint*> buffer_pointers; + std::vector<GLuint> buffers; std::vector<GLintptr> offsets; std::vector<GLsizeiptr> sizes; |