Add OpenGL 2 support on Windows using ANGLE.

This commit performs two main changes:
  * Alters the shaders to use only strictly conformant GLSL 2.0.
  * Alters the Windows UI to use ANGLE via GL ES 2.0 and EGL 1.4.

This commit also drops official support for Windows XP, since ANGLE
requires a non-XP toolset to build. It is still possible to build
SolveSpace for Windows XP using:

  cmake -T v120_xp -DOPENGL=1
single-window
whitequark 2016-11-16 02:22:10 +00:00
parent 9db50ed077
commit c8ff17f4a2
24 changed files with 133 additions and 79 deletions

3
.gitmodules vendored
View File

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

View File

@ -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)

View File

@ -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"

View File

@ -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()

1
extlib/angle Submodule

@ -0,0 +1 @@
Subproject commit bbe1370b860d94583e5b825dbd72c731f51ff8f0

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
const float feather = 0.5;
uniform vec4 color;

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
const float feather = 0.5;
attribute vec3 pos;

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
uniform vec4 color;
void main() {

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
attribute vec3 pos;
uniform mat4 modelview;

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
const float feather = 0.5;
uniform vec4 color;

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
const float feather = 0.5;
attribute vec3 pos;

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
uniform vec4 color;
uniform sampler2D texture;

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
attribute vec3 pos;
attribute vec2 tex;

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
uniform vec4 color;
uniform sampler2D texture;

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
uniform vec3 lightDir0;
uniform vec3 lightDir1;
uniform float lightInt0;

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
attribute vec3 pos;
attribute vec3 nor;
attribute vec4 col;

View File

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

View File

@ -3,8 +3,6 @@
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#version 120
attribute vec3 pos;
uniform mat4 modelview;

View File

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

View File

@ -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)

View File

@ -22,16 +22,23 @@
# undef uint32_t // thanks but no thanks
#endif
#define EGLAPI /*static linkage*/
#include <EGL/egl.h>
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;

View File

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

View File

@ -7,10 +7,16 @@
#define SOLVESPACE_GL2UTILS_H
#ifdef WIN32
# include <windows.h> // required by GL headers
#endif
#ifdef __APPLE__
# define GL_APICALL /*static linkage*/
# define GL_GLEXT_PROTOTYPES
# include <GLES2/gl2.h>
# include <GLES2/gl2ext.h>
#elif __APPLE__
# include <OpenGL/gl.h>
// 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 <GL/gl.h>

View File

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