diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 7da5e74d18..557f73a513 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -13,14 +13,16 @@
 class OGLTexture : private NonCopyable {
 public:
     OGLTexture() = default;
-    OGLTexture(OGLTexture&& o) {
-        std::swap(handle, o.handle);
-    }
+
+    OGLTexture(OGLTexture&& o) : handle(std::exchange(o.handle, 0)) {}
+
     ~OGLTexture() {
         Release();
     }
+
     OGLTexture& operator=(OGLTexture&& o) {
-        std::swap(handle, o.handle);
+        Release();
+        handle = std::exchange(o.handle, 0);
         return *this;
     }
 
@@ -46,14 +48,16 @@ public:
 class OGLSampler : private NonCopyable {
 public:
     OGLSampler() = default;
-    OGLSampler(OGLSampler&& o) {
-        std::swap(handle, o.handle);
-    }
+
+    OGLSampler(OGLSampler&& o) : handle(std::exchange(o.handle, 0)) {}
+
     ~OGLSampler() {
         Release();
     }
+
     OGLSampler& operator=(OGLSampler&& o) {
-        std::swap(handle, o.handle);
+        Release();
+        handle = std::exchange(o.handle, 0);
         return *this;
     }
 
@@ -79,25 +83,71 @@ public:
 class OGLShader : private NonCopyable {
 public:
     OGLShader() = default;
-    OGLShader(OGLShader&& o) {
-        std::swap(handle, o.handle);
-    }
+
+    OGLShader(OGLShader&& o) : handle(std::exchange(o.handle, 0)) {}
+
     ~OGLShader() {
         Release();
     }
+
     OGLShader& operator=(OGLShader&& o) {
-        std::swap(handle, o.handle);
+        Release();
+        handle = std::exchange(o.handle, 0);
         return *this;
     }
 
-    /// Creates a new internal OpenGL resource and stores the handle
-    void Create(const char* vert_shader, const char* geo_shader, const char* frag_shader,
-                const std::vector<const char*>& feedback_vars = {},
-                bool separable_program = false) {
+    void Create(const char* source, GLenum type) {
         if (handle != 0)
             return;
-        handle = GLShader::LoadProgram(vert_shader, geo_shader, frag_shader, feedback_vars,
-                                       separable_program);
+        if (source == nullptr)
+            return;
+        handle = GLShader::LoadShader(source, type);
+    }
+
+    void Release() {
+        if (handle == 0)
+            return;
+        glDeleteShader(handle);
+        handle = 0;
+    }
+
+    GLuint handle = 0;
+};
+
+class OGLProgram : private NonCopyable {
+public:
+    OGLProgram() = default;
+
+    OGLProgram(OGLProgram&& o) : handle(std::exchange(o.handle, 0)) {}
+
+    ~OGLProgram() {
+        Release();
+    }
+
+    OGLProgram& operator=(OGLProgram&& o) {
+        Release();
+        handle = std::exchange(o.handle, 0);
+        return *this;
+    }
+
+    template <typename... T>
+    void Create(bool separable_program = false, T... shaders) {
+        if (handle != 0)
+            return;
+        handle = GLShader::LoadProgram(separable_program, shaders...);
+    }
+
+    /// Creates a new internal OpenGL resource and stores the handle
+    void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader,
+                          bool separable_program = false) {
+        OGLShader vert, geo, frag;
+        if (vert_shader)
+            vert.Create(vert_shader, GL_VERTEX_SHADER);
+        if (geo_shader)
+            geo.Create(geo_shader, GL_GEOMETRY_SHADER);
+        if (frag_shader)
+            frag.Create(frag_shader, GL_FRAGMENT_SHADER);
+        Create(separable_program, vert.handle, geo.handle, frag.handle);
     }
 
     /// Deletes the internal OpenGL resource
@@ -148,14 +198,16 @@ public:
 class OGLBuffer : private NonCopyable {
 public:
     OGLBuffer() = default;
-    OGLBuffer(OGLBuffer&& o) {
-        std::swap(handle, o.handle);
-    }
+
+    OGLBuffer(OGLBuffer&& o) : handle(std::exchange(o.handle, 0)) {}
+
     ~OGLBuffer() {
         Release();
     }
+
     OGLBuffer& operator=(OGLBuffer&& o) {
-        std::swap(handle, o.handle);
+        Release();
+        handle = std::exchange(o.handle, 0);
         return *this;
     }
 
@@ -214,14 +266,16 @@ public:
 class OGLVertexArray : private NonCopyable {
 public:
     OGLVertexArray() = default;
-    OGLVertexArray(OGLVertexArray&& o) {
-        std::swap(handle, o.handle);
-    }
+
+    OGLVertexArray(OGLVertexArray&& o) : handle(std::exchange(o.handle, 0)) {}
+
     ~OGLVertexArray() {
         Release();
     }
+
     OGLVertexArray& operator=(OGLVertexArray&& o) {
-        std::swap(handle, o.handle);
+        Release();
+        handle = std::exchange(o.handle, 0);
         return *this;
     }
 
@@ -247,14 +301,16 @@ public:
 class OGLFramebuffer : private NonCopyable {
 public:
     OGLFramebuffer() = default;
-    OGLFramebuffer(OGLFramebuffer&& o) {
-        std::swap(handle, o.handle);
-    }
+
+    OGLFramebuffer(OGLFramebuffer&& o) : handle(std::exchange(o.handle, 0)) {}
+
     ~OGLFramebuffer() {
         Release();
     }
+
     OGLFramebuffer& operator=(OGLFramebuffer&& o) {
-        std::swap(handle, o.handle);
+        Release();
+        handle = std::exchange(o.handle, 0);
         return *this;
     }