citra-emu
/
citra-canary
Archived
1
0
Fork 0

Merge pull request #4613 from BreadFish64/gles5

video_core: add GLES support
This commit is contained in:
Weiyi Wang 2019-02-17 15:44:39 -05:00 committed by GitHub
commit f409342ab5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 8937 additions and 1747 deletions

View File

@ -1,5 +1,5 @@
These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command: These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command:
``` ```
python -m glad --profile core --out-path glad/ --api gl=3.3 --generator=c python -m glad --profile core --out-path glad/ --api "gl=3.3,gles2=3.2" --generator=c
``` ```

View File

@ -2,7 +2,7 @@
#define __khrplatform_h_ #define __khrplatform_h_
/* /*
** Copyright (c) 2008-2009 The Khronos Group Inc. ** Copyright (c) 2008-2018 The Khronos Group Inc.
** **
** Permission is hereby granted, free of charge, to any person obtaining a ** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the ** copy of this software and/or associated documentation files (the
@ -26,18 +26,16 @@
/* Khronos platform-specific types and definitions. /* Khronos platform-specific types and definitions.
* *
* $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $ * The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
* *
* Adopters may modify this file to suit their platform. Adopters are * Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos * encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file. * group so that they can be included in future versions of this file.
* Please submit changes by sending them to the public Khronos Bugzilla * Please submit changes by filing pull requests or issues on
* (http://khronos.org/bugzilla) by filing a bug against product * the EGL Registry repository linked above.
* "Khronos (general)" component "Registry".
*
* A predefined template which fills in some of the bug fields can be
* reached using http://tinyurl.com/khrplatform-h-bugreport, but you
* must create a Bugzilla login first.
* *
* *
* See the Implementer's Guidelines for information about where this file * See the Implementer's Guidelines for information about where this file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -105,6 +105,7 @@ void Config::ReadValues() {
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
// Renderer // Renderer
Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", false);
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
#ifdef __APPLE__ #ifdef __APPLE__
// Hardware shader is broken on macos thanks to poor drivers. // Hardware shader is broken on macos thanks to poor drivers.

View File

@ -92,6 +92,10 @@ udp_pad_index=
use_cpu_jit = use_cpu_jit =
[Renderer] [Renderer]
# Whether to render using GLES or OpenGL
# 0 (default): OpenGL, 1: GLES
use_gles =
# Whether to use software or hardware rendering. # Whether to use software or hardware rendering.
# 0: Software, 1 (default): Hardware # 0: Software, 1 (default): Hardware
use_hw_renderer = use_hw_renderer =

View File

@ -122,8 +122,13 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
SDL_SetMainReady(); SDL_SetMainReady();
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
if (Settings::values.use_gles) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
} else {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
}
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
@ -155,7 +160,9 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
exit(1); exit(1);
} }
if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { auto gl_load_func = Settings::values.use_gles ? gladLoadGLES2Loader : gladLoadGLLoader;
if (!gl_load_func(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError()); LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError());
exit(1); exit(1);
} }

View File

@ -67,6 +67,7 @@ void LogSetting(const std::string& name, const T& value) {
void LogSettings() { void LogSettings() {
LOG_INFO(Config, "Citra Configuration:"); LOG_INFO(Config, "Citra Configuration:");
LogSetting("Core_UseCpuJit", Settings::values.use_cpu_jit); LogSetting("Core_UseCpuJit", Settings::values.use_cpu_jit);
LogSetting("Renderer_UseGLES", Settings::values.use_gles);
LogSetting("Renderer_UseHwRenderer", Settings::values.use_hw_renderer); LogSetting("Renderer_UseHwRenderer", Settings::values.use_hw_renderer);
LogSetting("Renderer_UseHwShader", Settings::values.use_hw_shader); LogSetting("Renderer_UseHwShader", Settings::values.use_hw_shader);
LogSetting("Renderer_ShadersAccurateGs", Settings::values.shaders_accurate_gs); LogSetting("Renderer_ShadersAccurateGs", Settings::values.shaders_accurate_gs);

View File

@ -130,6 +130,7 @@ struct Values {
u64 init_time; u64 init_time;
// Renderer // Renderer
bool use_gles;
bool use_hw_renderer; bool use_hw_renderer;
bool use_hw_shader; bool use_hw_shader;
bool shaders_accurate_gs; bool shaders_accurate_gs;

View File

@ -41,6 +41,8 @@ add_library(video_core STATIC
renderer_opengl/gl_state.h renderer_opengl/gl_state.h
renderer_opengl/gl_stream_buffer.cpp renderer_opengl/gl_stream_buffer.cpp
renderer_opengl/gl_stream_buffer.h renderer_opengl/gl_stream_buffer.h
renderer_opengl/gl_vars.cpp
renderer_opengl/gl_vars.h
renderer_opengl/pica_to_gl.h renderer_opengl/pica_to_gl.h
renderer_opengl/renderer_opengl.cpp renderer_opengl/renderer_opengl.cpp
renderer_opengl/renderer_opengl.h renderer_opengl/renderer_opengl.h

View File

@ -28,6 +28,7 @@
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.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/utils.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
@ -50,6 +51,17 @@ static constexpr std::array<FormatTuple, 5> fb_format_tuples = {{
{GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4 {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<FormatTuple, 5> 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<FormatTuple, 4> depth_format_tuples = {{ static constexpr std::array<FormatTuple, 4> depth_format_tuples = {{
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16 {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); const SurfaceType type = SurfaceParams::GetFormatType(pixel_format);
if (type == SurfaceType::Color) { if (type == SurfaceType::Color) {
ASSERT(static_cast<std::size_t>(pixel_format) < fb_format_tuples.size()); ASSERT(static_cast<std::size_t>(pixel_format) < fb_format_tuples.size());
if (GLES) {
return fb_format_tuples_oes[static_cast<unsigned int>(pixel_format)];
}
return fb_format_tuples[static_cast<unsigned int>(pixel_format)]; return fb_format_tuples[static_cast<unsigned int>(pixel_format)];
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) { } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
std::size_t tuple_idx = static_cast<std::size_t>(pixel_format) - 14; std::size_t tuple_idx = static_cast<std::size_t>(pixel_format) - 14;
@ -72,6 +87,77 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {
return tex_tuple; 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 <typename Map, typename Interval> template <typename Map, typename Interval>
constexpr auto RangeFromInterval(Map& map, const Interval& interval) { constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
return boost::make_iterator_range(map.equal_range(interval)); return boost::make_iterator_range(map.equal_range(interval));
@ -841,7 +927,12 @@ void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLui
state.Apply(); state.Apply();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
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]); glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]);
}
} else { } else {
state.ResetTexture(texture.handle); state.ResetTexture(texture.handle);
state.draw.read_framebuffer = read_fb_handle; state.draw.read_framebuffer = read_fb_handle;
@ -982,16 +1073,15 @@ RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
d24s8_abgr_buffer.Create(); d24s8_abgr_buffer.Create();
d24s8_abgr_buffer_size = 0; d24s8_abgr_buffer_size = 0;
const char* vs_source = R"( std::string vs_source = R"(
#version 330 core
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)); 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() { void main() {
gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); 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 samplerBuffer tbo;
uniform vec2 tbo_size; uniform vec2 tbo_size;
uniform vec4 viewport; uniform vec4 viewport;
@ -1004,7 +1094,7 @@ void main() {
color = texelFetch(tbo, tbo_offset).rabg; 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(); OpenGLState state = OpenGLState::GetCurState();
GLuint old_program = state.draw.shader_program; GLuint old_program = state.draw.shader_program;

View File

@ -18,6 +18,7 @@
#include "video_core/renderer_opengl/gl_shader_decompiler.h" #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_gen.h"
#include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_shader_util.h"
#include "video_core/renderer_opengl/gl_vars.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
using Pica::FramebufferRegs; using Pica::FramebufferRegs;
@ -1250,7 +1251,6 @@ std::string GenerateFragmentShader(const PicaFSConfig& config, bool separable_sh
const auto& state = config.state; const auto& state = config.state;
std::string out = R"( std::string out = R"(
#version 330 core
#extension GL_ARB_shader_image_load_store : enable #extension GL_ARB_shader_image_load_store : enable
#extension GL_ARB_shader_image_size : enable #extension GL_ARB_shader_image_size : enable
#define ALLOW_SHADOW (defined(GL_ARB_shader_image_load_store) && defined(GL_ARB_shader_image_size)) #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"; out += "#extension GL_ARB_separate_shader_objects : enable\n";
} }
if (GLES) {
out += fragment_shader_precision_OES;
}
out += GetVertexInterfaceDeclaration(false, separable_shader); out += GetVertexInterfaceDeclaration(false, separable_shader);
out += R"( out += R"(
#ifndef CITRA_GLES
in vec4 gl_FragCoord; in vec4 gl_FragCoord;
#endif // CITRA_GLES
out vec4 color; out vec4 color;
@ -1300,13 +1306,13 @@ float LookupLightingLUT(int lut_index, int index, float delta) {
float LookupLightingLUTUnsigned(int lut_index, float pos) { float LookupLightingLUTUnsigned(int lut_index, float pos) {
int index = clamp(int(pos * 256.0), 0, 255); 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); return LookupLightingLUT(lut_index, index, delta);
} }
float LookupLightingLUTSigned(int lut_index, float pos) { float LookupLightingLUTSigned(int lut_index, float pos) {
int index = clamp(int(pos * 128.0), -128, 127); 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; if (index < 0) index += 256;
return LookupLightingLUT(lut_index, index, delta); return LookupLightingLUT(lut_index, index, delta);
} }
@ -1492,10 +1498,10 @@ vec4 secondary_fragment_color = vec4(0.0);
// Negate the condition if we have to keep only the pixels outside the scissor box // Negate the condition if we have to keep only the pixels outside the scissor box
if (state.scissor_test_mode == RasterizerRegs::ScissorMode::Include) if (state.scissor_test_mode == RasterizerRegs::ScissorMode::Include)
out += "!"; out += "!";
out += "(gl_FragCoord.x >= scissor_x1 && " out += "(gl_FragCoord.x >= float(scissor_x1) && "
"gl_FragCoord.y >= scissor_y1 && " "gl_FragCoord.y >= float(scissor_y1) && "
"gl_FragCoord.x < scissor_x2 && " "gl_FragCoord.x < float(scissor_x2) && "
"gl_FragCoord.y < scissor_y2)) discard;\n"; "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 // After perspective divide, OpenGL transform z_over_w from [-1, 1] to [near, far]. Here we use
@ -1527,7 +1533,7 @@ vec4 secondary_fragment_color = vec4(0.0);
if (state.fog_mode == TexturingRegs::FogMode::Fog) { if (state.fog_mode == TexturingRegs::FogMode::Fog) {
// Get index into fog LUT // Get index into fog LUT
if (state.fog_flip) { 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 { } else {
out += "float fog_index = depth * 128.0;\n"; out += "float fog_index = depth * 128.0;\n";
} }
@ -1589,7 +1595,7 @@ do {
} }
std::string GenerateTrivialVertexShader(bool separable_shader) { std::string GenerateTrivialVertexShader(bool separable_shader) {
std::string out = "#version 330 core\n"; std::string out = "";
if (separable_shader) { if (separable_shader) {
out += "#extension GL_ARB_separate_shader_objects : enable\n"; out += "#extension GL_ARB_separate_shader_objects : enable\n";
} }
@ -1624,8 +1630,10 @@ void main() {
normquat = vert_normquat; normquat = vert_normquat;
view = vert_view; view = vert_view;
gl_Position = vert_position; 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[0] = -vert_position.z; // fixed PICA clipping plane z <= 0
gl_ClipDistance[1] = dot(clip_coef, vert_position); gl_ClipDistance[1] = dot(clip_coef, vert_position);
#endif // !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance)
} }
)"; )";
@ -1634,7 +1642,7 @@ void main() {
std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup,
const PicaVSConfig& config, bool separable_shader) { const PicaVSConfig& config, bool separable_shader) {
std::string out = "#version 330 core\n"; std::string out = "";
if (separable_shader) { if (separable_shader) {
out += "#extension GL_ARB_separate_shader_objects : enable\n"; out += "#extension GL_ARB_separate_shader_objects : enable\n";
} }
@ -1742,9 +1750,12 @@ struct Vertex {
semantic(VSOutputAttributes::POSITION_Y) + ", " + semantic(VSOutputAttributes::POSITION_Y) + ", " +
semantic(VSOutputAttributes::POSITION_Z) + ", " + semantic(VSOutputAttributes::POSITION_Z) + ", " +
semantic(VSOutputAttributes::POSITION_W) + ");\n"; semantic(VSOutputAttributes::POSITION_W) + ");\n";
semantic(VSOutputAttributes::POSITION_W) + ");\n";
out += " gl_Position = vtx_pos;\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[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 += " vec4 vtx_quat = GetVertexQuaternion(vtx);\n";
out += " normquat = mix(vtx_quat, -vtx_quat, bvec4(quats_opposite));\n\n"; out += " normquat = mix(vtx_quat, -vtx_quat, bvec4(quats_opposite));\n\n";
@ -1787,7 +1798,7 @@ void EmitPrim(Vertex vtx0, Vertex vtx1, Vertex vtx2) {
}; };
std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config, bool separable_shader) { std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config, bool separable_shader) {
std::string out = "#version 330 core\n"; std::string out = "";
if (separable_shader) { if (separable_shader) {
out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
} }
@ -1822,7 +1833,7 @@ void main() {
std::optional<std::string> GenerateGeometryShader(const Pica::Shader::ShaderSetup& setup, std::optional<std::string> GenerateGeometryShader(const Pica::Shader::ShaderSetup& setup,
const PicaGSConfig& config, const PicaGSConfig& config,
bool separable_shader) { bool separable_shader) {
std::string out = "#version 330 core\n"; std::string out = "";
if (separable_shader) { if (separable_shader) {
out += "#extension GL_ARB_separate_shader_objects : enable\n"; out += "#extension GL_ARB_separate_shader_objects : enable\n";
} }

View File

@ -2,15 +2,33 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <array>
#include <string>
#include <vector> #include <vector>
#include <glad/glad.h> #include <glad/glad.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_shader_util.h"
#include "video_core/renderer_opengl/gl_vars.h"
namespace OpenGL { namespace OpenGL {
GLuint LoadShader(const char* source, GLenum type) { 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; const char* debug_type;
switch (type) { switch (type) {
case GL_VERTEX_SHADER: case GL_VERTEX_SHADER:
@ -26,8 +44,9 @@ GLuint LoadShader(const char* source, GLenum type) {
UNREACHABLE(); UNREACHABLE();
} }
std::array<const char*, 2> src_arr{version.data(), source};
GLuint shader_id = glCreateShader(type); GLuint shader_id = glCreateShader(type);
glShaderSource(shader_id, 1, &source, nullptr); glShaderSource(shader_id, static_cast<GLsizei>(src_arr.size()), src_arr.data(), nullptr);
LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
glCompileShader(shader_id); glCompileShader(shader_id);
@ -44,7 +63,7 @@ GLuint LoadShader(const char* source, GLenum type) {
} else { } else {
LOG_ERROR(Render_OpenGL, "Error compiling {} shader:\n{}", debug_type, LOG_ERROR(Render_OpenGL, "Error compiling {} shader:\n{}", debug_type,
&shader_error[0]); &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; return shader_id;

View File

@ -9,6 +9,17 @@
namespace OpenGL { 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 * Utility function to create and compile an OpenGL GLSL shader
* @param source String of the GLSL shader program * @param source String of the GLSL shader program

View File

@ -6,6 +6,7 @@
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/gl_vars.h"
namespace OpenGL { namespace OpenGL {
@ -193,9 +194,14 @@ void OpenGLState::Apply() const {
glBlendEquationSeparate(blend.rgb_equation, blend.a_equation); glBlendEquationSeparate(blend.rgb_equation, blend.a_equation);
} }
// GLES3 does not support glLogicOp
if (!GLES) {
if (logic_op != cur_state.logic_op) { if (logic_op != cur_state.logic_op) {
glLogicOp(logic_op); glLogicOp(logic_op);
} }
} else {
LOG_TRACE(Render_OpenGL, "glLogicOps are unimplemented...");
}
// Textures // Textures
for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) { for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
@ -319,7 +325,8 @@ void OpenGLState::Apply() const {
} }
// Clip distance // Clip distance
for (std::size_t i = 0; i < clip_distance.size(); ++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] != cur_state.clip_distance[i]) {
if (clip_distance[i]) { if (clip_distance[i]) {
glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i)); glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i));
@ -328,6 +335,7 @@ void OpenGLState::Apply() const {
} }
} }
} }
}
cur_state = *this; cur_state = *this;
} }

View File

@ -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;
}

View File

@ -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;
}

View File

@ -21,14 +21,13 @@
#include "core/tracer/recorder.h" #include "core/tracer/recorder.h"
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
#include "video_core/rasterizer_interface.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/renderer_opengl/renderer_opengl.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
namespace OpenGL { namespace OpenGL {
static const char vertex_shader[] = R"( static const char vertex_shader[] = R"(
#version 150 core
in vec2 vert_position; in vec2 vert_position;
in vec2 vert_tex_coord; in vec2 vert_tex_coord;
out vec2 frag_tex_coord; out vec2 frag_tex_coord;
@ -50,8 +49,6 @@ void main() {
)"; )";
static const char fragment_shader[] = R"( static const char fragment_shader[] = R"(
#version 150 core
in vec2 frag_tex_coord; in vec2 frag_tex_coord;
out vec4 color; out vec4 color;
@ -279,7 +276,13 @@ void RendererOpenGL::InitOpenGLObjects() {
0.0f); 0.0f);
// Link shaders and get variable locations // Link shaders and get variable locations
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); shader.Create(vertex_shader, fragment_shader);
}
state.draw.shader_program = shader.handle; state.draw.shader_program = shader.handle;
state.Apply(); state.Apply();
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix"); uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
@ -344,7 +347,7 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
case GPU::Regs::PixelFormat::RGBA8: case GPU::Regs::PixelFormat::RGBA8:
internal_format = GL_RGBA; internal_format = GL_RGBA;
texture.gl_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; break;
case GPU::Regs::PixelFormat::RGB8: case GPU::Regs::PixelFormat::RGB8:
@ -353,7 +356,9 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
// mostly everywhere) for words or half-words. // mostly everywhere) for words or half-words.
// TODO: check how those behave on big-endian processors. // TODO: check how those behave on big-endian processors.
internal_format = GL_RGB; 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; texture.gl_type = GL_UNSIGNED_BYTE;
break; break;
@ -555,7 +560,7 @@ Core::System::ResultStatus RendererOpenGL::Init() {
return Core::System::ResultStatus::ErrorVideoCore_ErrorGenericDrivers; 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; return Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL33;
} }

View File

@ -7,6 +7,7 @@
#include "core/settings.h" #include "core/settings.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/renderer_base.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/renderer_opengl/renderer_opengl.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
@ -36,6 +37,8 @@ Core::System::ResultStatus Init(EmuWindow& emu_window, Memory::MemorySystem& mem
g_memory = &memory; g_memory = &memory;
Pica::Init(); Pica::Init();
OpenGL::GLES = Settings::values.use_gles;
g_renderer = std::make_unique<OpenGL::RendererOpenGL>(emu_window); g_renderer = std::make_unique<OpenGL::RendererOpenGL>(emu_window);
Core::System::ResultStatus result = g_renderer->Init(); Core::System::ResultStatus result = g_renderer->Init();