shithub: zelda3

Download patch

ref: 4f97999d66ba83fa51dbfda53e2e1bcd348f6150
parent: 70a08f116cc48d82908b440def4a5fbb2da9edff
author: vanfanel <redwindwanderer@gmail.com>
date: Mon Dec 12 08:28:09 EST 2022

Fix OpenGL ES support (Fixes #192)

Co-authored-by: Snesrev <snesrev@protonmail.com>

--- a/config.c
+++ b/config.c
@@ -354,7 +354,9 @@
       return true;
     } else if (StringEqualsNoCase(key, "OutputMethod")) {
       g_config.output_method = StringEqualsNoCase(value, "SDL-Software") ? kOutputMethod_SDLSoftware :
-                               StringEqualsNoCase(value, "OpenGL") ? kOutputMethod_OpenGL : kOutputMethod_SDL;
+                               StringEqualsNoCase(value, "OpenGL") ? kOutputMethod_OpenGL : 
+                               StringEqualsNoCase(value, "OpenGL ES") ? kOutputMethod_OpenGL_ES :
+                                                                        kOutputMethod_SDL;
       return true;
     } else if (StringEqualsNoCase(key, "LinearFiltering")) {
       return ParseBool(value, &g_config.linear_filtering);
--- a/config.h
+++ b/config.h
@@ -41,6 +41,7 @@
   kOutputMethod_SDL,
   kOutputMethod_SDLSoftware,
   kOutputMethod_OpenGL,
+  kOutputMethod_OpenGL_ES,
 };
 
 typedef struct Config {
--- a/glsl_shader.c
+++ b/glsl_shader.c
@@ -212,9 +212,11 @@
   free(data_org);
 }
 
-static bool GlslPass_Compile(GlslPass *p, uint type, const uint8 *data, size_t size) {
+static bool GlslPass_Compile(GlslPass *p, uint type, const uint8 *data, size_t size, bool use_opengl_es) {
   static const char kVertexPrefix[] =   "#define VERTEX\n#define PARAMETER_UNIFORM\n";
-  static const char kFragmentPrefix[] = "#define FRAGMENT\n#define PARAMETER_UNIFORM\n";
+  static const char kFragmentPrefixCore[] = "#define FRAGMENT\n#define PARAMETER_UNIFORM\n";
+  static const char kFragmentPrefixEs[] = "#define FRAGMENT\n#define PARAMETER_UNIFORM\n" \
+					"precision mediump float;";
   const GLchar *strings[3];
   GLint lengths[3];
   char buffer[256];
@@ -222,15 +224,25 @@
   size_t skip = 0;
 
   if (size < 8 || memcmp(data, "#version", 8) != 0) {
-    strings[0] = "#version 330\n";
-    lengths[0] = sizeof("#version 330\n") - 1;
+    if (!use_opengl_es) {
+      strings[0] = "#version 330\n";
+      lengths[0] = sizeof("#version 330\n") - 1;
+    } else {
+      strings[0] = "#version 300 es\n";
+      lengths[0] = sizeof("#version 300 es\n") - 1;
+    }
   } else {
     while (skip < size && data[skip++] != '\n') {}
     strings[0] = (char*)data;
     lengths[0] = (int)skip;
   }
-  strings[1] = (type == GL_VERTEX_SHADER) ? (char*)kVertexPrefix : kFragmentPrefix;
-  lengths[1] = (type == GL_VERTEX_SHADER) ? sizeof(kVertexPrefix) - 1 : sizeof(kFragmentPrefix) - 1;
+  if (type == GL_VERTEX_SHADER) {
+    strings[1] = (char *)kVertexPrefix;
+    lengths[1] = sizeof(kVertexPrefix) - 1;
+  } else {
+    strings[1] = (use_opengl_es) ? (char *)kFragmentPrefixEs : kFragmentPrefixCore;
+    lengths[1] = (use_opengl_es) ? sizeof(kFragmentPrefixEs) - 1 : sizeof(kFragmentPrefixCore) - 1;
+  }
   strings[2] = (GLchar *)data + skip;
   lengths[2] = (int)(size - skip);
   uint shader = glCreateShader(type);
@@ -311,7 +323,7 @@
   return len >= 5 && memcmp(filename + len - 5, ".glsl", 5) == 0;
 }
 
-GlslShader *GlslShader_CreateFromFile(const char *filename) {
+GlslShader *GlslShader_CreateFromFile(const char *filename, bool opengl_es) {
   char buffer[256];
   GLint link_status;
   ByteArray shader_code = { 0 };
@@ -348,8 +360,8 @@
       goto FAIL;
     }
     p->gl_program = glCreateProgram();
-    if (!GlslPass_Compile(p, GL_VERTEX_SHADER, shader_code.data, shader_code.size) ||
-        !GlslPass_Compile(p, GL_FRAGMENT_SHADER, shader_code.data, shader_code.size)) {
+    if (!GlslPass_Compile(p, GL_VERTEX_SHADER, shader_code.data, shader_code.size, opengl_es) ||
+        !GlslPass_Compile(p, GL_FRAGMENT_SHADER, shader_code.data, shader_code.size, opengl_es)) {
       goto FAIL;
     }
     glLinkProgram(p->gl_program);
--- a/glsl_shader.h
+++ b/glsl_shader.h
@@ -91,7 +91,7 @@
   GlTextureWithSize prev_frame[8];
 } GlslShader;
 
-GlslShader *GlslShader_CreateFromFile(const char *filename);
+GlslShader *GlslShader_CreateFromFile(const char *filename, bool opengl_es);
 void GlslShader_Destroy(GlslShader *gs);
 void GlslShader_Render(GlslShader *gs, GlTextureWithSize *tex, int viewport_x, int viewport_y, int viewport_width, int viewport_height);
 
--- a/main.c
+++ b/main.c
@@ -29,8 +29,6 @@
 
 static bool g_run_without_emu = 0;
 
-void ShaderInit();
-
 // Forwards
 static bool LoadRom(const char *filename);
 static void LoadLinkGraphics();
@@ -45,7 +43,6 @@
 static void LoadAssets();
 static void SwitchDirectory();
 
-
 enum {
   kDefaultFullscreen = 0,
   kMaxWindowScale = 10,
@@ -55,7 +52,6 @@
 };
 
 static const char kWindowTitle[] = "The Legend of Zelda: A Link to the Past";
-
 static uint32 g_win_flags = SDL_WINDOW_RESIZABLE;
 static SDL_Window *g_window;
 
@@ -278,7 +274,7 @@
   &SdlRenderer_EndDraw,
 };
 
-void OpenGLRenderer_Create(struct RendererFuncs *funcs);
+void OpenGLRenderer_Create(struct RendererFuncs *funcs, bool use_opengl_es);
 
 #undef main
 int main(int argc, char** argv) {
@@ -339,9 +335,10 @@
   int window_width  = custom_size ? g_config.window_width  : g_current_window_scale * g_snes_width;
   int window_height = custom_size ? g_config.window_height : g_current_window_scale * g_snes_height;
 
-  if (g_config.output_method == kOutputMethod_OpenGL) {
+  if (g_config.output_method == kOutputMethod_OpenGL ||
+      g_config.output_method == kOutputMethod_OpenGL_ES) {
     g_win_flags |= SDL_WINDOW_OPENGL;
-    OpenGLRenderer_Create(&g_renderer_funcs);
+    OpenGLRenderer_Create(&g_renderer_funcs, (g_config.output_method == kOutputMethod_OpenGL_ES));
   } else {
     g_renderer_funcs = kSdlRendererFuncs;
   }
--- a/opengl.c
+++ b/opengl.c
@@ -16,6 +16,7 @@
 static unsigned int g_program, g_VAO;
 static GlTextureWithSize g_texture;
 static GlslShader *g_glsl_shader;
+static bool g_opengl_es;
 
 static void GL_APIENTRY MessageCallback(GLenum source,
                 GLenum type,
@@ -42,9 +43,18 @@
   SDL_GL_SetSwapInterval(1);
   ogl_LoadFunctions();
 
-  if (!ogl_IsVersionGEQ(3, 3))
-    Die("You need OpenGL 3.3");
+  if (!g_opengl_es) {
+    if (!ogl_IsVersionGEQ(3, 3))
+      Die("You need OpenGL 3.3");
+  } else {
+    int majorVersion = 0, minorVersion = 0;
+    SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &majorVersion);
+    SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minorVersion);
+    if (majorVersion < 3)
+      Die("You need OpenGL ES 3.0");
 
+  }
+
   if (kDebugFlag) {
     glEnable(GL_DEBUG_OUTPUT);
     glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
@@ -80,7 +90,7 @@
   glEnableVertexAttribArray(1);
 
   // vertex shader
-  const GLchar *vs_code = "#version 330 core\n" CODE(
+  const GLchar *vs_code_core = "#version 330 core\n" CODE(
   layout(location = 0) in vec3 aPos;
   layout(location = 1) in vec2 aTexCoord;
   out vec2 TexCoord;
@@ -90,6 +100,17 @@
   }
 );
 
+  const GLchar *vs_code_es = "#version 300 es\n" CODE(
+  layout(location = 0) in vec3 aPos;
+  layout(location = 1) in vec2 aTexCoord;
+  out vec2 TexCoord;
+  void main() {
+    gl_Position = vec4(aPos, 1.0);
+    TexCoord = vec2(aTexCoord.x, aTexCoord.y);
+  }
+);
+
+  const GLchar *vs_code = g_opengl_es ? vs_code_es : vs_code_core;
   unsigned int vs = glCreateShader(GL_VERTEX_SHADER);
   glShaderSource(vs, 1, &vs_code, NULL);
   glCompileShader(vs);
@@ -103,7 +124,7 @@
   }
 
   // fragment shader
-  const GLchar *fs_code = "#version 330 core\n" CODE(
+  const GLchar *fs_code_core = "#version 330 core\n" CODE(
   out vec4 FragColor;
   in vec2 TexCoord;
   // texture samplers
@@ -113,6 +134,19 @@
   }
 );
 
+  const GLchar *fs_code_es = "#version 300 es\n" CODE(
+  precision mediump float;
+  out vec4 FragColor;
+  in vec2 TexCoord;
+  // texture samplers
+  uniform sampler2D texture1;
+  void main() {
+    FragColor = texture(texture1, TexCoord);
+  }
+);
+
+
+  const GLchar *fs_code = g_opengl_es ? fs_code_es : fs_code_core;
   unsigned int fs = glCreateShader(GL_FRAGMENT_SHADER);
   glShaderSource(fs, 1, &fs_code, NULL);
   glCompileShader(fs);
@@ -136,7 +170,7 @@
   }
 
   if (g_config.shader)
-    g_glsl_shader = GlslShader_CreateFromFile(g_config.shader);
+    g_glsl_shader = GlslShader_CreateFromFile(g_config.shader, g_opengl_es);
   
   return true;
 }
@@ -178,11 +212,17 @@
 
   glBindTexture(GL_TEXTURE_2D, g_texture.gl_texture);
   if (g_draw_width == g_texture.width && g_draw_height == g_texture.height) {
-    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_draw_width, g_draw_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, g_screen_buffer);
+    if (!g_opengl_es)
+      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_draw_width, g_draw_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, g_screen_buffer);
+    else
+      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_draw_width, g_draw_height, GL_BGRA, GL_UNSIGNED_BYTE, g_screen_buffer);
   } else {
     g_texture.width = g_draw_width;
     g_texture.height = g_draw_height;
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, g_draw_width, g_draw_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, g_screen_buffer);
+    if (!g_opengl_es)
+      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, g_draw_width, g_draw_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, g_screen_buffer);
+    else
+      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, g_draw_width, g_draw_height, 0, GL_BGRA, GL_UNSIGNED_BYTE, g_screen_buffer);
   }
 
   glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
@@ -203,7 +243,6 @@
   SDL_GL_SwapWindow(g_window);
 }
 
-
 static const struct RendererFuncs kOpenGLRendererFuncs = {
   &OpenGLRenderer_Init,
   &OpenGLRenderer_Destroy,
@@ -211,10 +250,17 @@
   &OpenGLRenderer_EndDraw,
 };
 
-void OpenGLRenderer_Create(struct RendererFuncs *funcs) {
-  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
-  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
-  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+void OpenGLRenderer_Create(struct RendererFuncs *funcs, bool use_opengl_es) {
+  g_opengl_es = use_opengl_es;
+  if (!g_opengl_es) {
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+  } else {
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+  }
   *funcs = kOpenGLRendererFuncs;
 }