From 8381490a04f4618ec5be90904815b409e3f4ca59 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 20 Jul 2021 03:05:41 -0300
Subject: [PATCH] opengl: Fix asynchronous shaders

Wait for shader to build before configuring it, and wait for the shader
to build before sharing it with other contexts.
---
 .../renderer_opengl/gl_graphics_pipeline.cpp  | 30 +++++++++++++++++--
 .../renderer_opengl/gl_graphics_pipeline.h    |  7 ++++-
 2 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index c8b2d833d..fac0034fb 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -237,10 +237,12 @@ GraphicsPipeline::GraphicsPipeline(
     if (key.xfb_enabled && device.UseAssemblyShaders()) {
         GenerateTransformFeedbackState();
     }
-    auto func{[this, device, sources, sources_spirv,
-               shader_notify](ShaderContext::Context*) mutable {
+    const bool in_parallel = thread_worker != nullptr;
+    const auto backend = device.GetShaderBackend();
+    auto func{[this, sources = std::move(sources), sources_spirv = std::move(sources_spirv),
+               shader_notify, backend, in_parallel](ShaderContext::Context*) mutable {
         for (size_t stage = 0; stage < 5; ++stage) {
-            switch (device.GetShaderBackend()) {
+            switch (backend) {
             case Settings::ShaderBackend::GLSL:
                 if (!sources[stage].empty()) {
                     source_programs[stage] = CreateProgram(sources[stage], Stage(stage));
@@ -249,6 +251,10 @@ GraphicsPipeline::GraphicsPipeline(
             case Settings::ShaderBackend::GLASM:
                 if (!sources[stage].empty()) {
                     assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage));
+                    if (in_parallel) {
+                        // Make sure program is built before continuing when building in parallel
+                        glGetString(GL_PROGRAM_ERROR_STRING_NV);
+                    }
                 }
                 break;
             case Settings::ShaderBackend::SPIRV:
@@ -258,10 +264,20 @@ GraphicsPipeline::GraphicsPipeline(
                 break;
             }
         }
+        if (in_parallel && backend != Settings::ShaderBackend::GLASM) {
+            // Make sure programs have built if we are building shaders in parallel
+            for (OGLProgram& program : source_programs) {
+                if (program.handle != 0) {
+                    GLint status{};
+                    glGetProgramiv(program.handle, GL_LINK_STATUS, &status);
+                }
+            }
+        }
         if (shader_notify) {
             shader_notify->MarkShaderComplete();
         }
         is_built = true;
+        built_condvar.notify_one();
     }};
     if (thread_worker) {
         thread_worker->QueueWork(std::move(func));
@@ -434,6 +450,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
     buffer_cache.UpdateGraphicsBuffers(is_indexed);
     buffer_cache.BindHostGeometryBuffers(is_indexed);
 
+    if (!is_built.load(std::memory_order::relaxed)) {
+        WaitForBuild();
+    }
     if (assembly_programs[0].handle != 0) {
         program_manager.BindAssemblyPrograms(assembly_programs, enabled_stages_mask);
     } else {
@@ -545,4 +564,9 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
     num_xfb_strides = static_cast<GLsizei>(current_stream - xfb_streams.data());
 }
 
+void GraphicsPipeline::WaitForBuild() {
+    std::unique_lock lock{built_mutex};
+    built_condvar.wait(lock, [this] { return is_built.load(std::memory_order::relaxed); });
+}
+
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 5e34b9537..4e28d9a42 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -119,6 +119,8 @@ private:
 
     void GenerateTransformFeedbackState();
 
+    void WaitForBuild();
+
     TextureCache& texture_cache;
     BufferCache& buffer_cache;
     Tegra::MemoryManager& gpu_memory;
@@ -143,13 +145,16 @@ private:
 
     bool use_storage_buffers{};
     bool writes_global_memory{};
-    std::atomic_bool is_built{false};
 
     static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
     GLsizei num_xfb_attribs{};
     GLsizei num_xfb_strides{};
     std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{};
     std::array<GLint, Maxwell::NumTransformFeedbackBuffers> xfb_streams{};
+
+    std::mutex built_mutex;
+    std::condition_variable built_condvar;
+    std::atomic_bool is_built{false};
 };
 
 } // namespace OpenGL