diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 5a0e337a55e4257d8f92e07fa297157680cb71cb..fd4731d808b54ad3e363131868bf67d405f1904e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -44,14 +44,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window)
         state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
     }
 
-    // Create SSBOs
-    for (size_t stage = 0; stage < ssbos.size(); ++stage) {
-        for (size_t buffer = 0; buffer < ssbos[stage].size(); ++buffer) {
-            ssbos[stage][buffer].Create();
-            state.draw.const_buffers[stage][buffer].ssbo = ssbos[stage][buffer].handle;
-        }
-    }
-
     GLint ext_num;
     glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num);
     for (GLint i = 0; i < ext_num; i++) {
@@ -255,9 +247,9 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr
             static_cast<Maxwell::ShaderStage>(stage));
 
         // Configure the const buffers for this shader stage.
-        current_constbuffer_bindpoint =
-            SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program,
-                              current_constbuffer_bindpoint, shader_resources.const_buffer_entries);
+        std::tie(buffer_ptr, buffer_offset, current_constbuffer_bindpoint) = SetupConstBuffers(
+            buffer_ptr, buffer_offset, static_cast<Maxwell::ShaderStage>(stage), gl_stage_program,
+            current_constbuffer_bindpoint, shader_resources.const_buffer_entries);
 
         // Configure the textures for this shader stage.
         current_texture_bindpoint =
@@ -453,6 +445,9 @@ void RasterizerOpenGL::DrawArrays() {
         Common::AlignUp<size_t>(buffer_size, 4) +
         (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage;
 
+    // Add space for at least 18 constant buffers
+    buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment);
+
     u8* buffer_ptr;
     GLintptr buffer_offset;
     std::tie(buffer_ptr, buffer_offset, std::ignore) =
@@ -627,9 +622,9 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
     }
 }
 
-u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program,
-                                        u32 current_bindpoint,
-                                        const std::vector<GLShader::ConstBufferEntry>& entries) {
+std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers(
+    u8* buffer_ptr, GLintptr buffer_offset, Maxwell::ShaderStage stage, GLuint program,
+    u32 current_bindpoint, const std::vector<GLShader::ConstBufferEntry>& entries) {
     const auto& gpu = Core::System::GetInstance().GPU();
     const auto& maxwell3d = gpu.Maxwell3D();
 
@@ -678,12 +673,16 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
         size = Common::AlignUp(size, sizeof(GLvec4));
         ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
 
-        std::vector<u8> data(size);
-        Memory::ReadBlock(*addr, data.data(), data.size());
+        std::tie(buffer_ptr, buffer_offset) =
+            AlignBuffer(buffer_ptr, buffer_offset, static_cast<size_t>(uniform_buffer_alignment));
 
-        glBindBuffer(GL_UNIFORM_BUFFER, buffer_draw_state.ssbo);
-        glBufferData(GL_UNIFORM_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW);
-        glBindBuffer(GL_UNIFORM_BUFFER, 0);
+        buffer_draw_state.size = size;
+        buffer_draw_state.offset = buffer_offset;
+        buffer_draw_state.ssbo = stream_buffer.GetHandle();
+
+        Memory::ReadBlock(*addr, buffer_ptr, size);
+        buffer_ptr += size;
+        buffer_offset += size;
 
         // Now configure the bindpoint of the buffer inside the shader
         const std::string buffer_name = used_buffer.GetName();
@@ -696,7 +695,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
 
     state.Apply();
 
-    return current_bindpoint + static_cast<u32>(entries.size());
+    return {buffer_ptr, buffer_offset, current_bindpoint + static_cast<u32>(entries.size())};
 }
 
 u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit,
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 6f8503703be4b64c0e0caa7a8815ae72107152ea..aa6afc1de8df071d2d0803b5b93ae69acb16d50a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -7,6 +7,7 @@
 #include <array>
 #include <cstddef>
 #include <memory>
+#include <tuple>
 #include <utility>
 #include <vector>
 #include <glad/glad.h>
@@ -100,9 +101,10 @@ private:
      * @param entries Vector describing the buffers that are actually used in the guest shader.
      * @returns The next available bindpoint for use in the next shader stage.
      */
-    u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, GLuint program,
-                          u32 current_bindpoint,
-                          const std::vector<GLShader::ConstBufferEntry>& entries);
+    std::tuple<u8*, GLintptr, u32> SetupConstBuffers(
+        u8* buffer_ptr, GLintptr buffer_offset, Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
+        GLuint program, u32 current_bindpoint,
+        const std::vector<GLShader::ConstBufferEntry>& entries);
 
     /*
      * Configures the current textures to use for the draw command.
@@ -154,9 +156,6 @@ private:
     OGLVertexArray hw_vao;
 
     std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers;
-    std::array<std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers>,
-               Tegra::Engines::Maxwell3D::Regs::MaxShaderStage>
-        ssbos;
 
     static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
     OGLStreamBuffer stream_buffer;
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 68bacd4c5f76c32f024fd5f2d37b820ae1ce624c..212c87e0c5f6c25f7def9618e6df7e00dc7a2c81 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -209,10 +209,13 @@ void OpenGLState::Apply() const {
             const auto& current = cur_state.draw.const_buffers[stage][buffer_id];
             const auto& new_state = draw.const_buffers[stage][buffer_id];
 
-            if (current.enabled != new_state.enabled || current.bindpoint != new_state.bindpoint ||
-                current.ssbo != new_state.ssbo) {
+            if (std::tie(current.enabled, current.bindpoint, current.ssbo, current.size,
+                         current.offset) != std::tie(new_state.enabled, new_state.bindpoint,
+                                                     new_state.ssbo, new_state.size,
+                                                     new_state.offset)) {
                 if (new_state.enabled) {
-                    glBindBufferBase(GL_UNIFORM_BUFFER, new_state.bindpoint, new_state.ssbo);
+                    glBindBufferRange(GL_UNIFORM_BUFFER, new_state.bindpoint, new_state.ssbo,
+                                      new_state.offset, new_state.size);
                 }
             }
         }
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 5c7b636e4f71513026c1d60730b26bb362d9127d..26e5460e579b7351690cd48b7397d7260848fd2c 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -123,6 +123,8 @@ public:
             bool enabled = false;
             GLuint bindpoint;
             GLuint ssbo;
+            GLsizeiptr size;
+            GLintptr offset;
         };
         std::array<std::array<ConstBufferConfig, Regs::MaxConstBuffers>, 5> const_buffers;
     } draw;