Jelajahi Sumber

renderer_opengl: Use ARB_separate_shader_objects

Ensures that states set for a particular stage are not attached to other
stages which may not need them.
ReinUsesLisp 5 tahun lalu
induk
melakukan
e1ed218b41

+ 3 - 7
src/video_core/renderer_opengl/gl_compute_pipeline.cpp

@@ -46,17 +46,13 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac
       kepler_compute{kepler_compute_}, program_manager{program_manager_}, info{info_} {
     switch (device.GetShaderBackend()) {
     case Settings::ShaderBackend::GLSL:
-        source_program.handle = glCreateProgram();
-        AttachShader(GL_COMPUTE_SHADER, source_program.handle, code);
-        LinkProgram(source_program.handle);
+        source_program = CreateProgram(code, GL_COMPUTE_SHADER);
         break;
     case Settings::ShaderBackend::GLASM:
         assembly_program = CompileProgram(code, GL_COMPUTE_PROGRAM_NV);
         break;
     case Settings::ShaderBackend::SPIRV:
-        source_program.handle = glCreateProgram();
-        AttachShader(GL_COMPUTE_SHADER, source_program.handle, code_v);
-        LinkProgram(source_program.handle);
+        source_program = CreateProgram(code_v, GL_COMPUTE_SHADER);
         break;
     }
     std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(),
@@ -154,7 +150,7 @@ void ComputePipeline::Configure() {
     if (assembly_program.handle != 0) {
         program_manager.BindComputeAssemblyProgram(assembly_program.handle);
     } else {
-        program_manager.BindProgram(source_program.handle);
+        program_manager.BindComputeProgram(source_program.handle);
     }
     buffer_cache.UnbindComputeTextureBuffers();
     size_t texbuf_index{};

+ 25 - 37
src/video_core/renderer_opengl/gl_graphics_pipeline.cpp

@@ -237,44 +237,32 @@ GraphicsPipeline::GraphicsPipeline(
     if (key.xfb_enabled && device.UseAssemblyShaders()) {
         GenerateTransformFeedbackState();
     }
-    auto func{
-        [this, device, sources, sources_spirv, shader_notify](ShaderContext::Context*) mutable {
-            if (!device.UseAssemblyShaders()) {
-                program.handle = glCreateProgram();
-            }
-            for (size_t stage = 0; stage < 5; ++stage) {
-                switch (device.GetShaderBackend()) {
-                case Settings::ShaderBackend::GLSL: {
-                    const auto code{sources[stage]};
-                    if (code.empty()) {
-                        continue;
-                    }
-                    AttachShader(Stage(stage), program.handle, code);
-                } break;
-                case Settings::ShaderBackend::GLASM: {
-                    const auto code{sources[stage]};
-                    if (code.empty()) {
-                        continue;
-                    }
-                    assembly_programs[stage] = CompileProgram(code, AssemblyStage(stage));
-                } break;
-                case Settings::ShaderBackend::SPIRV: {
-                    const auto code{sources_spirv[stage]};
-                    if (code.empty()) {
-                        continue;
-                    }
-                    AttachShader(Stage(stage), program.handle, code);
-                } break;
+    auto func{[this, device, sources, sources_spirv,
+               shader_notify](ShaderContext::Context*) mutable {
+        for (size_t stage = 0; stage < 5; ++stage) {
+            switch (device.GetShaderBackend()) {
+            case Settings::ShaderBackend::GLSL:
+                if (!sources[stage].empty()) {
+                    source_programs[stage] = CreateProgram(sources[stage], Stage(stage));
                 }
+                break;
+            case Settings::ShaderBackend::GLASM:
+                if (!sources[stage].empty()) {
+                    assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage));
+                }
+                break;
+            case Settings::ShaderBackend::SPIRV:
+                if (!sources_spirv[stage].empty()) {
+                    source_programs[stage] = CreateProgram(sources_spirv[stage], Stage(stage));
+                }
+                break;
             }
-            if (!device.UseAssemblyShaders()) {
-                LinkProgram(program.handle);
-            }
-            if (shader_notify) {
-                shader_notify->MarkShaderComplete();
-            }
-            is_built.store(true, std::memory_order_relaxed);
-        }};
+        }
+        if (shader_notify) {
+            shader_notify->MarkShaderComplete();
+        }
+        is_built = true;
+    }};
     if (thread_worker) {
         thread_worker->QueueWork(std::move(func));
     } else {
@@ -449,7 +437,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
     if (assembly_programs[0].handle != 0) {
         program_manager.BindAssemblyPrograms(assembly_programs, enabled_stages_mask);
     } else {
-        program_manager.BindProgram(program.handle);
+        program_manager.BindSourcePrograms(source_programs);
     }
     const ImageId* views_it{image_view_ids.data()};
     GLsizei texture_binding = 0;

+ 1 - 1
src/video_core/renderer_opengl/gl_graphics_pipeline.h

@@ -129,7 +129,7 @@ private:
 
     void (*configure_func)(GraphicsPipeline*, bool){};
 
-    OGLProgram program;
+    std::array<OGLProgram, 5> source_programs;
     std::array<OGLAssemblyProgram, 5> assembly_programs;
     u32 enabled_stages_mask{};
 

+ 78 - 22
src/video_core/renderer_opengl/gl_shader_manager.h

@@ -24,34 +24,68 @@ class ProgramManager {
 
 public:
     explicit ProgramManager(const Device& device) {
+        glCreateProgramPipelines(1, &pipeline.handle);
         if (device.UseAssemblyShaders()) {
             glEnable(GL_COMPUTE_PROGRAM_NV);
         }
     }
 
-    void BindProgram(GLuint program) {
-        if (current_source_program == program) {
-            return;
-        }
-        current_source_program = program;
+    void BindComputeProgram(GLuint program) {
         glUseProgram(program);
+        is_compute_bound = true;
     }
 
     void BindComputeAssemblyProgram(GLuint program) {
-        if (current_compute_assembly_program != program) {
-            current_compute_assembly_program = program;
+        if (current_assembly_compute_program != program) {
+            current_assembly_compute_program = program;
             glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program);
         }
-        if (current_source_program != 0) {
-            current_source_program = 0;
-            glUseProgram(0);
+        UnbindPipeline();
+    }
+
+    void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) {
+        static constexpr std::array<GLenum, 5> stage_enums{
+            GL_VERTEX_SHADER_BIT,   GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT,
+            GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT,
+        };
+        for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
+            if (current_programs[stage] != programs[stage].handle) {
+                current_programs[stage] = programs[stage].handle;
+                glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle);
+            }
+        }
+        BindPipeline();
+    }
+
+    void BindPresentPrograms(GLuint vertex, GLuint fragment) {
+        if (current_programs[0] != vertex) {
+            current_programs[0] = vertex;
+            glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex);
+        }
+        if (current_programs[4] != fragment) {
+            current_programs[4] = fragment;
+            glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment);
+        }
+        glUseProgramStages(
+            pipeline.handle,
+            GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0);
+        current_programs[1] = 0;
+        current_programs[2] = 0;
+        current_programs[3] = 0;
+
+        if (current_stage_mask != 0) {
+            current_stage_mask = 0;
+            for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) {
+                glDisable(program_type);
+            }
         }
+        BindPipeline();
     }
 
     void BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs,
                               u32 stage_mask) {
-        const u32 changed_mask = current_assembly_mask ^ stage_mask;
-        current_assembly_mask = stage_mask;
+        const u32 changed_mask = current_stage_mask ^ stage_mask;
+        current_stage_mask = stage_mask;
 
         if (changed_mask != 0) {
             for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
@@ -65,25 +99,47 @@ public:
             }
         }
         for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
-            if (current_assembly_programs[stage] != programs[stage].handle) {
-                current_assembly_programs[stage] = programs[stage].handle;
+            if (current_programs[stage] != programs[stage].handle) {
+                current_programs[stage] = programs[stage].handle;
                 glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle);
             }
         }
-        if (current_source_program != 0) {
-            current_source_program = 0;
-            glUseProgram(0);
-        }
+        UnbindPipeline();
     }
 
     void RestoreGuestCompute() {}
 
 private:
-    GLuint current_source_program = 0;
+    void BindPipeline() {
+        if (!is_pipeline_bound) {
+            is_pipeline_bound = true;
+            glBindProgramPipeline(pipeline.handle);
+        }
+        UnbindCompute();
+    }
+
+    void UnbindPipeline() {
+        if (is_pipeline_bound) {
+            is_pipeline_bound = false;
+            glBindProgramPipeline(0);
+        }
+        UnbindCompute();
+    }
+
+    void UnbindCompute() {
+        if (is_compute_bound) {
+            is_compute_bound = false;
+            glUseProgram(0);
+        }
+    }
+
+    OGLPipeline pipeline;
+    bool is_pipeline_bound{};
+    bool is_compute_bound{};
 
-    u32 current_assembly_mask = 0;
-    std::array<GLuint, NUM_STAGES> current_assembly_programs{};
-    GLuint current_compute_assembly_program = 0;
+    u32 current_stage_mask = 0;
+    std::array<GLuint, NUM_STAGES> current_programs{};
+    GLuint current_assembly_compute_program = 0;
 };
 
 } // namespace OpenGL

+ 31 - 26
src/video_core/renderer_opengl/gl_shader_util.cpp

@@ -13,6 +13,33 @@
 
 namespace OpenGL {
 
+static OGLProgram LinkSeparableProgram(GLuint shader) {
+    OGLProgram program;
+    program.handle = glCreateProgram();
+    glProgramParameteri(program.handle, GL_PROGRAM_SEPARABLE, GL_TRUE);
+    glAttachShader(program.handle, shader);
+    glLinkProgram(program.handle);
+    if (!Settings::values.renderer_debug) {
+        return program;
+    }
+    GLint link_status{};
+    glGetProgramiv(program.handle, GL_LINK_STATUS, &link_status);
+
+    GLint log_length{};
+    glGetProgramiv(program.handle, GL_INFO_LOG_LENGTH, &log_length);
+    if (log_length == 0) {
+        return program;
+    }
+    std::string log(log_length, 0);
+    glGetProgramInfoLog(program.handle, log_length, nullptr, log.data());
+    if (link_status == GL_FALSE) {
+        LOG_ERROR(Render_OpenGL, "{}", log);
+    } else {
+        LOG_WARNING(Render_OpenGL, "{}", log);
+    }
+    return program;
+}
+
 static void LogShader(GLuint shader, std::string_view code = {}) {
     GLint shader_status{};
     glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_status);
@@ -36,7 +63,7 @@ static void LogShader(GLuint shader, std::string_view code = {}) {
     }
 }
 
-void AttachShader(GLenum stage, GLuint program, std::string_view code) {
+OGLProgram CreateProgram(std::string_view code, GLenum stage) {
     OGLShader shader;
     shader.handle = glCreateShader(stage);
 
@@ -44,45 +71,23 @@ void AttachShader(GLenum stage, GLuint program, std::string_view code) {
     const GLchar* const code_ptr = code.data();
     glShaderSource(shader.handle, 1, &code_ptr, &length);
     glCompileShader(shader.handle);
-    glAttachShader(program, shader.handle);
     if (Settings::values.renderer_debug) {
         LogShader(shader.handle, code);
     }
+    return LinkSeparableProgram(shader.handle);
 }
 
-void AttachShader(GLenum stage, GLuint program, std::span<const u32> code) {
+OGLProgram CreateProgram(std::span<const u32> code, GLenum stage) {
     OGLShader shader;
     shader.handle = glCreateShader(stage);
 
     glShaderBinary(1, &shader.handle, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, code.data(),
                    static_cast<GLsizei>(code.size_bytes()));
     glSpecializeShader(shader.handle, "main", 0, nullptr, nullptr);
-    glAttachShader(program, shader.handle);
     if (Settings::values.renderer_debug) {
         LogShader(shader.handle);
     }
-}
-
-void LinkProgram(GLuint program) {
-    glLinkProgram(program);
-    if (!Settings::values.renderer_debug) {
-        return;
-    }
-    GLint link_status{};
-    glGetProgramiv(program, GL_LINK_STATUS, &link_status);
-
-    GLint log_length{};
-    glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
-    if (log_length == 0) {
-        return;
-    }
-    std::string log(log_length, 0);
-    glGetProgramInfoLog(program, log_length, nullptr, log.data());
-    if (link_status == GL_FALSE) {
-        LOG_ERROR(Render_OpenGL, "{}", log);
-    } else {
-        LOG_WARNING(Render_OpenGL, "{}", log);
-    }
+    return LinkSeparableProgram(shader.handle);
 }
 
 OGLAssemblyProgram CompileProgram(std::string_view code, GLenum target) {

+ 2 - 4
src/video_core/renderer_opengl/gl_shader_util.h

@@ -17,11 +17,9 @@
 
 namespace OpenGL {
 
-void AttachShader(GLenum stage, GLuint program, std::string_view code);
+OGLProgram CreateProgram(std::string_view code, GLenum stage);
 
-void AttachShader(GLenum stage, GLuint program, std::span<const u32> code);
-
-void LinkProgram(GLuint program);
+OGLProgram CreateProgram(std::span<const u32> code, GLenum stage);
 
 OGLAssemblyProgram CompileProgram(std::string_view code, GLenum target);
 

+ 5 - 6
src/video_core/renderer_opengl/renderer_opengl.cpp

@@ -251,10 +251,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
 
 void RendererOpenGL::InitOpenGLObjects() {
     // Create shader programs
-    present_program.handle = glCreateProgram();
-    AttachShader(GL_VERTEX_SHADER, present_program.handle, HostShaders::OPENGL_PRESENT_VERT);
-    AttachShader(GL_FRAGMENT_SHADER, present_program.handle, HostShaders::OPENGL_PRESENT_FRAG);
-    LinkProgram(present_program.handle);
+    present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
+    present_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
 
     // Generate presentation sampler
     present_sampler.Create();
@@ -340,8 +338,9 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     // Set projection matrix
     const std::array ortho_matrix =
         MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
-    program_manager.BindProgram(present_program.handle);
-    glUniformMatrix3x2fv(ModelViewMatrixLocation, 1, GL_FALSE, ortho_matrix.data());
+    program_manager.BindPresentPrograms(present_vertex.handle, present_fragment.handle);
+    glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE,
+                                ortho_matrix.data());
 
     const auto& texcoords = screen_info.display_texcoords;
     auto left = texcoords.left;

+ 2 - 1
src/video_core/renderer_opengl/renderer_opengl.h

@@ -110,7 +110,8 @@ private:
     // OpenGL object IDs
     OGLSampler present_sampler;
     OGLBuffer vertex_buffer;
-    OGLProgram present_program;
+    OGLProgram present_vertex;
+    OGLProgram present_fragment;
     OGLFramebuffer screenshot_framebuffer;
 
     // GPU address of the vertex buffer

+ 7 - 12
src/video_core/renderer_opengl/util_shaders.cpp

@@ -42,12 +42,7 @@ using VideoCore::Surface::BytesPerBlock;
 
 namespace {
 OGLProgram MakeProgram(std::string_view source) {
-    OGLProgram program;
-    OGLShader shader;
-    program.handle = glCreateProgram();
-    AttachShader(GL_COMPUTE_SHADER, program.handle, source);
-    LinkProgram(program.handle);
-    return program;
+    return CreateProgram(source, GL_COMPUTE_SHADER);
 }
 
 size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) {
@@ -84,7 +79,7 @@ void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map,
         .width = VideoCore::Surface::DefaultBlockWidth(image.info.format),
         .height = VideoCore::Surface::DefaultBlockHeight(image.info.format),
     };
-    program_manager.BindProgram(astc_decoder_program.handle);
+    program_manager.BindComputeProgram(astc_decoder_program.handle);
     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_ENC_BUFFER, astc_buffer.handle);
 
@@ -132,7 +127,7 @@ void UtilShaders::BlockLinearUpload2D(Image& image, const ImageBufferMap& map,
     static constexpr GLuint BINDING_INPUT_BUFFER = 1;
     static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
 
-    program_manager.BindProgram(block_linear_unswizzle_2d_program.handle);
+    program_manager.BindComputeProgram(block_linear_unswizzle_2d_program.handle);
     glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
 
@@ -171,7 +166,7 @@ void UtilShaders::BlockLinearUpload3D(Image& image, const ImageBufferMap& map,
     static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
 
     glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
-    program_manager.BindProgram(block_linear_unswizzle_3d_program.handle);
+    program_manager.BindComputeProgram(block_linear_unswizzle_3d_program.handle);
     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
 
     const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
@@ -220,7 +215,7 @@ void UtilShaders::PitchUpload(Image& image, const ImageBufferMap& map,
     UNIMPLEMENTED_IF_MSG(!std::has_single_bit(bytes_per_block),
                          "Non-power of two images are not implemented");
 
-    program_manager.BindProgram(pitch_unswizzle_program.handle);
+    program_manager.BindComputeProgram(pitch_unswizzle_program.handle);
     glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
     glUniform2ui(LOC_ORIGIN, 0, 0);
     glUniform2i(LOC_DESTINATION, 0, 0);
@@ -248,7 +243,7 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im
     static constexpr GLuint LOC_SRC_OFFSET = 0;
     static constexpr GLuint LOC_DST_OFFSET = 1;
 
-    program_manager.BindProgram(copy_bc4_program.handle);
+    program_manager.BindComputeProgram(copy_bc4_program.handle);
 
     for (const ImageCopy& copy : copies) {
         ASSERT(copy.src_subresource.base_layer == 0);
@@ -284,7 +279,7 @@ void UtilShaders::CopyBGR(Image& dst_image, Image& src_image,
         break;
     case 4: {
         // BGRA8 copy
-        program_manager.BindProgram(copy_bgra_program.handle);
+        program_manager.BindComputeProgram(copy_bgra_program.handle);
         constexpr GLenum FORMAT = GL_RGBA8;
         for (const ImageCopy& copy : copies) {
             ASSERT(copy.src_offset == zero_offset);