diff --git a/.gitmodules b/.gitmodules index db27b12a..0ba0f4e4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,6 @@ [submodule "extlib/cairo"] path = extlib/cairo url = https://github.com/solvespace/cairo +[submodule "extlib/angle"] + path = extlib/angle + url = https://github.com/solvespace/angle diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b1d50a8..cee03758 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,8 @@ set(ENABLE_COVERAGE OFF CACHE BOOL set(ENABLE_SANITIZERS OFF CACHE BOOL "Whether to enable Clang's AddressSanitizer and UndefinedBehaviorSanitizer") +set(OPENGL 2 CACHE STRING "OpenGL version to use (one of: 1 2)") + if(NOT WIN32 AND NOT APPLE) set(GUI gtk2 CACHE STRING "GUI toolkit to use (one of: gtk2 gtk3)") endif() @@ -61,6 +63,11 @@ endif() if(MINGW) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") + + if(TRIPLE STREQUAL "i686-w64-mingw32") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2") + endif() endif() if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") @@ -88,8 +95,6 @@ endif() # dependencies -find_package(OpenGL REQUIRED) - message(STATUS "Using in-tree libdxfrw") add_subdirectory(extlib/libdxfrw) @@ -116,6 +121,20 @@ if(WIN32) PNG_PNG_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/libpng) list(APPEND PNG_PNG_INCLUDE_DIR ${CMAKE_BINARY_DIR}/extlib/libpng) + if(OPENGL STREQUAL "2") + message(STATUS "Using in-tree ANGLE") + set(ANGLE_STATIC ON CACHE INTERNAL "") + set(ANGLE_ENABLE_D3D9 ON CACHE INTERNAL "") + set(ANGLE_ENABLE_D3D11 ON CACHE INTERNAL "") + set(ANGLE_ENABLE_OPENGL OFF CACHE INTERNAL "") + set(ANGLE_ENABLE_ESSL OFF CACHE INTERNAL "") + set(ANGLE_ENABLE_GLSL OFF CACHE INTERNAL "") + set(ANGLE_ENABLE_HLSL ON CACHE INTERNAL "") + add_vendored_subdirectory(extlib/angle) + set(OPENGL_LIBRARIES EGL GLESv2) + set(OPENGL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/angle/include) + endif() + if(ENABLE_TESTS) message(STATUS "Using in-tree pixman") add_vendored_subdirectory(extlib/pixman) @@ -146,6 +165,7 @@ elseif(APPLE) find_package(ZLIB REQUIRED) find_package(PNG REQUIRED) find_package(Freetype REQUIRED) + find_package(OpenGL REQUIRED) if(ENABLE_TESTS) find_library(CAIRO_LIBRARIES cairo REQUIRED) @@ -162,6 +182,7 @@ else() # Linux and compatible systems find_package(ZLIB REQUIRED) find_package(PNG REQUIRED) find_package(Freetype REQUIRED) + find_package(OpenGL REQUIRED) if(ENABLE_TESTS) pkg_check_modules(CAIRO REQUIRED cairo) diff --git a/appveyor.yml b/appveyor.yml index 3296c071..8d839946 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.1.{build} +version: 3.0.{build} clone_depth: 1 before_build: - git submodule update --init @@ -6,7 +6,7 @@ before_build: - cd build - set tag=x%APPVEYOR_REPO_TAG_NAME% - if %tag:~,2% == xv (set BUILD_TYPE=RelWithDebInfo) else (set BUILD_TYPE=Debug) - - cmake -G"Visual Studio 12" -T v120_xp .. + - cmake -G"Visual Studio 12" -T v120 .. build_script: - msbuild "src\solvespace.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" - msbuild "test\solvespace_testsuite.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" diff --git a/cmake/FindVendoredPackage.cmake b/cmake/FindVendoredPackage.cmake index 78da0e8b..17166119 100644 --- a/cmake/FindVendoredPackage.cmake +++ b/cmake/FindVendoredPackage.cmake @@ -21,7 +21,7 @@ function(find_vendored_package PKG_NAME PKG_PATH) if(NOT cfg_name) set(cfg_name ${item}) else() - set(${cfg_name} ${item}) + set(${cfg_name} ${item} CACHE INTERNAL "") set(cfg_name) endif() endforeach() diff --git a/extlib/angle b/extlib/angle new file mode 160000 index 00000000..bbe1370b --- /dev/null +++ b/extlib/angle @@ -0,0 +1 @@ +Subproject commit bbe1370b860d94583e5b825dbd72c731f51ff8f0 diff --git a/res/shaders/edge.frag b/res/shaders/edge.frag index 37281d74..87951e2b 100644 --- a/res/shaders/edge.frag +++ b/res/shaders/edge.frag @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - const float feather = 0.5; uniform vec4 color; diff --git a/res/shaders/edge.vert b/res/shaders/edge.vert index 3dfb9b1b..902bf037 100644 --- a/res/shaders/edge.vert +++ b/res/shaders/edge.vert @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - const float feather = 0.5; attribute vec3 pos; diff --git a/res/shaders/imesh.frag b/res/shaders/imesh.frag index c3dbff1e..d4a41601 100644 --- a/res/shaders/imesh.frag +++ b/res/shaders/imesh.frag @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - uniform vec4 color; void main() { diff --git a/res/shaders/imesh.vert b/res/shaders/imesh.vert index c32dcfee..0c6742d7 100644 --- a/res/shaders/imesh.vert +++ b/res/shaders/imesh.vert @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - attribute vec3 pos; uniform mat4 modelview; diff --git a/res/shaders/imesh_point.frag b/res/shaders/imesh_point.frag index edb31206..799bfba5 100644 --- a/res/shaders/imesh_point.frag +++ b/res/shaders/imesh_point.frag @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - const float feather = 0.5; uniform vec4 color; diff --git a/res/shaders/imesh_point.vert b/res/shaders/imesh_point.vert index 3a3eb774..f32f24d7 100644 --- a/res/shaders/imesh_point.vert +++ b/res/shaders/imesh_point.vert @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - const float feather = 0.5; attribute vec3 pos; diff --git a/res/shaders/imesh_tex.frag b/res/shaders/imesh_tex.frag index 4113f7ad..bb347848 100644 --- a/res/shaders/imesh_tex.frag +++ b/res/shaders/imesh_tex.frag @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - uniform vec4 color; uniform sampler2D texture; diff --git a/res/shaders/imesh_tex.vert b/res/shaders/imesh_tex.vert index 1ec88d14..d40dd958 100644 --- a/res/shaders/imesh_tex.vert +++ b/res/shaders/imesh_tex.vert @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - attribute vec3 pos; attribute vec2 tex; diff --git a/res/shaders/imesh_texa.frag b/res/shaders/imesh_texa.frag index 4cfa2a4f..85359eec 100644 --- a/res/shaders/imesh_texa.frag +++ b/res/shaders/imesh_texa.frag @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - uniform vec4 color; uniform sampler2D texture; diff --git a/res/shaders/mesh.frag b/res/shaders/mesh.frag index 18de9b74..bfd78bf4 100644 --- a/res/shaders/mesh.frag +++ b/res/shaders/mesh.frag @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - uniform vec3 lightDir0; uniform vec3 lightDir1; uniform float lightInt0; diff --git a/res/shaders/mesh.vert b/res/shaders/mesh.vert index c06375ae..7aa8efb9 100644 --- a/res/shaders/mesh.vert +++ b/res/shaders/mesh.vert @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - attribute vec3 pos; attribute vec3 nor; attribute vec4 col; diff --git a/res/shaders/mesh_fill.frag b/res/shaders/mesh_fill.frag index 09658621..1a05f160 100644 --- a/res/shaders/mesh_fill.frag +++ b/res/shaders/mesh_fill.frag @@ -3,12 +3,10 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - uniform vec4 color; uniform sampler2D texture; void main() { - if(texture2D(texture, gl_FragCoord.xy / 32.0f).a < 0.5) discard; + if(texture2D(texture, gl_FragCoord.xy / 32.0).a < 0.5) discard; gl_FragColor = color; } diff --git a/res/shaders/mesh_fill.vert b/res/shaders/mesh_fill.vert index 83abcbd8..e7ebc251 100644 --- a/res/shaders/mesh_fill.vert +++ b/res/shaders/mesh_fill.vert @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - attribute vec3 pos; uniform mat4 modelview; diff --git a/res/shaders/outline.vert b/res/shaders/outline.vert index 36141d2c..4de3e879 100644 --- a/res/shaders/outline.vert +++ b/res/shaders/outline.vert @@ -3,8 +3,6 @@ // // Copyright 2016 Aleksey Egorov //----------------------------------------------------------------------------- -#version 120 - const int EMPHASIZED_AND_CONTOUR = 0; const int EMPHASIZED_WITHOUT_CONTOUR = 1; const int CONTOUR_ONLY = 2; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ff782ee7..0cef9f6e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,13 +70,15 @@ if(SPACEWARE_FOUND) ${SPACEWARE_INCLUDE_DIR}) endif() -if(WIN32) - set(gl_SOURCES - render/rendergl1.cpp) -else() +if(OPENGL STREQUAL 2) set(gl_SOURCES render/gl2shader.cpp render/rendergl2.cpp) +elseif(OPENGL STREQUAL 1) + set(gl_SOURCES + render/rendergl1.cpp) +else() + message(FATAL_ERROR "Unsupported OpenGL version ${OPENGL}") endif() if(WIN32) diff --git a/src/platform/w32main.cpp b/src/platform/w32main.cpp index 2b41b6a6..cbca989a 100644 --- a/src/platform/w32main.cpp +++ b/src/platform/w32main.cpp @@ -22,16 +22,23 @@ # undef uint32_t // thanks but no thanks #endif +#define EGLAPI /*static linkage*/ +#include + HINSTANCE Instance; HWND TextWnd; HWND TextWndScrollBar; HWND TextEditControl; -HGLRC TextGl; +EGLDisplay TextGlDisplay; +EGLSurface TextGlSurface; +EGLContext TextGlContext; HWND GraphicsWnd; -HGLRC GraphicsGl; HWND GraphicsEditControl; +EGLDisplay GraphicsGlDisplay; +EGLSurface GraphicsGlSurface; +EGLContext GraphicsGlContext; static struct { int x, y; } LastMousePos; @@ -426,16 +433,16 @@ void SolveSpace::SetMousePointerToHand(bool yes) { SetCursor(LoadCursor(NULL, yes ? IDC_HAND : IDC_ARROW)); } -static void PaintTextWnd(HDC hdc) +static void PaintTextWnd() { - wglMakeCurrent(GetDC(TextWnd), TextGl); + eglMakeCurrent(TextGlDisplay, TextGlSurface, TextGlSurface, TextGlContext); SS.TW.Paint(); - SwapBuffers(GetDC(TextWnd)); + eglSwapBuffers(TextGlDisplay, TextGlSurface); // Leave the graphics window context active, except when we're painting // this text window. - wglMakeCurrent(GetDC(GraphicsWnd), GraphicsGl); + eglMakeCurrent(GraphicsGlDisplay, GraphicsGlSurface, GraphicsGlSurface, GraphicsGlContext); } void SolveSpace::MoveTextScrollbarTo(int pos, int maxPos, int page) @@ -530,7 +537,7 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_PAINT: { // Actually paint the text window, with gl. - PaintTextWnd(GetDC(TextWnd)); + PaintTextWnd(); // And then just make Windows happy. PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); @@ -731,37 +738,48 @@ void SolveSpace::ShowTextWindow(bool visible) const bool SolveSpace::FLIP_FRAMEBUFFER = false; -static void CreateGlContext(HWND hwnd, HGLRC *glrc) -{ - HDC hdc = GetDC(hwnd); +static void CreateGlContext(HWND hwnd, EGLDisplay *eglDisplay, EGLSurface *eglSurface, + EGLContext *eglContext) { + ssassert(eglBindAPI(EGL_OPENGL_ES_API), "Cannot bind EGL API"); - PIXELFORMATDESCRIPTOR pfd = {}; - int pixelFormat; + *eglDisplay = eglGetDisplay(GetDC(hwnd)); + ssassert(eglInitialize(*eglDisplay, NULL, NULL), "Cannot initialize EGL"); - pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | - PFD_DOUBLEBUFFER; - pfd.dwLayerMask = PFD_MAIN_PLANE; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 32; - pfd.cDepthBits = 24; - pfd.cAccumBits = 0; - pfd.cStencilBits = 0; + EGLint configAttributes[] = { + EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + EGLint numConfigs; + EGLConfig windowConfig; + ssassert(eglChooseConfig(*eglDisplay, configAttributes, &windowConfig, 1, &numConfigs), + "Cannot choose EGL configuration"); - pixelFormat = ChoosePixelFormat(hdc, &pfd); - ssassert(pixelFormat != 0, "Expected a valid pixel format to be chosen"); + EGLint surfaceAttributes[] = { + EGL_NONE + }; + *eglSurface = eglCreateWindowSurface(*eglDisplay, windowConfig, hwnd, surfaceAttributes); + ssassert(eglSurface != EGL_NO_SURFACE, "Cannot create EGL window surface"); - ssassert(SetPixelFormat(hdc, pixelFormat, &pfd), "Cannot set pixel format"); + EGLint contextAttributes[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + *eglContext = eglCreateContext(*eglDisplay, windowConfig, NULL, contextAttributes); + ssassert(eglContext != EGL_NO_CONTEXT, "Cannot create EGL context"); - *glrc = wglCreateContext(hdc); - wglMakeCurrent(hdc, *glrc); + eglMakeCurrent(*eglDisplay, *eglSurface, *eglSurface, *eglContext); } void SolveSpace::PaintGraphics() { SS.GW.Paint(); - SwapBuffers(GetDC(GraphicsWnd)); + eglSwapBuffers(GraphicsGlDisplay, GraphicsGlSurface); } void SolveSpace::InvalidateGraphics() { @@ -1311,8 +1329,8 @@ static void CreateMainWindows() 50, 50, 100, 21, TextWnd, NULL, Instance, NULL); // Now that all our windows exist, set up gl contexts. - CreateGlContext(TextWnd, &TextGl); - CreateGlContext(GraphicsWnd, &GraphicsGl); + CreateGlContext(TextWnd, &TextGlDisplay, &TextGlSurface, &TextGlContext); + CreateGlContext(GraphicsWnd, &GraphicsGlDisplay, &GraphicsGlSurface, &GraphicsGlContext); RECT r, rc; GetWindowRect(TextWnd, &r); @@ -1437,6 +1455,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, if(!filename.empty()) SS.OpenFile(Narrow(filename)); + // Repaint one more time, after we've set everything up. + PaintGraphics(); + PaintTextWnd(); + // And now it's the message loop. All calls in to the rest of the code // will be from the wndprocs. MSG msg; diff --git a/src/render/gl2shader.cpp b/src/render/gl2shader.cpp index 912ed504..b86c5ec7 100644 --- a/src/render/gl2shader.cpp +++ b/src/render/gl2shader.cpp @@ -58,13 +58,40 @@ Vector4f Vector4f::From(const RgbaColor &c) { static GLuint CompileShader(const std::string &res, GLenum type) { size_t size; - const char *src = (const char *)LoadResource(res, &size); + const char *resData = (const char *)LoadResource(res, &size); + + // Sigh, here we go... We want to deploy to four platforms: Linux, Windows, OS X, mobile+web. + // These platforms are basically disjunctive in the OpenGL versions and profiles that they + // support: mobile+web support GLES2, Windows can only be guaranteed to support GL1 without + // vendor's drivers installed but supports D3D9+ natively, Linux supports GL3.2+ and/or + // GLES2+ depending on whether we run on X11 or Wayland, and OS X supports either a legacy + // profile or a GL3.2 core profile or (on 10.9+) a GL4.1 core profile. + // The platforms barely have a common subset of features: + // * Linux Mesa/NVidia accept basically everything thrown at it; + // * mobile+web and Windows (D3D9 through ANGLE) are strictly GLES2/GLSL1.0; + // * OS X legacy compatibility profile has GLSL1.2 only shaders, and GL3.2 core profile + // that has GLSL1.0 shaders compatible with GLES2 makes mandatory the use of vertex array + // objects, which cannot be used in GLES2 at all; similarly GL3.2 core has GL_RED but not + // GL_ALPHA whereas GLES2 has GL_ALPHA but not GL_RED. + // While we're at it, let's remember that GLES2 has *only* glDepthRangef, GL3.2 has *only* + // glDepthRange, and GL4.1+ has both glDepthRangef and glDepthRange. Also, that GLSL1.0 + // makes `precision highp float;` mandatory in fragment shaders, and GLSL1.2 removes + // the `precision` keyword entirely, because that's clearly how minor versions work. + // Christ, what a trash fire. + + std::string src(resData, size); +#ifdef __APPLE__ + src = "#version 120\n" + src; +#else + src = "#version 100\nprecision highp float;\n" + src; +#endif GLuint shader = glCreateShader(type); ssassert(shader != 0, "glCreateShader failed"); - GLint glSize = size; - glShaderSource(shader, 1, &src, &glSize); + const GLint glSize[] = { (int)src.length() }; + const GLchar* glSource[] = { src.c_str() }; + glShaderSource(shader, 1, glSource, glSize); glCompileShader(shader); GLint infoLen; diff --git a/src/render/gl2shader.h b/src/render/gl2shader.h index f52d0c45..5ae71e38 100644 --- a/src/render/gl2shader.h +++ b/src/render/gl2shader.h @@ -7,10 +7,16 @@ #define SOLVESPACE_GL2UTILS_H #ifdef WIN32 -# include // required by GL headers -#endif -#ifdef __APPLE__ +# define GL_APICALL /*static linkage*/ +# define GL_GLEXT_PROTOTYPES +# include +# include +#elif __APPLE__ # include +// glDepthRange is in GL1+ but not GLES2, glDepthRangef is in GL4.1+ and GLES2. +// Consistency! +# define glClearDepthf glClearDepth +# define glDepthRangef glDepthRange #else # define GL_GLEXT_PROTOTYPES # include diff --git a/src/render/rendergl2.cpp b/src/render/rendergl2.cpp index 740ec926..d4882691 100644 --- a/src/render/rendergl2.cpp +++ b/src/render/rendergl2.cpp @@ -171,11 +171,11 @@ static void ssglDepthRange(Canvas::Layer layer, int zIndex) { switch(layer) { case Canvas::Layer::FRONT: - glDepthRange(0.0, 0.0); + glDepthRangef(0.0f, 0.0f); break; case Canvas::Layer::BACK: - glDepthRange(1.0, 1.0); + glDepthRangef(1.0f, 1.0f); break; case Canvas::Layer::NORMAL: @@ -184,7 +184,7 @@ static void ssglDepthRange(Canvas::Layer layer, int zIndex) { // The size of this step depends on the resolution of the Z buffer; for // a 16-bit buffer, this should be fine. double offset = 1.0 / (65535 * 0.8) * zIndex; - glDepthRange(0.1 - offset, 1.0 - offset); + glDepthRangef((float)(0.1 - offset), (float)(1.0 - offset)); break; } } @@ -607,7 +607,7 @@ void OpenGl2Renderer::UpdateProjection(bool flip) { outlineRenderer.SetProjection(projection); outlineRenderer.SetModelview(modelview); - glClearDepth(1.0); + glClearDepthf(1.0f); glClear(GL_DEPTH_BUFFER_BIT); } @@ -626,7 +626,7 @@ void OpenGl2Renderer::NewFrame() { RgbaColor backgroundColor = lighting.backgroundColor; glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), backgroundColor.alphaF()); - glClearDepth(1.0); + glClearDepthf(1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPolygonOffset(2.0, 1.0);