From 21f52cba339b2f79b0b3db9b53df3815e984b961 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Thu, 7 Sep 2023 21:17:21 -0400 Subject: [PATCH] ES2 Support? --- CMakeLists.txt | 14 +++- LICENSE | 21 ++++++ include/GLES/gl.h | 2 + src/draw.c | 5 +- src/passthrough.c | 2 + src/passthrough.h | 15 +++- src/shaders/es2/main.fsh | 50 +++++++++++++ src/shaders/es2/main.vsh | 48 +++++++++++++ src/shaders/{ => es3}/main.fsh | 0 src/shaders/{ => es3}/main.vsh | 0 test/.gitignore | 1 + test/CMakeLists.txt | 11 +++ test/src/main.cpp | 125 +++++++++++++++++++++++++++++++++ 13 files changed, 290 insertions(+), 4 deletions(-) create mode 100644 LICENSE create mode 100644 src/shaders/es2/main.fsh create mode 100644 src/shaders/es2/main.vsh rename src/shaders/{ => es3}/main.fsh (100%) rename src/shaders/{ => es3}/main.vsh (100%) create mode 100644 test/.gitignore create mode 100644 test/CMakeLists.txt create mode 100644 test/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 52b36ef..149ed0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,10 +11,20 @@ if(GLES_COMPATIBILITY_LAYER_USE_DEFAULT_INCLUDE_PATH) target_include_directories(gles-compatibility-layer PUBLIC include) endif() +# GL Version +option(GLES_COMPATIBILITY_LAYER_USE_ES3 "Use OpenGL ES 3" TRUE) +if(GLES_COMPATIBILITY_LAYER_USE_ES3) + target_compile_definitions(gles-compatibility-layer PUBLIC GLES_COMPATIBILITY_LAYER_USE_ES3) +endif() + # Shaders include(cmake/util.cmake) -embed_resource(gles-compatibility-layer src/shaders/main.vsh) -embed_resource(gles-compatibility-layer src/shaders/main.fsh) +set(SHADER_FOLDER "es2") +if(GLES_COMPATIBILITY_LAYER_USE_ES3) + set(SHADER_FOLDER "es3") +endif() +embed_resource(gles-compatibility-layer "src/shaders/${SHADER_FOLDER}/main.vsh") +embed_resource(gles-compatibility-layer "src/shaders/${SHADER_FOLDER}/main.fsh") # Warnings target_compile_options(gles-compatibility-layer PRIVATE -Wall -Wextra -Werror -Wpointer-arith -Wshadow -Wnull-dereference) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b4dae87 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 TheBrokenRail + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/include/GLES/gl.h b/include/GLES/gl.h index b1f67ab..7f54430 100644 --- a/include/GLES/gl.h +++ b/include/GLES/gl.h @@ -170,6 +170,7 @@ void glLightModelfv(GLenum pname, const GLfloat *params); void glPixelStorei(GLenum pname, GLint param); // Not Part Of OpenGL ES 1.1 +#ifdef GLES_COMPATIBILITY_LAYER_USE_ES3 #define GL_SAMPLES_PASSED_ARB 0x8c2f // GL_ANY_SAMPLES_PASSED #define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 // GL_QUERY_RESULT_AVAILABLE #define GL_QUERY_RESULT_ARB 0x8866 // GL_QUERY_RESULT @@ -178,6 +179,7 @@ void glDeleteQueriesARB(GLsizei n, const GLuint *ids); void glBeginQueryARB(GLenum target, GLuint id); void glEndQueryARB(GLenum target); void glGetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params); +#endif void extra_enable_highlight_mode(float red, float green, float blue, float alpha); void extra_disable_highlight_mode(); diff --git a/src/draw.c b/src/draw.c index 4efb68f..8dcf06d 100644 --- a/src/draw.c +++ b/src/draw.c @@ -115,9 +115,10 @@ static GLuint get_shader() { } // Init +#ifdef GLES_COMPATIBILITY_LAYER_USE_ES3 GL_FUNC(glGenVertexArrays, void, (GLsizei n, GLuint *arrays)); GL_FUNC(glBindVertexArray, void, (GLuint array)); -GL_FUNC(glActiveTexture, void, (GLenum texture)); +#endif void init_gles_compatibility_layer() { // State _init_gles_compatibility_layer_state(); @@ -126,9 +127,11 @@ void init_gles_compatibility_layer() { reset_variables(); // Setup VAO +#ifdef GLES_COMPATIBILITY_LAYER_USE_ES3 GLuint vao; real_glGenVertexArrays()(1, &vao); real_glBindVertexArray()(vao); +#endif // Load Shader GLuint program = get_shader(); diff --git a/src/passthrough.c b/src/passthrough.c index fdaee30..de77113 100644 --- a/src/passthrough.c +++ b/src/passthrough.c @@ -120,6 +120,7 @@ GL_FUNC(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) { real_glBufferSubData()(target, offset, size, data); } +#ifdef GLES_COMPATIBILITY_LAYER_USE_ES3 GL_FUNC(glGenQueries, void, (GLsizei n, GLuint *ids)); void glGenQueriesARB(GLsizei n, GLuint *ids) { real_glGenQueries()(n, ids); @@ -140,6 +141,7 @@ GL_FUNC(glGetQueryObjectuiv, void, (GLuint id, GLenum pname, GLuint *params)); void glGetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params) { real_glGetQueryObjectuiv()(id, pname, params); } +#endif GL_FUNC(glPixelStorei, void, (GLenum pname, GLint param)); void glPixelStorei(GLenum pname, GLint param) { real_glPixelStorei()(pname, param); diff --git a/src/passthrough.h b/src/passthrough.h index 7966589..c90ec17 100644 --- a/src/passthrough.h +++ b/src/passthrough.h @@ -8,6 +8,18 @@ #include "log.h" +// Testing +#ifdef GLES_COMPATIBILITY_LAYER_TESTING +typedef void (*test_t)(); +extern void add_test(const char *function_name); +#define ADD_TEST(test) \ + __attribute__((constructor)) static void add_##test##_test() { \ + add_test(#test); \ + } +#else +#define ADD_TEST(test) +#endif + // Load GL Function #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) #define GL_APIENTRY __stdcall @@ -26,4 +38,5 @@ } \ } \ return func; \ - } + } \ + ADD_TEST(name) diff --git a/src/shaders/es2/main.fsh b/src/shaders/es2/main.fsh new file mode 100644 index 0000000..8d8fd9d --- /dev/null +++ b/src/shaders/es2/main.fsh @@ -0,0 +1,50 @@ +#version 100 +precision highp float; +// Texture +uniform bool u_has_texture; +uniform sampler2D u_texture_unit; +// Color +varying vec4 v_color; +varying vec4 v_texture_pos; +// Highlight Mode +uniform bool u_highlight_mode; +uniform vec4 u_highlight_mode_color; +// Alpha Test +uniform bool u_alpha_test; +// Fog +uniform bool u_fog; +uniform vec4 u_fog_color; +uniform bool u_fog_is_linear; +uniform float u_fog_start; +uniform float u_fog_end; +varying vec4 v_fog_eye_position; +// Main +void main(void) { + gl_FragColor = v_color; + // Texture + if (u_has_texture) { + vec4 texture_color = texture(u_texture_unit, v_texture_pos.xy); + if (u_highlight_mode) { + texture_color.rgb = u_highlight_mode_color.rgb; + texture_color.a *= u_highlight_mode_color.a; + gl_FragColor = texture_color; + } else { + gl_FragColor *= texture_color; + } + } + // Fog + if (u_fog) { + float fog_factor; + if (u_fog_is_linear) { + fog_factor = (u_fog_end - length(v_fog_eye_position)) / (u_fog_end - u_fog_start); + } else { + fog_factor = exp(-u_fog_start * length(v_fog_eye_position)); + } + fog_factor = clamp(fog_factor, 0.0, 1.0); + gl_FragColor.rgb = mix(gl_FragColor, u_fog_color, 1.0 - fog_factor).rgb; + } + // Alpha Test + if (u_alpha_test && gl_FragColor.a <= 0.1) { + discard; + } +} diff --git a/src/shaders/es2/main.vsh b/src/shaders/es2/main.vsh new file mode 100644 index 0000000..15f7b3c --- /dev/null +++ b/src/shaders/es2/main.vsh @@ -0,0 +1,48 @@ +#version 100 +precision highp float; +// Matrices +uniform mat4 u_projection; +uniform mat4 u_model_view; +uniform mat4 u_texture; +// Texture +attribute vec3 a_vertex_coords; +attribute vec2 a_texture_coords; +varying vec4 v_texture_pos; +// Color +attribute vec4 a_color; +varying vec4 v_color; +// Normal +attribute vec3 a_normal; +uniform float u_normal_rescale_factor; +// Lighting +uniform bool u_lighting; +uniform vec4 u_lighting_ambient; +uniform bool u_lighting_light_source_0; +uniform vec3 u_lighting_light_source_0_position; +uniform vec4 u_lighting_light_source_0_diffuse; +uniform bool u_lighting_light_source_1; +uniform vec3 u_lighting_light_source_1_position; +uniform vec4 u_lighting_light_source_1_diffuse; +// Fog +varying vec4 v_fog_eye_position; +// Main +void main(void) { + v_texture_pos = u_texture * vec4(a_texture_coords.xy, 0.0, 1.0); + gl_Position = u_projection * u_model_view * vec4(a_vertex_coords.xyz, 1.0); + v_color = a_color; + v_fog_eye_position = u_model_view * vec4(a_vertex_coords.xyz, 1.0); + + // Lighting + if (u_lighting) { + vec4 transformed_normal = u_model_view * vec4(a_normal, 0.0) * u_normal_rescale_factor; + vec4 total_light = u_lighting_ambient; + if (u_lighting_light_source_0) { + total_light += max(0.0, dot(transformed_normal.xyz, u_lighting_light_source_0_position)) * u_lighting_light_source_0_diffuse; + } + if (u_lighting_light_source_1) { + total_light += max(0.0, dot(transformed_normal.xyz, u_lighting_light_source_1_position)) * u_lighting_light_source_1_diffuse; + } + total_light = clamp(total_light, 0.0, 1.0); + v_color *= total_light; + } +} diff --git a/src/shaders/main.fsh b/src/shaders/es3/main.fsh similarity index 100% rename from src/shaders/main.fsh rename to src/shaders/es3/main.fsh diff --git a/src/shaders/main.vsh b/src/shaders/es3/main.vsh similarity index 100% rename from src/shaders/main.vsh rename to src/shaders/es3/main.vsh diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +/build diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..688ce8b --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16.0) +project(gles-compatibility-layer-test) + +# Build Library +set(GLES_COMPATIBILITY_LAYER_USE_SDL FALSE CACHE BOOL "" FORCE) +add_subdirectory(.. gles-compatibility-layer) +target_compile_definitions(gles-compatibility-layer PRIVATE GLES_COMPATIBILITY_LAYER_TESTING) + +# Build +add_executable(main src/main.cpp) +target_link_libraries(main gles-compatibility-layer) diff --git a/test/src/main.cpp b/test/src/main.cpp new file mode 100644 index 0000000..7b99c23 --- /dev/null +++ b/test/src/main.cpp @@ -0,0 +1,125 @@ +#include +#include + +#include +#include +#include + +#define GLFW_INCLUDE_NONE +#include + +#include + +#define INFO(format, ...) \ + { \ + fprintf(stderr, "[INFO]: " format "\n", ##__VA_ARGS__); \ + } +#define WARN(format, ...) \ + { \ + fprintf(stderr, "[WARN]: " format "\n", ##__VA_ARGS__); \ + } +#define ERR(format, ...) \ + { \ + fprintf(stderr, "[ERR]: (%s:%i): " format "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ + exit(EXIT_FAILURE); \ + } + +// Tests +#define MAX_TESTS 255 +static const char *tests[MAX_TESTS] = { 0 }; +extern "C" void add_test(const char *function_name) { + static int i = 0; + if (i >= MAX_TESTS) { + ERR("Too Many Tests!"); + } + tests[i++] = function_name; +} + +// Header Files +static std::vector header_lines; +static void load_header(std::string filename) { + std::ifstream file(filename); + if (!file.good()) { + ERR("Unable To Load Header: %s", filename.c_str()); + } + std::string line; + while (std::getline(file, line)) { + header_lines.push_back(line); + } + file.close(); +} +static void load_headers() { + header_lines.clear(); +#ifdef GLES_COMPATIBILITY_LAYER_USE_ES3 + load_header("/usr/include/GLES3/gl3.h"); +#else + load_header("/usr/include/GLES2/gl2.h"); +#endif +} + +// Run Test +static void run_test(std::string function_name) { + // Log + INFO("Checking %s...", function_name.c_str()); + + // Search Headers + std::string check_str = " " + function_name + " "; + bool found = false; + for (std::string line : header_lines) { + if (line.find(check_str) != std::string::npos) { + // Found + found = true; + break; + } + } + if (!found) { + ERR("Unable To Find Function"); + } +} + +// Handle GLFW Error +static void glfw_error(__attribute__((unused)) int error, const char *description) { + WARN("GLFW Error: %s", description); +} + +int main() { + INFO("Starting Test"); + + // Init GLFW + glfwSetErrorCallback(glfw_error); + if (!glfwInit()) { + ERR("Unable To Initialize GLFW"); + } + + // Create OpenGL ES Context + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); +#ifdef GLES_COMPATIBILITY_LAYER_USE_ES3 + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); +#else + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); +#endif + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + // Create Window + GLFWwindow *glfw_window = glfwCreateWindow(640, 480, "Test Window", NULL, NULL); + if (!glfw_window) { + ERR("Unable To Create GLFW Window"); + } + + // Make Window Context Current + glfwMakeContextCurrent(glfw_window); + + // Setup Compatibility Layer + init_gles_compatibility_layer(); + + // Run Tests + load_headers(); + for (int i = 0; tests[i] != NULL; i++) { + run_test(tests[i]); + } + + // Exit + glfwDestroyWindow(glfw_window); + glfwTerminate(); + INFO("Test Complete"); +}