diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index 0212793f98..8329b74e79 100644 --- a/Source/Core/VideoCommon/PostProcessing.cpp +++ b/Source/Core/VideoCommon/PostProcessing.cpp @@ -719,15 +719,10 @@ bool PostProcessing::CompilePixelShader() bool PostProcessing::CompilePipeline() { - // OpenGL doesn't render to a 2-layer backbuffer like D3D/Vulkan for quad-buffered stereo, instead - // drawing twice and the eye selected by glDrawBuffer() (see OGL::Renderer::RenderXFBToScreen). - const bool use_quad_buffer_gs = g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer && - g_ActiveConfig.backend_info.api_type != APIType::OpenGL; - AbstractPipelineConfig config = {}; config.vertex_shader = m_vertex_shader.get(); config.geometry_shader = - use_quad_buffer_gs ? g_shader_cache->GetTexcoordGeometryShader() : nullptr; + g_renderer->UseGeometryShaderForUI() ? g_shader_cache->GetTexcoordGeometryShader() : nullptr; config.pixel_shader = m_pixel_shader.get(); config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); config.depth_state = RenderState::GetNoDepthTestingDepthState(); diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 31d3192933..c533814fc8 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -59,6 +59,7 @@ #include "VideoCommon/FPSCounter.h" #include "VideoCommon/FrameDump.h" #include "VideoCommon/FramebufferManager.h" +#include "VideoCommon/FramebufferShaderGen.h" #include "VideoCommon/ImageWrite.h" #include "VideoCommon/NetPlayChatUI.h" #include "VideoCommon/NetPlayGolfUI.h" @@ -442,6 +443,13 @@ void Renderer::CheckForConfigChanges() // Notify the backend of the changes, if any. OnConfigChanged(changed_bits); + // If there's any shader changes, wait for the GPU to finish before destroying anything. + if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) + { + WaitForGPUIdle(); + SetPipeline(nullptr); + } + // Framebuffer changed? if (changed_bits & (CONFIG_CHANGE_BIT_MULTISAMPLES | CONFIG_CHANGE_BIT_STEREO_MODE | CONFIG_CHANGE_BIT_TARGET_SIZE)) @@ -453,8 +461,6 @@ void Renderer::CheckForConfigChanges() if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) { OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); - WaitForGPUIdle(); - SetPipeline(nullptr); g_vertex_manager->InvalidatePipelineObject(); g_shader_cache->SetHostConfig(new_host_config); g_shader_cache->Reload(); @@ -467,6 +473,14 @@ void Renderer::CheckForConfigChanges() BPFunctions::SetViewport(); BPFunctions::SetScissor(); } + + // Stereo mode change requires recompiling our post processing pipeline and imgui pipelines for + // rendering the UI. + if (changed_bits & CONFIG_CHANGE_BIT_STEREO_MODE) + { + RecompileImGuiPipeline(); + m_post_processor->RecompilePipeline(); + } } // Create On-Screen-Messages @@ -916,8 +930,8 @@ static std::string GenerateImGuiVertexShader() ss << "void main(in float2 rawpos : POSITION,\n" << " in float2 rawtex0 : TEXCOORD,\n" << " in float4 rawcolor0 : COLOR,\n" - << " out float2 frag_uv : TEXCOORD,\n" - << " out float4 frag_color : COLOR,\n" + << " out float3 v_tex0 : TEXCOORD,\n" + << " out float4 v_col0 : COLOR,\n" << " out float4 out_pos : SV_Position)\n"; } else @@ -925,14 +939,14 @@ static std::string GenerateImGuiVertexShader() ss << "ATTRIBUTE_LOCATION(" << SHADER_POSITION_ATTRIB << ") in float2 rawpos;\n" << "ATTRIBUTE_LOCATION(" << SHADER_TEXTURE0_ATTRIB << ") in float2 rawtex0;\n" << "ATTRIBUTE_LOCATION(" << SHADER_COLOR0_ATTRIB << ") in float4 rawcolor0;\n" - << "VARYING_LOCATION(0) out float2 frag_uv;\n" - << "VARYING_LOCATION(1) out float4 frag_color;\n" + << "VARYING_LOCATION(0) out float3 v_tex0;\n" + << "VARYING_LOCATION(1) out float4 v_col0;\n" << "void main()\n"; } ss << "{\n" - << " frag_uv = rawtex0;\n" - << " frag_color = rawcolor0;\n"; + << " v_tex0 = float3(rawtex0, 0.0);\n" + << " v_col0 = rawcolor0;\n"; ss << " " << (api_type == APIType::D3D ? "out_pos" : "gl_Position") << "= float4(rawpos.x * u_rcp_viewport_size_mul2.x - 1.0, 1.0 - rawpos.y * " @@ -955,15 +969,15 @@ static std::string GenerateImGuiPixelShader() { ss << "Texture2DArray tex0 : register(t0);\n" << "SamplerState samp0 : register(s0);\n" - << "void main(in float2 frag_uv : TEXCOORD,\n" - << " in float4 frag_color : COLOR,\n" + << "void main(in float3 v_tex0 : TEXCOORD,\n" + << " in float4 v_col0 : COLOR,\n" << " out float4 ocol0 : SV_Target)\n"; } else { ss << "SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n" - << "VARYING_LOCATION(0) in float2 frag_uv; \n" - << "VARYING_LOCATION(1) in float4 frag_color;\n" + << "VARYING_LOCATION(0) in float3 v_tex0; \n" + << "VARYING_LOCATION(1) in float4 v_col0;\n" << "FRAGMENT_OUTPUT_LOCATION(0) out float4 ocol0;\n" << "void main()\n"; } @@ -971,9 +985,9 @@ static std::string GenerateImGuiPixelShader() ss << "{\n"; if (api_type == APIType::D3D) - ss << " ocol0 = tex0.Sample(samp0, float3(frag_uv, 0.0)) * frag_color;\n"; + ss << " ocol0 = tex0.Sample(samp0, float3(v_tex0.xy, 0.0)) * v_col0;\n"; else - ss << " ocol0 = texture(samp0, float3(frag_uv, 0.0)) * frag_color;\n"; + ss << " ocol0 = texture(samp0, float3(v_tex0.xy, 0.0)) * v_col0;\n"; ss << "}\n"; @@ -1007,42 +1021,6 @@ bool Renderer::InitializeImGui() return false; } - const std::string vertex_shader_source = GenerateImGuiVertexShader(); - const std::string pixel_shader_source = GenerateImGuiPixelShader(); - std::unique_ptr vertex_shader = - CreateShaderFromSource(ShaderStage::Vertex, vertex_shader_source); - std::unique_ptr pixel_shader = - CreateShaderFromSource(ShaderStage::Pixel, pixel_shader_source); - if (!vertex_shader || !pixel_shader) - { - PanicAlert("Failed to compile imgui shaders"); - return false; - } - - AbstractPipelineConfig pconfig = {}; - pconfig.vertex_format = m_imgui_vertex_format.get(); - pconfig.vertex_shader = vertex_shader.get(); - pconfig.pixel_shader = pixel_shader.get(); - pconfig.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); - pconfig.depth_state = RenderState::GetNoDepthTestingDepthState(); - pconfig.blending_state = RenderState::GetNoBlendingBlendState(); - pconfig.blending_state.blendenable = true; - pconfig.blending_state.srcfactor = BlendMode::SRCALPHA; - pconfig.blending_state.dstfactor = BlendMode::INVSRCALPHA; - pconfig.blending_state.srcfactoralpha = BlendMode::ZERO; - pconfig.blending_state.dstfactoralpha = BlendMode::ONE; - pconfig.framebuffer_state.color_texture_format = m_backbuffer_format; - pconfig.framebuffer_state.depth_texture_format = AbstractTextureFormat::Undefined; - pconfig.framebuffer_state.samples = 1; - pconfig.framebuffer_state.per_sample_shading = false; - pconfig.usage = AbstractPipelineUsage::Utility; - m_imgui_pipeline = CreatePipeline(pconfig); - if (!m_imgui_pipeline) - { - PanicAlert("Failed to create imgui pipeline"); - return false; - } - // Font texture(s). { ImGuiIO& io = ImGui::GetIO(); @@ -1066,11 +1044,67 @@ bool Renderer::InitializeImGui() m_imgui_textures.push_back(std::move(font_tex)); } + if (!RecompileImGuiPipeline()) + return false; + m_imgui_last_frame_time = Common::Timer::GetTimeUs(); BeginImGuiFrame(); return true; } +bool Renderer::RecompileImGuiPipeline() +{ + std::unique_ptr vertex_shader = + CreateShaderFromSource(ShaderStage::Vertex, GenerateImGuiVertexShader()); + std::unique_ptr pixel_shader = + CreateShaderFromSource(ShaderStage::Pixel, GenerateImGuiPixelShader()); + if (!vertex_shader || !pixel_shader) + { + PanicAlert("Failed to compile imgui shaders"); + return false; + } + + // GS is used to render the UI to both eyes in stereo modes. + std::unique_ptr geometry_shader; + if (UseGeometryShaderForUI()) + { + geometry_shader = CreateShaderFromSource( + ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(1, 1)); + if (!geometry_shader) + { + PanicAlert("Failed to compile imgui geometry shader"); + return false; + } + } + + AbstractPipelineConfig pconfig = {}; + pconfig.vertex_format = m_imgui_vertex_format.get(); + pconfig.vertex_shader = vertex_shader.get(); + pconfig.geometry_shader = geometry_shader.get(); + pconfig.pixel_shader = pixel_shader.get(); + pconfig.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); + pconfig.depth_state = RenderState::GetNoDepthTestingDepthState(); + pconfig.blending_state = RenderState::GetNoBlendingBlendState(); + pconfig.blending_state.blendenable = true; + pconfig.blending_state.srcfactor = BlendMode::SRCALPHA; + pconfig.blending_state.dstfactor = BlendMode::INVSRCALPHA; + pconfig.blending_state.srcfactoralpha = BlendMode::ZERO; + pconfig.blending_state.dstfactoralpha = BlendMode::ONE; + pconfig.framebuffer_state.color_texture_format = m_backbuffer_format; + pconfig.framebuffer_state.depth_texture_format = AbstractTextureFormat::Undefined; + pconfig.framebuffer_state.samples = 1; + pconfig.framebuffer_state.per_sample_shading = false; + pconfig.usage = AbstractPipelineUsage::Utility; + m_imgui_pipeline = CreatePipeline(pconfig); + if (!m_imgui_pipeline) + { + PanicAlert("Failed to create imgui pipeline"); + return false; + } + + return true; +} + void Renderer::ShutdownImGui() { ImGui::EndFrame(); @@ -1160,6 +1194,15 @@ void Renderer::DrawImGui() m_current_framebuffer)); } +bool Renderer::UseGeometryShaderForUI() const +{ + // OpenGL doesn't render to a 2-layer backbuffer like D3D/Vulkan for quad-buffered stereo, + // instead drawing twice and the eye selected by glDrawBuffer() (see + // OGL::Renderer::RenderXFBToScreen). + return g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer && + g_ActiveConfig.backend_info.api_type != APIType::OpenGL; +} + std::unique_lock Renderer::GetImGuiLock() { return std::unique_lock(m_imgui_mutex); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 16ce52aaa4..b323946c5a 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -199,9 +199,6 @@ public: void SaveScreenshot(std::string filename, bool wait_for_completion); void DrawDebugText(); - // ImGui initialization depends on being able to create textures and pipelines, so do it last. - bool InitializeImGui(); - virtual void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z); virtual void ReinterpretPixelData(EFBReinterpretType convtype); @@ -243,6 +240,10 @@ public: virtual std::unique_ptr CreateAsyncShaderCompiler(); + // Returns true if a layer-expanding geometry shader should be used when rendering the user + // interface and final XFB. + bool UseGeometryShaderForUI() const; + // Returns a lock for the ImGui mutex, enabling data structures to be modified from outside. // Use with care, only non-drawing functions should be called from outside the video thread, // as the drawing is tied to a "frame". @@ -275,6 +276,12 @@ protected: void CheckFifoRecording(); void RecordVideoMemory(); + // ImGui initialization depends on being able to create textures and pipelines, so do it last. + bool InitializeImGui(); + + // Recompiles ImGui pipeline - call when stereo mode changes. + bool RecompileImGuiPipeline(); + // Sets up ImGui state for the next frame. // This function itself acquires the ImGui lock, so it should not be held. void BeginImGuiFrame();