Renderer: Draw ImGui interface to both eyes

This commit is contained in:
Stenzek 2019-10-02 12:19:47 +10:00
parent b44a0980eb
commit c98a5f7dfd
3 changed files with 104 additions and 59 deletions

View File

@ -719,15 +719,10 @@ bool PostProcessing::CompilePixelShader()
bool PostProcessing::CompilePipeline() 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 = {}; AbstractPipelineConfig config = {};
config.vertex_shader = m_vertex_shader.get(); config.vertex_shader = m_vertex_shader.get();
config.geometry_shader = 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.pixel_shader = m_pixel_shader.get();
config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles);
config.depth_state = RenderState::GetNoDepthTestingDepthState(); config.depth_state = RenderState::GetNoDepthTestingDepthState();

View File

@ -59,6 +59,7 @@
#include "VideoCommon/FPSCounter.h" #include "VideoCommon/FPSCounter.h"
#include "VideoCommon/FrameDump.h" #include "VideoCommon/FrameDump.h"
#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/FramebufferShaderGen.h"
#include "VideoCommon/ImageWrite.h" #include "VideoCommon/ImageWrite.h"
#include "VideoCommon/NetPlayChatUI.h" #include "VideoCommon/NetPlayChatUI.h"
#include "VideoCommon/NetPlayGolfUI.h" #include "VideoCommon/NetPlayGolfUI.h"
@ -442,6 +443,13 @@ void Renderer::CheckForConfigChanges()
// Notify the backend of the changes, if any. // Notify the backend of the changes, if any.
OnConfigChanged(changed_bits); 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? // Framebuffer changed?
if (changed_bits & (CONFIG_CHANGE_BIT_MULTISAMPLES | CONFIG_CHANGE_BIT_STEREO_MODE | if (changed_bits & (CONFIG_CHANGE_BIT_MULTISAMPLES | CONFIG_CHANGE_BIT_STEREO_MODE |
CONFIG_CHANGE_BIT_TARGET_SIZE)) CONFIG_CHANGE_BIT_TARGET_SIZE))
@ -453,8 +461,6 @@ void Renderer::CheckForConfigChanges()
if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES))
{ {
OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL);
WaitForGPUIdle();
SetPipeline(nullptr);
g_vertex_manager->InvalidatePipelineObject(); g_vertex_manager->InvalidatePipelineObject();
g_shader_cache->SetHostConfig(new_host_config); g_shader_cache->SetHostConfig(new_host_config);
g_shader_cache->Reload(); g_shader_cache->Reload();
@ -467,6 +473,14 @@ void Renderer::CheckForConfigChanges()
BPFunctions::SetViewport(); BPFunctions::SetViewport();
BPFunctions::SetScissor(); 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 // Create On-Screen-Messages
@ -916,8 +930,8 @@ static std::string GenerateImGuiVertexShader()
ss << "void main(in float2 rawpos : POSITION,\n" ss << "void main(in float2 rawpos : POSITION,\n"
<< " in float2 rawtex0 : TEXCOORD,\n" << " in float2 rawtex0 : TEXCOORD,\n"
<< " in float4 rawcolor0 : COLOR,\n" << " in float4 rawcolor0 : COLOR,\n"
<< " out float2 frag_uv : TEXCOORD,\n" << " out float3 v_tex0 : TEXCOORD,\n"
<< " out float4 frag_color : COLOR,\n" << " out float4 v_col0 : COLOR,\n"
<< " out float4 out_pos : SV_Position)\n"; << " out float4 out_pos : SV_Position)\n";
} }
else else
@ -925,14 +939,14 @@ static std::string GenerateImGuiVertexShader()
ss << "ATTRIBUTE_LOCATION(" << SHADER_POSITION_ATTRIB << ") in float2 rawpos;\n" ss << "ATTRIBUTE_LOCATION(" << SHADER_POSITION_ATTRIB << ") in float2 rawpos;\n"
<< "ATTRIBUTE_LOCATION(" << SHADER_TEXTURE0_ATTRIB << ") in float2 rawtex0;\n" << "ATTRIBUTE_LOCATION(" << SHADER_TEXTURE0_ATTRIB << ") in float2 rawtex0;\n"
<< "ATTRIBUTE_LOCATION(" << SHADER_COLOR0_ATTRIB << ") in float4 rawcolor0;\n" << "ATTRIBUTE_LOCATION(" << SHADER_COLOR0_ATTRIB << ") in float4 rawcolor0;\n"
<< "VARYING_LOCATION(0) out float2 frag_uv;\n" << "VARYING_LOCATION(0) out float3 v_tex0;\n"
<< "VARYING_LOCATION(1) out float4 frag_color;\n" << "VARYING_LOCATION(1) out float4 v_col0;\n"
<< "void main()\n"; << "void main()\n";
} }
ss << "{\n" ss << "{\n"
<< " frag_uv = rawtex0;\n" << " v_tex0 = float3(rawtex0, 0.0);\n"
<< " frag_color = rawcolor0;\n"; << " v_col0 = rawcolor0;\n";
ss << " " << (api_type == APIType::D3D ? "out_pos" : "gl_Position") ss << " " << (api_type == APIType::D3D ? "out_pos" : "gl_Position")
<< "= float4(rawpos.x * u_rcp_viewport_size_mul2.x - 1.0, 1.0 - rawpos.y * " << "= 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" ss << "Texture2DArray tex0 : register(t0);\n"
<< "SamplerState samp0 : register(s0);\n" << "SamplerState samp0 : register(s0);\n"
<< "void main(in float2 frag_uv : TEXCOORD,\n" << "void main(in float3 v_tex0 : TEXCOORD,\n"
<< " in float4 frag_color : COLOR,\n" << " in float4 v_col0 : COLOR,\n"
<< " out float4 ocol0 : SV_Target)\n"; << " out float4 ocol0 : SV_Target)\n";
} }
else else
{ {
ss << "SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n" ss << "SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n"
<< "VARYING_LOCATION(0) in float2 frag_uv; \n" << "VARYING_LOCATION(0) in float3 v_tex0; \n"
<< "VARYING_LOCATION(1) in float4 frag_color;\n" << "VARYING_LOCATION(1) in float4 v_col0;\n"
<< "FRAGMENT_OUTPUT_LOCATION(0) out float4 ocol0;\n" << "FRAGMENT_OUTPUT_LOCATION(0) out float4 ocol0;\n"
<< "void main()\n"; << "void main()\n";
} }
@ -971,9 +985,9 @@ static std::string GenerateImGuiPixelShader()
ss << "{\n"; ss << "{\n";
if (api_type == APIType::D3D) 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 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"; ss << "}\n";
@ -1007,42 +1021,6 @@ bool Renderer::InitializeImGui()
return false; return false;
} }
const std::string vertex_shader_source = GenerateImGuiVertexShader();
const std::string pixel_shader_source = GenerateImGuiPixelShader();
std::unique_ptr<AbstractShader> vertex_shader =
CreateShaderFromSource(ShaderStage::Vertex, vertex_shader_source);
std::unique_ptr<AbstractShader> 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). // Font texture(s).
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
@ -1066,11 +1044,67 @@ bool Renderer::InitializeImGui()
m_imgui_textures.push_back(std::move(font_tex)); m_imgui_textures.push_back(std::move(font_tex));
} }
if (!RecompileImGuiPipeline())
return false;
m_imgui_last_frame_time = Common::Timer::GetTimeUs(); m_imgui_last_frame_time = Common::Timer::GetTimeUs();
BeginImGuiFrame(); BeginImGuiFrame();
return true; return true;
} }
bool Renderer::RecompileImGuiPipeline()
{
std::unique_ptr<AbstractShader> vertex_shader =
CreateShaderFromSource(ShaderStage::Vertex, GenerateImGuiVertexShader());
std::unique_ptr<AbstractShader> 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<AbstractShader> 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() void Renderer::ShutdownImGui()
{ {
ImGui::EndFrame(); ImGui::EndFrame();
@ -1160,6 +1194,15 @@ void Renderer::DrawImGui()
m_current_framebuffer)); 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<std::mutex> Renderer::GetImGuiLock() std::unique_lock<std::mutex> Renderer::GetImGuiLock()
{ {
return std::unique_lock<std::mutex>(m_imgui_mutex); return std::unique_lock<std::mutex>(m_imgui_mutex);

View File

@ -199,9 +199,6 @@ public:
void SaveScreenshot(std::string filename, bool wait_for_completion); void SaveScreenshot(std::string filename, bool wait_for_completion);
void DrawDebugText(); 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<int>& rc, bool colorEnable, bool alphaEnable, virtual void ClearScreen(const MathUtil::Rectangle<int>& rc, bool colorEnable, bool alphaEnable,
bool zEnable, u32 color, u32 z); bool zEnable, u32 color, u32 z);
virtual void ReinterpretPixelData(EFBReinterpretType convtype); virtual void ReinterpretPixelData(EFBReinterpretType convtype);
@ -243,6 +240,10 @@ public:
virtual std::unique_ptr<VideoCommon::AsyncShaderCompiler> CreateAsyncShaderCompiler(); virtual std::unique_ptr<VideoCommon::AsyncShaderCompiler> 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. // 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, // Use with care, only non-drawing functions should be called from outside the video thread,
// as the drawing is tied to a "frame". // as the drawing is tied to a "frame".
@ -275,6 +276,12 @@ protected:
void CheckFifoRecording(); void CheckFifoRecording();
void RecordVideoMemory(); 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. // Sets up ImGui state for the next frame.
// This function itself acquires the ImGui lock, so it should not be held. // This function itself acquires the ImGui lock, so it should not be held.
void BeginImGuiFrame(); void BeginImGuiFrame();