From d63acfc1e95637e5ab56b4c5df2aa5fbbda024dd Mon Sep 17 00:00:00 2001 From: SachinVin Date: Wed, 30 Jan 2019 14:10:33 -0600 Subject: [PATCH] video_core: add workarounds to enable GLES support video_core: shorten GetGLSLVersionString video_core: make GLES version and extensions consistent video_core: move some logic to LoadShader video_core: deduplicate fragment shader precision specifier --- src/video_core/CMakeLists.txt | 2 + .../renderer_opengl/gl_rasterizer_cache.cpp | 102 ++++++++++++++++-- .../renderer_opengl/gl_shader_gen.cpp | 37 ++++--- .../renderer_opengl/gl_shader_util.cpp | 23 +++- .../renderer_opengl/gl_shader_util.h | 11 ++ src/video_core/renderer_opengl/gl_state.cpp | 24 +++-- src/video_core/renderer_opengl/gl_vars.cpp | 9 ++ src/video_core/renderer_opengl/gl_vars.h | 9 ++ .../renderer_opengl/renderer_opengl.cpp | 21 ++-- src/video_core/video_core.cpp | 3 + 10 files changed, 204 insertions(+), 37 deletions(-) create mode 100644 src/video_core/renderer_opengl/gl_vars.cpp create mode 100644 src/video_core/renderer_opengl/gl_vars.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 2ad6d2d99..77f9144fd 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -41,6 +41,8 @@ add_library(video_core STATIC renderer_opengl/gl_state.h renderer_opengl/gl_stream_buffer.cpp renderer_opengl/gl_stream_buffer.h + renderer_opengl/gl_vars.cpp + renderer_opengl/gl_vars.h renderer_opengl/pica_to_gl.h renderer_opengl/renderer_opengl.cpp renderer_opengl/renderer_opengl.h diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 220458967..6f951a852 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -28,6 +28,7 @@ #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_state.h" +#include "video_core/renderer_opengl/gl_vars.h" #include "video_core/utils.h" #include "video_core/video_core.h" @@ -50,6 +51,17 @@ static constexpr std::array fb_format_tuples = {{ {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4 }}; +// Same as above, with minor changes for OpenGL ES. Replaced +// GL_UNSIGNED_INT_8_8_8_8 with GL_UNSIGNED_BYTE and +// GL_BGR with GL_RGB +static constexpr std::array fb_format_tuples_oes = {{ + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8 + {GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // RGB8 + {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // RGB5A1 + {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565 + {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4 +}}; + static constexpr std::array depth_format_tuples = {{ {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16 {}, @@ -63,6 +75,9 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) { const SurfaceType type = SurfaceParams::GetFormatType(pixel_format); if (type == SurfaceType::Color) { ASSERT(static_cast(pixel_format) < fb_format_tuples.size()); + if (GLES) { + return fb_format_tuples_oes[static_cast(pixel_format)]; + } return fb_format_tuples[static_cast(pixel_format)]; } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) { std::size_t tuple_idx = static_cast(pixel_format) - 14; @@ -72,6 +87,77 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) { return tex_tuple; } +/** + * OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the + * texture to a framebuffer. + * Originally from https://github.com/apitrace/apitrace/blob/master/retrace/glstate_images.cpp + */ +static inline void GetTexImageOES(GLenum target, GLint level, GLenum format, GLenum type, + GLint height, GLint width, GLint depth, GLubyte* pixels) { + + memset(pixels, 0x80, height * width * 4); + + GLenum texture_binding = GL_NONE; + switch (target) { + case GL_TEXTURE_2D: + texture_binding = GL_TEXTURE_BINDING_2D; + break; + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + texture_binding = GL_TEXTURE_BINDING_CUBE_MAP; + break; + case GL_TEXTURE_3D_OES: + texture_binding = GL_TEXTURE_BINDING_3D_OES; + default: + return; + } + + GLint texture = 0; + glGetIntegerv(texture_binding, &texture); + if (!texture) { + return; + } + + GLint prev_fbo = 0; + GLuint fbo = 0; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo); + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + switch (target) { + case GL_TEXTURE_2D: + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LOG_DEBUG(Render_OpenGL, "Framebuffer is incomplete, status: {:X}", status); + } + glReadPixels(0, 0, width, height, format, type, pixels); + break; + } + case GL_TEXTURE_3D_OES: + for (int i = 0; i < depth; i++) { + glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, texture, + level, i); + glReadPixels(0, 0, width, height, format, type, pixels + 4 * i * width * height); + } + break; + } + + glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo); + + glDeleteFramebuffers(1, &fbo); +} + template constexpr auto RangeFromInterval(Map& map, const Interval& interval) { return boost::make_iterator_range(map.equal_range(interval)); @@ -841,7 +927,12 @@ void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle& rect, GLui state.Apply(); glActiveTexture(GL_TEXTURE0); - glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]); + if (GLES) { + GetTexImageOES(GL_TEXTURE_2D, 0, tuple.format, tuple.type, height, width, 0, + &gl_buffer[buffer_offset]); + } else { + glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]); + } } else { state.ResetTexture(texture.handle); state.draw.read_framebuffer = read_fb_handle; @@ -982,16 +1073,15 @@ RasterizerCacheOpenGL::RasterizerCacheOpenGL() { d24s8_abgr_buffer.Create(); d24s8_abgr_buffer_size = 0; - const char* vs_source = R"( -#version 330 core + std::string vs_source = R"( const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0)); void main() { gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); } )"; - const char* fs_source = R"( -#version 330 core + std::string fs_source = GLES ? fragment_shader_precision_OES : ""; + fs_source += R"( uniform samplerBuffer tbo; uniform vec2 tbo_size; uniform vec4 viewport; @@ -1004,7 +1094,7 @@ void main() { color = texelFetch(tbo, tbo_offset).rabg; } )"; - d24s8_abgr_shader.Create(vs_source, fs_source); + d24s8_abgr_shader.Create(vs_source.c_str(), fs_source.c_str()); OpenGLState state = OpenGLState::GetCurState(); GLuint old_program = state.draw.shader_program; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 62b1162ec..24cc8e72f 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -18,6 +18,7 @@ #include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/renderer_opengl/gl_shader_gen.h" #include "video_core/renderer_opengl/gl_shader_util.h" +#include "video_core/renderer_opengl/gl_vars.h" #include "video_core/video_core.h" using Pica::FramebufferRegs; @@ -1250,7 +1251,6 @@ std::string GenerateFragmentShader(const PicaFSConfig& config, bool separable_sh const auto& state = config.state; std::string out = R"( -#version 330 core #extension GL_ARB_shader_image_load_store : enable #extension GL_ARB_shader_image_size : enable #define ALLOW_SHADOW (defined(GL_ARB_shader_image_load_store) && defined(GL_ARB_shader_image_size)) @@ -1260,10 +1260,16 @@ std::string GenerateFragmentShader(const PicaFSConfig& config, bool separable_sh out += "#extension GL_ARB_separate_shader_objects : enable\n"; } + if (GLES) { + out += fragment_shader_precision_OES; + } + out += GetVertexInterfaceDeclaration(false, separable_shader); out += R"( +#ifndef CITRA_GLES in vec4 gl_FragCoord; +#endif // CITRA_GLES out vec4 color; @@ -1300,13 +1306,13 @@ float LookupLightingLUT(int lut_index, int index, float delta) { float LookupLightingLUTUnsigned(int lut_index, float pos) { int index = clamp(int(pos * 256.0), 0, 255); - float delta = pos * 256.0 - index; + float delta = pos * 256.0 - float(index); return LookupLightingLUT(lut_index, index, delta); } float LookupLightingLUTSigned(int lut_index, float pos) { int index = clamp(int(pos * 128.0), -128, 127); - float delta = pos * 128.0 - index; + float delta = pos * 128.0 - float(index); if (index < 0) index += 256; return LookupLightingLUT(lut_index, index, delta); } @@ -1494,10 +1500,10 @@ vec4 secondary_fragment_color = vec4(0.0); // Negate the condition if we have to keep only the pixels outside the scissor box if (state.scissor_test_mode == RasterizerRegs::ScissorMode::Include) out += "!"; - out += "(gl_FragCoord.x >= scissor_x1 && " - "gl_FragCoord.y >= scissor_y1 && " - "gl_FragCoord.x < scissor_x2 && " - "gl_FragCoord.y < scissor_y2)) discard;\n"; + out += "(gl_FragCoord.x >= float(scissor_x1) && " + "gl_FragCoord.y >= float(scissor_y1) && " + "gl_FragCoord.x < float(scissor_x2) && " + "gl_FragCoord.y < float(scissor_y2))) discard;\n"; } // After perspective divide, OpenGL transform z_over_w from [-1, 1] to [near, far]. Here we use @@ -1529,7 +1535,7 @@ vec4 secondary_fragment_color = vec4(0.0); if (state.fog_mode == TexturingRegs::FogMode::Fog) { // Get index into fog LUT if (state.fog_flip) { - out += "float fog_index = (1.0 - depth) * 128.0;\n"; + out += "float fog_index = (1.0 - float(depth)) * 128.0;\n"; } else { out += "float fog_index = depth * 128.0;\n"; } @@ -1591,7 +1597,7 @@ do { } std::string GenerateTrivialVertexShader(bool separable_shader) { - std::string out = "#version 330 core\n"; + std::string out = ""; if (separable_shader) { out += "#extension GL_ARB_separate_shader_objects : enable\n"; } @@ -1626,8 +1632,10 @@ void main() { normquat = vert_normquat; view = vert_view; gl_Position = vert_position; +#if !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance) gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 gl_ClipDistance[1] = dot(clip_coef, vert_position); +#endif // !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance) } )"; @@ -1636,7 +1644,7 @@ void main() { std::optional GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config, bool separable_shader) { - std::string out = "#version 330 core\n"; + std::string out = ""; if (separable_shader) { out += "#extension GL_ARB_separate_shader_objects : enable\n"; } @@ -1744,9 +1752,12 @@ struct Vertex { semantic(VSOutputAttributes::POSITION_Y) + ", " + semantic(VSOutputAttributes::POSITION_Z) + ", " + semantic(VSOutputAttributes::POSITION_W) + ");\n"; + semantic(VSOutputAttributes::POSITION_W) + ");\n"; out += " gl_Position = vtx_pos;\n"; + out += "#if !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance)\n"; out += " gl_ClipDistance[0] = -vtx_pos.z;\n"; // fixed PICA clipping plane z <= 0 - out += " gl_ClipDistance[1] = dot(clip_coef, vtx_pos);\n\n"; + out += " gl_ClipDistance[1] = dot(clip_coef, vtx_pos);\n"; + out += "#endif // !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance)\n\n"; out += " vec4 vtx_quat = GetVertexQuaternion(vtx);\n"; out += " normquat = mix(vtx_quat, -vtx_quat, bvec4(quats_opposite));\n\n"; @@ -1789,7 +1800,7 @@ void EmitPrim(Vertex vtx0, Vertex vtx1, Vertex vtx2) { }; std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config, bool separable_shader) { - std::string out = "#version 330 core\n"; + std::string out = ""; if (separable_shader) { out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; } @@ -1824,7 +1835,7 @@ void main() { std::optional GenerateGeometryShader(const Pica::Shader::ShaderSetup& setup, const PicaGSConfig& config, bool separable_shader) { - std::string out = "#version 330 core\n"; + std::string out = ""; if (separable_shader) { out += "#extension GL_ARB_separate_shader_objects : enable\n"; } diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 5ffa71562..35edac34a 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -2,15 +2,33 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include #include #include #include "common/assert.h" #include "common/logging/log.h" #include "video_core/renderer_opengl/gl_shader_util.h" +#include "video_core/renderer_opengl/gl_vars.h" namespace OpenGL { GLuint LoadShader(const char* source, GLenum type) { + const std::string version = GLES ? R"( +#version 310 es + +#define CITRA_GLES + +#if defined(GL_ANDROID_extension_pack_es31a) +#extension GL_ANDROID_extension_pack_es31a : enable +#endif // defined(GL_ANDROID_extension_pack_es31a) + +#if defined(GL_EXT_clip_cull_distance) +#extension GL_EXT_clip_cull_distance : enable +#endif // defined(GL_EXT_clip_cull_distance) +)" + : "#version 330\n"; + const char* debug_type; switch (type) { case GL_VERTEX_SHADER: @@ -26,8 +44,9 @@ GLuint LoadShader(const char* source, GLenum type) { UNREACHABLE(); } + std::array src_arr{version.data(), source}; GLuint shader_id = glCreateShader(type); - glShaderSource(shader_id, 1, &source, nullptr); + glShaderSource(shader_id, static_cast(src_arr.size()), src_arr.data(), nullptr); LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); glCompileShader(shader_id); @@ -44,7 +63,7 @@ GLuint LoadShader(const char* source, GLenum type) { } else { LOG_ERROR(Render_OpenGL, "Error compiling {} shader:\n{}", debug_type, &shader_error[0]); - LOG_ERROR(Render_OpenGL, "Shader source code:\n{}", source); + LOG_ERROR(Render_OpenGL, "Shader source code:\n{}{}", src_arr[0], src_arr[1]); } } return shader_id; diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index 9f0cf5c1e..1871403f9 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -9,6 +9,17 @@ namespace OpenGL { +// High precision may or may not supported in GLES3. If it isn't, use medium precision instead. +static constexpr char fragment_shader_precision_OES[] = R"( +#ifdef GL_FRAGMENT_PRECISION_HIGH + precision highp float; +precision highp samplerBuffer; +#else + precision mediump float; +precision mediump samplerBuffer; +#endif // GL_FRAGMENT_PRECISION_HIGH +)"; + /** * Utility function to create and compile an OpenGL GLSL shader * @param source String of the GLSL shader program diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 250b8b4aa..17bf2b2f7 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -6,6 +6,7 @@ #include "common/common_funcs.h" #include "common/logging/log.h" #include "video_core/renderer_opengl/gl_state.h" +#include "video_core/renderer_opengl/gl_vars.h" namespace OpenGL { @@ -193,8 +194,13 @@ void OpenGLState::Apply() const { glBlendEquationSeparate(blend.rgb_equation, blend.a_equation); } - if (logic_op != cur_state.logic_op) { - glLogicOp(logic_op); + // GLES3 does not support glLogicOp + if (!GLES) { + if (logic_op != cur_state.logic_op) { + glLogicOp(logic_op); + } + } else { + LOG_TRACE(Render_OpenGL, "glLogicOps are unimplemented..."); } // Textures @@ -319,12 +325,14 @@ void OpenGLState::Apply() const { } // Clip distance - for (std::size_t i = 0; i < clip_distance.size(); ++i) { - if (clip_distance[i] != cur_state.clip_distance[i]) { - if (clip_distance[i]) { - glEnable(GL_CLIP_DISTANCE0 + static_cast(i)); - } else { - glDisable(GL_CLIP_DISTANCE0 + static_cast(i)); + if (!GLES || GLAD_GL_EXT_clip_cull_distance) { + for (size_t i = 0; i < clip_distance.size(); ++i) { + if (clip_distance[i] != cur_state.clip_distance[i]) { + if (clip_distance[i]) { + glEnable(GL_CLIP_DISTANCE0 + static_cast(i)); + } else { + glDisable(GL_CLIP_DISTANCE0 + static_cast(i)); + } } } } diff --git a/src/video_core/renderer_opengl/gl_vars.cpp b/src/video_core/renderer_opengl/gl_vars.cpp new file mode 100644 index 000000000..7eb0f1152 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_vars.cpp @@ -0,0 +1,9 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "video_core/renderer_opengl/gl_vars.h" + +namespace OpenGL { +bool GLES; +} diff --git a/src/video_core/renderer_opengl/gl_vars.h b/src/video_core/renderer_opengl/gl_vars.h new file mode 100644 index 000000000..3a6d494ca --- /dev/null +++ b/src/video_core/renderer_opengl/gl_vars.h @@ -0,0 +1,9 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace OpenGL { +extern bool GLES; +} diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 1b00695d7..c3e830a21 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -21,14 +21,13 @@ #include "core/tracer/recorder.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/rasterizer_interface.h" +#include "video_core/renderer_opengl/gl_vars.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/video_core.h" namespace OpenGL { static const char vertex_shader[] = R"( -#version 150 core - in vec2 vert_position; in vec2 vert_tex_coord; out vec2 frag_tex_coord; @@ -50,8 +49,6 @@ void main() { )"; static const char fragment_shader[] = R"( -#version 150 core - in vec2 frag_tex_coord; out vec4 color; @@ -279,7 +276,13 @@ void RendererOpenGL::InitOpenGLObjects() { 0.0f); // Link shaders and get variable locations - shader.Create(vertex_shader, fragment_shader); + if (GLES) { + std::string frag_source(fragment_shader_precision_OES); + frag_source += fragment_shader; + shader.Create(vertex_shader, frag_source.data()); + } else { + shader.Create(vertex_shader, fragment_shader); + } state.draw.shader_program = shader.handle; state.Apply(); uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix"); @@ -344,7 +347,7 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, case GPU::Regs::PixelFormat::RGBA8: internal_format = GL_RGBA; texture.gl_format = GL_RGBA; - texture.gl_type = GL_UNSIGNED_INT_8_8_8_8; + texture.gl_type = GLES ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_8_8_8_8; break; case GPU::Regs::PixelFormat::RGB8: @@ -353,7 +356,9 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, // mostly everywhere) for words or half-words. // TODO: check how those behave on big-endian processors. internal_format = GL_RGB; - texture.gl_format = GL_BGR; + + // GLES Dosen't support BGR , Use RGB instead + texture.gl_format = GLES ? GL_RGB : GL_BGR; texture.gl_type = GL_UNSIGNED_BYTE; break; @@ -555,7 +560,7 @@ Core::System::ResultStatus RendererOpenGL::Init() { return Core::System::ResultStatus::ErrorVideoCore_ErrorGenericDrivers; } - if (!GLAD_GL_VERSION_3_3) { + if (!(GLAD_GL_VERSION_3_3 || GLAD_GL_ES_VERSION_3_1)) { return Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL33; } diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index fa68e1d1f..d7cd90d71 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -7,6 +7,7 @@ #include "core/settings.h" #include "video_core/pica.h" #include "video_core/renderer_base.h" +#include "video_core/renderer_opengl/gl_vars.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/video_core.h" @@ -36,6 +37,8 @@ Core::System::ResultStatus Init(EmuWindow& emu_window, Memory::MemorySystem& mem g_memory = &memory; Pica::Init(); + OpenGL::GLES = Settings::values.use_gles; + g_renderer = std::make_unique(emu_window); Core::System::ResultStatus result = g_renderer->Init();