diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8398b884f3..9411663264 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -777,21 +777,20 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
         // which causes unpredictable behavior on the host.
         // Making a copy to sample from eliminates this issue and seems to be fairly cheap.
         temp_tex.Create();
-        glActiveTexture(GL_TEXTURE0);
         glBindTexture(GL_TEXTURE_2D, temp_tex.handle);
         auto [internal_format, format, type] = GetFormatTuple(color_surface->pixel_format);
-        ASSERT_MSG(GLAD_GL_ARB_texture_storage, "ARB_texture_storage not supported");
-        glTexStorage2D(GL_TEXTURE_2D, color_surface->max_level + 1, internal_format,
-                       color_surface->GetScaledWidth(), color_surface->GetScaledHeight());
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        OGLTexture::Allocate(GL_TEXTURE_2D, color_surface->max_level, internal_format, format, type,
+                             color_surface->GetScaledWidth(), color_surface->GetScaledHeight());
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
         glBindTexture(GL_TEXTURE_2D, state.texture_units[0].texture_2d);
 
-        for (std::size_t mip{0}; mip <= color_surface->max_level; ++mip) {
-            glCopyImageSubData(color_surface->texture.handle, GL_TEXTURE_2D, mip, 0, 0, 0,
-                               temp_tex.handle, GL_TEXTURE_2D, mip, 0, 0, 0,
-                               color_surface->GetScaledWidth() >> mip,
-                               color_surface->GetScaledHeight() >> mip, 1);
+        for (std::size_t level{0}; level <= color_surface->max_level; ++level) {
+            glCopyImageSubData(color_surface->texture.handle, GL_TEXTURE_2D, level, 0, 0, 0,
+                               temp_tex.handle, GL_TEXTURE_2D, level, 0, 0, 0,
+                               color_surface->GetScaledWidth() >> level,
+                               color_surface->GetScaledHeight() >> level, 1);
         }
 
         for (auto& unit : state.texture_units) {
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index 836cbb90c4..19bc83aa37 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -9,6 +9,7 @@
 #include "video_core/renderer_opengl/gl_resource_manager.h"
 #include "video_core/renderer_opengl/gl_shader_util.h"
 #include "video_core/renderer_opengl/gl_state.h"
+#include "video_core/renderer_opengl/gl_vars.h"
 
 MICROPROFILE_DEFINE(OpenGL_ResourceCreation, "OpenGL", "Resource Creation", MP_RGB(128, 128, 192));
 MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_RGB(128, 128, 192));
@@ -51,6 +52,62 @@ void OGLTexture::Release() {
     handle = 0;
 }
 
+void OGLTexture::Allocate(GLenum target, GLsizei levels, GLenum internalformat,
+                                 GLenum format, GLenum type, GLsizei width, GLsizei height,
+                                 GLsizei depth) {
+    const bool tex_storage = GLAD_GL_ARB_texture_storage || GLES;
+
+    switch (target) {
+    case GL_TEXTURE_1D:
+    case GL_TEXTURE:
+        if (tex_storage) {
+            glTexStorage1D(target, levels, internalformat, width);
+        } else {
+            for (GLsizei level{0}; level < levels; ++level) {
+                width >>= 1;
+                glTexImage1D(target, level, internalformat, width, 0, format, type, nullptr);
+            }
+        }
+        break;
+    case GL_TEXTURE_2D:
+    case GL_TEXTURE_1D_ARRAY:
+    case GL_TEXTURE_RECTANGLE:
+    case GL_TEXTURE_CUBE_MAP:
+        if (tex_storage) {
+            glTexStorage2D(target, levels, internalformat, width, height);
+        } else {
+            for (GLsizei level{0}; level < levels; ++level) {
+                width >>= 1;
+                if (target != GL_TEXTURE_1D_ARRAY)
+                    height >>= 1;
+                glTexImage2D(target, level, internalformat, width, height, 0, format, type,
+                             nullptr);
+            }
+        }
+        break;
+    case GL_TEXTURE_3D:
+    case GL_TEXTURE_2D_ARRAY:
+    case GL_TEXTURE_CUBE_MAP_ARRAY:
+        if (tex_storage) {
+            glTexStorage3D(target, levels, internalformat, width, height, depth);
+        } else {
+            for (GLsizei level{0}; level < levels; ++level) {
+                glTexImage3D(target, level, internalformat, width, height, depth, 0, format, type,
+                             nullptr);
+            }
+            width >>= 1;
+            height >>= 1;
+            if (target == GL_TEXTURE_3D)
+                depth >>= 1;
+        }
+        break;
+    }
+
+    if (!tex_storage) {
+        glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels - 1);
+    }
+}
+
 void OGLSampler::Create() {
     if (handle != 0)
         return;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 9b9f150e1f..db326ed9af 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -59,6 +59,8 @@ public:
     /// Deletes the internal OpenGL resource
     void Release();
 
+    static void Allocate(GLenum target, GLsizei levels, GLenum internalformat, GLenum format, GLenum type, GLsizei width, GLsizei height = 1, GLsizei depth = 1);
+
     GLuint handle = 0;
 };