From 7f058169b91d98b11405cbc391ddf66dd670c2df Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 21 Nov 2020 18:08:39 +1000 Subject: [PATCH] OpenGLHostDisplay: Add a GLES2 render path --- src/frontend-common/imgui_impl_opengl3.cpp | 83 ++++--- src/frontend-common/opengl_host_display.cpp | 229 +++++++++++++++----- src/frontend-common/opengl_host_display.h | 2 + 3 files changed, 236 insertions(+), 78 deletions(-) diff --git a/src/frontend-common/imgui_impl_opengl3.cpp b/src/frontend-common/imgui_impl_opengl3.cpp index 403f77331..7cf5621b5 100644 --- a/src/frontend-common/imgui_impl_opengl3.cpp +++ b/src/frontend-common/imgui_impl_opengl3.cpp @@ -90,6 +90,7 @@ static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; static int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location static unsigned int g_VboHandle = 0, g_VaoHandle = 0, g_ElementsHandle = 0; static bool g_IsGLES = false; +static bool g_IsGLES2 = false; // Forward Declarations static void ImGui_ImplOpenGL3_InitPlatformInterface(); @@ -104,23 +105,26 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. - if (GLAD_GL_ES_VERSION_2_0) - { - if (glsl_version == NULL) - glsl_version = "#version 100"; - g_IsGLES = true; - } - else if (GLAD_GL_ES_VERSION_3_0) + if (GLAD_GL_ES_VERSION_3_0) { if (glsl_version == NULL) glsl_version = "#version 300 es"; g_IsGLES = true; + g_IsGLES2 = false; + } + else if (GLAD_GL_ES_VERSION_2_0) + { + if (glsl_version == NULL) + glsl_version = "#version 100"; + g_IsGLES = true; + g_IsGLES2 = true; } else { if (glsl_version == NULL) glsl_version = "#version 130"; g_IsGLES = false; + g_IsGLES2 = false; } if (!g_IsGLES) @@ -179,11 +183,24 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid glUseProgram(g_ShaderHandle); glUniform1i(g_AttribLocationTex, 0); glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); -#ifndef IMGUI_IMPL_OPENGL_ES2 - glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. -#endif - glBindVertexArray(g_VaoHandle); + if (!g_IsGLES2) + { + glBindVertexArray(g_VaoHandle); + } + else + { + glEnableVertexAttribArray(g_AttribLocationVtxPos); + glEnableVertexAttribArray(g_AttribLocationVtxUV); + glEnableVertexAttribArray(g_AttribLocationVtxColor); + glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), + (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); + glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), + (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); + glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), + (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); + } + glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); } @@ -250,6 +267,19 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) } } } + + if (!g_IsGLES2) + { + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + else + { + glDisableVertexAttribArray(g_AttribLocationVtxPos); + glDisableVertexAttribArray(g_AttribLocationVtxUV); + glDisableVertexAttribArray(g_AttribLocationVtxColor); + } } bool ImGui_ImplOpenGL3_CreateFontsTexture() @@ -499,27 +529,30 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); glBufferData(GL_ARRAY_BUFFER, sizeof(ImDrawVert), nullptr, GL_STREAM_DRAW); - glGenVertexArrays(1, &g_VaoHandle); - glBindVertexArray(g_VaoHandle); - glEnableVertexAttribArray(g_AttribLocationVtxPos); - glEnableVertexAttribArray(g_AttribLocationVtxUV); - glEnableVertexAttribArray(g_AttribLocationVtxColor); - glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), - (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); - glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), - (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); - glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), - (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); + if (!g_IsGLES2) + { + glGenVertexArrays(1, &g_VaoHandle); + glBindVertexArray(g_VaoHandle); + + glEnableVertexAttribArray(g_AttribLocationVtxPos); + glEnableVertexAttribArray(g_AttribLocationVtxUV); + glEnableVertexAttribArray(g_AttribLocationVtxColor); + glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), + (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); + glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), + (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); + glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), + (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); + } ImGui_ImplOpenGL3_CreateFontsTexture(); // Restore modified GL state glBindTexture(GL_TEXTURE_2D, last_texture); glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); -#ifndef IMGUI_IMPL_OPENGL_ES2 - glBindVertexArray(last_vertex_array); -#endif + if (!g_IsGLES2) + glBindVertexArray(last_vertex_array); return true; } diff --git a/src/frontend-common/opengl_host_display.cpp b/src/frontend-common/opengl_host_display.cpp index b3b78242b..112feaaba 100644 --- a/src/frontend-common/opengl_host_display.cpp +++ b/src/frontend-common/opengl_host_display.cpp @@ -466,7 +466,10 @@ void OpenGLHostDisplay::DestroyImGuiContext() bool OpenGLHostDisplay::CreateResources() { - static constexpr char fullscreen_quad_vertex_shader[] = R"( + m_use_gles2_draw_path = m_gl_context->IsGLES() && !GLAD_GL_ES_VERSION_3_0; + if (!m_use_gles2_draw_path) + { + static constexpr char fullscreen_quad_vertex_shader[] = R"( uniform vec4 u_src_rect; out vec2 v_tex0; @@ -478,7 +481,7 @@ void main() } )"; - static constexpr char display_fragment_shader[] = R"( + static constexpr char display_fragment_shader[] = R"( uniform sampler2D samp0; in vec2 v_tex0; @@ -490,7 +493,7 @@ void main() } )"; - static constexpr char cursor_fragment_shader[] = R"( + static constexpr char cursor_fragment_shader[] = R"( uniform sampler2D samp0; in vec2 v_tex0; @@ -502,46 +505,114 @@ void main() } )"; - if (!m_display_program.Compile(GetGLSLVersionHeader() + fullscreen_quad_vertex_shader, {}, - GetGLSLVersionHeader() + display_fragment_shader) || - !m_cursor_program.Compile(GetGLSLVersionHeader() + fullscreen_quad_vertex_shader, {}, - GetGLSLVersionHeader() + cursor_fragment_shader)) - { - Log_ErrorPrintf("Failed to compile display shaders"); - return false; + if (!m_display_program.Compile(GetGLSLVersionHeader() + fullscreen_quad_vertex_shader, {}, + GetGLSLVersionHeader() + display_fragment_shader) || + !m_cursor_program.Compile(GetGLSLVersionHeader() + fullscreen_quad_vertex_shader, {}, + GetGLSLVersionHeader() + cursor_fragment_shader)) + { + Log_ErrorPrintf("Failed to compile display shaders"); + return false; + } + + if (GetRenderAPI() != RenderAPI::OpenGLES) + { + m_display_program.BindFragData(0, "o_col0"); + m_cursor_program.BindFragData(0, "o_col0"); + } + + if (!m_display_program.Link() || !m_cursor_program.Link()) + { + Log_ErrorPrintf("Failed to link display programs"); + return false; + } + + m_display_program.Bind(); + m_display_program.RegisterUniform("u_src_rect"); + m_display_program.RegisterUniform("samp0"); + m_display_program.Uniform1i(1, 0); + m_cursor_program.Bind(); + m_cursor_program.RegisterUniform("u_src_rect"); + m_cursor_program.RegisterUniform("samp0"); + m_cursor_program.Uniform1i(1, 0); + + glGenVertexArrays(1, &m_display_vao); + + // samplers + glGenSamplers(1, &m_display_nearest_sampler); + glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glGenSamplers(1, &m_display_linear_sampler); + glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - - if (GetRenderAPI() != RenderAPI::OpenGLES) + else { - m_display_program.BindFragData(0, "o_col0"); - m_cursor_program.BindFragData(0, "o_col0"); + static constexpr char fullscreen_quad_vertex_shader[] = R"( +#version 100 + +attribute highp vec2 a_pos; +attribute highp vec2 a_tex0; +varying highp vec2 v_tex0; + +void main() +{ + gl_Position = vec4(a_pos, 0.0, 1.0); + v_tex0 = a_tex0; +} +)"; + + static constexpr char display_fragment_shader[] = R"( +#version 100 + +uniform highp sampler2D samp0; + +varying highp vec2 v_tex0; + +void main() +{ + gl_FragColor = vec4(texture2D(samp0, v_tex0).rgb, 1.0); +} +)"; + + static constexpr char cursor_fragment_shader[] = R"( +#version 100 + +uniform highp sampler2D samp0; + +varying highp vec2 v_tex0; + +void main() +{ + gl_FragColor = texture2D(samp0, v_tex0); +} +)"; + + if (!m_display_program.Compile(fullscreen_quad_vertex_shader, {}, display_fragment_shader) || + !m_cursor_program.Compile(fullscreen_quad_vertex_shader, {}, cursor_fragment_shader)) + { + Log_ErrorPrintf("Failed to compile display shaders"); + return false; + } + + m_display_program.BindAttribute(0, "a_pos"); + m_display_program.BindAttribute(1, "a_tex0"); + m_cursor_program.BindAttribute(0, "a_pos"); + m_cursor_program.BindAttribute(1, "a_tex0"); + + if (!m_display_program.Link() || !m_cursor_program.Link()) + { + Log_ErrorPrintf("Failed to link display programs"); + return false; + } + + m_display_program.Bind(); + m_display_program.RegisterUniform("samp0"); + m_display_program.Uniform1i(0, 0); + m_cursor_program.Bind(); + m_cursor_program.RegisterUniform("samp0"); + m_cursor_program.Uniform1i(0, 0); } - if (!m_display_program.Link() || !m_cursor_program.Link()) - { - Log_ErrorPrintf("Failed to link display programs"); - return false; - } - - m_display_program.Bind(); - m_display_program.RegisterUniform("u_src_rect"); - m_display_program.RegisterUniform("samp0"); - m_display_program.Uniform1i(1, 0); - m_cursor_program.Bind(); - m_cursor_program.RegisterUniform("u_src_rect"); - m_cursor_program.RegisterUniform("samp0"); - m_cursor_program.Uniform1i(1, 0); - - glGenVertexArrays(1, &m_display_vao); - - // samplers - glGenSamplers(1, &m_display_nearest_sampler); - glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glGenSamplers(1, &m_display_linear_sampler); - glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - return true; } @@ -561,11 +632,20 @@ void OpenGLHostDisplay::DestroyResources() } if (m_display_vao != 0) + { glDeleteVertexArrays(1, &m_display_vao); + m_display_vao = 0; + } if (m_display_linear_sampler != 0) + { glDeleteSamplers(1, &m_display_linear_sampler); + m_display_linear_sampler = 0; + } if (m_display_nearest_sampler != 0) + { glDeleteSamplers(1, &m_display_nearest_sampler); + m_display_nearest_sampler = 0; + } m_cursor_program.Destroy(); m_display_program.Destroy(); @@ -641,6 +721,30 @@ void OpenGLHostDisplay::RenderDisplay() m_display_texture_view_width, m_display_texture_view_height, m_display_linear_filtering); } +static void DrawFullscreenQuadES2(s32 tex_view_x, s32 tex_view_y, s32 tex_view_width, s32 tex_view_height, + s32 tex_width, s32 tex_height) +{ + const float tex_left = static_cast(tex_view_x) / static_cast(tex_width); + const float tex_right = tex_left + static_cast(tex_view_width) / static_cast(tex_width); + const float tex_top = static_cast(tex_view_y) / static_cast(tex_height); + const float tex_bottom = tex_top + static_cast(tex_view_height) / static_cast(tex_height); + const std::array, 4> vertices = {{ + {{-1.0f, -1.0f, tex_left, tex_bottom}}, // bottom-left + {{1.0f, -1.0f, tex_right, tex_bottom}}, // bottom-right + {{-1.0f, 1.0f, tex_left, tex_top}}, // top-left + {{1.0f, 1.0f, tex_right, tex_top}}, // top-right + }}; + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), &vertices[0][0]); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), &vertices[0][2]); + glEnableVertexAttribArray(1); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(0); +} + void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 height, void* texture_handle, u32 texture_width, s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, bool linear_filter) @@ -651,16 +755,25 @@ void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 heigh glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); glDepthMask(GL_FALSE); - m_display_program.Bind(); - m_display_program.Uniform4f(0, static_cast(texture_view_x) / static_cast(texture_width), - static_cast(texture_view_y) / static_cast(texture_height), - (static_cast(texture_view_width) - 0.5f) / static_cast(texture_width), - (static_cast(texture_view_height) + 0.5f) / static_cast(texture_height)); glBindTexture(GL_TEXTURE_2D, static_cast(reinterpret_cast(texture_handle))); - glBindSampler(0, linear_filter ? m_display_linear_sampler : m_display_nearest_sampler); - glBindVertexArray(m_display_vao); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindSampler(0, 0); + m_display_program.Bind(); + + if (!m_use_gles2_draw_path) + { + m_display_program.Uniform4f(0, static_cast(texture_view_x) / static_cast(texture_width), + static_cast(texture_view_y) / static_cast(texture_height), + (static_cast(texture_view_width) - 0.5f) / static_cast(texture_width), + (static_cast(texture_view_height) + 0.5f) / static_cast(texture_height)); + glBindSampler(0, linear_filter ? m_display_linear_sampler : m_display_nearest_sampler); + glBindVertexArray(m_display_vao); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindSampler(0, 0); + } + else + { + DrawFullscreenQuadES2(m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width, + m_display_texture_view_height, m_display_texture_width, m_display_texture_height); + } } void OpenGLHostDisplay::RenderSoftwareCursor() @@ -684,12 +797,22 @@ void OpenGLHostDisplay::RenderSoftwareCursor(s32 left, s32 bottom, s32 width, s3 glDisable(GL_SCISSOR_TEST); glDepthMask(GL_FALSE); m_cursor_program.Bind(); - m_cursor_program.Uniform4f(0, 0.0f, 0.0f, 1.0f, 1.0f); glBindTexture(GL_TEXTURE_2D, static_cast(texture_handle)->GetGLID()); - glBindSampler(0, m_display_linear_sampler); - glBindVertexArray(m_display_vao); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindSampler(0, 0); + + if (!m_use_gles2_draw_path) + { + m_cursor_program.Uniform4f(0, 0.0f, 0.0f, 1.0f, 1.0f); + glBindSampler(0, m_display_linear_sampler); + glBindVertexArray(m_display_vao); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindSampler(0, 0); + } + else + { + const s32 tex_width = static_cast(static_cast(texture_handle)->GetWidth()); + const s32 tex_height = static_cast(static_cast(texture_handle)->GetHeight()); + DrawFullscreenQuadES2(0, 0, tex_width, tex_height, tex_width, tex_height); + } } #ifndef LIBRETRO diff --git a/src/frontend-common/opengl_host_display.h b/src/frontend-common/opengl_host_display.h index 616486ef0..c943b273c 100644 --- a/src/frontend-common/opengl_host_display.h +++ b/src/frontend-common/opengl_host_display.h @@ -120,6 +120,8 @@ protected: std::unique_ptr m_post_processing_ubo; std::vector m_post_processing_stages; #endif + + bool m_use_gles2_draw_path = false; }; } // namespace FrontendCommon \ No newline at end of file