diff --git a/CMake/Packages/FindOpenGLES2.cmake b/CMake/Packages/FindOpenGLES2.cmake new file mode 100644 index 000000000..fae333b32 --- /dev/null +++ b/CMake/Packages/FindOpenGLES2.cmake @@ -0,0 +1,32 @@ +# FindOpenGLES +# ------------ +# Finds the OpenGLES2 library +# +# This will define the following variables:: +# +# OPENGLES2_FOUND - system has OpenGLES +# OPENGLES2_INCLUDE_DIRS - the OpenGLES include directory +# OPENGLES2_LIBRARIES - the OpenGLES libraries + +if(NOT HINT_GLES_LIBNAME) + set(HINT_GLES_LIBNAME GLESv2) +endif() + +find_path(OPENGLES2_INCLUDE_DIR GLES3/gl3.h + PATHS "${CMAKE_FIND_ROOT_PATH}/usr/include" + HINTS ${HINT_GLES_INCDIR} +) + +find_library(OPENGLES2_gl_LIBRARY + NAMES ${HINT_GLES_LIBNAME} + HINTS ${HINT_GLES_LIBDIR} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OpenGLES2 REQUIRED_VARS OPENGLES2_gl_LIBRARY OPENGLES2_INCLUDE_DIR) + +if(OPENGLES2_FOUND) + set(OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY}) + set(OPENGLES2_INCLUDE_DIRS ${OPENGLES2_INCLUDE_DIR}) + mark_as_advanced(OPENGLES2_INCLUDE_DIR OPENGLES2_gl_LIBRARY) +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index fcccfcb3d..b280f9db9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,8 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake/Utils # Define the options. option(GL "Set to ON if targeting Desktop OpenGL" ${GL}) -option(GLES "Set to ON if targeting Embedded OpenGL" ${GLES}) +option(GLES1 "Set to ON if targeting OpenGL ES 1.0" ${GLES1}) +option(GLES "Set to ON if targeting OpenGL ES 2.0" ${GLES}) option(RPI "Set to ON to enable Raspberry Pi specific build" ${RPI}) option(BUNDLED_CERTS "Set to ON to use bundled TLS/SSL certificates" ${BUNDLED_CERTS}) option(CEC "Set to ON to enable CEC" ${CEC}) @@ -62,7 +63,7 @@ endif() #--------------------------------------------------------------------------------------------------- # OpenGL setup. -if(GLES) +if(GLES1 OR GLES) set(GLSYSTEM "Embedded OpenGL" CACHE STRING "The OpenGL system to be used") else() set(GLSYSTEM "Desktop OpenGL" CACHE STRING "The OpenGL system to be used") @@ -73,7 +74,7 @@ set_property(CACHE GLSYSTEM PROPERTY STRINGS "Desktop OpenGL" "Embedded OpenGL") #--------------------------------------------------------------------------------------------------- # Raspberry Pi setup. -# Raspberry Pi OS 32-bit (armv7l) +# Raspberry Pi OS 32-bit (armv7l). if(EXISTS "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/bcm_host.h") set(RPI ON) set(RPI_32 ON) @@ -81,7 +82,7 @@ if(EXISTS "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/bcm_host.h") message("-- Building on a Raspberry Pi (32-bit OS)") endif() -# Raspberry Pi OS 64-bit (aarch64) +# Raspberry Pi OS 64-bit (aarch64). if(EXISTS "/usr/include/bcm_host.h") set(RPI ON) set(RPI_64 ON) @@ -89,14 +90,21 @@ if(EXISTS "/usr/include/bcm_host.h") message("-- Building on a Raspberry Pi (64-bit OS)") endif() +# On the Raspberry Pi, desktop OpenGL 2.1 or OpenGL ES 2.0 should be used. +if(GLES1 AND RPI) + message(FATAL_ERROR "-- The OpenGL ES 1.0 renderer is not supported on Raspberry Pi") +endif() + #--------------------------------------------------------------------------------------------------- # Package dependencies. if(GLSYSTEM MATCHES "Desktop OpenGL") set(OpenGL_GL_PREFERENCE "GLVND") find_package(OpenGL REQUIRED) -else() +elseif(GLES1) find_package(OpenGLES REQUIRED) +elseif(GLES) + find_package(OpenGLES2 REQUIRED) endif() # On macOS and Windows all dependencies are kept in-tree in the "external" directory. @@ -258,8 +266,10 @@ endif() if(GLSYSTEM MATCHES "Desktop OpenGL") add_definitions(-DUSE_OPENGL_21) -else() +elseif(GLES1) add_definitions(-DUSE_OPENGLES_10) +elseif(GLES) + add_definitions(-DUSE_OPENGLES_20) endif() if(RPI) @@ -456,18 +466,13 @@ if(BCMHOST) endif() endif() -# Note: Building with GLES support on the Raspberry Pi currently seems to be broken. -if(GLES AND RPI_32) - list(APPEND COMMON_LIBRARIES brcmEGL ${OPENGLES_LIBRARIES}) -elseif(GLES AND RPI_64) - list(APPEND COMMON_LIBRARIES ${OPENGLES_LIBRARIES}) -endif() - # OpenGL. if(GLSYSTEM MATCHES "Desktop OpenGL") list(APPEND COMMON_LIBRARIES ${OPENGL_LIBRARIES}) -else() +elseif(GLES1) list(APPEND COMMON_LIBRARIES EGL ${OPENGLES_LIBRARIES}) +elseif(GLES) + list(APPEND COMMON_LIBRARIES ${OPENGLES2_LIBRARIES}) endif() #--------------------------------------------------------------------------------------------------- diff --git a/es-core/CMakeLists.txt b/es-core/CMakeLists.txt index 5d440c124..d028db4dd 100644 --- a/es-core/CMakeLists.txt +++ b/es-core/CMakeLists.txt @@ -146,6 +146,7 @@ set(CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer_GL21.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer_GLES10.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer_GLES20.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Shader_GL21.cpp # Resources diff --git a/es-core/src/components/LottieComponent.cpp b/es-core/src/components/LottieComponent.cpp index b6453d08f..b1cb43d35 100644 --- a/es-core/src/components/LottieComponent.cpp +++ b/es-core/src/components/LottieComponent.cpp @@ -42,7 +42,7 @@ LottieComponent::LottieComponent(Window* window) { // Get an empty texture for rendering the animation. mTexture = TextureResource::get(""); -#if defined(USE_OPENGLES_10) +#if defined(USE_OPENGLES_10) || defined(USE_OPENGLES_20) // This is not really supported by the OpenGL ES standard so hopefully it works // with all drivers and on all operating systems. mTexture->setFormat(Renderer::Texture::BGRA); diff --git a/es-core/src/renderers/Renderer_GLES20.cpp b/es-core/src/renderers/Renderer_GLES20.cpp new file mode 100644 index 000000000..62a72a024 --- /dev/null +++ b/es-core/src/renderers/Renderer_GLES20.cpp @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// Renderer_GLES20.cpp +// +// OpenGL ES 2.0 rendering functions. +// + +#if defined(USE_OPENGLES_20) + +#include "Log.h" +#include "Settings.h" +#include "renderers/Renderer.h" +#include "utils/StringUtil.h" + +#include +#include + +namespace Renderer +{ + static SDL_GLContext sdlContext{nullptr}; + static glm::mat4 projectionMatrix{getIdentity()}; + glm::mat4 worldViewMatrix{getIdentity()}; + static GLuint shaderProgram{0}; + static GLint mvpUniform{0}; + static GLint texAttrib{0}; + static GLint colAttrib{0}; + static GLint posAttrib{0}; + static GLuint vertexBuffer{0}; + static GLuint whiteTexture{0}; + + static void setupShaders() + { + // Vertex shader. + const GLchar* vertexSource{"uniform mat4 u_mvp; \n" + "attribute vec2 a_pos; \n" + "attribute vec2 a_tex; \n" + "attribute vec4 a_col; \n" + "varying vec2 v_tex; \n" + "varying vec4 v_col; \n" + "void main(void) \n" + "{ \n" + " gl_Position = u_mvp * vec4(a_pos.xy, 0.0, 1.0); \n" + " v_tex = a_tex; \n" + " v_col = a_col; \n" + "} \n"}; + + const GLuint vertexShader{glCreateShader(GL_VERTEX_SHADER)}; + GL_CHECK_ERROR(glShaderSource(vertexShader, 1, &vertexSource, nullptr)); + GL_CHECK_ERROR(glCompileShader(vertexShader)); + + { + GLint isCompiled{GL_FALSE}; + GLint maxLength{0}; + + GL_CHECK_ERROR(glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &isCompiled)); + GL_CHECK_ERROR(glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength)); + + if (maxLength > 1) { + std::string infoLog(maxLength + 1, 0); + + GL_CHECK_ERROR( + glGetShaderInfoLog(vertexShader, maxLength, &maxLength, &infoLog[0])); + if (isCompiled == GL_FALSE) { + LOG(LogError) << "GLSL Vertex Compile Error\n" << &infoLog[0]; + } + else { + if (Utils::String::toUpper(infoLog).find("WARNING") != std::string::npos) { + LOG(LogWarning) << "GLSL Vertex Compile Warning\n" << infoLog; + } + else { + LOG(LogInfo) << "GLSL Vertex Compile Message\n" << infoLog; + } + } + } + } + + // Fragment shader. + const GLchar* fragmentSource{"precision highp float; \n" + "uniform sampler2D u_tex; \n" + "varying vec2 v_tex; \n" + "varying vec4 v_col; \n" + "void main(void) \n" + "{ \n" + " gl_FragColor = texture2D(u_tex, v_tex) * v_col; \n" + "} \n"}; + + const GLuint fragmentShader{glCreateShader(GL_FRAGMENT_SHADER)}; + GL_CHECK_ERROR(glShaderSource(fragmentShader, 1, &fragmentSource, nullptr)); + GL_CHECK_ERROR(glCompileShader(fragmentShader)); + + { + GLint isCompiled{GL_FALSE}; + GLint maxLength{0}; + + GL_CHECK_ERROR(glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &isCompiled)); + GL_CHECK_ERROR(glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength)); + + if (maxLength > 1) { + std::string infoLog(maxLength + 1, 0); + + GL_CHECK_ERROR( + glGetShaderInfoLog(fragmentShader, maxLength, &maxLength, &infoLog[0])); + + if (isCompiled == GL_FALSE) { + LOG(LogError) << "GLSL Fragment Compile Error\n" << infoLog; + } + else { + if (Utils::String::toUpper(infoLog).find("WARNING") != std::string::npos) { + LOG(LogWarning) << "GLSL Fragment Compile Warning\n" << infoLog; + } + else { + LOG(LogInfo) << "GLSL Fragment Compile Message\n" << infoLog; + } + } + } + } + + // Shader program. + shaderProgram = glCreateProgram(); + GL_CHECK_ERROR(glAttachShader(shaderProgram, vertexShader)); + GL_CHECK_ERROR(glAttachShader(shaderProgram, fragmentShader)); + GL_CHECK_ERROR(glLinkProgram(shaderProgram)); + + { + GLint isCompiled{GL_FALSE}; + GLint maxLength{0}; + + GL_CHECK_ERROR(glGetProgramiv(shaderProgram, GL_LINK_STATUS, &isCompiled)); + GL_CHECK_ERROR(glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &maxLength)); + + if (maxLength > 1) { + std::string infoLog(maxLength + 1, 0); + + GL_CHECK_ERROR( + glGetProgramInfoLog(shaderProgram, maxLength, &maxLength, &infoLog[0])); + + if (isCompiled == GL_FALSE) { + LOG(LogError) << "GLSL Link Error\n" << infoLog; + } + else { + if (Utils::String::toUpper(infoLog).find("WARNING") != std::string::npos) { + LOG(LogWarning) << "GLSL Link Warning\n" << infoLog; + } + else { + LOG(LogInfo) << "GLSL Link Message\n" << infoLog; + } + } + } + } + + GL_CHECK_ERROR(glUseProgram(shaderProgram)); + + mvpUniform = glGetUniformLocation(shaderProgram, "u_mvp"); + posAttrib = glGetAttribLocation(shaderProgram, "a_pos"); + texAttrib = glGetAttribLocation(shaderProgram, "a_tex"); + colAttrib = glGetAttribLocation(shaderProgram, "a_col"); + GLint texUniform = glGetUniformLocation(shaderProgram, "u_tex"); + GL_CHECK_ERROR(glEnableVertexAttribArray(posAttrib)); + GL_CHECK_ERROR(glEnableVertexAttribArray(texAttrib)); + GL_CHECK_ERROR(glEnableVertexAttribArray(colAttrib)); + GL_CHECK_ERROR(glUniform1i(texUniform, 0)); + } + + static void setupVertexBuffer() + { + GL_CHECK_ERROR(glGenBuffers(1, &vertexBuffer)); + GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer)); + } + + static GLenum convertBlendFactor(const Blend::Factor blendFactor) + { + // clang-format off + switch (blendFactor) { + case Blend::ZERO: { return GL_ZERO; } break; + case Blend::ONE: { return GL_ONE; } break; + case Blend::SRC_COLOR: { return GL_SRC_COLOR; } break; + case Blend::ONE_MINUS_SRC_COLOR: { return GL_ONE_MINUS_SRC_COLOR; } break; + case Blend::SRC_ALPHA: { return GL_SRC_ALPHA; } break; + case Blend::ONE_MINUS_SRC_ALPHA: { return GL_ONE_MINUS_SRC_ALPHA; } break; + case Blend::DST_COLOR: { return GL_DST_COLOR; } break; + case Blend::ONE_MINUS_DST_COLOR: { return GL_ONE_MINUS_DST_COLOR; } break; + case Blend::DST_ALPHA: { return GL_DST_ALPHA; } break; + case Blend::ONE_MINUS_DST_ALPHA: { return GL_ONE_MINUS_DST_ALPHA; } break; + default: { return GL_ZERO; } + } + // clang-format on + } + + static GLenum convertTextureType(const Texture::Type type) + { + // clang-format off + switch (type) { + case Texture::RGBA: { return GL_RGBA; } break; + case Texture::BGRA: { return GL_BGRA_EXT; } break; + case Texture::ALPHA: { return GL_LUMINANCE_ALPHA; } break; + default: { return GL_ZERO; } + } + // clang-format on + } + + void setupWindow() + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); + } + + bool createContext() + { + sdlContext = SDL_GL_CreateContext(getSDLWindow()); + SDL_GL_MakeCurrent(getSDLWindow(), sdlContext); + + std::string vendor = + glGetString(GL_VENDOR) ? reinterpret_cast(glGetString(GL_VENDOR)) : ""; + std::string renderer = + glGetString(GL_RENDERER) ? reinterpret_cast(glGetString(GL_RENDERER)) : ""; + std::string version = + glGetString(GL_VERSION) ? reinterpret_cast(glGetString(GL_VERSION)) : ""; + std::string extensions = glGetString(GL_EXTENSIONS) ? + reinterpret_cast(glGetString(GL_EXTENSIONS)) : + ""; + + LOG(LogInfo) << "GL vendor: " << vendor; + LOG(LogInfo) << "GL renderer: " << renderer; + LOG(LogInfo) << "GL version: " << version; + LOG(LogInfo) << "EmulationStation renderer: OpenGL ES 2.0"; + LOG(LogInfo) << "Checking available OpenGL ES extensions..."; + std::string glExts = glGetString(GL_EXTENSIONS) ? + reinterpret_cast(glGetString(GL_EXTENSIONS)) : + ""; + LOG(LogInfo) << "GL_OES_texture_npot: " + << (extensions.find("GL_OES_texture_npot") != std::string::npos ? "OK" : + "MISSING"); + + setupShaders(); + setupVertexBuffer(); + + uint8_t data[4] = {255, 255, 255, 255}; + whiteTexture = createTexture(Texture::RGBA, Texture::RGBA, false, false, true, 1, 1, data); + + GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + GL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0)); + GL_CHECK_ERROR(glEnable(GL_BLEND)); + GL_CHECK_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, 1)); + GL_CHECK_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + + return true; + } + + void destroyContext() + { + SDL_GL_DeleteContext(sdlContext); + sdlContext = nullptr; + } + + unsigned int createTexture(const Texture::Type type, + const Texture::Type format, + const bool linearMinify, + const bool linearMagnify, + const bool repeat, + const unsigned int width, + const unsigned int height, + void* data) + { + const GLenum textureType{convertTextureType(type)}; + unsigned int texture; + + GL_CHECK_ERROR(glGenTextures(1, &texture)); + GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); + + // Not sure why the corresponding variables are missing in the OpenGL ES include files + // when specifying the values manually seems to work with all graphics drivers. + int _GL_TEXTURE_SWIZZLE_R{0x8E42}; + int _GL_TEXTURE_SWIZZLE_B{0x8E44}; + int _GL_RED{0x1903}; + int _GL_BLUE{0x1905}; + + // Convert from BGRA to RGBA. + if (format == Texture::Type::BGRA) { + glTexParameteri(GL_TEXTURE_2D, _GL_TEXTURE_SWIZZLE_B, _GL_RED); + glTexParameteri(GL_TEXTURE_2D, _GL_TEXTURE_SWIZZLE_R, _GL_BLUE); + } + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE)); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE)); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + linearMinify ? GL_LINEAR : GL_NEAREST)); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + linearMagnify ? GL_LINEAR : GL_NEAREST)); + + // Regular GL_ALPHA textures are black + alpha when used in shaders, so create a + // GL_LUMINANCE_ALPHA texture instead so it's white + alpha. + if (textureType == GL_LUMINANCE_ALPHA) { + uint8_t* a_data{reinterpret_cast(data)}; + uint8_t* la_data{new uint8_t[width * height * 2]}; + for (uint32_t i = 0; i < (width * height); ++i) { + la_data[(i * 2) + 0] = 255; + la_data[(i * 2) + 1] = a_data ? a_data[i] : 255; + } + + GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, + textureType, GL_UNSIGNED_BYTE, la_data)); + + delete[] la_data; + } + else { + GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, + textureType, GL_UNSIGNED_BYTE, data)); + } + + return texture; + } + + void destroyTexture(const unsigned int _texture) + { + GL_CHECK_ERROR(glDeleteTextures(1, &_texture)); + } + + void updateTexture(const unsigned int texture, + const Texture::Type type, + const unsigned int x, + const unsigned int y, + const unsigned int width, + const unsigned int height, + void* data) + { + const GLenum textureType = convertTextureType(type); + + GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); + + // Regular GL_ALPHA textures are black + alpha when used in shaders, so create a + // GL_LUMINANCE_ALPHA texture instead so it's white + alpha. + if (textureType == GL_LUMINANCE_ALPHA) { + uint8_t* a_data{reinterpret_cast(data)}; + uint8_t* la_data{new uint8_t[width * height * 2]}; + for (uint32_t i = 0; i < (width * height); ++i) { + la_data[(i * 2) + 0] = 255; + la_data[(i * 2) + 1] = a_data ? a_data[i] : 255; + } + + GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, textureType, + GL_UNSIGNED_BYTE, la_data)); + + delete[] la_data; + } + else { + GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, textureType, + GL_UNSIGNED_BYTE, data)); + } + + GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, whiteTexture)); + } + + void bindTexture(const unsigned int texture) + { + if (texture == 0) + GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, whiteTexture)); + else + GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); + } + + void drawLines(const Vertex* vertices, + const unsigned int numVertices, + const Blend::Factor srcBlendFactor, + const Blend::Factor dstBlendFactor) + { + GL_CHECK_ERROR(glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, pos)))); + GL_CHECK_ERROR(glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, tex)))); + GL_CHECK_ERROR(glVertexAttribPointer(colAttrib, 4, GL_UNSIGNED_BYTE, GL_TRUE, + sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, col)))); + + GL_CHECK_ERROR( + glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, GL_DYNAMIC_DRAW)); + GL_CHECK_ERROR( + glBlendFunc(convertBlendFactor(srcBlendFactor), convertBlendFactor(dstBlendFactor))); + + GL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, numVertices)); + } + + void drawTriangleStrips(const Vertex* vertices, + const unsigned int numVertices, + const glm::mat4& trans, + const Blend::Factor srcBlendFactor, + const Blend::Factor dstBlendFactor, + const shaderParameters& parameters) + { + GL_CHECK_ERROR(glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, pos)))); + GL_CHECK_ERROR(glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, tex)))); + GL_CHECK_ERROR(glVertexAttribPointer(colAttrib, 4, GL_UNSIGNED_BYTE, GL_TRUE, + sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, col)))); + + GL_CHECK_ERROR( + glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, GL_DYNAMIC_DRAW)); + GL_CHECK_ERROR( + glBlendFunc(convertBlendFactor(srcBlendFactor), convertBlendFactor(dstBlendFactor))); + + GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); + } + + void setProjection(const glm::mat4& projection) + { + projectionMatrix = projection; + + glm::mat4 mvpMatrix{projectionMatrix * worldViewMatrix}; + GL_CHECK_ERROR( + glUniformMatrix4fv(mvpUniform, 1, GL_FALSE, reinterpret_cast(&mvpMatrix))); + } + + void setMatrix(const glm::mat4& matrix) + { + worldViewMatrix = matrix; + worldViewMatrix[3] = glm::round(worldViewMatrix[3]); + + glm::mat4 mvpMatrix{projectionMatrix * worldViewMatrix}; + GL_CHECK_ERROR( + glUniformMatrix4fv(mvpUniform, 1, GL_FALSE, reinterpret_cast(&mvpMatrix))); + } + + void setViewport(const Rect& viewport) + { + // glViewport starts at the bottom left of the window. + GL_CHECK_ERROR(glViewport(viewport.x, getWindowHeight() - viewport.y - viewport.h, + viewport.w, viewport.h)); + } + + void setScissor(const Rect& scissor) + { + if ((scissor.x == 0) && (scissor.y == 0) && (scissor.w == 0) && (scissor.h == 0)) { + GL_CHECK_ERROR(glDisable(GL_SCISSOR_TEST)); + } + else { + // glScissor starts at the bottom left of the window. + GL_CHECK_ERROR(glScissor(scissor.x, getWindowHeight() - scissor.y - scissor.h, + scissor.w, scissor.h)); + GL_CHECK_ERROR(glEnable(GL_SCISSOR_TEST)); + } + } + + void setSwapInterval() + { + if (Settings::getInstance()->getBool("VSync")) { + // Adaptive VSync seems to be nonfunctional or having issues on some hardware + // and drivers, so only attempt to apply regular VSync. + if (SDL_GL_SetSwapInterval(1) == 0) { + LOG(LogInfo) << "Enabling VSync..."; + } + else { + LOG(LogWarning) << "Could not enable VSync: " << SDL_GetError(); + } + } + else { + SDL_GL_SetSwapInterval(0); + LOG(LogInfo) << "Disabling VSync..."; + } + } + + void swapBuffers() + { + SDL_GL_SwapWindow(getSDLWindow()); + GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + } + +} // namespace Renderer + +#endif // USE_OPENGLES_20