From 476a9971469854a3586d563683f779e5fa3e61c1 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Mon, 5 Feb 2024 13:40:45 -0800 Subject: [PATCH 01/18] partial WIP on EGL --- .../polyscope/render/opengl/egl_gl_engine.h | 487 +++ src/CMakeLists.txt | 32 +- src/render/initialize_backend.cpp | 6 +- src/render/opengl/egl_gl_engine.cpp | 2974 +++++++++++++++++ 4 files changed, 3483 insertions(+), 16 deletions(-) create mode 100644 include/polyscope/render/opengl/egl_gl_engine.h create mode 100644 src/render/opengl/egl_gl_engine.cpp diff --git a/include/polyscope/render/opengl/egl_gl_engine.h b/include/polyscope/render/opengl/egl_gl_engine.h new file mode 100644 index 00000000..0c616df9 --- /dev/null +++ b/include/polyscope/render/opengl/egl_gl_engine.h @@ -0,0 +1,487 @@ +// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run + +#pragma once + +#include "polyscope/options.h" +#include "polyscope/render/engine.h" +#include "polyscope/utilities.h" + +#ifdef __APPLE__ +#define GLFW_INCLUDE_GLCOREARB +#include "GLFW/glfw3.h" +#else +#include "glad/glad.h" +// glad must come first +#include "GLFW/glfw3.h" +#endif + +#include + +#undef None // for some terrible reason egl.h defines 'None', which we use below + +#ifdef _WIN32 +#undef APIENTRY +#define GLFW_EXPOSE_NATIVE_WIN32 +#define GLFW_EXPOSE_NATIVE_WGL +#include +#endif + +#include "imgui.h" +#define IMGUI_IMPL_OPENGL_LOADER_GLAD +#include "backends/imgui_impl_glfw.h" +#include "backends/imgui_impl_opengl3.h" + +#include + +// Note: DO NOT include this header throughout polyscope, and do not directly make openGL calls. This header should only +// be used to construct an instance of Engine. engine.h gives the render API, all render calls should pass through that. + + +namespace polyscope { +namespace render { +namespace backend_openGL3_glfw_egl { + +// Some very nice typdefs +typedef GLuint TextureBufferHandle; +typedef GLuint RenderBufferHandle; +typedef GLuint FrameBufferHandle; +typedef GLuint ShaderHandle; +typedef GLuint ProgramHandle; +typedef GLuint AttributeHandle; +typedef GLuint VertexBufferHandle; + +typedef GLint UniformLocation; +typedef GLint AttributeLocation; +typedef GLint TextureLocation; + +class GLAttributeBuffer : public AttributeBuffer { +public: + GLAttributeBuffer(RenderDataType dataType_, int arrayCount_); + virtual ~GLAttributeBuffer(); + + void bind(); + VertexBufferHandle getHandle() const { return VBOLoc; } + + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + + // Array-valued attributes + // (adding these lazily as we need them) + // (sadly we cannot template the virtual function) + void setData(const std::vector>& data) override; + void setData(const std::vector>& data) override; + void setData(const std::vector>& data) override; + + // get data at a single index from the buffer + float getData_float(size_t ind) override; + double getData_double(size_t ind) override; + glm::vec2 getData_vec2(size_t ind) override; + glm::vec3 getData_vec3(size_t ind) override; + glm::vec4 getData_vec4(size_t ind) override; + int getData_int(size_t ind) override; + uint32_t getData_uint32(size_t ind) override; + glm::uvec2 getData_uvec2(size_t ind) override; + glm::uvec3 getData_uvec3(size_t ind) override; + glm::uvec4 getData_uvec4(size_t ind) override; + + // get data at a range of indices from the buffer + std::vector getDataRange_float(size_t ind, size_t count) override; + std::vector getDataRange_double(size_t ind, size_t count) override; + std::vector getDataRange_vec2(size_t ind, size_t count) override; + std::vector getDataRange_vec3(size_t ind, size_t count) override; + std::vector getDataRange_vec4(size_t ind, size_t count) override; + std::vector getDataRange_int(size_t ind, size_t count) override; + std::vector getDataRange_uint32(size_t ind, size_t count) override; + std::vector getDataRange_uvec2(size_t ind, size_t count) override; + std::vector getDataRange_uvec3(size_t ind, size_t count) override; + std::vector getDataRange_uvec4(size_t ind, size_t count) override; + + uint32_t getNativeBufferID() override; + +protected: + VertexBufferHandle VBOLoc; + +private: + void checkType(RenderDataType targetType); + void checkArray(int arrayCount); + GLenum getTarget(); + + + // internal implementation helpers + template + void setData_helper(const std::vector& data); + + template + T getData_helper(size_t ind); + + template + std::vector getDataRange_helper(size_t start, size_t count); +}; + +class GLTextureBuffer : public TextureBuffer { +public: + // create a 1D texture from data + GLTextureBuffer(TextureFormat format, unsigned int size1D, const unsigned char* data = nullptr); + GLTextureBuffer(TextureFormat format, unsigned int size1D, const float* data); + + // create a 2D texture from data + GLTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, const unsigned char* data = nullptr); + GLTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, const float* data); + + // create a 3D texture from data + GLTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, unsigned int sizeZ_, + const unsigned char* data = nullptr); + GLTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, unsigned int sizeZ_, + const float* data); + + ~GLTextureBuffer() override; + + + // Resize the underlying buffer (contents are lost) + void resize(unsigned int newLen) override; + void resize(unsigned int newX, unsigned int newY) override; + void resize(unsigned int newX, unsigned int newY, unsigned int newZ) override; + + // Fill with data + // NOTE: some of these are not implemented yet + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + void setData(const std::vector& data) override; + + // Array-valued + // NOTE: some of these are not implemented yet + // (adding these lazily as we need them) + // (sadly we cannot template the virtual function) + void setData(const std::vector>& data) override; + void setData(const std::vector>& data) override; + void setData(const std::vector>& data) override; + + void setFilterMode(FilterMode newMode) override; + void* getNativeHandle() override; + uint32_t getNativeBufferID() override; + + std::vector getDataScalar() override; + std::vector getDataVector2() override; + std::vector getDataVector3() override; + + void bind(); + GLenum textureType(); + TextureBufferHandle getHandle() const { return handle; } + + +protected: + TextureBufferHandle handle; +}; + +class GLRenderBuffer : public RenderBuffer { +public: + GLRenderBuffer(RenderBufferType type, unsigned int sizeX_, unsigned int sizeY_); + ~GLRenderBuffer() override; + + void resize(unsigned int newX, unsigned int newY) override; + + void bind(); + RenderBufferHandle getHandle() const { return handle; } + + RenderBufferHandle handle; +}; + + +class GLFrameBuffer : public FrameBuffer { + +public: + GLFrameBuffer(unsigned int sizeX_, unsigned int sizeY_, bool isDefault = false); + ~GLFrameBuffer() override; + + void bind() override; + + // Bind to this framebuffer so subsequent draw calls will go to it + // If return is false, binding failed and the framebuffer should not be used. + bool bindForRendering() override; + + // Clear to redraw + void clear() override; + + // Bind to textures/renderbuffers for output + void addColorBuffer(std::shared_ptr renderBuffer) override; + void addColorBuffer(std::shared_ptr textureBuffer) override; + void addDepthBuffer(std::shared_ptr renderBuffer) override; + void addDepthBuffer(std::shared_ptr textureBuffer) override; + + void setDrawBuffers() override; + + // Query pixels + std::vector readBuffer() override; + std::array readFloat4(int xPos, int yPos) override; + float readDepth(int xPos, int yPos) override; + void blitTo(FrameBuffer* other) override; + + // Getters + FrameBufferHandle getHandle() const { return handle; } + uint32_t getNativeBufferID() override; + + FrameBufferHandle handle; +}; + +// Classes to keep track of attributes and uniforms +struct GLShaderUniform { + std::string name; + RenderDataType type; + bool isSet; // has a value been assigned to this uniform? + UniformLocation location; // -1 means "no location", usually because it was optimized out +}; + +struct GLShaderAttribute { + std::string name; + RenderDataType type; + int arrayCount; + AttributeLocation location; // -1 means "no location", usually because it was optimized out + std::shared_ptr buff; // the buffer that we will actually use +}; + +struct GLShaderTexture { + std::string name; + int dim; + uint32_t index; + bool isSet; + GLTextureBuffer* textureBuffer; + std::shared_ptr textureBufferOwned; // might be empty, if texture isn't owned + TextureLocation location; // -1 means "no location", usually because it was optimized out +}; + +// A thin wrapper around a program handle. +// This class takes ownership and handles program deletion in its destructor +class GLCompiledProgram { +public: + GLCompiledProgram(const std::vector& stages, DrawMode dm); + ~GLCompiledProgram(); + + ProgramHandle getHandle() const { return programHandle; } + DrawMode getDrawMode() const { return drawMode; } + std::vector getUniforms() const { return uniforms; } + std::vector getAttributes() const { return attributes; } + std::vector getTextures() const { return textures; } + +private: + ProgramHandle programHandle; + DrawMode drawMode; + std::vector uniforms; + std::vector attributes; + std::vector textures; + + void compileGLProgram(const std::vector& stages); + void setDataLocations(); + + void addUniqueAttribute(ShaderSpecAttribute attribute); + void addUniqueUniform(ShaderSpecUniform uniform); + void addUniqueTexture(ShaderSpecTexture texture); +}; + +class GLShaderProgram : public ShaderProgram { + +public: + GLShaderProgram(const std::shared_ptr& compiledProgram); + ~GLShaderProgram() override; + + // === Store data + // If update is set to "true", data is updated rather than allocated (must be allocated first) + + // Uniforms + bool hasUniform(std::string name) override; + void setUniform(std::string name, int val) override; + void setUniform(std::string name, unsigned int val) override; + void setUniform(std::string name, float val) override; + void setUniform(std::string name, double val) override; // WARNING casts down to float + void setUniform(std::string name, float* val) override; + void setUniform(std::string name, glm::vec2 val) override; + void setUniform(std::string name, glm::vec3 val) override; + void setUniform(std::string name, glm::vec4 val) override; + void setUniform(std::string name, std::array val) override; + void setUniform(std::string name, float x, float y, float z, float w) override; + void setUniform(std::string name, glm::uvec2 val) override; + void setUniform(std::string name, glm::uvec3 val) override; + void setUniform(std::string name, glm::uvec4 val) override; + + // = Attributes + // clang-format off + bool hasAttribute(std::string name) override; + bool attributeIsSet(std::string name) override; + std::shared_ptr getAttributeBuffer(std::string name) override; + void setAttribute(std::string name, std::shared_ptr externalBuffer) override; + void setAttribute(std::string name, const std::vector& data) override; + void setAttribute(std::string name, const std::vector& data) override; + void setAttribute(std::string name, const std::vector& data) override; + void setAttribute(std::string name, const std::vector& data) override; + void setAttribute(std::string name, const std::vector& data) override; + void setAttribute(std::string name, const std::vector& data) override; + void setAttribute(std::string name, const std::vector& data) override; + // clang-format on + + + // Indices + void setIndex(std::shared_ptr externalBuffer) override; + void setPrimitiveRestartIndex(unsigned int restartIndex) override; + + // Instancing + void setInstanceCount(uint32_t instanceCount) override; + + // Textures + bool hasTexture(std::string name) override; + bool textureIsSet(std::string name) override; + void setTexture1D(std::string name, unsigned char* texData, unsigned int length) override; + void setTexture2D(std::string name, unsigned char* texData, unsigned int width, unsigned int height, + bool withAlpha = true, bool useMipMap = false, bool repeat = false) override; + void setTextureFromColormap(std::string name, const std::string& colorMap, bool allowUpdate = false) override; + void setTextureFromBuffer(std::string name, TextureBuffer* textureBuffer) override; + + // Draw! + void draw() override; + void validateData() override; + +protected: + // Lists of attributes and uniforms that need to be set + std::vector uniforms; + std::vector attributes; + std::vector textures; + +private: + // Setup routines + void compileGLProgram(const std::vector& stages); + void setDataLocations(); + void bindVAO(); + void createBuffers(); + void ensureBufferExists(GLShaderAttribute& a); + void createBuffer(GLShaderAttribute& a); + void assignBufferToVAO(GLShaderAttribute& a); + + // Drawing related + void activateTextures(); + + // GL pointers for various useful things + std::shared_ptr compiledProgram; + AttributeHandle vaoHandle; +}; + + +class GLEngine : public Engine { +public: + GLEngine(); + + // High-level control + void initialize(); + void checkError(bool fatal = false) override; + + void swapDisplayBuffers() override; + std::vector readDisplayBuffer() override; + + // Manage render state + void setDepthMode(DepthMode newMode) override; + void setBlendMode(BlendMode newMode) override; + void setColorMask(std::array mask = {true, true, true, true}) override; + void setBackfaceCull(bool newVal) override; + + // === Windowing and framework things + void makeContextCurrent() override; + void focusWindow() override; + void showWindow() override; + void hideWindow() override; + void updateWindowSize(bool force = false) override; + void applyWindowSize() override; + void setWindowResizable(bool newVal) override; + bool getWindowResizable() override; + std::tuple getWindowPos() override; + bool windowRequestsClose() override; + void pollEvents() override; + bool isKeyPressed(char c) override; // for lowercase a-z and 0-9 only + int getKeyCode(char c) override; // for lowercase a-z and 0-9 only + std::string getClipboardText() override; + void setClipboardText(std::string text) override; + + // ImGui + void initializeImGui() override; + void shutdownImGui() override; + void ImGuiNewFrame() override; + void ImGuiRender() override; + + // === Factory methods + + // create attribute buffers + std::shared_ptr generateAttributeBuffer(RenderDataType dataType_, int arrayCount_) override; + + // create textures + std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int size1D, + const unsigned char* data = nullptr) override; // 1d + std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int size1D, + const float* data) override; // 1d + std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, + const unsigned char* data = nullptr) override; // 2d + std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, + const float* data) override; // 2d + std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, + unsigned int sizeZ_, + const unsigned char* data = nullptr) override; // 3d + std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, + unsigned int sizeZ_, + const float* data) override; // 3d + + // create render buffers + std::shared_ptr generateRenderBuffer(RenderBufferType type, unsigned int sizeX_, + unsigned int sizeY_) override; + // create frame buffers + std::shared_ptr generateFrameBuffer(unsigned int sizeX_, unsigned int sizeY_) override; + + // general flexible interface + std::shared_ptr + requestShader(const std::string& programName, const std::vector& customRules, + ShaderReplacementDefaults defaults = ShaderReplacementDefaults::SceneObject) override; + + // === Implementation details + + // Add a shader programs/rules so that they can be requested above + void registerShaderProgram(const std::string& name, const std::vector& spec, + const DrawMode& dm); + void registerShaderRule(const std::string& name, const ShaderReplacementRule& rule); + + // Transparency + virtual void applyTransparencySettings() override; + + virtual void setFrontFaceCCW(bool newVal) override; + +protected: + // Helpers + virtual void createSlicePlaneFliterRule(std::string name) override; + + // Internal windowing and engine details + GLFWwindow* mainWindow = nullptr; + + // Shader program & rule caches + std::unordered_map, DrawMode>> registeredShaderPrograms; + std::unordered_map registeredShaderRules; + void populateDefaultShadersAndRules(); + + std::unordered_map> compiledProgamCache; + std::string programKeyFromRules(const std::string& programName, const std::vector& rules, + ShaderReplacementDefaults defaults); + std::shared_ptr getCompiledProgram(const std::string& programName, + const std::vector& customRules, + ShaderReplacementDefaults defaults); +}; + +} // namespace backend_openGL3_glfw +} // namespace render +} // namespace polyscope diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 76477c5d..f6aa9bc8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,21 +19,22 @@ if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") message("Polyscope backend openGL3_glfw enabled") list (APPEND BACKEND_SRCS - render/opengl/gl_engine.cpp - render/opengl/shaders/texture_draw_shaders.cpp - render/opengl/shaders/lighting_shaders.cpp - render/opengl/shaders/grid_shaders.cpp - render/opengl/shaders/ground_plane_shaders.cpp - render/opengl/shaders/gizmo_shaders.cpp - render/opengl/shaders/histogram_shaders.cpp - render/opengl/shaders/surface_mesh_shaders.cpp - render/opengl/shaders/volume_mesh_shaders.cpp - render/opengl/shaders/vector_shaders.cpp - render/opengl/shaders/sphere_shaders.cpp - render/opengl/shaders/ribbon_shaders.cpp - render/opengl/shaders/cylinder_shaders.cpp - render/opengl/shaders/rules.cpp - render/opengl/shaders/common.cpp + render/opengl/egl_gl_engine.cpp # TODO + render/opengl/gl_engine.cpp + render/opengl/shaders/texture_draw_shaders.cpp + render/opengl/shaders/lighting_shaders.cpp + render/opengl/shaders/grid_shaders.cpp + render/opengl/shaders/ground_plane_shaders.cpp + render/opengl/shaders/gizmo_shaders.cpp + render/opengl/shaders/histogram_shaders.cpp + render/opengl/shaders/surface_mesh_shaders.cpp + render/opengl/shaders/volume_mesh_shaders.cpp + render/opengl/shaders/vector_shaders.cpp + render/opengl/shaders/sphere_shaders.cpp + render/opengl/shaders/ribbon_shaders.cpp + render/opengl/shaders/cylinder_shaders.cpp + render/opengl/shaders/rules.cpp + render/opengl/shaders/common.cpp ) list(APPEND BACKEND_HEADERS @@ -64,6 +65,7 @@ if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") # On Windows/Linux, use the glad openGL loader list(APPEND BACKEND_LIBS glad) + list(APPEND BACKEND_LIBS EGL) # TODO endif() add_definitions(-DPOLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED) diff --git a/src/render/initialize_backend.cpp b/src/render/initialize_backend.cpp index a8bc03c8..acf8b5ea 100644 --- a/src/render/initialize_backend.cpp +++ b/src/render/initialize_backend.cpp @@ -17,10 +17,12 @@ std::string engineBackendName = ""; namespace backend_openGL3_glfw { void initializeRenderEngine(); } +namespace backend_openGL3_glfw_egl { +void initializeRenderEngine(); +} namespace backend_openGL_mock { void initializeRenderEngine(); } -// void initializeRenderEngine_openGL_mock(); void initializeRenderEngine(std::string backend) { @@ -47,6 +49,8 @@ void initializeRenderEngine(std::string backend) { // Initialize the appropriate backend if (backend == "openGL3_glfw") { backend_openGL3_glfw::initializeRenderEngine(); + } else if (backend == "openGL3_glfw_egl") { + backend_openGL3_glfw_egl::initializeRenderEngine(); } else if (backend == "openGL_mock") { backend_openGL_mock::initializeRenderEngine(); } else { diff --git a/src/render/opengl/egl_gl_engine.cpp b/src/render/opengl/egl_gl_engine.cpp new file mode 100644 index 00000000..b3a7ffd2 --- /dev/null +++ b/src/render/opengl/egl_gl_engine.cpp @@ -0,0 +1,2974 @@ +// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run + +#include "backends/imgui_impl_opengl3.h" +#include "polyscope/render/engine.h" + +#ifdef POLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED +#include "polyscope/render/opengl/egl_gl_engine.h" + +#include "polyscope/messages.h" +#include "polyscope/options.h" +#include "polyscope/polyscope.h" +#include "polyscope/utilities.h" + +#include "polyscope/render/shader_builder.h" + +// all the shaders +#include "polyscope/render/opengl/shaders/common.h" +#include "polyscope/render/opengl/shaders/cylinder_shaders.h" +#include "polyscope/render/opengl/shaders/gizmo_shaders.h" +#include "polyscope/render/opengl/shaders/grid_shaders.h" +#include "polyscope/render/opengl/shaders/ground_plane_shaders.h" +#include "polyscope/render/opengl/shaders/histogram_shaders.h" +#include "polyscope/render/opengl/shaders/lighting_shaders.h" +#include "polyscope/render/opengl/shaders/ribbon_shaders.h" +#include "polyscope/render/opengl/shaders/rules.h" +#include "polyscope/render/opengl/shaders/sphere_shaders.h" +#include "polyscope/render/opengl/shaders/surface_mesh_shaders.h" +#include "polyscope/render/opengl/shaders/texture_draw_shaders.h" +#include "polyscope/render/opengl/shaders/vector_shaders.h" +#include "polyscope/render/opengl/shaders/volume_mesh_shaders.h" + +#include "stb_image.h" + +#include +#include + +namespace polyscope { +namespace render { + +namespace backend_openGL3_glfw_egl { + +using namespace backend_openGL3_glfw; + +GLEngine* glEngine = nullptr; // alias for global engine pointer + +void initializeRenderEngine() { + glEngine = new GLEngine(); + engine = glEngine; + glEngine->initialize(); + engine->allocateGlobalBuffersAndPrograms(); +} + +// EGL configuration +// see https://developer.nvidia.com/blog/egl-eye-opengl-visualization-without-x-server/ +static const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE}; + +static const int pbufferWidth = 9; +static const int pbufferHeight = 9; + +static const EGLint pbufferAttribs[] = { + EGL_WIDTH, pbufferWidth, EGL_HEIGHT, pbufferHeight, EGL_NONE, +}; + +// == Map enums to native values + +// clang-format off + +inline GLenum internalFormat(const TextureFormat& x) { + switch (x) { + case TextureFormat::RGB8: return GL_RGB8; + case TextureFormat::RGBA8: return GL_RGBA8; + case TextureFormat::RG16F: return GL_RG16F; + case TextureFormat::RGB16F: return GL_RGB16F; + case TextureFormat::RGBA16F: return GL_RGBA16F; + case TextureFormat::R32F: return GL_R32F; + case TextureFormat::R16F: return GL_R16F; + case TextureFormat::RGB32F: return GL_RGBA32F; + case TextureFormat::RGBA32F: return GL_RGBA32F; + case TextureFormat::DEPTH24: return GL_DEPTH_COMPONENT24; + } + exception("bad enum"); + return GL_RGB8; +} + +inline GLenum formatF(const TextureFormat& x) { + switch (x) { + case TextureFormat::RGB8: return GL_RGB; + case TextureFormat::RGBA8: return GL_RGBA; + case TextureFormat::RG16F: return GL_RG; + case TextureFormat::RGB16F: return GL_RGB; + case TextureFormat::RGBA16F: return GL_RGBA; + case TextureFormat::R32F: return GL_RED; + case TextureFormat::R16F: return GL_RED; + case TextureFormat::RGB32F: return GL_RGB; + case TextureFormat::RGBA32F: return GL_RGBA; + case TextureFormat::DEPTH24: return GL_DEPTH_COMPONENT; + } + exception("bad enum"); + return GL_RGB; +} + +inline GLenum type(const TextureFormat& x) { + switch (x) { + case TextureFormat::RGB8: return GL_UNSIGNED_BYTE; + case TextureFormat::RGBA8: return GL_UNSIGNED_BYTE; + case TextureFormat::RG16F: return GL_HALF_FLOAT; + case TextureFormat::RGB16F: return GL_HALF_FLOAT; + case TextureFormat::RGBA16F: return GL_HALF_FLOAT; + case TextureFormat::R32F: return GL_FLOAT; + case TextureFormat::R16F: return GL_FLOAT; + case TextureFormat::RGB32F: return GL_FLOAT; + case TextureFormat::RGBA32F: return GL_FLOAT; + case TextureFormat::DEPTH24: return GL_FLOAT; + } + exception("bad enum"); + return GL_UNSIGNED_BYTE; +} + +inline GLenum native(const ShaderStageType& x) { + switch (x) { + case ShaderStageType::Vertex: return GL_VERTEX_SHADER; + case ShaderStageType::Geometry: return GL_GEOMETRY_SHADER; + //case ShaderStageType::Compute: return GL_COMPUTE_SHADER; + case ShaderStageType::Fragment: return GL_FRAGMENT_SHADER; + } + exception("bad enum"); + return GL_VERTEX_SHADER; +} + +inline GLenum native(const RenderBufferType& x) { + switch (x) { + case RenderBufferType::ColorAlpha: return GL_RGBA; + case RenderBufferType::Color: return GL_RGB; + case RenderBufferType::Depth: return GL_DEPTH_COMPONENT; + case RenderBufferType::Float4: return GL_RGBA32F; + } + exception("bad enum"); + return GL_RGBA; +} + +inline GLenum colorAttachNum(const unsigned int i) { + // can we just add to the 0 one? couldn't find documentation saying yes for sure. + switch (i) { + case 0: return GL_COLOR_ATTACHMENT0; + case 1: return GL_COLOR_ATTACHMENT1; + case 2: return GL_COLOR_ATTACHMENT2; + case 3: return GL_COLOR_ATTACHMENT3; + case 4: return GL_COLOR_ATTACHMENT4; + case 5: return GL_COLOR_ATTACHMENT5; + case 6: return GL_COLOR_ATTACHMENT6; + case 7: return GL_COLOR_ATTACHMENT7; + default: exception("tried to use too many color attachments"); + } + exception("bad enum"); + return GL_COLOR_ATTACHMENT0; +} + +// clang-format on + + +// Stateful error checker +void checkGLError(bool fatal = true) { + + if (!options::enableRenderErrorChecks) { + return; + } + + // Map the GL error enums to strings + GLenum err = GL_NO_ERROR; + while ((err = glGetError()) != GL_NO_ERROR) { + std::string errText; + switch (err) { + case GL_NO_ERROR: + errText = "No error"; + break; + case GL_INVALID_ENUM: + errText = "Invalid enum"; + break; + case GL_INVALID_VALUE: + errText = "Invalid value"; + break; + case GL_INVALID_OPERATION: + errText = "Invalid operation"; + break; + // case GL_STACK_OVERFLOW: std::cerr << "Stack overflow"; break; + // case GL_STACK_UNDERFLOW: std::cerr << "Stack underflow"; break; + case GL_OUT_OF_MEMORY: + errText = "Out of memory"; + break; + default: + errText = "Unknown error " + std::to_string(static_cast(err)); + } + + if (polyscope::options::verbosity > 0) { + std::cout << polyscope::options::printPrefix << "Polyscope OpenGL Error! Type: " << errText << std::endl; + } + if (fatal) { + exception("OpenGl error occurred. Text: " + errText); + } + } +} + +void checkEGLError(bool fatal = true) { + + if (!options::enableRenderErrorChecks) { + return; + } + + EGLint err = eglGetError(); + while (err != EGL_SUCCESS) { + std::string errText; + switch (err) { + case GL_NO_ERROR: + errText = "No error"; + break; + + case EGL_NOT_INITIALIZED: + errText = "EGL is not initialized, or could not be initialized, for the specified EGL display connection."; + break; + + case EGL_BAD_ACCESS: + errText = "EGL cannot access a requested resource (for example a context is bound in another thread)."; + break; + + case EGL_BAD_ALLOC: + errText = "EGL failed to allocate resources for the requested operation."; + break; + + case EGL_BAD_ATTRIBUTE: + errText = "An unrecognized attribute or attribute value was passed in the attribute list."; + break; + + case EGL_BAD_CONTEXT: + errText = "An EGLContext argument does not name a valid EGL rendering context."; + break; + + case EGL_BAD_CONFIG: + errText = "An EGLConfig argument does not name a valid EGL frame buffer configuration."; + break; + + case EGL_BAD_CURRENT_SURFACE: + errText = "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer " + "valid."; + break; + + case EGL_BAD_DISPLAY: + errText = "An EGLDisplay argument does not name a valid EGL display connection."; + break; + + case EGL_BAD_SURFACE: + errText = "An EGLSurface argument does not name a valid surface (window, pixel buffer or pixmap) configured for " + "GL rendering."; + break; + + case EGL_BAD_MATCH: + errText = + "Arguments are inconsistent (for example, a valid context requires buffers not supplied by a valid surface)."; + break; + + case EGL_BAD_PARAMETER: + errText = "One or more argument values are invalid."; + break; + + case EGL_BAD_NATIVE_PIXMAP: + errText = "A NativePixmapType argument does not refer to a valid native pixmap."; + break; + + case EGL_BAD_NATIVE_WINDOW: + errText = "A NativeWindowType argument does not refer to a valid native window."; + break; + + case EGL_CONTEXT_LOST: + errText = "A power management event has occurred. The application must destroy all contexts and reinitialise " + "OpenGL ES state and objects to continue rendering."; + break; + + default: + errText = "Unknown error " + std::to_string(static_cast(err)); + } + + if (polyscope::options::verbosity > 0) { + std::cout << polyscope::options::printPrefix << "Polyscope EGL Error! Text: " << errText << std::endl; + } + if (fatal) { + exception("EGL error occurred. Text: " + errText); + } + + err = eglGetError(); // get the next error + } +} + +// Helper function to print compile logs +void printShaderInfoLog(ShaderHandle shaderHandle) { + int logLen = 0; + int chars = 0; + char* log; + + glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &logLen); + + if (options::verbosity > 0 && logLen > 1) { // for some reason we often get logs of length 1 with no + // visible characters + log = (char*)malloc(logLen); + glGetShaderInfoLog(shaderHandle, logLen, &chars, log); + printf("Shader info log:\n%s\n", log); + free(log); + + exception("shader compile failed"); + } +} +void printProgramInfoLog(GLuint handle) { + int logLen = 0; + int chars = 0; + char* log; + + glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &logLen); + + if (options::verbosity > 0 && logLen > 1) { // for some reason we often get logs of length 1 with no + // visible characters + log = (char*)malloc(logLen); + glGetProgramInfoLog(handle, logLen, &chars, log); + printf("Program info log:\n%s\n", log); + free(log); + } +} + +// ============================================================= +// =================== Attribute buffer ======================== +// ============================================================= + +GLAttributeBuffer::GLAttributeBuffer(RenderDataType dataType_, int arrayCount_) + : AttributeBuffer(dataType_, arrayCount_) { + glGenBuffers(1, &VBOLoc); +} + +GLAttributeBuffer::~GLAttributeBuffer() { + bind(); + glDeleteBuffers(1, &VBOLoc); +} + +void GLAttributeBuffer::bind() { glBindBuffer(getTarget(), VBOLoc); } + +void GLAttributeBuffer::checkType(RenderDataType targetType) { + if (dataType != targetType) { + throw std::invalid_argument("Tried to set GLAttributeBuffer with wrong type. Actual type: " + + renderDataTypeName(dataType) + " Attempted type: " + renderDataTypeName(targetType)); + } +} + +void GLAttributeBuffer::checkArray(int testArrayCount) { + if (testArrayCount != arrayCount) { + throw std::invalid_argument("Tried to set GLAttributeBuffer with wrong array count. Actual count: " + + std::to_string(arrayCount) + " Attempted count: " + std::to_string(testArrayCount)); + } +} + +GLenum GLAttributeBuffer::getTarget() { return GL_ARRAY_BUFFER; } + + +template +void GLAttributeBuffer::setData_helper(const std::vector& data) { + bind(); + + // allocate if needed + if (!isSet() || data.size() > bufferSize) { + setFlag = true; + uint64_t newSize = data.size(); + newSize = std::max(newSize, 2 * bufferSize); // if we're expanding, at-least double + glBufferData(getTarget(), newSize * sizeof(T), NULL, GL_STATIC_DRAW); + bufferSize = newSize; + } + + // do the actual copy + dataSize = data.size(); + glBufferSubData(getTarget(), 0, dataSize * sizeof(T), &data[0]); + + checkGLError(); +} + +void GLAttributeBuffer::setData(const std::vector& data) { + checkType(RenderDataType::Vector2Float); + setData_helper(data); +} + +void GLAttributeBuffer::setData(const std::vector& data) { + checkType(RenderDataType::Vector3Float); + setData_helper(data); +} + +void GLAttributeBuffer::setData(const std::vector>& data) { + checkType(RenderDataType::Vector3Float); + checkArray(2); + setData_helper(data); +} + +void GLAttributeBuffer::setData(const std::vector>& data) { + checkType(RenderDataType::Vector3Float); + checkArray(3); + setData_helper(data); +} + +void GLAttributeBuffer::setData(const std::vector>& data) { + checkType(RenderDataType::Vector3Float); + checkArray(4); + setData_helper(data); +} + +void GLAttributeBuffer::setData(const std::vector& data) { + checkType(RenderDataType::Vector4Float); + setData_helper(data); +} + +void GLAttributeBuffer::setData(const std::vector& data) { + checkType(RenderDataType::Float); + setData_helper(data); +} + +void GLAttributeBuffer::setData(const std::vector& data) { + checkType(RenderDataType::Float); + + // Convert input data to floats + std::vector floatData(data.size()); + for (unsigned int i = 0; i < data.size(); i++) { + floatData[i] = static_cast(data[i]); + } + + setData_helper(floatData); +} + +void GLAttributeBuffer::setData(const std::vector& data) { + checkType(RenderDataType::Int); + setData_helper(data); +} + +void GLAttributeBuffer::setData(const std::vector& data) { + checkType(RenderDataType::UInt); + setData_helper(data); +} + +void GLAttributeBuffer::setData(const std::vector& data) { + checkType(RenderDataType::Vector2UInt); + setData_helper(data); +} +void GLAttributeBuffer::setData(const std::vector& data) { + checkType(RenderDataType::Vector3UInt); + setData_helper(data); +} + +void GLAttributeBuffer::setData(const std::vector& data) { + checkType(RenderDataType::Vector4UInt); + setData_helper(data); +} + +// === get single data values + +template +T GLAttributeBuffer::getData_helper(size_t ind) { + if (!isSet() || ind >= static_cast(getDataSize() * getArrayCount())) exception("bad getData"); + bind(); + T readValue; + glGetBufferSubData(getTarget(), ind * sizeof(T), sizeof(T), &readValue); + return readValue; +} + +float GLAttributeBuffer::getData_float(size_t ind) { + if (getType() != RenderDataType::Float) exception("bad getData type"); + return getData_helper(ind); +} + +double GLAttributeBuffer::getData_double(size_t ind) { return getData_float(ind); } + +glm::vec2 GLAttributeBuffer::getData_vec2(size_t ind) { + if (getType() != RenderDataType::Vector2Float) exception("bad getData type"); + return getData_helper(ind); +} +glm::vec3 GLAttributeBuffer::getData_vec3(size_t ind) { + if (getType() != RenderDataType::Vector3Float) exception("bad getData type"); + return getData_helper(ind); +} +glm::vec4 GLAttributeBuffer::getData_vec4(size_t ind) { + if (getType() != RenderDataType::Vector4Float) exception("bad getData type"); + return getData_helper(ind); +} +int GLAttributeBuffer::getData_int(size_t ind) { + if (getType() != RenderDataType::Int) exception("bad getData type"); + return getData_helper(ind); +} +uint32_t GLAttributeBuffer::getData_uint32(size_t ind) { + if (getType() != RenderDataType::UInt) exception("bad getData type"); + return getData_helper(ind); +} +glm::uvec2 GLAttributeBuffer::getData_uvec2(size_t ind) { + if (getType() != RenderDataType::Vector2Float) exception("bad getData type"); + return getData_helper(ind); +} +glm::uvec3 GLAttributeBuffer::getData_uvec3(size_t ind) { + if (getType() != RenderDataType::Vector3Float) exception("bad getData type"); + return getData_helper(ind); +} +glm::uvec4 GLAttributeBuffer::getData_uvec4(size_t ind) { + if (getType() != RenderDataType::Vector4Float) exception("bad getData type"); + return getData_helper(ind); +} + +// === get ranges of values + +template +std::vector GLAttributeBuffer::getDataRange_helper(size_t start, size_t count) { + if (!isSet() || start + count > static_cast(getDataSize() * getArrayCount())) exception("bad getData"); + bind(); + std::vector readValues(count); + glGetBufferSubData(getTarget(), start * sizeof(T), count * sizeof(T), &readValues.front()); + return readValues; +} + +std::vector GLAttributeBuffer::getDataRange_float(size_t start, size_t count) { + if (getType() != RenderDataType::Float) exception("bad getData type"); + return getDataRange_helper(start, count); +} + +std::vector GLAttributeBuffer::getDataRange_double(size_t start, size_t count) { + std::vector floatValues = getDataRange_float(start, count); + std::vector values(count); + for (size_t i = 0; i < count; i++) { + values[i] = static_cast(floatValues[i]); + } + return values; +} + +std::vector GLAttributeBuffer::getDataRange_vec2(size_t start, size_t count) { + if (getType() != RenderDataType::Vector2Float) exception("bad getData type"); + return getDataRange_helper(start, count); +} +std::vector GLAttributeBuffer::getDataRange_vec3(size_t start, size_t count) { + if (getType() != RenderDataType::Vector3Float) exception("bad getData type"); + return getDataRange_helper(start, count); +} +std::vector GLAttributeBuffer::getDataRange_vec4(size_t start, size_t count) { + if (getType() != RenderDataType::Vector4Float) exception("bad getData type"); + return getDataRange_helper(start, count); +} +std::vector GLAttributeBuffer::getDataRange_int(size_t start, size_t count) { + if (getType() != RenderDataType::Int) exception("bad getData type"); + return getDataRange_helper(start, count); +} +std::vector GLAttributeBuffer::getDataRange_uint32(size_t start, size_t count) { + if (getType() != RenderDataType::UInt) exception("bad getData type"); + return getDataRange_helper(start, count); +} +std::vector GLAttributeBuffer::getDataRange_uvec2(size_t start, size_t count) { + if (getType() != RenderDataType::Vector2UInt) exception("bad getData type"); + return getDataRange_helper(start, count); +} +std::vector GLAttributeBuffer::getDataRange_uvec3(size_t start, size_t count) { + if (getType() != RenderDataType::Vector3UInt) exception("bad getData type"); + return getDataRange_helper(start, count); +} +std::vector GLAttributeBuffer::getDataRange_uvec4(size_t start, size_t count) { + if (getType() != RenderDataType::Vector4UInt) exception("bad getData type"); + bind(); + return getDataRange_helper(start, count); +} + + +uint32_t GLAttributeBuffer::getNativeBufferID() { return static_cast(VBOLoc); } + +// ============================================================= +// ==================== Texture buffer ========================= +// ============================================================= + + +// create a 1D texture from data +GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int size1D, const unsigned char* data) + : TextureBuffer(1, format_, size1D) { + + glEnable(GL_TEXTURE_1D); + + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_1D, handle); + glTexImage1D(GL_TEXTURE_1D, 0, internalFormat(format), size1D, 0, formatF(format), GL_UNSIGNED_BYTE, data); + checkGLError(); + + setFilterMode(FilterMode::Nearest); +} + +GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int size1D, const float* data) + : TextureBuffer(1, format_, size1D) { + + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_1D, handle); + glTexImage1D(GL_TEXTURE_1D, 0, internalFormat(format), size1D, 0, formatF(format), GL_FLOAT, data); + checkGLError(); + + setFilterMode(FilterMode::Nearest); +} + +// create a 2D texture from data +GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int sizeX_, unsigned int sizeY_, + const unsigned char* data) + : TextureBuffer(2, format_, sizeX_, sizeY_) { + + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_2D, handle); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat(format), sizeX, sizeY, 0, formatF(format), GL_UNSIGNED_BYTE, data); + checkGLError(); + + setFilterMode(FilterMode::Nearest); +} + +GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int sizeX_, unsigned int sizeY_, const float* data) + : TextureBuffer(2, format_, sizeX_, sizeY_) { + + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_2D, handle); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat(format), sizeX, sizeY, 0, formatF(format), GL_FLOAT, data); + checkGLError(); + + setFilterMode(FilterMode::Nearest); +} + +// create a 3D texture from data +GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int sizeX_, unsigned int sizeY_, unsigned int sizeZ_, + const unsigned char* data) + : TextureBuffer(3, format_, sizeX_, sizeY_, sizeZ_) { + + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_3D, handle); + glTexImage3D(GL_TEXTURE_3D, 0, internalFormat(format), sizeX, sizeY, sizeZ, 0, formatF(format), GL_UNSIGNED_BYTE, + data); + checkGLError(); + + setFilterMode(FilterMode::Nearest); +} + +GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int sizeX_, unsigned int sizeY_, unsigned int sizeZ_, + const float* data) + : TextureBuffer(3, format_, sizeX_, sizeY_, sizeZ_) { + + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_3D, handle); + glTexImage3D(GL_TEXTURE_3D, 0, internalFormat(format), sizeX, sizeY, sizeZ, 0, formatF(format), GL_FLOAT, data); + checkGLError(); + + setFilterMode(FilterMode::Nearest); +} + +GLTextureBuffer::~GLTextureBuffer() { glDeleteTextures(1, &handle); } + +void GLTextureBuffer::resize(unsigned int newLen) { + + TextureBuffer::resize(newLen); + + bind(); + if (dim == 1) { + glTexImage1D(GL_TEXTURE_1D, 0, internalFormat(format), sizeX, 0, formatF(format), type(format), nullptr); + } else { + exception("OpenGL error: called 1D resize on not-1D texture"); + } + checkGLError(); +} + +void GLTextureBuffer::resize(unsigned int newX, unsigned int newY) { + + TextureBuffer::resize(newX, newY); + + bind(); + if (dim == 2) { + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat(format), sizeX, sizeY, 0, formatF(format), type(format), nullptr); + } else { + exception("OpenGL error: called 2D resize on not-2D texture"); + } + checkGLError(); +} + +void GLTextureBuffer::resize(unsigned int newX, unsigned int newY, unsigned int newZ) { + + TextureBuffer::resize(newX, newY, newZ); + + bind(); + if (dim == 3) { + glTexImage3D(GL_TEXTURE_3D, 0, internalFormat(format), sizeX, sizeY, sizeZ, 0, formatF(format), type(format), + nullptr); + } else { + exception("OpenGL error: called 3D resize on not-3D texture"); + } + checkGLError(); +} + +void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; +void GLTextureBuffer::setData(const std::vector& data) { + + bind(); + + if (data.size() != getTotalSize()) { + exception("OpenGL error: texture buffer data is not the right size."); + } + + switch (dim) { + case 1: + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeX, formatF(format), type(format), &data.front().x); + break; + case 2: + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeX, sizeY, formatF(format), type(format), &data.front().x); + break; + case 3: + glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, formatF(format), type(format), &data.front().x); + break; + } + + checkGLError(); +}; + +void GLTextureBuffer::setData(const std::vector& data) { + + bind(); + + if (data.size() != getTotalSize()) { + exception("OpenGL error: texture buffer data is not the right size."); + } + + switch (dim) { + case 1: + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeX, formatF(format), type(format), &data.front().x); + break; + case 2: + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeX, sizeY, formatF(format), type(format), &data.front().x); + break; + case 3: + glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, formatF(format), type(format), &data.front().x); + break; + } + + checkGLError(); +}; + +void GLTextureBuffer::setData(const std::vector& data) { + bind(); + + if (data.size() != getTotalSize()) { + exception("OpenGL error: texture buffer data is not the right size."); + } + + switch (dim) { + case 1: + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeX, formatF(format), type(format), &data.front()); + break; + case 2: + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeX, sizeY, formatF(format), type(format), &data.front()); + break; + case 3: + glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, formatF(format), type(format), &data.front()); + break; + } + + checkGLError(); +}; + + +void GLTextureBuffer::setData(const std::vector& data) { + + // Convert to float + std::vector dataFloat(data.size()); + for (size_t i = 0; i < data.size(); i++) { + dataFloat[i] = static_cast(data[i]); + } + + bind(); + + if (data.size() != getTotalSize()) { + exception("OpenGL error: texture buffer data is not the right size."); + } + + switch (dim) { + case 1: + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeX, formatF(format), type(format), &dataFloat.front()); + break; + case 2: + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeX, sizeY, formatF(format), type(format), &dataFloat.front()); + break; + case 3: + glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, formatF(format), type(format), &dataFloat.front()); + break; + } + + checkGLError(); +}; +void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; +void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; +void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; +void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; +void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; +void GLTextureBuffer::setData(const std::vector>& data) { exception("not implemented"); }; +void GLTextureBuffer::setData(const std::vector>& data) { exception("not implemented"); }; +void GLTextureBuffer::setData(const std::vector>& data) { exception("not implemented"); }; + + +void GLTextureBuffer::setFilterMode(FilterMode newMode) { + + bind(); + + switch (newMode) { + case FilterMode::Nearest: + glTexParameteri(textureType(), GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(textureType(), GL_TEXTURE_MIN_FILTER, GL_NEAREST); + break; + case FilterMode::Linear: + glTexParameteri(textureType(), GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(textureType(), GL_TEXTURE_MIN_FILTER, GL_LINEAR); + break; + } + glTexParameteri(textureType(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + if (dim >= 2) { + glTexParameteri(textureType(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + if (dim >= 3) { + glTexParameteri(textureType(), GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } + + checkGLError(); +} + +void* GLTextureBuffer::getNativeHandle() { return reinterpret_cast(getHandle()); } + +uint32_t GLTextureBuffer::getNativeBufferID() { return static_cast(getHandle()); }; + +std::vector GLTextureBuffer::getDataScalar() { + if (dimension(format) != 1) exception("called getDataScalar on texture which does not have a 1 dimensional format"); + + std::vector outData; + outData.resize(getTotalSize()); + + bind(); + glGetTexImage(textureType(), 0, formatF(format), GL_FLOAT, static_cast(&outData.front())); + checkGLError(); + + return outData; +} + +std::vector GLTextureBuffer::getDataVector2() { + if (dimension(format) != 2) exception("called getDataVector2 on texture which does not have a 2 dimensional format"); + + std::vector outData; + outData.resize(getTotalSize()); + + bind(); + glGetTexImage(textureType(), 0, formatF(format), GL_FLOAT, static_cast(&outData.front())); + checkGLError(); + + return outData; +} + +std::vector GLTextureBuffer::getDataVector3() { + if (dimension(format) != 3) exception("called getDataVector3 on texture which does not have a 3 dimensional format"); + exception("not implemented"); + + std::vector outData; + outData.resize(getTotalSize()); + + bind(); + glGetTexImage(textureType(), 0, formatF(format), GL_FLOAT, static_cast(&outData.front())); + checkGLError(); + + return outData; +} + +GLenum GLTextureBuffer::textureType() { + if (dim == 1) { + return GL_TEXTURE_1D; + } else if (dim == 2) { + return GL_TEXTURE_2D; + } else if (dim == 3) { + return GL_TEXTURE_3D; + } + exception("bad texture type"); + return GL_TEXTURE_1D; +} + +void GLTextureBuffer::bind() { + glBindTexture(textureType(), handle); + checkGLError(); +} + +// ============================================================= +// ===================== Render buffer ========================= +// ============================================================= + +GLRenderBuffer::GLRenderBuffer(RenderBufferType type_, unsigned int sizeX_, unsigned int sizeY_) + : RenderBuffer(type_, sizeX_, sizeY_) { + glGenRenderbuffers(1, &handle); + checkGLError(); + resize(sizeX, sizeY); +} + + +GLRenderBuffer::~GLRenderBuffer() { glDeleteRenderbuffers(1, &handle); } + +void GLRenderBuffer::resize(unsigned int newX, unsigned int newY) { + RenderBuffer::resize(newX, newY); + bind(); + + glRenderbufferStorage(GL_RENDERBUFFER, native(type), sizeX, sizeY); + checkGLError(); +} + +void GLRenderBuffer::bind() { + glBindRenderbuffer(GL_RENDERBUFFER, handle); + checkGLError(); +} + + +// ============================================================= +// ===================== Framebuffer =========================== +// ============================================================= + +GLFrameBuffer::GLFrameBuffer(unsigned int sizeX_, unsigned int sizeY_, bool isDefault) { + sizeX = sizeX_; + sizeY = sizeY_; + if (isDefault) { + handle = 0; + } else { + glGenFramebuffers(1, &handle); + glBindFramebuffer(GL_FRAMEBUFFER, handle); + } + checkGLError(); +}; + +GLFrameBuffer::~GLFrameBuffer() { + if (handle != 0) { + glDeleteFramebuffers(1, &handle); + } +} + +void GLFrameBuffer::bind() { + glBindFramebuffer(GL_FRAMEBUFFER, handle); + checkGLError(); +} + +void GLFrameBuffer::addColorBuffer(std::shared_ptr renderBufferIn) { + + // it _better_ be a GL buffer + std::shared_ptr renderBuffer = std::dynamic_pointer_cast(renderBufferIn); + if (!renderBuffer) exception("tried to bind to non-GL render buffer"); + + renderBuffer->bind(); + bind(); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, colorAttachNum(nColorBuffers), GL_RENDERBUFFER, renderBuffer->getHandle()); + checkGLError(); + renderBuffersColor.push_back(renderBuffer); + nColorBuffers++; +} + +void GLFrameBuffer::addDepthBuffer(std::shared_ptr renderBufferIn) { + // it _better_ be a GL buffer + std::shared_ptr renderBuffer = std::dynamic_pointer_cast(renderBufferIn); + if (!renderBuffer) exception("tried to bind to non-GL render buffer"); + + renderBuffer->bind(); + bind(); + + // Sanity checks + // if (depthRenderBuffer != nullptr) exception("OpenGL error: already bound to render buffer"); + // if (depthTexture != nullptr) exception("OpenGL error: already bound to texture buffer"); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer->getHandle()); + checkGLError(); + renderBuffersDepth.push_back(renderBuffer); +} + +void GLFrameBuffer::addColorBuffer(std::shared_ptr textureBufferIn) { + + // it _better_ be a GL buffer + std::shared_ptr textureBuffer = std::dynamic_pointer_cast(textureBufferIn); + if (!textureBuffer) exception("tried to bind to non-GL texture buffer"); + + textureBuffer->bind(); + bind(); + checkGLError(); + + glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachNum(nColorBuffers), GL_TEXTURE_2D, textureBuffer->getHandle(), 0); + + checkGLError(); + textureBuffersColor.push_back(textureBuffer); + nColorBuffers++; +} + +void GLFrameBuffer::addDepthBuffer(std::shared_ptr textureBufferIn) { + + // it _better_ be a GL buffer + std::shared_ptr textureBuffer = std::dynamic_pointer_cast(textureBufferIn); + if (!textureBuffer) exception("tried to bind to non-GL texture buffer"); + + textureBuffer->bind(); + bind(); + checkGLError(); + + // Sanity checks + // if (depthRenderBuffer != nullptr) exception("OpenGL error: already bound to render buffer"); + // if (depthTexture != nullptr) exception("OpenGL error: already bound to texture buffer"); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureBuffer->getHandle(), 0); + checkGLError(); + textureBuffersDepth.push_back(textureBuffer); +} + +void GLFrameBuffer::setDrawBuffers() { + bind(); + + std::vector buffs; + for (int i = 0; i < nColorBuffers; i++) { + buffs.push_back(GL_COLOR_ATTACHMENT0 + i); + } + if (nColorBuffers > 0) { + glDrawBuffers(nColorBuffers, &buffs.front()); + } + checkGLError(); +} + +bool GLFrameBuffer::bindForRendering() { + verifyBufferSizes(); + bind(); + + // Check if the frame buffer is okay + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + // it would be nice to error out here, but it seems that on some platforms this happens even during normal flow. + // For instance, on Windows we get an incomplete framebuffer when the application is minimized see + // https://github.com/nmwsharp/polyscope/issues/36 + + // exception("OpenGL error occurred: framebuffer not complete!"); + // std::cout << "OpenGL error occurred: framebuffer not complete!\n"; + return false; + } + + render::engine->currRenderFramebuffer = this; + + // Set the viewport + if (!viewportSet) { + exception("OpenGL error: viewport not set for framebuffer object. Call GLFrameBuffer::setViewport()"); + } + glViewport(viewportX, viewportY, viewportSizeX, viewportSizeY); + render::engine->setCurrentViewport({viewportX, viewportY, viewportSizeX, viewportSizeY}); + checkGLError(); + + // Enable depth testing + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + + // Enable blending + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + checkGLError(); + return true; +} + +void GLFrameBuffer::clear() { + if (!bindForRendering()) return; + + glClearColor(clearColor[0], clearColor[1], clearColor[2], clearAlpha); + glClearDepth(clearDepth); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +std::array GLFrameBuffer::readFloat4(int xPos, int yPos) { + + // if (colorRenderBuffer == nullptr || colorRenderBuffer->getType() != RenderBufferType::Float4) { + // exception("OpenGL error: buffer is not of right type to read float4 from"); + //} + + glFlush(); + glFinish(); + bind(); + + // Read from the buffer + std::array result; + glReadPixels(xPos, yPos, 1, 1, GL_RGBA, GL_FLOAT, &result); + + return result; +} + +float GLFrameBuffer::readDepth(int xPos, int yPos) { + + // TODO does no error checking for the case where no depth buffer is attached + + glFlush(); + glFinish(); + bind(); + + // Read from the buffer + float result; + glReadPixels(xPos, yPos, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &result); + + return result; +} + +std::vector GLFrameBuffer::readBuffer() { + + glFlush(); + glFinish(); + + bind(); + + int w = getSizeX(); + int h = getSizeY(); + + // Read from openGL + size_t buffSize = w * h * 4; + std::vector buff(buffSize); + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, &(buff.front())); + + return buff; +} + +void GLFrameBuffer::blitTo(FrameBuffer* targetIn) { + + // it _better_ be a GL buffer + GLFrameBuffer* target = dynamic_cast(targetIn); + if (!target) exception("tried to blitTo() non-GL framebuffer"); + + // target->bindForRendering(); + bindForRendering(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target->getHandle()); + + glBlitFramebuffer(0, 0, getSizeX(), getSizeY(), 0, 0, target->getSizeX(), target->getSizeY(), GL_COLOR_BUFFER_BIT, + GL_LINEAR); + checkGLError(); +} + +uint32_t GLFrameBuffer::getNativeBufferID() { return handle; } + +// ============================================================= +// ================== Shader Program ========================= +// ============================================================= + + +GLCompiledProgram::GLCompiledProgram(const std::vector& stages, DrawMode dm) : drawMode(dm) { + + // Collect attributes and uniforms from all of the shaders + for (const ShaderStageSpecification& s : stages) { + for (ShaderSpecUniform u : s.uniforms) { + addUniqueUniform(u); + } + for (ShaderSpecAttribute a : s.attributes) { + addUniqueAttribute(a); + } + for (ShaderSpecTexture t : s.textures) { + addUniqueTexture(t); + } + } + + if (attributes.size() == 0) { + throw std::invalid_argument("Uh oh... GLProgram has no attributes"); + } + + // Perform setup tasks + compileGLProgram(stages); + checkGLError(); + + setDataLocations(); + checkGLError(); +} + +GLCompiledProgram::~GLCompiledProgram() { glDeleteProgram(programHandle); } + +void GLCompiledProgram::compileGLProgram(const std::vector& stages) { + + + // Compile all of the shaders + std::vector handles; + for (const ShaderStageSpecification& s : stages) { + ShaderHandle h = glCreateShader(native(s.stage)); + std::array srcs = {s.src.c_str(), shaderCommonSource}; + glShaderSource(h, 2, &(srcs[0]), nullptr); + glCompileShader(h); + + // Catch the error here, so we can print shader source before re-throwing + try { + + GLint status; + glGetShaderiv(h, GL_COMPILE_STATUS, &status); + if (!status) { + printShaderInfoLog(h); + std::cout << "Program text:" << std::endl; + std::cout << s.src.c_str() << std::endl; + exception("[polyscope] GL shader compile failed"); + } + + if (options::verbosity > 2) { + printShaderInfoLog(h); + } + if (options::verbosity > 100) { + std::cout << "Program text:" << std::endl; + std::cout << s.src.c_str() << std::endl; + } + + checkGLError(); + } catch (...) { + std::cout << "GLError() after shader compilation! Program text:" << std::endl; + + // process shader line-by-line to print line numbers: + std::stringstream ss(s.src); + std::string line; + size_t lineNo = 1; + while (std::getline(ss, line, '\n')) { + std::cout << std::setw(4) << lineNo << ": " << line << std::endl; + lineNo++; + } + throw; + } + + handles.push_back(h); + } + + // Create the program and attach the shaders + programHandle = glCreateProgram(); + for (ShaderHandle h : handles) { + glAttachShader(programHandle, h); + } + + // Link the program + glLinkProgram(programHandle); + if (options::verbosity > 2) { + printProgramInfoLog(programHandle); + } + GLint status; + glGetProgramiv(programHandle, GL_LINK_STATUS, &status); + if (!status) { + printProgramInfoLog(programHandle); + exception("[polyscope] GL program compile failed"); + } + + // Delete the shaders we just compiled, they aren't used after link + for (ShaderHandle h : handles) { + glDeleteShader(h); + } + + checkGLError(); +} + +void GLCompiledProgram::setDataLocations() { + glUseProgram(programHandle); + + // Uniforms + for (GLShaderUniform& u : uniforms) { + u.location = glGetUniformLocation(programHandle, u.name.c_str()); + if (u.location == -1) { + if (options::verbosity > 3) { + info("failed to get location for uniform " + u.name); + } + } + } + + // Attributes + for (GLShaderAttribute& a : attributes) { + a.location = glGetAttribLocation(programHandle, a.name.c_str()); + if (a.location == -1) { + if (options::verbosity > 3) { + info("failed to get location for attribute " + a.name); + } + } + } + + // Textures + for (GLShaderTexture& t : textures) { + t.location = glGetUniformLocation(programHandle, t.name.c_str()); + if (t.location == -1) { + if (options::verbosity > 3) { + info("failed to get location for texture " + t.name); + } + } + } + + checkGLError(); +} + +void GLCompiledProgram::addUniqueAttribute(ShaderSpecAttribute newAttribute) { + for (GLShaderAttribute& a : attributes) { + if (a.name == newAttribute.name) { + + // if it occurs twice, confirm that the occurences match + if (a.type != newAttribute.type) + exception("attribute " + a.name + " appears twice in program with different types"); + + return; + } + } + attributes.push_back(GLShaderAttribute{newAttribute.name, newAttribute.type, newAttribute.arrayCount, -1, nullptr}); +} + +void GLCompiledProgram::addUniqueUniform(ShaderSpecUniform newUniform) { + for (GLShaderUniform& u : uniforms) { + if (u.name == newUniform.name) { + + // if it occurs twice, confirm that the occurences match + if (u.type != newUniform.type) exception("uniform " + u.name + " appears twice in program with different types"); + + return; + } + } + uniforms.push_back(GLShaderUniform{newUniform.name, newUniform.type, false, 777}); +} + +void GLCompiledProgram::addUniqueTexture(ShaderSpecTexture newTexture) { + for (GLShaderTexture& t : textures) { + if (t.name == newTexture.name) { + + // if it occurs twice, confirm that the occurences match + if (t.dim != newTexture.dim) + exception("texture " + t.name + " appears twice in program with different dimensions"); + + return; + } + } + textures.push_back(GLShaderTexture{newTexture.name, newTexture.dim, 777, false, nullptr, nullptr, 777}); +} + + +GLShaderProgram::GLShaderProgram(const std::shared_ptr& compiledProgram_) + : ShaderProgram(compiledProgram_->getDrawMode()), uniforms(compiledProgram_->getUniforms()), + attributes(compiledProgram_->getAttributes()), textures(compiledProgram_->getTextures()), + compiledProgram(compiledProgram_) { + + // Create a VAO + glGenVertexArrays(1, &vaoHandle); + checkGLError(); + + createBuffers(); // only handles texture & index things, attributes are lazily created + checkGLError(); +} + +GLShaderProgram::~GLShaderProgram() { glDeleteVertexArrays(1, &vaoHandle); } + +void GLShaderProgram::bindVAO() { glBindVertexArray(vaoHandle); } + +void GLShaderProgram::createBuffers() { + bindVAO(); + + // === Generate textures + + // Verify we have enough texture units + GLint nAvailTextureUnits; + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &nAvailTextureUnits); + if ((int)textures.size() > nAvailTextureUnits) { + throw std::invalid_argument("Attempted to load more textures than the number of available texture " + "units (" + + std::to_string(nAvailTextureUnits) + ")."); + } + + // Set indices sequentially + uint32_t iTexture = 0; + for (GLShaderTexture& t : textures) { + t.index = iTexture++; + } + + checkGLError(); +} + +void GLShaderProgram::setAttribute(std::string name, std::shared_ptr externalBuffer) { + bindVAO(); + checkGLError(); + + for (GLShaderAttribute& a : attributes) { + if (a.name == name) { + + if (a.location == -1) return; // attributes which were optimized out or something, do nothing + + // check that types match + int compatCount = renderDataTypeCountCompatbility(a.type, externalBuffer->getType()); + if (compatCount == 0) + throw std::invalid_argument("Tried to set attribute " + name + " to incompatibile type. Attribute " + + renderDataTypeName(a.type) + " set with buffer of type " + + renderDataTypeName(externalBuffer->getType())); + + // check multiple-set errors (duplicates in externalBuffers list?) + if (a.buff) throw std::invalid_argument("attribute " + name + " is already set"); + + // cast to the engine type (booooooo) + std::shared_ptr engineExtBuff = std::dynamic_pointer_cast(externalBuffer); + if (!engineExtBuff) throw std::invalid_argument("attribute " + name + " external buffer engine type cast failed"); + + a.buff = engineExtBuff; + checkGLError(); + + a.buff->bind(); + checkGLError(); + + assignBufferToVAO(a); + checkGLError(); + return; + } + } + + throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); +} + +void GLShaderProgram::assignBufferToVAO(GLShaderAttribute& a) { + bindVAO(); + a.buff->bind(); + checkGLError(); + + // Choose the correct type for the buffer + for (int iArrInd = 0; iArrInd < a.arrayCount; iArrInd++) { + + glEnableVertexAttribArray(a.location + iArrInd); + + switch (a.type) { + case RenderDataType::Float: + glVertexAttribPointer(a.location + iArrInd, 1, GL_FLOAT, GL_FALSE, sizeof(float) * 1 * a.arrayCount, + reinterpret_cast(sizeof(float) * 1 * iArrInd)); + break; + case RenderDataType::Int: + glVertexAttribPointer(a.location + iArrInd, 1, GL_INT, GL_FALSE, sizeof(int) * 1 * a.arrayCount, + reinterpret_cast(sizeof(int) * 1 * iArrInd)); + break; + case RenderDataType::UInt: + glVertexAttribPointer(a.location + iArrInd, 1, GL_UNSIGNED_INT, GL_FALSE, sizeof(uint32_t) * 1 * a.arrayCount, + reinterpret_cast(sizeof(uint32_t) * 1 * iArrInd)); + break; + case RenderDataType::Vector2Float: + glVertexAttribPointer(a.location + iArrInd, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2 * a.arrayCount, + reinterpret_cast(sizeof(float) * 2 * iArrInd)); + break; + case RenderDataType::Vector3Float: + glVertexAttribPointer(a.location + iArrInd, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3 * a.arrayCount, + reinterpret_cast(sizeof(float) * 3 * iArrInd)); + break; + case RenderDataType::Vector4Float: + glVertexAttribPointer(a.location + iArrInd, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4 * a.arrayCount, + reinterpret_cast(sizeof(float) * 4 * iArrInd)); + break; + case RenderDataType::Vector2UInt: + glVertexAttribPointer(a.location + iArrInd, 2, GL_UNSIGNED_INT, GL_FALSE, sizeof(uint32_t) * 2 * a.arrayCount, + reinterpret_cast(sizeof(uint32_t) * 2 * iArrInd)); + break; + case RenderDataType::Vector3UInt: + glVertexAttribPointer(a.location + iArrInd, 3, GL_UNSIGNED_INT, GL_FALSE, sizeof(uint32_t) * 3 * a.arrayCount, + reinterpret_cast(sizeof(uint32_t) * 3 * iArrInd)); + break; + case RenderDataType::Vector4UInt: + glVertexAttribPointer(a.location + iArrInd, 4, GL_UNSIGNED_INT, GL_FALSE, sizeof(uint32_t) * 4 * a.arrayCount, + reinterpret_cast(sizeof(uint32_t) * 4 * iArrInd)); + break; + default: + throw std::invalid_argument("Unrecognized GLShaderAttribute type"); + break; + } + } + + checkGLError(); +} + +void GLShaderProgram::createBuffer(GLShaderAttribute& a) { + if (a.location == -1) return; + + // generate the buffer if needed + std::shared_ptr newBuff = glEngine->generateAttributeBuffer(a.type, a.arrayCount); + std::shared_ptr engineNewBuff = std::dynamic_pointer_cast(newBuff); + if (!engineNewBuff) throw std::invalid_argument("buffer type cast failed"); + a.buff = engineNewBuff; + + assignBufferToVAO(a); + + checkGLError(); +} + +void GLShaderProgram::ensureBufferExists(GLShaderAttribute& a) { + if (a.location != -1 && !a.buff) { + createBuffer(a); + } +} + +bool GLShaderProgram::hasUniform(std::string name) { + for (GLShaderUniform& u : uniforms) { + if (u.name == name && u.location != -1) { + return true; + } + } + return false; +} + +// Set an integer +void GLShaderProgram::setUniform(std::string name, int val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Int) { + glUniform1i(u.location, val); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set an unsigned integer +void GLShaderProgram::setUniform(std::string name, unsigned int val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::UInt) { + glUniform1ui(u.location, val); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a float +void GLShaderProgram::setUniform(std::string name, float val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Float) { + glUniform1f(u.location, val); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a double --- WARNING casts down to float +void GLShaderProgram::setUniform(std::string name, double val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Float) { + glUniform1f(u.location, static_cast(val)); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a 4x4 uniform matrix +// TODO why do we use a pointer here... makes no sense +void GLShaderProgram::setUniform(std::string name, float* val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Matrix44Float) { + glUniformMatrix4fv(u.location, 1, false, val); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a vector2 uniform +void GLShaderProgram::setUniform(std::string name, glm::vec2 val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Vector2Float) { + glUniform2f(u.location, val.x, val.y); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a vector3 uniform +void GLShaderProgram::setUniform(std::string name, glm::vec3 val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Vector3Float) { + glUniform3f(u.location, val.x, val.y, val.z); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a vector4 uniform +void GLShaderProgram::setUniform(std::string name, glm::vec4 val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Vector4Float) { + glUniform4f(u.location, val.x, val.y, val.z, val.w); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a vector3 uniform from a float array +void GLShaderProgram::setUniform(std::string name, std::array val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Vector3Float) { + glUniform3f(u.location, val[0], val[1], val[2]); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a vec4 uniform +void GLShaderProgram::setUniform(std::string name, float x, float y, float z, float w) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Vector4Float) { + glUniform4f(u.location, x, y, z, w); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a uint vector2 uniform +void GLShaderProgram::setUniform(std::string name, glm::uvec2 val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Vector2UInt) { + glUniform2ui(u.location, val.x, val.y); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a uint vector3 uniform +void GLShaderProgram::setUniform(std::string name, glm::uvec3 val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Vector3UInt) { + glUniform3ui(u.location, val.x, val.y, val.z); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +// Set a uint vector4 uniform +void GLShaderProgram::setUniform(std::string name, glm::uvec4 val) { + glUseProgram(compiledProgram->getHandle()); + + for (GLShaderUniform& u : uniforms) { + if (u.name == name) { + if (u.location == -1) return; + if (u.type == RenderDataType::Vector4UInt) { + glUniform4ui(u.location, val.x, val.y, val.z, val.w); + u.isSet = true; + } else { + throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); + } + return; + } + } + throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); +} + +bool GLShaderProgram::hasAttribute(std::string name) { + for (GLShaderAttribute& a : attributes) { + if (a.name == name && a.location != -1) { + return true; + } + } + return false; +} + +bool GLShaderProgram::attributeIsSet(std::string name) { + for (GLShaderAttribute& a : attributes) { + if (a.name == name && a.location != -1) { + return a.buff->isSet(); + } + } + return false; +} + +std::shared_ptr GLShaderProgram::getAttributeBuffer(std::string name) { + // WARNING: may be null if the attribute was optimized out + for (GLShaderAttribute& a : attributes) { + if (a.name == name) { + return a.buff; + } + } + throw std::invalid_argument("No attribute with name " + name); + return nullptr; +}; + + +void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { + glBindVertexArray(vaoHandle); + + // pass-through to the buffer + for (GLShaderAttribute& a : attributes) { + if (a.name == name && a.location != -1) { + ensureBufferExists(a); + a.buff->setData(data); + return; + } + } + + throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); +} + +void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { + glBindVertexArray(vaoHandle); // TODO remove these? + + // pass-through to the buffer + for (GLShaderAttribute& a : attributes) { + if (a.name == name && a.location != -1) { + ensureBufferExists(a); + a.buff->setData(data); + return; + } + } + + throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); +} + +void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { + glBindVertexArray(vaoHandle); + + // pass-through to the buffer + for (GLShaderAttribute& a : attributes) { + if (a.name == name && a.location != -1) { + ensureBufferExists(a); + a.buff->setData(data); + return; + } + } + + throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); +} + +void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { + glBindVertexArray(vaoHandle); + + // pass-through to the buffer + for (GLShaderAttribute& a : attributes) { + if (a.name == name && a.location != -1) { + ensureBufferExists(a); + a.buff->setData(data); + return; + } + } + + throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); +} + +void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { + glBindVertexArray(vaoHandle); + + // pass-through to the buffer + for (GLShaderAttribute& a : attributes) { + if (a.name == name && a.location != -1) { + ensureBufferExists(a); + a.buff->setData(data); + return; + } + } + + throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); +} + +void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { + glBindVertexArray(vaoHandle); + + // pass-through to the buffer + for (GLShaderAttribute& a : attributes) { + if (a.name == name && a.location != -1) { + ensureBufferExists(a); + a.buff->setData(data); + return; + } + } + + throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); +} + +void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { + glBindVertexArray(vaoHandle); + + // pass-through to the buffer + for (GLShaderAttribute& a : attributes) { + if (a.name == name && a.location != -1) { + ensureBufferExists(a); + a.buff->setData(data); + return; + } + } + + throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); +} + +bool GLShaderProgram::hasTexture(std::string name) { + for (GLShaderTexture& t : textures) { + if (t.name == name && t.location != -1) { + return true; + } + } + return false; +} + +bool GLShaderProgram::textureIsSet(std::string name) { + for (GLShaderTexture& t : textures) { + if (t.name == name && t.location != -1) { + return t.isSet; + } + } + return false; +} + +void GLShaderProgram::setTexture1D(std::string name, unsigned char* texData, unsigned int length) { + throw std::invalid_argument("This code hasn't been testded yet."); + + // Find the right texture + for (GLShaderTexture& t : textures) { + if (t.name != name || t.location == -1) continue; + + if (t.isSet) { + throw std::invalid_argument("Attempted to set texture twice"); + } + + if (t.dim != 1) { + throw std::invalid_argument("Tried to use texture with mismatched dimension " + std::to_string(t.dim)); + } + + // Create a new texture object + t.textureBufferOwned.reset(new GLTextureBuffer(TextureFormat::RGB8, length, texData)); + t.textureBuffer = t.textureBufferOwned.get(); + + + // Set policies + // glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + // glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + t.isSet = true; + return; + } + + throw std::invalid_argument("No texture with name " + name); +} + +void GLShaderProgram::setTexture2D(std::string name, unsigned char* texData, unsigned int width, unsigned int height, + bool withAlpha, bool useMipMap, bool repeat) { + // Find the right texture + for (GLShaderTexture& t : textures) { + if (t.name != name || t.location == -1) continue; + + if (t.isSet) { + throw std::invalid_argument("Attempted to set texture twice"); + } + + if (t.dim != 2) { + throw std::invalid_argument("Tried to use texture with mismatched dimension " + std::to_string(t.dim)); + } + + if (withAlpha) { + t.textureBufferOwned.reset(new GLTextureBuffer(TextureFormat::RGBA8, width, height, texData)); + } else { + t.textureBufferOwned.reset(new GLTextureBuffer(TextureFormat::RGB8, width, height, texData)); + } + t.textureBuffer = t.textureBufferOwned.get(); + + + // Set policies + if (repeat) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Use mip maps + if (useMipMap) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glGenerateMipmap(GL_TEXTURE_2D); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + t.isSet = true; + return; + } + + throw std::invalid_argument("No texture with name " + name); +} + +void GLShaderProgram::setTextureFromBuffer(std::string name, TextureBuffer* textureBuffer) { + glUseProgram(compiledProgram->getHandle()); + + // Find the right texture + for (GLShaderTexture& t : textures) { + if (t.name != name || t.location == -1) continue; + + if (t.dim != (int)textureBuffer->getDimension()) { + throw std::invalid_argument("Tried to use texture with mismatched dimension " + std::to_string(t.dim)); + } + + t.textureBuffer = dynamic_cast(textureBuffer); + if (!t.textureBuffer) { + throw std::invalid_argument("Bad texture in setTextureFromBuffer()"); + } + + t.isSet = true; + return; + } + + throw std::invalid_argument("No texture with name " + name); +} + +void GLShaderProgram::setTextureFromColormap(std::string name, const std::string& colormapName, bool allowUpdate) { + const ValueColorMap& colormap = render::engine->getColorMap(colormapName); + + // TODO switch to global shared buffers from colormap + + // Find the right texture + for (GLShaderTexture& t : textures) { + if (t.name != name || t.location == -1) continue; + + if (t.isSet && !allowUpdate) { + throw std::invalid_argument("Attempted to set texture twice"); + } + + if (t.dim != 1) { + throw std::invalid_argument("Tried to use texture with mismatched dimension " + std::to_string(t.dim)); + } + + // Fill a buffer with the data + unsigned int dataLength = colormap.values.size() * 3; + std::vector colorBuffer(dataLength); + for (unsigned int i = 0; i < colormap.values.size(); i++) { + colorBuffer[3 * i + 0] = static_cast(colormap.values[i][0]); + colorBuffer[3 * i + 1] = static_cast(colormap.values[i][1]); + colorBuffer[3 * i + 2] = static_cast(colormap.values[i][2]); + } + + // glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, colormap.values.size(), 0, GL_RGB, GL_FLOAT, &(colorBuffer[0])); + t.textureBufferOwned = std::dynamic_pointer_cast( + engine->generateTextureBuffer(TextureFormat::RGB32F, colormap.values.size(), &(colorBuffer[0]))); + t.textureBufferOwned->setFilterMode(FilterMode::Linear); + t.textureBuffer = t.textureBufferOwned.get(); + + + t.isSet = true; + return; + } + + throw std::invalid_argument("No texture with name " + name); +} + +void GLShaderProgram::setIndex(std::shared_ptr externalBuffer) { + if (!useIndex) { + throw std::invalid_argument("Tried to setIndex() when program drawMode does not use indexed " + "drawing"); + } + + // cast to the engine type (booooooo) + std::shared_ptr engineExtBuff = std::dynamic_pointer_cast(externalBuffer); + if (!engineExtBuff) throw std::invalid_argument("index attribute external buffer engine type cast failed"); + + switch (engineExtBuff->getType()) { + case RenderDataType::Int: + // NOTE: the render pass expects these to be unsigned.... but negative + // values don't make sense anyway, so I think it's okay to just let it slide + indexSizeMult = 1; + break; + case RenderDataType::UInt: + indexSizeMult = 1; + break; + case RenderDataType::Vector2UInt: + indexSizeMult = 2; + break; + case RenderDataType::Vector3UInt: + indexSizeMult = 3; + break; + case RenderDataType::Vector4UInt: + indexSizeMult = 4; + break; + case RenderDataType::Float: + case RenderDataType::Vector2Float: + case RenderDataType::Vector3Float: + case RenderDataType::Vector4Float: + case RenderDataType::Matrix44Float: + throw std::invalid_argument("index buffer should be integer type"); + break; + } + + indexBuffer = engineExtBuff; + + // bind it as the VAOs index buffer + bindVAO(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, engineExtBuff->getHandle()); + + checkGLError(); +} + +// Check that uniforms and attributes are all set and of consistent size +void GLShaderProgram::validateData() { + + // WARNING: this function is poorly named, it doesn't just do sanity checks, it also sets important values + + // Check uniforms + for (GLShaderUniform& u : uniforms) { + if (u.location == -1) continue; + if (!u.isSet) { + throw std::invalid_argument("Uniform " + u.name + " has not been set"); + } + } + + // Check attributes + int64_t attributeSize = -1; + for (GLShaderAttribute a : attributes) { + if (a.location == -1) continue; + if (!a.buff) { + throw std::invalid_argument("Attribute " + a.name + " has no buffer attached"); + } + if (a.buff->getDataSize() < 0) { + throw std::invalid_argument("Attribute " + a.name + " has not been set"); + } + + int compatCount = renderDataTypeCountCompatbility(a.type, a.buff->getType()); + + if (attributeSize == -1) { // first one we've seen + attributeSize = a.buff->getDataSize() / (compatCount); + } else { // not the first one we've seen + if (a.buff->getDataSize() / (compatCount) != attributeSize) { + throw std::invalid_argument("Attributes have inconsistent size. One attribute has size " + + std::to_string(attributeSize) + " and " + a.name + " has size " + + std::to_string(a.buff->getDataSize())); + } + } + } + + // Check textures + for (GLShaderTexture& t : textures) { + if (t.location == -1) continue; + if (!t.isSet) { + throw std::invalid_argument("Texture " + t.name + " has not been set"); + } + } + + // Check index (if applicable) + if (useIndex && !indexBuffer) { + throw std::invalid_argument("Index buffer has not been filled"); + } + + // Set the size + if (useIndex) { + drawDataLength = static_cast(indexSizeMult * indexBuffer->getDataSize()); + } else { + drawDataLength = static_cast(attributeSize); + } + + // Check instanced (if applicable) + if (drawMode == DrawMode::TrianglesInstanced || drawMode == DrawMode::TriangleStripInstanced) { + if (instanceCount == INVALID_IND_32) { + throw std::invalid_argument("Must set instance count to use instanced drawing"); + } + } +} + +void GLShaderProgram::setPrimitiveRestartIndex(unsigned int restartIndex_) { + if (!usePrimitiveRestart) { + exception("setPrimitiveRestartIndex() called, but draw mode does not support restart indices."); + } + restartIndex = restartIndex_; + primitiveRestartIndexSet = true; +} + +void GLShaderProgram::setInstanceCount(uint32_t instanceCount_) { instanceCount = instanceCount_; } + +void GLShaderProgram::activateTextures() { + for (GLShaderTexture& t : textures) { + if (t.location == -1) continue; + + glActiveTexture(GL_TEXTURE0 + t.index); + t.textureBuffer->bind(); + glUniform1i(t.location, t.index); + } +} + +void GLShaderProgram::draw() { + validateData(); + + glUseProgram(compiledProgram->getHandle()); + glBindVertexArray(vaoHandle); + + if (usePrimitiveRestart) { + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(restartIndex); + } + + activateTextures(); + + switch (drawMode) { + case DrawMode::Points: + glDrawArrays(GL_POINTS, 0, drawDataLength); + break; + case DrawMode::Triangles: + glDrawArrays(GL_TRIANGLES, 0, drawDataLength); + break; + case DrawMode::Lines: + glDrawArrays(GL_LINES, 0, drawDataLength); + break; + case DrawMode::TrianglesAdjacency: + glDrawArrays(GL_TRIANGLES_ADJACENCY, 0, drawDataLength); + break; + case DrawMode::LinesAdjacency: + glDrawArrays(GL_LINES_ADJACENCY, 0, drawDataLength); + break; + case DrawMode::IndexedLines: + // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); // TODO delete these + glDrawElements(GL_LINES, drawDataLength, GL_UNSIGNED_INT, 0); + break; + case DrawMode::IndexedLineStrip: + // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); + glDrawElements(GL_LINE_STRIP, drawDataLength, GL_UNSIGNED_INT, 0); + break; + case DrawMode::IndexedLinesAdjacency: + // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); + glDrawElements(GL_LINES_ADJACENCY, drawDataLength, GL_UNSIGNED_INT, 0); + break; + case DrawMode::IndexedLineStripAdjacency: + // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); + glDrawElements(GL_LINE_STRIP_ADJACENCY, drawDataLength, GL_UNSIGNED_INT, 0); + break; + case DrawMode::IndexedTriangles: + // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); + glDrawElements(GL_TRIANGLES, drawDataLength, GL_UNSIGNED_INT, 0); + break; + case DrawMode::TrianglesInstanced: + glDrawArraysInstanced(GL_TRIANGLES, 0, drawDataLength, instanceCount); + break; + case DrawMode::TriangleStripInstanced: + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, drawDataLength, instanceCount); + break; + } + + if (usePrimitiveRestart) { + glDisable(GL_PRIMITIVE_RESTART); + } + + checkGLError(); +} + +GLEngine::GLEngine() {} + +void GLEngine::initialize() { + + // initialize + EGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + EGLint major, minor; + eglInitialize(eglDpy, &major, &minor); + checkEGLError(); + + // choose config + EGLint numConfigs; + EGLConfig eglCfg; + eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs); + checkEGLError(); + + // create the surface + EGLSurface eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs); + checkEGLError(); + + // bind + eglBindAPI(EGL_OPENGL_API); + checkEGLError(); + + // make a context + EGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL); + eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx); + checkEGLError(); + + + /* + // Small callback function for GLFW errors + auto error_print_callback = [](int error, const char* description) { + if (polyscope::options::verbosity > 0) { + std::cout << "GLFW emitted error: " << description << std::endl; + } + }; + + // === Initialize glfw + glfwSetErrorCallback(error_print_callback); + if (!glfwInit()) { + exception(options::printPrefix + "ERROR: Failed to initialize glfw"); + } + + // OpenGL version things + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + #if __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + #endif + + // Create the window with context + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_FALSE); + mainWindow = glfwCreateWindow(view::windowWidth, view::windowHeight, options::programName.c_str(), NULL, NULL); + glfwMakeContextCurrent(mainWindow); + glfwSetWindowPos(mainWindow, view::initWindowPosX, view::initWindowPosY); + + // Set initial window size + int newBufferWidth, newBufferHeight, newWindowWidth, newWindowHeight; + glfwGetFramebufferSize(mainWindow, &newBufferWidth, &newBufferHeight); + glfwGetWindowSize(mainWindow, &newWindowWidth, &newWindowHeight); + view::bufferWidth = newBufferWidth; + view::bufferHeight = newBufferHeight; + view::windowWidth = newWindowWidth; + view::windowHeight = newWindowHeight; + + setWindowResizable(view::windowResizable); + */ + + view::bufferWidth = view::windowWidth; + view::bufferHeight = view::windowHeight; + +// === Initialize openGL +// Load openGL functions (using GLAD) +#ifndef __APPLE__ + if (!gladLoadGL()) { + exception(options::printPrefix + "ERROR: Failed to load openGL using GLAD"); + } +#endif + if (options::verbosity > 0) { + std::cout << options::printPrefix << "Backend: openGL3_glfw_egl -- " + << "Loaded openGL version: " << glGetString(GL_VERSION) << std::endl; + } + +#ifdef __APPLE__ + // Hack to classify the process as interactive + glfwPollEvents(); +#endif + + { // Manually create the screen frame buffer + GLFrameBuffer* glScreenBuffer = new GLFrameBuffer(view::bufferWidth, view::bufferHeight, true); + displayBuffer.reset(glScreenBuffer); + glScreenBuffer->bind(); + glClearColor(1., 1., 1., 0.); + // glClearColor(0., 0., 0., 0.); + // glClearDepth(1.); + } + + populateDefaultShadersAndRules(); +} + + +void GLEngine::initializeImGui() { + return; // TODO + + /* + bindDisplay(); + + ImGui::CreateContext(); // must call once at start + + // Set up ImGUI glfw bindings + ImGui_ImplGlfw_InitForOpenGL(mainWindow, true); + const char* glsl_version = "#version 150"; + ImGui_ImplOpenGL3_Init(glsl_version); + + configureImGui(); + */ +} + +void GLEngine::shutdownImGui() { + return; + + /* + // ImGui shutdown things + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + */ +} + +void GLEngine::swapDisplayBuffers() { + return; + + /* + bindDisplay(); + glfwSwapBuffers(mainWindow); + */ +} + +std::vector GLEngine::readDisplayBuffer() { + // TODO do we need to bind here? + + glFlush(); + glFinish(); + + // Get buffer size + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + int w = viewport[2]; + int h = viewport[3]; + + // Read from openGL + size_t buffSize = w * h * 4; + std::vector buff(buffSize); + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, &(buff.front())); + + return buff; +} + + +void GLEngine::checkError(bool fatal) { checkGLError(fatal); } + +void GLEngine::makeContextCurrent() { + glfwMakeContextCurrent(mainWindow); + glfwSwapInterval(options::enableVSync ? 1 : 0); +} + +void GLEngine::focusWindow() { glfwFocusWindow(mainWindow); } + +void GLEngine::showWindow() { glfwShowWindow(mainWindow); } + +void GLEngine::hideWindow() { + glfwHideWindow(mainWindow); + glfwPollEvents(); // this shouldn't be necessary, but seems to be needed at least on macOS. Perhaps realted to a + // glfw bug? e.g. https://github.com/glfw/glfw/issues/1300 and related bugs +} + +void GLEngine::updateWindowSize(bool force) { + int newBufferWidth, newBufferHeight, newWindowWidth, newWindowHeight; + glfwGetFramebufferSize(mainWindow, &newBufferWidth, &newBufferHeight); + glfwGetWindowSize(mainWindow, &newWindowWidth, &newWindowHeight); + if (force || newBufferWidth != view::bufferWidth || newBufferHeight != view::bufferHeight || + newWindowHeight != view::windowHeight || newWindowWidth != view::windowWidth) { + // Basically a resize callback + requestRedraw(); + + // prevent any division by zero for e.g. aspect ratio calcs + if (newBufferHeight == 0) newBufferHeight = 1; + if (newWindowHeight == 0) newWindowHeight = 1; + + view::bufferWidth = newBufferWidth; + view::bufferHeight = newBufferHeight; + view::windowWidth = newWindowWidth; + view::windowHeight = newWindowHeight; + + render::engine->resizeScreenBuffers(); + render::engine->setScreenBufferViewports(); + } +} + + +void GLEngine::applyWindowSize() { + glfwSetWindowSize(mainWindow, view::windowWidth, view::windowHeight); + + // on some platform size changes are asynchonous, need to ensure it completes + // we don't want to just retry until the resize has happened, because it could be impossible + // TODO it seems like on X11 sometimes even this isn't enough? + glfwWaitEvents(); + + updateWindowSize(true); +} + + +void GLEngine::setWindowResizable(bool newVal) { + glfwSetWindowAttrib(mainWindow, GLFW_RESIZABLE, newVal ? GLFW_TRUE : GLFW_FALSE); +} + +bool GLEngine::getWindowResizable() { return glfwGetWindowAttrib(mainWindow, GLFW_RESIZABLE); } + +std::tuple GLEngine::getWindowPos() { + int x, y; + glfwGetWindowPos(mainWindow, &x, &y); + return std::tuple{x, y}; +} + +bool GLEngine::windowRequestsClose() { + bool shouldClose = glfwWindowShouldClose(mainWindow); + if (shouldClose) { + glfwSetWindowShouldClose(mainWindow, false); // un-set the state bit so we can close again + return true; + } + return false; +} + +void GLEngine::pollEvents() { glfwPollEvents(); } + +bool GLEngine::isKeyPressed(char c) { + if (c >= '0' && c <= '9') return ImGui::IsKeyPressed(GLFW_KEY_0 + (c - '0')); + if (c >= 'a' && c <= 'z') return ImGui::IsKeyPressed(GLFW_KEY_A + (c - 'a')); + if (c >= 'A' && c <= 'Z') return ImGui::IsKeyPressed(GLFW_KEY_A + (c - 'A')); + exception("keyPressed only supports 0-9, a-z, A-Z"); + return false; +} + +int GLEngine::getKeyCode(char c) { + if (c >= '0' && c <= '9') return static_cast(GLFW_KEY_0) + (c - '0'); + if (c >= 'a' && c <= 'z') return static_cast(GLFW_KEY_A) + (c - 'a'); + if (c >= 'A' && c <= 'Z') return static_cast(GLFW_KEY_A) + (c - 'A'); + exception("getKeyCode only supports 0-9, a-z, A-Z"); + return -1; +} + +void GLEngine::ImGuiNewFrame() { + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + // ImGui::ShowDemoWindow(); +} + +void GLEngine::ImGuiRender() { + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); +} + +void GLEngine::setDepthMode(DepthMode newMode) { + switch (newMode) { + case DepthMode::Less: + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + break; + case DepthMode::LEqual: + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_TRUE); + break; + case DepthMode::LEqualReadOnly: + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_FALSE); + break; + case DepthMode::PassReadOnly: + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_FALSE); + break; + case DepthMode::Greater: + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_GREATER); + glDepthMask(GL_TRUE); + break; + case DepthMode::Disable: + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); // doesn't actually matter + break; + } +} + +void GLEngine::setBlendMode(BlendMode newMode) { + switch (newMode) { + case BlendMode::AlphaOver: + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // for premultiplied alpha + break; + case BlendMode::OverNoWrite: + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + break; + case BlendMode::AlphaUnder: + glEnable(GL_BLEND); + glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE); // for premultiplied alpha + break; + case BlendMode::Zero: + glEnable(GL_BLEND); + glBlendFunc(GL_ZERO, GL_ZERO); + break; + case BlendMode::WeightedAdd: + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE); + break; + case BlendMode::Add: + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + break; + case BlendMode::Source: + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ZERO); + break; + case BlendMode::Disable: + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // doesn't actually matter + break; + } +} + +void GLEngine::setColorMask(std::array mask) { glColorMask(mask[0], mask[1], mask[2], mask[3]); } + +void GLEngine::setBackfaceCull(bool newVal) { + if (newVal) { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + } else { + glDisable(GL_CULL_FACE); + } +} + +std::string GLEngine::getClipboardText() { + std::string clipboardData = ImGui::GetClipboardText(); + return clipboardData; +} + +void GLEngine::setClipboardText(std::string text) { ImGui::SetClipboardText(text.c_str()); } + +void GLEngine::applyTransparencySettings() { + // Remove any old transparency-related rules + switch (transparencyMode) { + case TransparencyMode::None: { + setBlendMode(BlendMode::AlphaOver); + setDepthMode(DepthMode::Less); + break; + } + case TransparencyMode::Simple: { + setBlendMode(BlendMode::Add); + setDepthMode(DepthMode::Disable); + break; + } + case TransparencyMode::Pretty: { + setBlendMode(BlendMode::Disable); + setDepthMode(DepthMode::Less); + break; + } + } +} + +void GLEngine::setFrontFaceCCW(bool newVal) { + if (newVal == frontFaceCCW) return; + frontFaceCCW = newVal; + if (frontFaceCCW) { + glFrontFace(GL_CCW); + } else { + glFrontFace(GL_CW); + } +} + +// == Factories + + +std::shared_ptr GLEngine::generateAttributeBuffer(RenderDataType dataType_, int arrayCount_) { + GLAttributeBuffer* newA = new GLAttributeBuffer(dataType_, arrayCount_); + return std::shared_ptr(newA); +} + +std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int size1D, + const unsigned char* data) { + GLTextureBuffer* newT = new GLTextureBuffer(format, size1D, data); + return std::shared_ptr(newT); +} + +std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int size1D, + const float* data) { + GLTextureBuffer* newT = new GLTextureBuffer(format, size1D, data); + return std::shared_ptr(newT); +} +std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int sizeX_, + unsigned int sizeY_, const unsigned char* data) { + GLTextureBuffer* newT = new GLTextureBuffer(format, sizeX_, sizeY_, data); + return std::shared_ptr(newT); +} +std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int sizeX_, + unsigned int sizeY_, const float* data) { + GLTextureBuffer* newT = new GLTextureBuffer(format, sizeX_, sizeY_, data); + return std::shared_ptr(newT); +} + +std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int sizeX_, + unsigned int sizeY_, unsigned int sizeZ_, + const unsigned char* data) { + GLTextureBuffer* newT = new GLTextureBuffer(format, sizeX_, sizeY_, sizeZ_, data); + return std::shared_ptr(newT); +} +std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int sizeX_, + unsigned int sizeY_, unsigned int sizeZ_, + const float* data) { + GLTextureBuffer* newT = new GLTextureBuffer(format, sizeX_, sizeY_, sizeZ_, data); + return std::shared_ptr(newT); +} + +std::shared_ptr GLEngine::generateRenderBuffer(RenderBufferType type, unsigned int sizeX_, + unsigned int sizeY_) { + GLRenderBuffer* newR = new GLRenderBuffer(type, sizeX_, sizeY_); + return std::shared_ptr(newR); +} + +std::shared_ptr GLEngine::generateFrameBuffer(unsigned int sizeX_, unsigned int sizeY_) { + GLFrameBuffer* newF = new GLFrameBuffer(sizeX_, sizeY_); + return std::shared_ptr(newF); +} + +std::string GLEngine::programKeyFromRules(const std::string& programName, const std::vector& rules, + ShaderReplacementDefaults defaults) { + + std::stringstream builder; + + // program name comes first + builder << "$PROGRAMNAME: "; + builder << programName << "#"; + + // then rules + builder << " $RULES: "; + for (const std::string& s : rules) builder << s << "# "; + + // then rules from the defaults + builder << " $DEFAULTS: "; + switch (defaults) { + case ShaderReplacementDefaults::SceneObject: { + for (const std::string& s : defaultRules_sceneObject) builder << s << "# "; + break; + } + case ShaderReplacementDefaults::SceneObjectNoSlice: { + for (const std::string& s : defaultRules_sceneObject) { + if (s.rfind("SLICE_PLANE_", 0) != 0) { + builder << s << "# "; + } + } + break; + } + case ShaderReplacementDefaults::Pick: { + for (const std::string& s : defaultRules_pick) builder << s << "# "; + break; + } + case ShaderReplacementDefaults::Process: { + for (const std::string& s : defaultRules_process) builder << s << "# "; + break; + } + case ShaderReplacementDefaults::None: { + break; + } + } + + return builder.str(); +} + +std::shared_ptr GLEngine::getCompiledProgram(const std::string& programName, + const std::vector& customRules, + ShaderReplacementDefaults defaults) { + + // Build a cache key for the program + std::string progKey = programKeyFromRules(programName, customRules, defaults); + + + // If the cache doesn't already contain the program, create it and add to cache + if (compiledProgamCache.find(progKey) == compiledProgamCache.end()) { + + if (polyscope::options::verbosity > 3) polyscope::info("compiling shader program " + progKey); + + // == Compile the program + + // Get the list of shaders comprising the program from the global cache + if (registeredShaderPrograms.find(programName) == registeredShaderPrograms.end()) { + exception("No shader program with name [" + programName + "] registered."); + } + const std::vector& stages = registeredShaderPrograms[programName].first; + DrawMode dm = registeredShaderPrograms[programName].second; + + // Add in the default rules + std::vector fullCustomRules = customRules; + switch (defaults) { + case ShaderReplacementDefaults::SceneObject: { + fullCustomRules.insert(fullCustomRules.end(), defaultRules_sceneObject.begin(), defaultRules_sceneObject.end()); + break; + } + case ShaderReplacementDefaults::SceneObjectNoSlice: { + for (const std::string& rule : defaultRules_sceneObject) { + if (rule.rfind("SLICE_PLANE_", 0) != 0) { + fullCustomRules.insert(fullCustomRules.end(), rule); + } + } + break; + } + case ShaderReplacementDefaults::Pick: { + fullCustomRules.insert(fullCustomRules.end(), defaultRules_pick.begin(), defaultRules_pick.end()); + break; + } + case ShaderReplacementDefaults::Process: { + fullCustomRules.insert(fullCustomRules.end(), defaultRules_process.begin(), defaultRules_process.end()); + break; + } + case ShaderReplacementDefaults::None: { + break; + } + } + + // Prepare rule substitutions + std::vector rules; + for (auto it = fullCustomRules.begin(); it < fullCustomRules.end(); it++) { + std::string& ruleName = *it; + + // Empty rule is a no-op + if (ruleName == "") { + continue; + } + + // Only process each rule the first time it is seen + if (std::find(fullCustomRules.begin(), it, ruleName) != it) { + continue; + } + + if (registeredShaderRules.find(ruleName) == registeredShaderRules.end()) { + exception("No shader replacement rule with name [" + ruleName + "] registered."); + } + ShaderReplacementRule& thisRule = registeredShaderRules[ruleName]; + rules.push_back(thisRule); + } + + // Actually apply rule substitutions + std::vector updatedStages = applyShaderReplacements(stages, rules); + + // Create a new compiled program (GL work happens in the constructor) + compiledProgamCache[progKey] = std::shared_ptr(new GLCompiledProgram(updatedStages, dm)); + } + + // Now that the cache must contain the compiled program, just return it + return compiledProgamCache[progKey]; +} + +std::shared_ptr GLEngine::requestShader(const std::string& programName, + const std::vector& customRules, + ShaderReplacementDefaults defaults) { + GLShaderProgram* newP = new GLShaderProgram(getCompiledProgram(programName, customRules, defaults)); + return std::shared_ptr(newP); +} + + +void GLEngine::registerShaderProgram(const std::string& name, const std::vector& spec, + const DrawMode& dm) { + registeredShaderPrograms.insert({name, {spec, dm}}); +} + +void GLEngine::registerShaderRule(const std::string& name, const ShaderReplacementRule& rule) { + registeredShaderRules.insert({name, rule}); +} + +void GLEngine::populateDefaultShadersAndRules() { + // clang-format off + + // == Load general base shaders + registerShaderProgram("MESH", {FLEX_MESH_VERT_SHADER, FLEX_MESH_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("INDEXED_MESH", {FLEX_MESH_VERT_SHADER, FLEX_MESH_FRAG_SHADER}, DrawMode::IndexedTriangles); + registerShaderProgram("SIMPLE_MESH", {SIMPLE_MESH_VERT_SHADER, SIMPLE_MESH_FRAG_SHADER}, DrawMode::IndexedTriangles); + registerShaderProgram("SLICE_TETS", {SLICE_TETS_VERT_SHADER, SLICE_TETS_GEOM_SHADER, SLICE_TETS_FRAG_SHADER}, DrawMode::Points); + registerShaderProgram("RAYCAST_SPHERE", {FLEX_SPHERE_VERT_SHADER, FLEX_SPHERE_GEOM_SHADER, FLEX_SPHERE_FRAG_SHADER}, DrawMode::Points); + registerShaderProgram("POINT_QUAD", {FLEX_POINTQUAD_VERT_SHADER, FLEX_POINTQUAD_GEOM_SHADER, FLEX_POINTQUAD_FRAG_SHADER}, DrawMode::Points); + registerShaderProgram("GRIDCUBE", {FLEX_GRIDCUBE_VERT_SHADER, FLEX_GRIDCUBE_GEOM_SHADER, FLEX_GRIDCUBE_FRAG_SHADER}, DrawMode::Points); + registerShaderProgram("GRIDCUBE_PLANE", {FLEX_GRIDCUBE_PLANE_VERT_SHADER, FLEX_GRIDCUBE_PLANE_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("RAYCAST_VECTOR", {FLEX_VECTOR_VERT_SHADER, FLEX_VECTOR_GEOM_SHADER, FLEX_VECTOR_FRAG_SHADER}, DrawMode::Points); + registerShaderProgram("RAYCAST_TANGENT_VECTOR", {FLEX_TANGENT_VECTOR_VERT_SHADER, FLEX_VECTOR_GEOM_SHADER, FLEX_VECTOR_FRAG_SHADER}, DrawMode::Points); + registerShaderProgram("RAYCAST_CYLINDER", {FLEX_CYLINDER_VERT_SHADER, FLEX_CYLINDER_GEOM_SHADER, FLEX_CYLINDER_FRAG_SHADER}, DrawMode::Points); + registerShaderProgram("HISTOGRAM", {HISTOGRAM_VERT_SHADER, HISTOGRAM_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("GROUND_PLANE_TILE", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_TILE_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("GROUND_PLANE_TILE_REFLECT", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_TILE_REFLECT_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("GROUND_PLANE_SHADOW", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_SHADOW_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("MAP_LIGHT", {TEXTURE_DRAW_VERT_SHADER, MAP_LIGHT_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("RIBBON", {RIBBON_VERT_SHADER, RIBBON_GEOM_SHADER, RIBBON_FRAG_SHADER}, DrawMode::IndexedLineStripAdjacency); + registerShaderProgram("SLICE_PLANE", {SLICE_PLANE_VERT_SHADER, SLICE_PLANE_FRAG_SHADER}, DrawMode::Triangles); + + registerShaderProgram("TEXTURE_DRAW_PLAIN", {TEXTURE_DRAW_VERT_SHADER, PLAIN_TEXTURE_DRAW_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("TEXTURE_DRAW_DOT3", {TEXTURE_DRAW_VERT_SHADER, DOT3_TEXTURE_DRAW_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("TEXTURE_DRAW_MAP3", {TEXTURE_DRAW_VERT_SHADER, MAP3_TEXTURE_DRAW_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("TEXTURE_DRAW_SPHEREBG", {SPHEREBG_DRAW_VERT_SHADER, SPHEREBG_DRAW_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("TEXTURE_DRAW_RENDERIMAGE_PLAIN", {TEXTURE_DRAW_VERT_SHADER, PLAIN_RENDERIMAGE_TEXTURE_DRAW_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("TEXTURE_DRAW_RAW_RENDERIMAGE_PLAIN", {TEXTURE_DRAW_VERT_SHADER, PLAIN_RAW_RENDERIMAGE_TEXTURE_DRAW_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("COMPOSITE_PEEL", {TEXTURE_DRAW_VERT_SHADER, COMPOSITE_PEEL}, DrawMode::Triangles); + registerShaderProgram("DEPTH_COPY", {TEXTURE_DRAW_VERT_SHADER, DEPTH_COPY}, DrawMode::Triangles); + registerShaderProgram("DEPTH_TO_MASK", {TEXTURE_DRAW_VERT_SHADER, DEPTH_TO_MASK}, DrawMode::Triangles); + registerShaderProgram("SCALAR_TEXTURE_COLORMAP", {TEXTURE_DRAW_VERT_SHADER, SCALAR_TEXTURE_COLORMAP}, DrawMode::Triangles); + registerShaderProgram("BLUR_RGB", {TEXTURE_DRAW_VERT_SHADER, BLUR_RGB}, DrawMode::Triangles); + registerShaderProgram("TRANSFORMATION_GIZMO_ROT", {TRANSFORMATION_GIZMO_ROT_VERT, TRANSFORMATION_GIZMO_ROT_FRAG}, DrawMode::Triangles); + + // === Load rules + + // Utility rules + registerShaderRule("GLSL_VERSION", GLSL_VERSION); + registerShaderRule("GLOBAL_FRAGMENT_FILTER", GLOBAL_FRAGMENT_FILTER); + registerShaderRule("DOWNSAMPLE_RESOLVE_1", DOWNSAMPLE_RESOLVE_1); + registerShaderRule("DOWNSAMPLE_RESOLVE_2", DOWNSAMPLE_RESOLVE_2); + registerShaderRule("DOWNSAMPLE_RESOLVE_3", DOWNSAMPLE_RESOLVE_3); + registerShaderRule("DOWNSAMPLE_RESOLVE_4", DOWNSAMPLE_RESOLVE_4); + + registerShaderRule("TRANSPARENCY_STRUCTURE", TRANSPARENCY_STRUCTURE); + registerShaderRule("TRANSPARENCY_RESOLVE_SIMPLE", TRANSPARENCY_RESOLVE_SIMPLE); + registerShaderRule("TRANSPARENCY_PEEL_STRUCTURE", TRANSPARENCY_PEEL_STRUCTURE); + registerShaderRule("TRANSPARENCY_PEEL_GROUND", TRANSPARENCY_PEEL_GROUND); + + registerShaderRule("GENERATE_VIEW_POS", GENERATE_VIEW_POS); + registerShaderRule("COMPUTE_SHADE_NORMAL_FROM_POSITION", COMPUTE_SHADE_NORMAL_FROM_POSITION); + registerShaderRule("PREMULTIPLY_LIT_COLOR", PREMULTIPLY_LIT_COLOR); + registerShaderRule("CULL_POS_FROM_VIEW", CULL_POS_FROM_VIEW); + registerShaderRule("PROJ_AND_INV_PROJ_MAT", PROJ_AND_INV_PROJ_MAT); + + // Lighting and shading things + registerShaderRule("LIGHT_MATCAP", LIGHT_MATCAP); + registerShaderRule("LIGHT_PASSTHRU", LIGHT_PASSTHRU); + registerShaderRule("SHADE_BASECOLOR", SHADE_BASECOLOR); + registerShaderRule("SHADE_COLOR", SHADE_COLOR); + registerShaderRule("SHADECOLOR_FROM_UNIFORM", SHADECOLOR_FROM_UNIFORM); + registerShaderRule("SHADE_COLORMAP_VALUE", SHADE_COLORMAP_VALUE); + registerShaderRule("SHADE_COLORMAP_ANGULAR2", SHADE_COLORMAP_ANGULAR2); + registerShaderRule("SHADE_GRID_VALUE2", SHADE_GRID_VALUE2); + registerShaderRule("SHADE_CHECKER_VALUE2", SHADE_CHECKER_VALUE2); + registerShaderRule("SHADE_CHECKER_CATEGORY", SHADE_CHECKER_CATEGORY); + registerShaderRule("SHADEVALUE_MAG_VALUE2", SHADEVALUE_MAG_VALUE2); + registerShaderRule("ISOLINE_STRIPE_VALUECOLOR", ISOLINE_STRIPE_VALUECOLOR); + registerShaderRule("CHECKER_VALUE2COLOR", CHECKER_VALUE2COLOR); + registerShaderRule("INVERSE_TONEMAP", INVERSE_TONEMAP); + + // Texture and image things + registerShaderRule("TEXTURE_ORIGIN_UPPERLEFT", TEXTURE_ORIGIN_UPPERLEFT); + registerShaderRule("TEXTURE_ORIGIN_LOWERLEFT", TEXTURE_ORIGIN_LOWERLEFT); + registerShaderRule("TEXTURE_SET_TRANSPARENCY", TEXTURE_SET_TRANSPARENCY); + registerShaderRule("TEXTURE_SET_TRANSPARENCY_PREMULTIPLIED", TEXTURE_SET_TRANSPARENCY_PREMULTIPLIED); + registerShaderRule("TEXTURE_PREMULTIPLY_OUT", TEXTURE_PREMULTIPLY_OUT); + registerShaderRule("TEXTURE_SHADE_COLOR", TEXTURE_SHADE_COLOR); + registerShaderRule("TEXTURE_SHADE_COLORALPHA", TEXTURE_SHADE_COLORALPHA); + registerShaderRule("TEXTURE_PROPAGATE_VALUE", TEXTURE_PROPAGATE_VALUE); + registerShaderRule("TEXTURE_PROPAGATE_COLOR", TEXTURE_PROPAGATE_COLOR); + registerShaderRule("TEXTURE_BILLBOARD_FROM_UNIFORMS", TEXTURE_BILLBOARD_FROM_UNIFORMS); + registerShaderRule("SHADE_NORMAL_FROM_TEXTURE", SHADE_NORMAL_FROM_TEXTURE); + registerShaderRule("SHADE_NORMAL_FROM_VIEWPOS_VAR", SHADE_NORMAL_FROM_VIEWPOS_VAR); + + // mesh things + registerShaderRule("MESH_WIREFRAME_FROM_BARY", MESH_WIREFRAME_FROM_BARY); + registerShaderRule("MESH_WIREFRAME", MESH_WIREFRAME); + registerShaderRule("MESH_WIREFRAME_ONLY", MESH_WIREFRAME_ONLY); + registerShaderRule("MESH_BACKFACE_NORMAL_FLIP", MESH_BACKFACE_NORMAL_FLIP); + registerShaderRule("MESH_BACKFACE_DIFFERENT", MESH_BACKFACE_DIFFERENT); + registerShaderRule("MESH_BACKFACE_DARKEN", MESH_BACKFACE_DARKEN); + registerShaderRule("MESH_PROPAGATE_VALUE", MESH_PROPAGATE_VALUE); + registerShaderRule("MESH_PROPAGATE_FLAT_VALUE", MESH_PROPAGATE_FLAT_VALUE); + registerShaderRule("MESH_PROPAGATE_VALUE2", MESH_PROPAGATE_VALUE2); + registerShaderRule("MESH_PROPAGATE_TCOORD", MESH_PROPAGATE_TCOORD); + registerShaderRule("MESH_PROPAGATE_COLOR", MESH_PROPAGATE_COLOR); + registerShaderRule("MESH_PROPAGATE_HALFEDGE_VALUE", MESH_PROPAGATE_HALFEDGE_VALUE); + registerShaderRule("MESH_PROPAGATE_CULLPOS", MESH_PROPAGATE_CULLPOS); + registerShaderRule("MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE", MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE); + registerShaderRule("MESH_PROPAGATE_PICK", MESH_PROPAGATE_PICK); + registerShaderRule("MESH_PROPAGATE_PICK_SIMPLE", MESH_PROPAGATE_PICK_SIMPLE); + + // volume gridcube things + registerShaderRule("GRIDCUBE_PROPAGATE_NODE_VALUE", GRIDCUBE_PROPAGATE_NODE_VALUE); + registerShaderRule("GRIDCUBE_PROPAGATE_CELL_VALUE", GRIDCUBE_PROPAGATE_CELL_VALUE); + registerShaderRule("GRIDCUBE_WIREFRAME", GRIDCUBE_WIREFRAME); + registerShaderRule("GRIDCUBE_CONSTANT_PICK", GRIDCUBE_CONSTANT_PICK); + registerShaderRule("GRIDCUBE_CULLPOS_FROM_CENTER", GRIDCUBE_CULLPOS_FROM_CENTER); + + // sphere things + registerShaderRule("SPHERE_PROPAGATE_VALUE", SPHERE_PROPAGATE_VALUE); + registerShaderRule("SPHERE_PROPAGATE_VALUE2", SPHERE_PROPAGATE_VALUE2); + registerShaderRule("SPHERE_PROPAGATE_COLOR", SPHERE_PROPAGATE_COLOR); + registerShaderRule("SPHERE_CULLPOS_FROM_CENTER", SPHERE_CULLPOS_FROM_CENTER); + registerShaderRule("SPHERE_CULLPOS_FROM_CENTER_QUAD", SPHERE_CULLPOS_FROM_CENTER_QUAD); + registerShaderRule("SPHERE_VARIABLE_SIZE", SPHERE_VARIABLE_SIZE); + + // vector things + registerShaderRule("VECTOR_PROPAGATE_COLOR", VECTOR_PROPAGATE_COLOR); + registerShaderRule("VECTOR_CULLPOS_FROM_TAIL", VECTOR_CULLPOS_FROM_TAIL); + registerShaderRule("TRANSFORMATION_GIZMO_VEC", TRANSFORMATION_GIZMO_VEC); + + // cylinder things + registerShaderRule("CYLINDER_PROPAGATE_VALUE", CYLINDER_PROPAGATE_VALUE); + registerShaderRule("CYLINDER_PROPAGATE_BLEND_VALUE", CYLINDER_PROPAGATE_BLEND_VALUE); + registerShaderRule("CYLINDER_PROPAGATE_COLOR", CYLINDER_PROPAGATE_COLOR); + registerShaderRule("CYLINDER_PROPAGATE_BLEND_COLOR", CYLINDER_PROPAGATE_BLEND_COLOR); + registerShaderRule("CYLINDER_PROPAGATE_PICK", CYLINDER_PROPAGATE_PICK); + registerShaderRule("CYLINDER_CULLPOS_FROM_MID", CYLINDER_CULLPOS_FROM_MID); + registerShaderRule("CYLINDER_VARIABLE_SIZE", CYLINDER_VARIABLE_SIZE); + + // marching tets things + registerShaderRule("SLICE_TETS_BASECOLOR_SHADE", SLICE_TETS_BASECOLOR_SHADE); + registerShaderRule("SLICE_TETS_PROPAGATE_VALUE", SLICE_TETS_PROPAGATE_VALUE); + registerShaderRule("SLICE_TETS_PROPAGATE_VECTOR", SLICE_TETS_PROPAGATE_VECTOR); + registerShaderRule("SLICE_TETS_VECTOR_COLOR", SLICE_TETS_VECTOR_COLOR); + registerShaderRule("SLICE_TETS_MESH_WIREFRAME", SLICE_TETS_MESH_WIREFRAME); + + // clang-format on +}; + +void GLEngine::createSlicePlaneFliterRule(std::string uniquePostfix) { + registeredShaderRules.insert({"SLICE_PLANE_CULL_" + uniquePostfix, generateSlicePlaneRule(uniquePostfix)}); + registeredShaderRules.insert( + {"SLICE_PLANE_VOLUMEGRID_CULL_" + uniquePostfix, generateVolumeGridSlicePlaneRule(uniquePostfix)}); +} + + +} // namespace backend_openGL3_glfw_egl +} // namespace render +} // namespace polyscope + +#else + +#include + +#include "polyscope/messages.h" + +namespace polyscope { +namespace render { +namespace backend_openGL3_glfw { +void initializeRenderEngine() { exception("Polyscope was not compiled with support for backend: openGL3_glfw"); } +} // namespace backend_openGL3_glfw +} // namespace render +} // namespace polyscope + +#endif From 489785c72174d943594a8b168185ea27d92cfd65 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Thu, 15 Feb 2024 17:28:30 -0800 Subject: [PATCH 02/18] WIP on headless EGL --- CMakeLists.txt | 8 + include/polyscope/render/engine.h | 13 +- include/polyscope/render/opengl/gl_engine.h | 54 +--- .../{egl_gl_engine.h => gl_engine_egl.h} | 1 - .../polyscope/render/opengl/gl_engine_glfw.h | 91 ++++++ src/CMakeLists.txt | 32 +-- src/render/engine.cpp | 4 + src/render/opengl/gl_engine.cpp | 100 +------ .../{egl_gl_engine.cpp => gl_engine_egl.cpp} | 0 src/render/opengl/gl_engine_glfw.cpp | 261 ++++++++++++++++++ 10 files changed, 397 insertions(+), 167 deletions(-) rename include/polyscope/render/opengl/{egl_gl_engine.h => gl_engine_egl.h} (99%) create mode 100644 include/polyscope/render/opengl/gl_engine_glfw.h rename src/render/opengl/{egl_gl_engine.cpp => gl_engine_egl.cpp} (100%) create mode 100644 src/render/opengl/gl_engine_glfw.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 88ad875c..39d3c943 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,14 @@ cmake_policy(SET CMP0054 NEW) # don't implicitly dereference inside if() set(POLYSCOPE_BACKEND_OPENGL3_GLFW "ON" CACHE BOOL "Enable openGL3_glfw backend") set(POLYSCOPE_BACKEND_OPENGL_MOCK "ON" CACHE BOOL "Enable openGL_mock backend") +if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + # the EGL backend is enabled by default when building on linux, and + # disabled by default otherwisek + set(POLYSCOPE_BACKEND_OPENGL3_EGL "ON" CACHE BOOL "Enable openGL3_egl backend") +else() + set(POLYSCOPE_BACKEND_OPENGL3_EGL "OFF" CACHE BOOL "Enable openGL3_egl backend") +endif() + ### Do anything needed for dependencies and bring their stuff in to scope add_subdirectory(deps) diff --git a/include/polyscope/render/engine.h b/include/polyscope/render/engine.h index 34bf039c..ac3b19b2 100644 --- a/include/polyscope/render/engine.h +++ b/include/polyscope/render/engine.h @@ -439,7 +439,9 @@ class ShaderProgram { class Engine { public: - // Options + + Engine(); + virtual ~Engine(); // High-level control virtual void checkError(bool fatal = false) = 0; @@ -505,13 +507,16 @@ class Engine { virtual std::string getClipboardText() = 0; virtual void setClipboardText(std::string text) = 0; - // ImGui + // === ImGui + + // NOTE: the imgui backend depends on the window manager (e.g. GLFW), so these must be implemented by the lowest-level concrete engine implementation virtual void initializeImGui() = 0; virtual void shutdownImGui() = 0; - void setImGuiStyle(); - ImFontAtlas* getImGuiGlobalFontAtlas(); virtual void ImGuiNewFrame() = 0; virtual void ImGuiRender() = 0; + + void setImGuiStyle(); + ImFontAtlas* getImGuiGlobalFontAtlas(); virtual void showTextureInImGuiWindow(std::string windowName, TextureBuffer* buffer); diff --git a/include/polyscope/render/opengl/gl_engine.h b/include/polyscope/render/opengl/gl_engine.h index eb09de71..fc856c35 100644 --- a/include/polyscope/render/opengl/gl_engine.h +++ b/include/polyscope/render/opengl/gl_engine.h @@ -6,27 +6,6 @@ #include "polyscope/render/engine.h" #include "polyscope/utilities.h" -#ifdef __APPLE__ -#define GLFW_INCLUDE_GLCOREARB -#include "GLFW/glfw3.h" -#else -#include "glad/glad.h" -// glad must come first -#include "GLFW/glfw3.h" -#endif - -#ifdef _WIN32 -#undef APIENTRY -#define GLFW_EXPOSE_NATIVE_WIN32 -#define GLFW_EXPOSE_NATIVE_WGL -#include -#endif - -#include "imgui.h" -#define IMGUI_IMPL_OPENGL_LOADER_GLAD -#include "backends/imgui_impl_glfw.h" -#include "backends/imgui_impl_opengl3.h" - #include // Note: DO NOT include this header throughout polyscope, and do not directly make openGL calls. This header should only @@ -35,7 +14,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // Some very nice typdefs typedef GLuint TextureBufferHandle; @@ -376,13 +355,13 @@ class GLShaderProgram : public ShaderProgram { class GLEngine : public Engine { public: + GLEngine(); + virtual ~GLEngine(); // High-level control - void initialize(); void checkError(bool fatal = false) override; - void swapDisplayBuffers() override; std::vector readDisplayBuffer() override; // Manage render state @@ -391,28 +370,6 @@ class GLEngine : public Engine { void setColorMask(std::array mask = {true, true, true, true}) override; void setBackfaceCull(bool newVal) override; - // === Windowing and framework things - void makeContextCurrent() override; - void focusWindow() override; - void showWindow() override; - void hideWindow() override; - void updateWindowSize(bool force = false) override; - void applyWindowSize() override; - void setWindowResizable(bool newVal) override; - bool getWindowResizable() override; - std::tuple getWindowPos() override; - bool windowRequestsClose() override; - void pollEvents() override; - bool isKeyPressed(char c) override; // for lowercase a-z and 0-9 only - int getKeyCode(char c) override; // for lowercase a-z and 0-9 only - std::string getClipboardText() override; - void setClipboardText(std::string text) override; - - // ImGui - void initializeImGui() override; - void shutdownImGui() override; - void ImGuiNewFrame() override; - void ImGuiRender() override; // === Factory methods @@ -462,9 +419,6 @@ class GLEngine : public Engine { // Helpers virtual void createSlicePlaneFliterRule(std::string name) override; - // Internal windowing and engine details - GLFWwindow* mainWindow = nullptr; - // Shader program & rule caches std::unordered_map, DrawMode>> registeredShaderPrograms; std::unordered_map registeredShaderRules; @@ -478,6 +432,6 @@ class GLEngine : public Engine { ShaderReplacementDefaults defaults); }; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/egl_gl_engine.h b/include/polyscope/render/opengl/gl_engine_egl.h similarity index 99% rename from include/polyscope/render/opengl/egl_gl_engine.h rename to include/polyscope/render/opengl/gl_engine_egl.h index 0c616df9..115c51c2 100644 --- a/include/polyscope/render/opengl/egl_gl_engine.h +++ b/include/polyscope/render/opengl/gl_engine_egl.h @@ -16,7 +16,6 @@ #endif #include - #undef None // for some terrible reason egl.h defines 'None', which we use below #ifdef _WIN32 diff --git a/include/polyscope/render/opengl/gl_engine_glfw.h b/include/polyscope/render/opengl/gl_engine_glfw.h new file mode 100644 index 00000000..59cc24bb --- /dev/null +++ b/include/polyscope/render/opengl/gl_engine_glfw.h @@ -0,0 +1,91 @@ +// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run + +#pragma once + +#include "polyscope/options.h" +#include "polyscope/render/engine.h" +#include "polyscope/utilities.h" + +#ifdef __APPLE__ +#define GLFW_INCLUDE_GLCOREARB +#include "GLFW/glfw3.h" +#else +#include "glad/glad.h" +// glad must come first +#include "GLFW/glfw3.h" +#endif + +#ifdef _WIN32 +#undef APIENTRY +#define GLFW_EXPOSE_NATIVE_WIN32 +#define GLFW_EXPOSE_NATIVE_WGL +#include +#endif + +#include "imgui.h" +#define IMGUI_IMPL_OPENGL_LOADER_GLAD +#include "backends/imgui_impl_glfw.h" +#include "backends/imgui_impl_opengl3.h" + +#include "polyscope/render/opengl/gl_engine.h" + +#include + +// Note: DO NOT include this header throughout polyscope, and do not directly make openGL calls. This header should only +// be used to construct an instance of Engine. engine.h gives the render API, all render calls should pass through that. + + +namespace polyscope { +namespace render { +namespace backend_openGL3 { + + +class GLEngineGLFW : public GLEngine { + +public: + + GLEngineGLFW(); + virtual ~GLEngineGLFW(); + + // High-level control + void initialize(); + void swapDisplayBuffers() override; + + // === Windowing and framework things + + void makeContextCurrent() override; + void pollEvents() override; + + void focusWindow() override; + void showWindow() override; + void hideWindow() override; + void updateWindowSize(bool force = false) override; + void applyWindowSize() override; + void setWindowResizable(bool newVal) override; + bool getWindowResizable() override; + std::tuple getWindowPos() override; + bool windowRequestsClose() override; + + bool isKeyPressed(char c) override; // for lowercase a-z and 0-9 only + int getKeyCode(char c) override; // for lowercase a-z and 0-9 only + std::string getClipboardText() override; + void setClipboardText(std::string text) override; + + + // === ImGui + + void initializeImGui() override; + void shutdownImGui() override; + void ImGuiNewFrame() override; + void ImGuiRender() override; + +protected: + + // Internal windowing and engine details + GLFWwindow* mainWindow = nullptr; + +}; + +} // namespace backend_openGL3 +} // namespace render +} // namespace polyscope diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f6aa9bc8..76477c5d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,22 +19,21 @@ if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") message("Polyscope backend openGL3_glfw enabled") list (APPEND BACKEND_SRCS - render/opengl/egl_gl_engine.cpp # TODO - render/opengl/gl_engine.cpp - render/opengl/shaders/texture_draw_shaders.cpp - render/opengl/shaders/lighting_shaders.cpp - render/opengl/shaders/grid_shaders.cpp - render/opengl/shaders/ground_plane_shaders.cpp - render/opengl/shaders/gizmo_shaders.cpp - render/opengl/shaders/histogram_shaders.cpp - render/opengl/shaders/surface_mesh_shaders.cpp - render/opengl/shaders/volume_mesh_shaders.cpp - render/opengl/shaders/vector_shaders.cpp - render/opengl/shaders/sphere_shaders.cpp - render/opengl/shaders/ribbon_shaders.cpp - render/opengl/shaders/cylinder_shaders.cpp - render/opengl/shaders/rules.cpp - render/opengl/shaders/common.cpp + render/opengl/gl_engine.cpp + render/opengl/shaders/texture_draw_shaders.cpp + render/opengl/shaders/lighting_shaders.cpp + render/opengl/shaders/grid_shaders.cpp + render/opengl/shaders/ground_plane_shaders.cpp + render/opengl/shaders/gizmo_shaders.cpp + render/opengl/shaders/histogram_shaders.cpp + render/opengl/shaders/surface_mesh_shaders.cpp + render/opengl/shaders/volume_mesh_shaders.cpp + render/opengl/shaders/vector_shaders.cpp + render/opengl/shaders/sphere_shaders.cpp + render/opengl/shaders/ribbon_shaders.cpp + render/opengl/shaders/cylinder_shaders.cpp + render/opengl/shaders/rules.cpp + render/opengl/shaders/common.cpp ) list(APPEND BACKEND_HEADERS @@ -65,7 +64,6 @@ if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") # On Windows/Linux, use the glad openGL loader list(APPEND BACKEND_LIBS glad) - list(APPEND BACKEND_LIBS EGL) # TODO endif() add_definitions(-DPOLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED) diff --git a/src/render/engine.cpp b/src/render/engine.cpp index a2f5d4ed..1fd11b5d 100644 --- a/src/render/engine.cpp +++ b/src/render/engine.cpp @@ -270,6 +270,10 @@ ShaderProgram::ShaderProgram(DrawMode dm) : drawMode(dm), uniqueID(render::engin } } + +Engine::Engine() {} +Engine::~Engine() {} + void Engine::buildEngineGui() { ImGui::SetNextItemOpen(false, ImGuiCond_FirstUseEver); diff --git a/src/render/opengl/gl_engine.cpp b/src/render/opengl/gl_engine.cpp index 044b907c..b84903b1 100644 --- a/src/render/opengl/gl_engine.cpp +++ b/src/render/opengl/gl_engine.cpp @@ -3,7 +3,7 @@ #include "backends/imgui_impl_opengl3.h" #include "polyscope/render/engine.h" -#ifdef POLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED +#ifdef POLYSCOPE_BACKEND_OPENGL_ENABLED #include "polyscope/render/opengl/gl_engine.h" #include "polyscope/messages.h" @@ -37,17 +37,10 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL { GLEngine* glEngine = nullptr; // alias for global engine pointer -void initializeRenderEngine() { - glEngine = new GLEngine(); - engine = glEngine; - glEngine->initialize(); - engine->allocateGlobalBuffersAndPrograms(); -} - // == Map enums to native values // clang-format off @@ -2114,76 +2107,7 @@ void GLShaderProgram::draw() { } GLEngine::GLEngine() {} - -void GLEngine::initialize() { - // Small callback function for GLFW errors - auto error_print_callback = [](int error, const char* description) { - if (polyscope::options::verbosity > 0) { - std::cout << "GLFW emitted error: " << description << std::endl; - } - }; - - // === Initialize glfw - glfwSetErrorCallback(error_print_callback); - if (!glfwInit()) { - exception(options::printPrefix + "ERROR: Failed to initialize glfw"); - } - - // OpenGL version things - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); -#if __APPLE__ - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); -#endif - - // Create the window with context - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); - glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_FALSE); - mainWindow = glfwCreateWindow(view::windowWidth, view::windowHeight, options::programName.c_str(), NULL, NULL); - glfwMakeContextCurrent(mainWindow); - glfwSetWindowPos(mainWindow, view::initWindowPosX, view::initWindowPosY); - - // Set initial window size - int newBufferWidth, newBufferHeight, newWindowWidth, newWindowHeight; - glfwGetFramebufferSize(mainWindow, &newBufferWidth, &newBufferHeight); - glfwGetWindowSize(mainWindow, &newWindowWidth, &newWindowHeight); - view::bufferWidth = newBufferWidth; - view::bufferHeight = newBufferHeight; - view::windowWidth = newWindowWidth; - view::windowHeight = newWindowHeight; - - setWindowResizable(view::windowResizable); - -// === Initialize openGL -// Load openGL functions (using GLAD) -#ifndef __APPLE__ - if (!gladLoadGL()) { - exception(options::printPrefix + "ERROR: Failed to load openGL using GLAD"); - } -#endif - if (options::verbosity > 0) { - std::cout << options::printPrefix << "Backend: openGL3_glfw -- " - << "Loaded openGL version: " << glGetString(GL_VERSION) << std::endl; - } - -#ifdef __APPLE__ - // Hack to classify the process as interactive - glfwPollEvents(); -#endif - - { // Manually create the screen frame buffer - GLFrameBuffer* glScreenBuffer = new GLFrameBuffer(view::bufferWidth, view::bufferHeight, true); - displayBuffer.reset(glScreenBuffer); - glScreenBuffer->bind(); - glClearColor(1., 1., 1., 0.); - // glClearColor(0., 0., 0., 0.); - // glClearDepth(1.); - } - - populateDefaultShadersAndRules(); -} - +GLEngine::~GLEngine() {} void GLEngine::initializeImGui() { bindDisplay(); @@ -2805,22 +2729,8 @@ void GLEngine::createSlicePlaneFliterRule(std::string uniquePostfix) { } -} // namespace backend_openGL3_glfw -} // namespace render -} // namespace polyscope - -#else - -#include - -#include "polyscope/messages.h" - -namespace polyscope { -namespace render { -namespace backend_openGL3_glfw { -void initializeRenderEngine() { exception("Polyscope was not compiled with support for backend: openGL3_glfw"); } -} // namespace backend_openGL3_glfw +} // namespace backend_openGL } // namespace render } // namespace polyscope -#endif +#endif // POLYSCOPE_BACKEND_OPENGL_ENABLED diff --git a/src/render/opengl/egl_gl_engine.cpp b/src/render/opengl/gl_engine_egl.cpp similarity index 100% rename from src/render/opengl/egl_gl_engine.cpp rename to src/render/opengl/gl_engine_egl.cpp diff --git a/src/render/opengl/gl_engine_glfw.cpp b/src/render/opengl/gl_engine_glfw.cpp new file mode 100644 index 00000000..5dd08a0b --- /dev/null +++ b/src/render/opengl/gl_engine_glfw.cpp @@ -0,0 +1,261 @@ +// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run + +#ifdef POLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED + +#include "polyscope/render/opengl/gl_engine_glfw.h" + +#include "backends/imgui_impl_opengl3.h" +#include "polyscope/render/engine.h" +#include "polyscope/polyscope.h" + +#include "stb_image.h" + +#include +#include + +namespace polyscope { +namespace render { +namespace backend_openGL3 { + +GLEngineGLFW* glEngineGLFW = nullptr; // alias for global engine pointer +extern GLEngine* glEngine; // defined in gl_engine.h + +void initializeRenderEngine() { + + glEngineGLFW = new GLEngineGLFW();// create the new global engine object + + engine = glEngineGLFW; // we keep a few copies of this pointer with various types + glEngine = glEngineGLFW; + + // initialize + glEngineGLFW->initialize(); + engine->allocateGlobalBuffersAndPrograms(); +} + +GLEngineGLFW::GLEngineGLFW() {} +GLEngineGLFW::~GLEngineGLFW() {} + +void GLEngineGLFW::initialize() { + + // Small callback function for GLFW errors + auto error_print_callback = [](int error, const char* description) { + if (polyscope::options::verbosity > 0) { + std::cout << "GLFW emitted error: " << description << std::endl; + } + }; + + // === Initialize glfw + glfwSetErrorCallback(error_print_callback); + if (!glfwInit()) { + exception(options::printPrefix + "ERROR: Failed to initialize glfw"); + } + + // OpenGL version things + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#if __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + + // Create the window with context + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_FALSE); + mainWindow = glfwCreateWindow(view::windowWidth, view::windowHeight, options::programName.c_str(), NULL, NULL); + glfwMakeContextCurrent(mainWindow); + glfwSetWindowPos(mainWindow, view::initWindowPosX, view::initWindowPosY); + + // Set initial window size + int newBufferWidth, newBufferHeight, newWindowWidth, newWindowHeight; + glfwGetFramebufferSize(mainWindow, &newBufferWidth, &newBufferHeight); + glfwGetWindowSize(mainWindow, &newWindowWidth, &newWindowHeight); + view::bufferWidth = newBufferWidth; + view::bufferHeight = newBufferHeight; + view::windowWidth = newWindowWidth; + view::windowHeight = newWindowHeight; + + setWindowResizable(view::windowResizable); + +// === Initialize openGL +// Load openGL functions (using GLAD) +#ifndef __APPLE__ + if (!gladLoadGL()) { + exception(options::printPrefix + "ERROR: Failed to load openGL using GLAD"); + } +#endif + if (options::verbosity > 0) { + std::cout << options::printPrefix << "Backend: openGL3_glfw -- " + << "Loaded openGL version: " << glGetString(GL_VERSION) << std::endl; + } + +#ifdef __APPLE__ + // Hack to classify the process as interactive + glfwPollEvents(); +#endif + + { // Manually create the screen frame buffer + GLFrameBuffer* glScreenBuffer = new GLFrameBuffer(view::bufferWidth, view::bufferHeight, true); + displayBuffer.reset(glScreenBuffer); + glScreenBuffer->bind(); + glClearColor(1., 1., 1., 0.); + // glClearColor(0., 0., 0., 0.); + // glClearDepth(1.); + } + + populateDefaultShadersAndRules(); +} + + +void GLEngineGLFW::initializeImGui() { + bindDisplay(); + + ImGui::CreateContext(); // must call once at start + + // Set up ImGUI glfw bindings + ImGui_ImplGlfw_InitForOpenGL(mainWindow, true); + const char* glsl_version = "#version 150"; + ImGui_ImplOpenGL3_Init(glsl_version); + + configureImGui(); +} + +void GLEngineGLFW::shutdownImGui() { + // ImGui shutdown things + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); +} + +void GLEngineGLFW::ImGuiNewFrame() { + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); +} + +void GLEngineGLFW::ImGuiRender() { + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); +} + + +void GLEngineGLFW::swapDisplayBuffers() { + bindDisplay(); + glfwSwapBuffers(mainWindow); +} + +void GLEngineGLFW::makeContextCurrent() { + glfwMakeContextCurrent(mainWindow); + glfwSwapInterval(options::enableVSync ? 1 : 0); +} + +void GLEngineGLFW::focusWindow() { glfwFocusWindow(mainWindow); } + +void GLEngineGLFW::showWindow() { glfwShowWindow(mainWindow); } + +void GLEngineGLFW::hideWindow() { + glfwHideWindow(mainWindow); + glfwPollEvents(); // this shouldn't be necessary, but seems to be needed at least on macOS. Perhaps realted to a + // glfw bug? e.g. https://github.com/glfw/glfw/issues/1300 and related bugs +} + +void GLEngineGLFW::updateWindowSize(bool force) { + int newBufferWidth, newBufferHeight, newWindowWidth, newWindowHeight; + glfwGetFramebufferSize(mainWindow, &newBufferWidth, &newBufferHeight); + glfwGetWindowSize(mainWindow, &newWindowWidth, &newWindowHeight); + if (force || newBufferWidth != view::bufferWidth || newBufferHeight != view::bufferHeight || + newWindowHeight != view::windowHeight || newWindowWidth != view::windowWidth) { + // Basically a resize callback + requestRedraw(); + + // prevent any division by zero for e.g. aspect ratio calcs + if (newBufferHeight == 0) newBufferHeight = 1; + if (newWindowHeight == 0) newWindowHeight = 1; + + view::bufferWidth = newBufferWidth; + view::bufferHeight = newBufferHeight; + view::windowWidth = newWindowWidth; + view::windowHeight = newWindowHeight; + + render::engine->resizeScreenBuffers(); + render::engine->setScreenBufferViewports(); + } +} + + +void GLEngineGLFW::applyWindowSize() { + glfwSetWindowSize(mainWindow, view::windowWidth, view::windowHeight); + + // on some platform size changes are asynchonous, need to ensure it completes + // we don't want to just retry until the resize has happened, because it could be impossible + // TODO it seems like on X11 sometimes even this isn't enough? + glfwWaitEvents(); + + updateWindowSize(true); +} + + +void GLEngineGLFW::setWindowResizable(bool newVal) { + glfwSetWindowAttrib(mainWindow, GLFW_RESIZABLE, newVal ? GLFW_TRUE : GLFW_FALSE); +} + +bool GLEngineGLFW::getWindowResizable() { return glfwGetWindowAttrib(mainWindow, GLFW_RESIZABLE); } + +std::tuple GLEngineGLFW::getWindowPos() { + int x, y; + glfwGetWindowPos(mainWindow, &x, &y); + return std::tuple{x, y}; +} + +bool GLEngineGLFW::windowRequestsClose() { + bool shouldClose = glfwWindowShouldClose(mainWindow); + if (shouldClose) { + glfwSetWindowShouldClose(mainWindow, false); // un-set the state bit so we can close again + return true; + } + return false; +} + +void GLEngineGLFW::pollEvents() { glfwPollEvents(); } + +bool GLEngineGLFW::isKeyPressed(char c) { + if (c >= '0' && c <= '9') return ImGui::IsKeyPressed(GLFW_KEY_0 + (c - '0')); + if (c >= 'a' && c <= 'z') return ImGui::IsKeyPressed(GLFW_KEY_A + (c - 'a')); + if (c >= 'A' && c <= 'Z') return ImGui::IsKeyPressed(GLFW_KEY_A + (c - 'A')); + exception("keyPressed only supports 0-9, a-z, A-Z"); + return false; +} + +int GLEngineGLFW::getKeyCode(char c) { + if (c >= '0' && c <= '9') return static_cast(GLFW_KEY_0) + (c - '0'); + if (c >= 'a' && c <= 'z') return static_cast(GLFW_KEY_A) + (c - 'a'); + if (c >= 'A' && c <= 'Z') return static_cast(GLFW_KEY_A) + (c - 'A'); + exception("getKeyCode only supports 0-9, a-z, A-Z"); + return -1; +} + +std::string GLEngineGLFW::getClipboardText() { + std::string clipboardData = ImGui::GetClipboardText(); + return clipboardData; +} + +void GLEngineGLFW::setClipboardText(std::string text) { ImGui::SetClipboardText(text.c_str()); } + +} // namespace backend_openGL3 +} // namespace render +} // namespace polyscope + +#else // POLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED + +#include "polyscope/messages.h" + +namespace polyscope { +namespace render { +namespace backend_openGL3 { + +void initializeRenderEngine() { exception("Polyscope was not compiled with support for backend: openGL3_glfw"); } + +} // namespace backend_openGL3 +} // namespace render +} // namespace polyscope + +#endif From f61d8f27b72e91db0d9df4358382d7a112db3f02 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 10 Mar 2024 23:40:26 -0700 Subject: [PATCH 03/18] reorganize openGL backends and namespaces, implement EGL backend --- include/polyscope/render/opengl/gl_engine.h | 9 +- .../polyscope/render/opengl/gl_engine_egl.h | 445 +-- .../polyscope/render/opengl/shaders/common.h | 4 +- .../render/opengl/shaders/cylinder_shaders.h | 4 +- .../render/opengl/shaders/gizmo_shaders.h | 4 +- .../render/opengl/shaders/grid_shaders.h | 4 +- .../opengl/shaders/ground_plane_shaders.h | 4 +- .../render/opengl/shaders/histogram_shaders.h | 4 +- .../render/opengl/shaders/lighting_shaders.h | 4 +- .../render/opengl/shaders/ribbon_shaders.h | 4 +- .../polyscope/render/opengl/shaders/rules.h | 4 +- .../render/opengl/shaders/sphere_shaders.h | 4 +- .../opengl/shaders/surface_mesh_shaders.h | 4 +- .../opengl/shaders/texture_draw_shaders.h | 4 +- .../render/opengl/shaders/vector_shaders.h | 4 +- .../opengl/shaders/volume_mesh_shaders.h | 4 +- src/render/initialize_backend.cpp | 16 +- src/render/mock_opengl/mock_gl_engine.cpp | 4 +- src/render/opengl/gl_engine.cpp | 151 +- src/render/opengl/gl_engine_egl.cpp | 3032 ++--------------- src/render/opengl/gl_engine_glfw.cpp | 15 +- src/render/opengl/shaders/common.cpp | 2 +- .../opengl/shaders/cylinder_shaders.cpp | 4 +- src/render/opengl/shaders/gizmo_shaders.cpp | 4 +- src/render/opengl/shaders/grid_shaders.cpp | 2 +- .../opengl/shaders/ground_plane_shaders.cpp | 4 +- .../opengl/shaders/histogram_shaders.cpp | 4 +- .../opengl/shaders/lighting_shaders.cpp | 4 +- src/render/opengl/shaders/ribbon_shaders.cpp | 4 +- src/render/opengl/shaders/rules.cpp | 4 +- src/render/opengl/shaders/sphere_shaders.cpp | 4 +- .../opengl/shaders/surface_mesh_shaders.cpp | 4 +- .../opengl/shaders/texture_draw_shaders.cpp | 4 +- src/render/opengl/shaders/vector_shaders.cpp | 4 +- .../opengl/shaders/volume_mesh_shaders.cpp | 4 +- 35 files changed, 314 insertions(+), 3466 deletions(-) diff --git a/include/polyscope/render/opengl/gl_engine.h b/include/polyscope/render/opengl/gl_engine.h index fc856c35..82cbee01 100644 --- a/include/polyscope/render/opengl/gl_engine.h +++ b/include/polyscope/render/opengl/gl_engine.h @@ -11,6 +11,14 @@ // Note: DO NOT include this header throughout polyscope, and do not directly make openGL calls. This header should only // be used to construct an instance of Engine. engine.h gives the render API, all render calls should pass through that. +#ifdef __APPLE__ +#else +#include "glad/glad.h" +#endif + +#ifdef _WIN32 +#undef APIENTRY +#endif namespace polyscope { namespace render { @@ -355,7 +363,6 @@ class GLShaderProgram : public ShaderProgram { class GLEngine : public Engine { public: - GLEngine(); virtual ~GLEngine(); diff --git a/include/polyscope/render/opengl/gl_engine_egl.h b/include/polyscope/render/opengl/gl_engine_egl.h index 115c51c2..40b0005a 100644 --- a/include/polyscope/render/opengl/gl_engine_egl.h +++ b/include/polyscope/render/opengl/gl_engine_egl.h @@ -7,29 +7,22 @@ #include "polyscope/utilities.h" #ifdef __APPLE__ -#define GLFW_INCLUDE_GLCOREARB -#include "GLFW/glfw3.h" +// TODO should we IFDEF this away instead? +#error "EGL backend is not supported on macOS. Disable it in the CMake build options." #else #include "glad/glad.h" // glad must come first -#include "GLFW/glfw3.h" -#endif - #include -#undef None // for some terrible reason egl.h defines 'None', which we use below - -#ifdef _WIN32 -#undef APIENTRY -#define GLFW_EXPOSE_NATIVE_WIN32 -#define GLFW_EXPOSE_NATIVE_WGL -#include #endif + #include "imgui.h" #define IMGUI_IMPL_OPENGL_LOADER_GLAD -#include "backends/imgui_impl_glfw.h" +#include "backends/imgui_impl_glfw.h" // TODO #include "backends/imgui_impl_opengl3.h" +#include "polyscope/render/opengl/gl_engine.h" + #include // Note: DO NOT include this header throughout polyscope, and do not directly make openGL calls. This header should only @@ -38,364 +31,25 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw_egl { - -// Some very nice typdefs -typedef GLuint TextureBufferHandle; -typedef GLuint RenderBufferHandle; -typedef GLuint FrameBufferHandle; -typedef GLuint ShaderHandle; -typedef GLuint ProgramHandle; -typedef GLuint AttributeHandle; -typedef GLuint VertexBufferHandle; - -typedef GLint UniformLocation; -typedef GLint AttributeLocation; -typedef GLint TextureLocation; - -class GLAttributeBuffer : public AttributeBuffer { -public: - GLAttributeBuffer(RenderDataType dataType_, int arrayCount_); - virtual ~GLAttributeBuffer(); - - void bind(); - VertexBufferHandle getHandle() const { return VBOLoc; } - - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - - // Array-valued attributes - // (adding these lazily as we need them) - // (sadly we cannot template the virtual function) - void setData(const std::vector>& data) override; - void setData(const std::vector>& data) override; - void setData(const std::vector>& data) override; - - // get data at a single index from the buffer - float getData_float(size_t ind) override; - double getData_double(size_t ind) override; - glm::vec2 getData_vec2(size_t ind) override; - glm::vec3 getData_vec3(size_t ind) override; - glm::vec4 getData_vec4(size_t ind) override; - int getData_int(size_t ind) override; - uint32_t getData_uint32(size_t ind) override; - glm::uvec2 getData_uvec2(size_t ind) override; - glm::uvec3 getData_uvec3(size_t ind) override; - glm::uvec4 getData_uvec4(size_t ind) override; - - // get data at a range of indices from the buffer - std::vector getDataRange_float(size_t ind, size_t count) override; - std::vector getDataRange_double(size_t ind, size_t count) override; - std::vector getDataRange_vec2(size_t ind, size_t count) override; - std::vector getDataRange_vec3(size_t ind, size_t count) override; - std::vector getDataRange_vec4(size_t ind, size_t count) override; - std::vector getDataRange_int(size_t ind, size_t count) override; - std::vector getDataRange_uint32(size_t ind, size_t count) override; - std::vector getDataRange_uvec2(size_t ind, size_t count) override; - std::vector getDataRange_uvec3(size_t ind, size_t count) override; - std::vector getDataRange_uvec4(size_t ind, size_t count) override; - - uint32_t getNativeBufferID() override; - -protected: - VertexBufferHandle VBOLoc; - -private: - void checkType(RenderDataType targetType); - void checkArray(int arrayCount); - GLenum getTarget(); - - - // internal implementation helpers - template - void setData_helper(const std::vector& data); - - template - T getData_helper(size_t ind); - - template - std::vector getDataRange_helper(size_t start, size_t count); -}; - -class GLTextureBuffer : public TextureBuffer { -public: - // create a 1D texture from data - GLTextureBuffer(TextureFormat format, unsigned int size1D, const unsigned char* data = nullptr); - GLTextureBuffer(TextureFormat format, unsigned int size1D, const float* data); - - // create a 2D texture from data - GLTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, const unsigned char* data = nullptr); - GLTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, const float* data); - - // create a 3D texture from data - GLTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, unsigned int sizeZ_, - const unsigned char* data = nullptr); - GLTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, unsigned int sizeZ_, - const float* data); - - ~GLTextureBuffer() override; - - - // Resize the underlying buffer (contents are lost) - void resize(unsigned int newLen) override; - void resize(unsigned int newX, unsigned int newY) override; - void resize(unsigned int newX, unsigned int newY, unsigned int newZ) override; - - // Fill with data - // NOTE: some of these are not implemented yet - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - void setData(const std::vector& data) override; - - // Array-valued - // NOTE: some of these are not implemented yet - // (adding these lazily as we need them) - // (sadly we cannot template the virtual function) - void setData(const std::vector>& data) override; - void setData(const std::vector>& data) override; - void setData(const std::vector>& data) override; - - void setFilterMode(FilterMode newMode) override; - void* getNativeHandle() override; - uint32_t getNativeBufferID() override; - - std::vector getDataScalar() override; - std::vector getDataVector2() override; - std::vector getDataVector3() override; - - void bind(); - GLenum textureType(); - TextureBufferHandle getHandle() const { return handle; } - - -protected: - TextureBufferHandle handle; -}; - -class GLRenderBuffer : public RenderBuffer { -public: - GLRenderBuffer(RenderBufferType type, unsigned int sizeX_, unsigned int sizeY_); - ~GLRenderBuffer() override; - - void resize(unsigned int newX, unsigned int newY) override; - - void bind(); - RenderBufferHandle getHandle() const { return handle; } - - RenderBufferHandle handle; -}; - - -class GLFrameBuffer : public FrameBuffer { - -public: - GLFrameBuffer(unsigned int sizeX_, unsigned int sizeY_, bool isDefault = false); - ~GLFrameBuffer() override; - - void bind() override; - - // Bind to this framebuffer so subsequent draw calls will go to it - // If return is false, binding failed and the framebuffer should not be used. - bool bindForRendering() override; - - // Clear to redraw - void clear() override; - - // Bind to textures/renderbuffers for output - void addColorBuffer(std::shared_ptr renderBuffer) override; - void addColorBuffer(std::shared_ptr textureBuffer) override; - void addDepthBuffer(std::shared_ptr renderBuffer) override; - void addDepthBuffer(std::shared_ptr textureBuffer) override; - - void setDrawBuffers() override; - - // Query pixels - std::vector readBuffer() override; - std::array readFloat4(int xPos, int yPos) override; - float readDepth(int xPos, int yPos) override; - void blitTo(FrameBuffer* other) override; +namespace backend_openGL3 { - // Getters - FrameBufferHandle getHandle() const { return handle; } - uint32_t getNativeBufferID() override; - FrameBufferHandle handle; -}; - -// Classes to keep track of attributes and uniforms -struct GLShaderUniform { - std::string name; - RenderDataType type; - bool isSet; // has a value been assigned to this uniform? - UniformLocation location; // -1 means "no location", usually because it was optimized out -}; - -struct GLShaderAttribute { - std::string name; - RenderDataType type; - int arrayCount; - AttributeLocation location; // -1 means "no location", usually because it was optimized out - std::shared_ptr buff; // the buffer that we will actually use -}; - -struct GLShaderTexture { - std::string name; - int dim; - uint32_t index; - bool isSet; - GLTextureBuffer* textureBuffer; - std::shared_ptr textureBufferOwned; // might be empty, if texture isn't owned - TextureLocation location; // -1 means "no location", usually because it was optimized out -}; - -// A thin wrapper around a program handle. -// This class takes ownership and handles program deletion in its destructor -class GLCompiledProgram { -public: - GLCompiledProgram(const std::vector& stages, DrawMode dm); - ~GLCompiledProgram(); - - ProgramHandle getHandle() const { return programHandle; } - DrawMode getDrawMode() const { return drawMode; } - std::vector getUniforms() const { return uniforms; } - std::vector getAttributes() const { return attributes; } - std::vector getTextures() const { return textures; } - -private: - ProgramHandle programHandle; - DrawMode drawMode; - std::vector uniforms; - std::vector attributes; - std::vector textures; - - void compileGLProgram(const std::vector& stages); - void setDataLocations(); - - void addUniqueAttribute(ShaderSpecAttribute attribute); - void addUniqueUniform(ShaderSpecUniform uniform); - void addUniqueTexture(ShaderSpecTexture texture); -}; - -class GLShaderProgram : public ShaderProgram { +class GLEngineEGL : public GLEngine { public: - GLShaderProgram(const std::shared_ptr& compiledProgram); - ~GLShaderProgram() override; - - // === Store data - // If update is set to "true", data is updated rather than allocated (must be allocated first) - - // Uniforms - bool hasUniform(std::string name) override; - void setUniform(std::string name, int val) override; - void setUniform(std::string name, unsigned int val) override; - void setUniform(std::string name, float val) override; - void setUniform(std::string name, double val) override; // WARNING casts down to float - void setUniform(std::string name, float* val) override; - void setUniform(std::string name, glm::vec2 val) override; - void setUniform(std::string name, glm::vec3 val) override; - void setUniform(std::string name, glm::vec4 val) override; - void setUniform(std::string name, std::array val) override; - void setUniform(std::string name, float x, float y, float z, float w) override; - void setUniform(std::string name, glm::uvec2 val) override; - void setUniform(std::string name, glm::uvec3 val) override; - void setUniform(std::string name, glm::uvec4 val) override; - - // = Attributes - // clang-format off - bool hasAttribute(std::string name) override; - bool attributeIsSet(std::string name) override; - std::shared_ptr getAttributeBuffer(std::string name) override; - void setAttribute(std::string name, std::shared_ptr externalBuffer) override; - void setAttribute(std::string name, const std::vector& data) override; - void setAttribute(std::string name, const std::vector& data) override; - void setAttribute(std::string name, const std::vector& data) override; - void setAttribute(std::string name, const std::vector& data) override; - void setAttribute(std::string name, const std::vector& data) override; - void setAttribute(std::string name, const std::vector& data) override; - void setAttribute(std::string name, const std::vector& data) override; - // clang-format on - - - // Indices - void setIndex(std::shared_ptr externalBuffer) override; - void setPrimitiveRestartIndex(unsigned int restartIndex) override; - - // Instancing - void setInstanceCount(uint32_t instanceCount) override; - - // Textures - bool hasTexture(std::string name) override; - bool textureIsSet(std::string name) override; - void setTexture1D(std::string name, unsigned char* texData, unsigned int length) override; - void setTexture2D(std::string name, unsigned char* texData, unsigned int width, unsigned int height, - bool withAlpha = true, bool useMipMap = false, bool repeat = false) override; - void setTextureFromColormap(std::string name, const std::string& colorMap, bool allowUpdate = false) override; - void setTextureFromBuffer(std::string name, TextureBuffer* textureBuffer) override; - - // Draw! - void draw() override; - void validateData() override; - -protected: - // Lists of attributes and uniforms that need to be set - std::vector uniforms; - std::vector attributes; - std::vector textures; - -private: - // Setup routines - void compileGLProgram(const std::vector& stages); - void setDataLocations(); - void bindVAO(); - void createBuffers(); - void ensureBufferExists(GLShaderAttribute& a); - void createBuffer(GLShaderAttribute& a); - void assignBufferToVAO(GLShaderAttribute& a); - - // Drawing related - void activateTextures(); - - // GL pointers for various useful things - std::shared_ptr compiledProgram; - AttributeHandle vaoHandle; -}; - - -class GLEngine : public Engine { -public: - GLEngine(); + GLEngineEGL(); + virtual ~GLEngineEGL(); // High-level control void initialize(); - void checkError(bool fatal = false) override; - void swapDisplayBuffers() override; - std::vector readDisplayBuffer() override; - - // Manage render state - void setDepthMode(DepthMode newMode) override; - void setBlendMode(BlendMode newMode) override; - void setColorMask(std::array mask = {true, true, true, true}) override; - void setBackfaceCull(bool newVal) override; + void checkError(bool fatal = false) override; // === Windowing and framework things + void makeContextCurrent() override; + void pollEvents() override; + void focusWindow() override; void showWindow() override; void hideWindow() override; @@ -405,82 +59,27 @@ class GLEngine : public Engine { bool getWindowResizable() override; std::tuple getWindowPos() override; bool windowRequestsClose() override; - void pollEvents() override; + bool isKeyPressed(char c) override; // for lowercase a-z and 0-9 only int getKeyCode(char c) override; // for lowercase a-z and 0-9 only std::string getClipboardText() override; void setClipboardText(std::string text) override; - // ImGui + + // === ImGui + void initializeImGui() override; void shutdownImGui() override; void ImGuiNewFrame() override; void ImGuiRender() override; - // === Factory methods - - // create attribute buffers - std::shared_ptr generateAttributeBuffer(RenderDataType dataType_, int arrayCount_) override; - - // create textures - std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int size1D, - const unsigned char* data = nullptr) override; // 1d - std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int size1D, - const float* data) override; // 1d - std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, - const unsigned char* data = nullptr) override; // 2d - std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, - const float* data) override; // 2d - std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, - unsigned int sizeZ_, - const unsigned char* data = nullptr) override; // 3d - std::shared_ptr generateTextureBuffer(TextureFormat format, unsigned int sizeX_, unsigned int sizeY_, - unsigned int sizeZ_, - const float* data) override; // 3d - - // create render buffers - std::shared_ptr generateRenderBuffer(RenderBufferType type, unsigned int sizeX_, - unsigned int sizeY_) override; - // create frame buffers - std::shared_ptr generateFrameBuffer(unsigned int sizeX_, unsigned int sizeY_) override; - - // general flexible interface - std::shared_ptr - requestShader(const std::string& programName, const std::vector& customRules, - ShaderReplacementDefaults defaults = ShaderReplacementDefaults::SceneObject) override; - - // === Implementation details - - // Add a shader programs/rules so that they can be requested above - void registerShaderProgram(const std::string& name, const std::vector& spec, - const DrawMode& dm); - void registerShaderRule(const std::string& name, const ShaderReplacementRule& rule); - - // Transparency - virtual void applyTransparencySettings() override; - - virtual void setFrontFaceCCW(bool newVal) override; - protected: - // Helpers - virtual void createSlicePlaneFliterRule(std::string name) override; - // Internal windowing and engine details - GLFWwindow* mainWindow = nullptr; - - // Shader program & rule caches - std::unordered_map, DrawMode>> registeredShaderPrograms; - std::unordered_map registeredShaderRules; - void populateDefaultShadersAndRules(); - - std::unordered_map> compiledProgamCache; - std::string programKeyFromRules(const std::string& programName, const std::vector& rules, - ShaderReplacementDefaults defaults); - std::shared_ptr getCompiledProgram(const std::string& programName, - const std::vector& customRules, - ShaderReplacementDefaults defaults); + EGLDisplay eglDisplay; + // EGLSurface eglSurface; + EGLContext eglContext; }; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/common.h b/include/polyscope/render/opengl/shaders/common.h index 6c6f5372..4d2bcd8a 100644 --- a/include/polyscope/render/opengl/shaders/common.h +++ b/include/polyscope/render/opengl/shaders/common.h @@ -8,10 +8,10 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { extern const char* shaderCommonSource; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/cylinder_shaders.h b/include/polyscope/render/opengl/shaders/cylinder_shaders.h index 3802dab5..ce63d638 100644 --- a/include/polyscope/render/opengl/shaders/cylinder_shaders.h +++ b/include/polyscope/render/opengl/shaders/cylinder_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // High level pipeline extern const ShaderStageSpecification FLEX_CYLINDER_VERT_SHADER; @@ -23,6 +23,6 @@ extern const ShaderReplacementRule CYLINDER_CULLPOS_FROM_MID; extern const ShaderReplacementRule CYLINDER_VARIABLE_SIZE; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/gizmo_shaders.h b/include/polyscope/render/opengl/shaders/gizmo_shaders.h index bd121e48..9015a18d 100644 --- a/include/polyscope/render/opengl/shaders/gizmo_shaders.h +++ b/include/polyscope/render/opengl/shaders/gizmo_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // Stages extern const ShaderStageSpecification TRANSFORMATION_GIZMO_ROT_VERT; @@ -17,6 +17,6 @@ extern const ShaderStageSpecification SLICE_PLANE_FRAG_SHADER; // Rules extern const ShaderReplacementRule TRANSFORMATION_GIZMO_VEC; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/grid_shaders.h b/include/polyscope/render/opengl/shaders/grid_shaders.h index 1c887769..225891c2 100644 --- a/include/polyscope/render/opengl/shaders/grid_shaders.h +++ b/include/polyscope/render/opengl/shaders/grid_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // High level pipeline extern const ShaderStageSpecification FLEX_GRIDCUBE_VERT_SHADER; @@ -24,6 +24,6 @@ extern const ShaderReplacementRule GRIDCUBE_CONSTANT_PICK; extern const ShaderReplacementRule GRIDCUBE_CULLPOS_FROM_CENTER; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/ground_plane_shaders.h b/include/polyscope/render/opengl/shaders/ground_plane_shaders.h index 0fe1163f..019d3321 100644 --- a/include/polyscope/render/opengl/shaders/ground_plane_shaders.h +++ b/include/polyscope/render/opengl/shaders/ground_plane_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { extern const ShaderStageSpecification GROUND_PLANE_VERT_SHADER; extern const ShaderStageSpecification GROUND_PLANE_TILE_FRAG_SHADER; @@ -16,6 +16,6 @@ extern const ShaderStageSpecification GROUND_PLANE_SHADOW_FRAG_SHADER; // Rules // extern const ShaderReplacementRule RULE_NAME; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/histogram_shaders.h b/include/polyscope/render/opengl/shaders/histogram_shaders.h index 00b4c79a..9f5403e5 100644 --- a/include/polyscope/render/opengl/shaders/histogram_shaders.h +++ b/include/polyscope/render/opengl/shaders/histogram_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // High level pipeline extern const ShaderStageSpecification HISTOGRAM_VERT_SHADER; @@ -15,6 +15,6 @@ extern const ShaderStageSpecification HISTOGRAM_FRAG_SHADER; // Rules // extern const ShaderReplacementRule RULE_NAME; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/lighting_shaders.h b/include/polyscope/render/opengl/shaders/lighting_shaders.h index 4323fe32..9122bb3c 100644 --- a/include/polyscope/render/opengl/shaders/lighting_shaders.h +++ b/include/polyscope/render/opengl/shaders/lighting_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // High level pipeline extern const ShaderStageSpecification MAP_LIGHT_FRAG_SHADER; @@ -24,6 +24,6 @@ extern const ShaderReplacementRule TRANSPARENCY_STRUCTURE; extern const ShaderReplacementRule TRANSPARENCY_PEEL_STRUCTURE; extern const ShaderReplacementRule TRANSPARENCY_PEEL_GROUND; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/ribbon_shaders.h b/include/polyscope/render/opengl/shaders/ribbon_shaders.h index 558e298f..c97ff5ca 100644 --- a/include/polyscope/render/opengl/shaders/ribbon_shaders.h +++ b/include/polyscope/render/opengl/shaders/ribbon_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // High level pipeline extern const ShaderStageSpecification RIBBON_VERT_SHADER; @@ -16,6 +16,6 @@ extern const ShaderStageSpecification RIBBON_FRAG_SHADER; // Rules // extern const ShaderReplacementRule RULE_NAME; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/rules.h b/include/polyscope/render/opengl/shaders/rules.h index 1fe39694..1e24766d 100644 --- a/include/polyscope/render/opengl/shaders/rules.h +++ b/include/polyscope/render/opengl/shaders/rules.h @@ -8,7 +8,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { extern const ShaderReplacementRule GLSL_VERSION; extern const ShaderReplacementRule GLOBAL_FRAGMENT_FILTER; @@ -44,6 +44,6 @@ ShaderReplacementRule generateVolumeGridSlicePlaneRule(std::string uniquePostfix // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/sphere_shaders.h b/include/polyscope/render/opengl/shaders/sphere_shaders.h index a5a10ee6..a447e2e6 100644 --- a/include/polyscope/render/opengl/shaders/sphere_shaders.h +++ b/include/polyscope/render/opengl/shaders/sphere_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // High level pipeline extern const ShaderStageSpecification FLEX_SPHERE_VERT_SHADER; @@ -26,6 +26,6 @@ extern const ShaderReplacementRule SPHERE_CULLPOS_FROM_CENTER; extern const ShaderReplacementRule SPHERE_CULLPOS_FROM_CENTER_QUAD; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h index a3e64bfb..34a87ca5 100644 --- a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h +++ b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // High level pipeline extern const ShaderStageSpecification FLEX_MESH_VERT_SHADER; @@ -36,6 +36,6 @@ extern const ShaderReplacementRule MESH_PROPAGATE_PICK_SIMPLE; extern const ShaderReplacementRule MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/texture_draw_shaders.h b/include/polyscope/render/opengl/shaders/texture_draw_shaders.h index c9fe9060..b156d4d8 100644 --- a/include/polyscope/render/opengl/shaders/texture_draw_shaders.h +++ b/include/polyscope/render/opengl/shaders/texture_draw_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { extern const ShaderStageSpecification TEXTURE_DRAW_VERT_SHADER; extern const ShaderStageSpecification TEXTURE_DRAW_UPPERLEFT_VERT_SHADER; @@ -50,6 +50,6 @@ extern const ShaderReplacementRule SHADE_NORMAL_FROM_VIEWPOS_VAR; extern const ShaderStageSpecification TEXTURE_DRAW_VERT_SHADER; extern const ShaderStageSpecification SPHEREBG_DRAW_VERT_SHADER; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/vector_shaders.h b/include/polyscope/render/opengl/shaders/vector_shaders.h index 0c5d1e15..80e95fc2 100644 --- a/include/polyscope/render/opengl/shaders/vector_shaders.h +++ b/include/polyscope/render/opengl/shaders/vector_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // High level pipeline extern const ShaderStageSpecification FLEX_VECTOR_VERT_SHADER; @@ -18,6 +18,6 @@ extern const ShaderStageSpecification FLEX_VECTOR_FRAG_SHADER; extern const ShaderReplacementRule VECTOR_PROPAGATE_COLOR; extern const ShaderReplacementRule VECTOR_CULLPOS_FROM_TAIL; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/volume_mesh_shaders.h b/include/polyscope/render/opengl/shaders/volume_mesh_shaders.h index c093845c..d0725634 100644 --- a/include/polyscope/render/opengl/shaders/volume_mesh_shaders.h +++ b/include/polyscope/render/opengl/shaders/volume_mesh_shaders.h @@ -6,7 +6,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // High level pipeline extern const ShaderStageSpecification SLICE_TETS_VERT_SHADER; @@ -19,6 +19,6 @@ extern const ShaderReplacementRule SLICE_TETS_PROPAGATE_VECTOR; extern const ShaderReplacementRule SLICE_TETS_VECTOR_COLOR; -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/initialize_backend.cpp b/src/render/initialize_backend.cpp index acf8b5ea..ea5aa6a9 100644 --- a/src/render/initialize_backend.cpp +++ b/src/render/initialize_backend.cpp @@ -14,12 +14,10 @@ std::string engineBackendName = ""; // Forward-declaration of initialize routines // we don't want to just include the appropriate headers, because they may define conflicting symbols -namespace backend_openGL3_glfw { -void initializeRenderEngine(); -} -namespace backend_openGL3_glfw_egl { -void initializeRenderEngine(); -} +namespace backend_openGL3 { +void initializeRenderEngine_glfw(); +void initializeRenderEngine_egl(); +} // namespace backend_openGL3 namespace backend_openGL_mock { void initializeRenderEngine(); } @@ -48,9 +46,9 @@ void initializeRenderEngine(std::string backend) { // Initialize the appropriate backend if (backend == "openGL3_glfw") { - backend_openGL3_glfw::initializeRenderEngine(); - } else if (backend == "openGL3_glfw_egl") { - backend_openGL3_glfw_egl::initializeRenderEngine(); + backend_openGL3::initializeRenderEngine_glfw(); + } else if (backend == "openGL3_egl") { + backend_openGL3::initializeRenderEngine_egl(); } else if (backend == "openGL_mock") { backend_openGL_mock::initializeRenderEngine(); } else { diff --git a/src/render/mock_opengl/mock_gl_engine.cpp b/src/render/mock_opengl/mock_gl_engine.cpp index ab008783..5d362604 100644 --- a/src/render/mock_opengl/mock_gl_engine.cpp +++ b/src/render/mock_opengl/mock_gl_engine.cpp @@ -1883,7 +1883,7 @@ void MockGLEngine::registerShaderRule(const std::string& name, const ShaderRepla void MockGLEngine::populateDefaultShadersAndRules() { - using namespace backend_openGL3_glfw; + using namespace backend_openGL3; // WARNING: duplicated from gl_engine.cpp @@ -2032,7 +2032,7 @@ void MockGLEngine::populateDefaultShadersAndRules() { void MockGLEngine::createSlicePlaneFliterRule(std::string uniquePostfix) { - using namespace backend_openGL3_glfw; + using namespace backend_openGL3; registeredShaderRules.insert({"SLICE_PLANE_CULL_" + uniquePostfix, generateSlicePlaneRule(uniquePostfix)}); registeredShaderRules.insert( {"SLICE_PLANE_VOLUMEGRID_CULL_" + uniquePostfix, generateVolumeGridSlicePlaneRule(uniquePostfix)}); diff --git a/src/render/opengl/gl_engine.cpp b/src/render/opengl/gl_engine.cpp index b84903b1..f493ca19 100644 --- a/src/render/opengl/gl_engine.cpp +++ b/src/render/opengl/gl_engine.cpp @@ -1,9 +1,8 @@ // Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run -#include "backends/imgui_impl_opengl3.h" #include "polyscope/render/engine.h" -#ifdef POLYSCOPE_BACKEND_OPENGL_ENABLED +#ifdef POLYSCOPE_BACKEND_OPENGL3_ENABLED #include "polyscope/render/opengl/gl_engine.h" #include "polyscope/messages.h" @@ -37,7 +36,7 @@ namespace polyscope { namespace render { -namespace backend_openGL { +namespace backend_openGL3 { GLEngine* glEngine = nullptr; // alias for global engine pointer @@ -162,11 +161,14 @@ void checkGLError(bool fatal = true) { case GL_INVALID_OPERATION: errText = "Invalid operation"; break; - // case GL_STACK_OVERFLOW: std::cerr << "Stack overflow"; break; - // case GL_STACK_UNDERFLOW: std::cerr << "Stack underflow"; break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + errText = "Invalid framebuffer operation"; + break; case GL_OUT_OF_MEMORY: errText = "Out of memory"; break; + // case GL_STACK_OVERFLOW: std::cerr << "Stack overflow"; break; + // case GL_STACK_UNDERFLOW: std::cerr << "Stack underflow"; break; default: errText = "Unknown error " + std::to_string(static_cast(err)); } @@ -2109,30 +2111,7 @@ void GLShaderProgram::draw() { GLEngine::GLEngine() {} GLEngine::~GLEngine() {} -void GLEngine::initializeImGui() { - bindDisplay(); - - ImGui::CreateContext(); // must call once at start - - // Set up ImGUI glfw bindings - ImGui_ImplGlfw_InitForOpenGL(mainWindow, true); - const char* glsl_version = "#version 150"; - ImGui_ImplOpenGL3_Init(glsl_version); - - configureImGui(); -} - -void GLEngine::shutdownImGui() { - // ImGui shutdown things - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); -} - -void GLEngine::swapDisplayBuffers() { - bindDisplay(); - glfwSwapBuffers(mainWindow); -} +void GLEngine::checkError(bool fatal) { checkGLError(fatal); } std::vector GLEngine::readDisplayBuffer() { // TODO do we need to bind here? @@ -2155,111 +2134,6 @@ std::vector GLEngine::readDisplayBuffer() { } -void GLEngine::checkError(bool fatal) { checkGLError(fatal); } - -void GLEngine::makeContextCurrent() { - glfwMakeContextCurrent(mainWindow); - glfwSwapInterval(options::enableVSync ? 1 : 0); -} - -void GLEngine::focusWindow() { glfwFocusWindow(mainWindow); } - -void GLEngine::showWindow() { glfwShowWindow(mainWindow); } - -void GLEngine::hideWindow() { - glfwHideWindow(mainWindow); - glfwPollEvents(); // this shouldn't be necessary, but seems to be needed at least on macOS. Perhaps realted to a - // glfw bug? e.g. https://github.com/glfw/glfw/issues/1300 and related bugs -} - -void GLEngine::updateWindowSize(bool force) { - int newBufferWidth, newBufferHeight, newWindowWidth, newWindowHeight; - glfwGetFramebufferSize(mainWindow, &newBufferWidth, &newBufferHeight); - glfwGetWindowSize(mainWindow, &newWindowWidth, &newWindowHeight); - if (force || newBufferWidth != view::bufferWidth || newBufferHeight != view::bufferHeight || - newWindowHeight != view::windowHeight || newWindowWidth != view::windowWidth) { - // Basically a resize callback - requestRedraw(); - - // prevent any division by zero for e.g. aspect ratio calcs - if (newBufferHeight == 0) newBufferHeight = 1; - if (newWindowHeight == 0) newWindowHeight = 1; - - view::bufferWidth = newBufferWidth; - view::bufferHeight = newBufferHeight; - view::windowWidth = newWindowWidth; - view::windowHeight = newWindowHeight; - - render::engine->resizeScreenBuffers(); - render::engine->setScreenBufferViewports(); - } -} - - -void GLEngine::applyWindowSize() { - glfwSetWindowSize(mainWindow, view::windowWidth, view::windowHeight); - - // on some platform size changes are asynchonous, need to ensure it completes - // we don't want to just retry until the resize has happened, because it could be impossible - // TODO it seems like on X11 sometimes even this isn't enough? - glfwWaitEvents(); - - updateWindowSize(true); -} - - -void GLEngine::setWindowResizable(bool newVal) { - glfwSetWindowAttrib(mainWindow, GLFW_RESIZABLE, newVal ? GLFW_TRUE : GLFW_FALSE); -} - -bool GLEngine::getWindowResizable() { return glfwGetWindowAttrib(mainWindow, GLFW_RESIZABLE); } - -std::tuple GLEngine::getWindowPos() { - int x, y; - glfwGetWindowPos(mainWindow, &x, &y); - return std::tuple{x, y}; -} - -bool GLEngine::windowRequestsClose() { - bool shouldClose = glfwWindowShouldClose(mainWindow); - if (shouldClose) { - glfwSetWindowShouldClose(mainWindow, false); // un-set the state bit so we can close again - return true; - } - return false; -} - -void GLEngine::pollEvents() { glfwPollEvents(); } - -bool GLEngine::isKeyPressed(char c) { - if (c >= '0' && c <= '9') return ImGui::IsKeyPressed(static_cast(ImGuiKey_0 + (c - '0'))); - if (c >= 'a' && c <= 'z') return ImGui::IsKeyPressed(static_cast(ImGuiKey_A + (c - 'a'))); - if (c >= 'A' && c <= 'Z') return ImGui::IsKeyPressed(static_cast(ImGuiKey_A + (c - 'A'))); - exception("keyPressed only supports 0-9, a-z, A-Z"); - return false; -} - -int GLEngine::getKeyCode(char c) { - if (c >= '0' && c <= '9') return static_cast(ImGuiKey_0) + (c - '0'); - if (c >= 'a' && c <= 'z') return static_cast(ImGuiKey_A) + (c - 'a'); - if (c >= 'A' && c <= 'Z') return static_cast(ImGuiKey_A) + (c - 'A'); - exception("getKeyCode only supports 0-9, a-z, A-Z"); - return -1; -} - -void GLEngine::ImGuiNewFrame() { - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - - // ImGui::ShowDemoWindow(); -} - -void GLEngine::ImGuiRender() { - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); -} - void GLEngine::setDepthMode(DepthMode newMode) { switch (newMode) { case DepthMode::Less: @@ -2342,13 +2216,6 @@ void GLEngine::setBackfaceCull(bool newVal) { } } -std::string GLEngine::getClipboardText() { - std::string clipboardData = ImGui::GetClipboardText(); - return clipboardData; -} - -void GLEngine::setClipboardText(std::string text) { ImGui::SetClipboardText(text.c_str()); } - void GLEngine::applyTransparencySettings() { // Remove any old transparency-related rules switch (transparencyMode) { @@ -2729,7 +2596,7 @@ void GLEngine::createSlicePlaneFliterRule(std::string uniquePostfix) { } -} // namespace backend_openGL +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index b3a7ffd2..3bf9f3fe 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -1,2973 +1,349 @@ // Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run -#include "backends/imgui_impl_opengl3.h" -#include "polyscope/render/engine.h" - -#ifdef POLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED -#include "polyscope/render/opengl/egl_gl_engine.h" - -#include "polyscope/messages.h" -#include "polyscope/options.h" -#include "polyscope/polyscope.h" -#include "polyscope/utilities.h" - -#include "polyscope/render/shader_builder.h" - -// all the shaders -#include "polyscope/render/opengl/shaders/common.h" -#include "polyscope/render/opengl/shaders/cylinder_shaders.h" -#include "polyscope/render/opengl/shaders/gizmo_shaders.h" -#include "polyscope/render/opengl/shaders/grid_shaders.h" -#include "polyscope/render/opengl/shaders/ground_plane_shaders.h" -#include "polyscope/render/opengl/shaders/histogram_shaders.h" -#include "polyscope/render/opengl/shaders/lighting_shaders.h" -#include "polyscope/render/opengl/shaders/ribbon_shaders.h" -#include "polyscope/render/opengl/shaders/rules.h" -#include "polyscope/render/opengl/shaders/sphere_shaders.h" -#include "polyscope/render/opengl/shaders/surface_mesh_shaders.h" -#include "polyscope/render/opengl/shaders/texture_draw_shaders.h" -#include "polyscope/render/opengl/shaders/vector_shaders.h" -#include "polyscope/render/opengl/shaders/volume_mesh_shaders.h" - -#include "stb_image.h" - -#include -#include - -namespace polyscope { -namespace render { - -namespace backend_openGL3_glfw_egl { - -using namespace backend_openGL3_glfw; - -GLEngine* glEngine = nullptr; // alias for global engine pointer - -void initializeRenderEngine() { - glEngine = new GLEngine(); - engine = glEngine; - glEngine->initialize(); - engine->allocateGlobalBuffersAndPrograms(); -} - -// EGL configuration -// see https://developer.nvidia.com/blog/egl-eye-opengl-visualization-without-x-server/ -static const EGLint configAttribs[] = { - EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 8, - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE}; - -static const int pbufferWidth = 9; -static const int pbufferHeight = 9; - -static const EGLint pbufferAttribs[] = { - EGL_WIDTH, pbufferWidth, EGL_HEIGHT, pbufferHeight, EGL_NONE, -}; - -// == Map enums to native values - -// clang-format off - -inline GLenum internalFormat(const TextureFormat& x) { - switch (x) { - case TextureFormat::RGB8: return GL_RGB8; - case TextureFormat::RGBA8: return GL_RGBA8; - case TextureFormat::RG16F: return GL_RG16F; - case TextureFormat::RGB16F: return GL_RGB16F; - case TextureFormat::RGBA16F: return GL_RGBA16F; - case TextureFormat::R32F: return GL_R32F; - case TextureFormat::R16F: return GL_R16F; - case TextureFormat::RGB32F: return GL_RGBA32F; - case TextureFormat::RGBA32F: return GL_RGBA32F; - case TextureFormat::DEPTH24: return GL_DEPTH_COMPONENT24; - } - exception("bad enum"); - return GL_RGB8; -} - -inline GLenum formatF(const TextureFormat& x) { - switch (x) { - case TextureFormat::RGB8: return GL_RGB; - case TextureFormat::RGBA8: return GL_RGBA; - case TextureFormat::RG16F: return GL_RG; - case TextureFormat::RGB16F: return GL_RGB; - case TextureFormat::RGBA16F: return GL_RGBA; - case TextureFormat::R32F: return GL_RED; - case TextureFormat::R16F: return GL_RED; - case TextureFormat::RGB32F: return GL_RGB; - case TextureFormat::RGBA32F: return GL_RGBA; - case TextureFormat::DEPTH24: return GL_DEPTH_COMPONENT; - } - exception("bad enum"); - return GL_RGB; -} - -inline GLenum type(const TextureFormat& x) { - switch (x) { - case TextureFormat::RGB8: return GL_UNSIGNED_BYTE; - case TextureFormat::RGBA8: return GL_UNSIGNED_BYTE; - case TextureFormat::RG16F: return GL_HALF_FLOAT; - case TextureFormat::RGB16F: return GL_HALF_FLOAT; - case TextureFormat::RGBA16F: return GL_HALF_FLOAT; - case TextureFormat::R32F: return GL_FLOAT; - case TextureFormat::R16F: return GL_FLOAT; - case TextureFormat::RGB32F: return GL_FLOAT; - case TextureFormat::RGBA32F: return GL_FLOAT; - case TextureFormat::DEPTH24: return GL_FLOAT; - } - exception("bad enum"); - return GL_UNSIGNED_BYTE; -} - -inline GLenum native(const ShaderStageType& x) { - switch (x) { - case ShaderStageType::Vertex: return GL_VERTEX_SHADER; - case ShaderStageType::Geometry: return GL_GEOMETRY_SHADER; - //case ShaderStageType::Compute: return GL_COMPUTE_SHADER; - case ShaderStageType::Fragment: return GL_FRAGMENT_SHADER; - } - exception("bad enum"); - return GL_VERTEX_SHADER; -} - -inline GLenum native(const RenderBufferType& x) { - switch (x) { - case RenderBufferType::ColorAlpha: return GL_RGBA; - case RenderBufferType::Color: return GL_RGB; - case RenderBufferType::Depth: return GL_DEPTH_COMPONENT; - case RenderBufferType::Float4: return GL_RGBA32F; - } - exception("bad enum"); - return GL_RGBA; -} - -inline GLenum colorAttachNum(const unsigned int i) { - // can we just add to the 0 one? couldn't find documentation saying yes for sure. - switch (i) { - case 0: return GL_COLOR_ATTACHMENT0; - case 1: return GL_COLOR_ATTACHMENT1; - case 2: return GL_COLOR_ATTACHMENT2; - case 3: return GL_COLOR_ATTACHMENT3; - case 4: return GL_COLOR_ATTACHMENT4; - case 5: return GL_COLOR_ATTACHMENT5; - case 6: return GL_COLOR_ATTACHMENT6; - case 7: return GL_COLOR_ATTACHMENT7; - default: exception("tried to use too many color attachments"); - } - exception("bad enum"); - return GL_COLOR_ATTACHMENT0; -} - -// clang-format on - - -// Stateful error checker -void checkGLError(bool fatal = true) { - - if (!options::enableRenderErrorChecks) { - return; - } - - // Map the GL error enums to strings - GLenum err = GL_NO_ERROR; - while ((err = glGetError()) != GL_NO_ERROR) { - std::string errText; - switch (err) { - case GL_NO_ERROR: - errText = "No error"; - break; - case GL_INVALID_ENUM: - errText = "Invalid enum"; - break; - case GL_INVALID_VALUE: - errText = "Invalid value"; - break; - case GL_INVALID_OPERATION: - errText = "Invalid operation"; - break; - // case GL_STACK_OVERFLOW: std::cerr << "Stack overflow"; break; - // case GL_STACK_UNDERFLOW: std::cerr << "Stack underflow"; break; - case GL_OUT_OF_MEMORY: - errText = "Out of memory"; - break; - default: - errText = "Unknown error " + std::to_string(static_cast(err)); - } - - if (polyscope::options::verbosity > 0) { - std::cout << polyscope::options::printPrefix << "Polyscope OpenGL Error! Type: " << errText << std::endl; - } - if (fatal) { - exception("OpenGl error occurred. Text: " + errText); - } - } -} - -void checkEGLError(bool fatal = true) { - - if (!options::enableRenderErrorChecks) { - return; - } - - EGLint err = eglGetError(); - while (err != EGL_SUCCESS) { - std::string errText; - switch (err) { - case GL_NO_ERROR: - errText = "No error"; - break; - - case EGL_NOT_INITIALIZED: - errText = "EGL is not initialized, or could not be initialized, for the specified EGL display connection."; - break; - - case EGL_BAD_ACCESS: - errText = "EGL cannot access a requested resource (for example a context is bound in another thread)."; - break; - - case EGL_BAD_ALLOC: - errText = "EGL failed to allocate resources for the requested operation."; - break; - - case EGL_BAD_ATTRIBUTE: - errText = "An unrecognized attribute or attribute value was passed in the attribute list."; - break; - - case EGL_BAD_CONTEXT: - errText = "An EGLContext argument does not name a valid EGL rendering context."; - break; - - case EGL_BAD_CONFIG: - errText = "An EGLConfig argument does not name a valid EGL frame buffer configuration."; - break; - - case EGL_BAD_CURRENT_SURFACE: - errText = "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer " - "valid."; - break; - - case EGL_BAD_DISPLAY: - errText = "An EGLDisplay argument does not name a valid EGL display connection."; - break; - - case EGL_BAD_SURFACE: - errText = "An EGLSurface argument does not name a valid surface (window, pixel buffer or pixmap) configured for " - "GL rendering."; - break; - - case EGL_BAD_MATCH: - errText = - "Arguments are inconsistent (for example, a valid context requires buffers not supplied by a valid surface)."; - break; - - case EGL_BAD_PARAMETER: - errText = "One or more argument values are invalid."; - break; - - case EGL_BAD_NATIVE_PIXMAP: - errText = "A NativePixmapType argument does not refer to a valid native pixmap."; - break; - - case EGL_BAD_NATIVE_WINDOW: - errText = "A NativeWindowType argument does not refer to a valid native window."; - break; - - case EGL_CONTEXT_LOST: - errText = "A power management event has occurred. The application must destroy all contexts and reinitialise " - "OpenGL ES state and objects to continue rendering."; - break; - - default: - errText = "Unknown error " + std::to_string(static_cast(err)); - } - - if (polyscope::options::verbosity > 0) { - std::cout << polyscope::options::printPrefix << "Polyscope EGL Error! Text: " << errText << std::endl; - } - if (fatal) { - exception("EGL error occurred. Text: " + errText); - } - - err = eglGetError(); // get the next error - } -} - -// Helper function to print compile logs -void printShaderInfoLog(ShaderHandle shaderHandle) { - int logLen = 0; - int chars = 0; - char* log; - - glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &logLen); - - if (options::verbosity > 0 && logLen > 1) { // for some reason we often get logs of length 1 with no - // visible characters - log = (char*)malloc(logLen); - glGetShaderInfoLog(shaderHandle, logLen, &chars, log); - printf("Shader info log:\n%s\n", log); - free(log); - - exception("shader compile failed"); - } -} -void printProgramInfoLog(GLuint handle) { - int logLen = 0; - int chars = 0; - char* log; - - glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &logLen); - - if (options::verbosity > 0 && logLen > 1) { // for some reason we often get logs of length 1 with no - // visible characters - log = (char*)malloc(logLen); - glGetProgramInfoLog(handle, logLen, &chars, log); - printf("Program info log:\n%s\n", log); - free(log); - } -} - -// ============================================================= -// =================== Attribute buffer ======================== -// ============================================================= - -GLAttributeBuffer::GLAttributeBuffer(RenderDataType dataType_, int arrayCount_) - : AttributeBuffer(dataType_, arrayCount_) { - glGenBuffers(1, &VBOLoc); -} - -GLAttributeBuffer::~GLAttributeBuffer() { - bind(); - glDeleteBuffers(1, &VBOLoc); -} - -void GLAttributeBuffer::bind() { glBindBuffer(getTarget(), VBOLoc); } - -void GLAttributeBuffer::checkType(RenderDataType targetType) { - if (dataType != targetType) { - throw std::invalid_argument("Tried to set GLAttributeBuffer with wrong type. Actual type: " + - renderDataTypeName(dataType) + " Attempted type: " + renderDataTypeName(targetType)); - } -} - -void GLAttributeBuffer::checkArray(int testArrayCount) { - if (testArrayCount != arrayCount) { - throw std::invalid_argument("Tried to set GLAttributeBuffer with wrong array count. Actual count: " + - std::to_string(arrayCount) + " Attempted count: " + std::to_string(testArrayCount)); - } -} - -GLenum GLAttributeBuffer::getTarget() { return GL_ARRAY_BUFFER; } - - -template -void GLAttributeBuffer::setData_helper(const std::vector& data) { - bind(); - - // allocate if needed - if (!isSet() || data.size() > bufferSize) { - setFlag = true; - uint64_t newSize = data.size(); - newSize = std::max(newSize, 2 * bufferSize); // if we're expanding, at-least double - glBufferData(getTarget(), newSize * sizeof(T), NULL, GL_STATIC_DRAW); - bufferSize = newSize; - } - - // do the actual copy - dataSize = data.size(); - glBufferSubData(getTarget(), 0, dataSize * sizeof(T), &data[0]); - - checkGLError(); -} - -void GLAttributeBuffer::setData(const std::vector& data) { - checkType(RenderDataType::Vector2Float); - setData_helper(data); -} - -void GLAttributeBuffer::setData(const std::vector& data) { - checkType(RenderDataType::Vector3Float); - setData_helper(data); -} - -void GLAttributeBuffer::setData(const std::vector>& data) { - checkType(RenderDataType::Vector3Float); - checkArray(2); - setData_helper(data); -} - -void GLAttributeBuffer::setData(const std::vector>& data) { - checkType(RenderDataType::Vector3Float); - checkArray(3); - setData_helper(data); -} - -void GLAttributeBuffer::setData(const std::vector>& data) { - checkType(RenderDataType::Vector3Float); - checkArray(4); - setData_helper(data); -} - -void GLAttributeBuffer::setData(const std::vector& data) { - checkType(RenderDataType::Vector4Float); - setData_helper(data); -} - -void GLAttributeBuffer::setData(const std::vector& data) { - checkType(RenderDataType::Float); - setData_helper(data); -} - -void GLAttributeBuffer::setData(const std::vector& data) { - checkType(RenderDataType::Float); - - // Convert input data to floats - std::vector floatData(data.size()); - for (unsigned int i = 0; i < data.size(); i++) { - floatData[i] = static_cast(data[i]); - } - - setData_helper(floatData); -} - -void GLAttributeBuffer::setData(const std::vector& data) { - checkType(RenderDataType::Int); - setData_helper(data); -} - -void GLAttributeBuffer::setData(const std::vector& data) { - checkType(RenderDataType::UInt); - setData_helper(data); -} - -void GLAttributeBuffer::setData(const std::vector& data) { - checkType(RenderDataType::Vector2UInt); - setData_helper(data); -} -void GLAttributeBuffer::setData(const std::vector& data) { - checkType(RenderDataType::Vector3UInt); - setData_helper(data); -} - -void GLAttributeBuffer::setData(const std::vector& data) { - checkType(RenderDataType::Vector4UInt); - setData_helper(data); -} - -// === get single data values - -template -T GLAttributeBuffer::getData_helper(size_t ind) { - if (!isSet() || ind >= static_cast(getDataSize() * getArrayCount())) exception("bad getData"); - bind(); - T readValue; - glGetBufferSubData(getTarget(), ind * sizeof(T), sizeof(T), &readValue); - return readValue; -} - -float GLAttributeBuffer::getData_float(size_t ind) { - if (getType() != RenderDataType::Float) exception("bad getData type"); - return getData_helper(ind); -} - -double GLAttributeBuffer::getData_double(size_t ind) { return getData_float(ind); } - -glm::vec2 GLAttributeBuffer::getData_vec2(size_t ind) { - if (getType() != RenderDataType::Vector2Float) exception("bad getData type"); - return getData_helper(ind); -} -glm::vec3 GLAttributeBuffer::getData_vec3(size_t ind) { - if (getType() != RenderDataType::Vector3Float) exception("bad getData type"); - return getData_helper(ind); -} -glm::vec4 GLAttributeBuffer::getData_vec4(size_t ind) { - if (getType() != RenderDataType::Vector4Float) exception("bad getData type"); - return getData_helper(ind); -} -int GLAttributeBuffer::getData_int(size_t ind) { - if (getType() != RenderDataType::Int) exception("bad getData type"); - return getData_helper(ind); -} -uint32_t GLAttributeBuffer::getData_uint32(size_t ind) { - if (getType() != RenderDataType::UInt) exception("bad getData type"); - return getData_helper(ind); -} -glm::uvec2 GLAttributeBuffer::getData_uvec2(size_t ind) { - if (getType() != RenderDataType::Vector2Float) exception("bad getData type"); - return getData_helper(ind); -} -glm::uvec3 GLAttributeBuffer::getData_uvec3(size_t ind) { - if (getType() != RenderDataType::Vector3Float) exception("bad getData type"); - return getData_helper(ind); -} -glm::uvec4 GLAttributeBuffer::getData_uvec4(size_t ind) { - if (getType() != RenderDataType::Vector4Float) exception("bad getData type"); - return getData_helper(ind); -} - -// === get ranges of values - -template -std::vector GLAttributeBuffer::getDataRange_helper(size_t start, size_t count) { - if (!isSet() || start + count > static_cast(getDataSize() * getArrayCount())) exception("bad getData"); - bind(); - std::vector readValues(count); - glGetBufferSubData(getTarget(), start * sizeof(T), count * sizeof(T), &readValues.front()); - return readValues; -} - -std::vector GLAttributeBuffer::getDataRange_float(size_t start, size_t count) { - if (getType() != RenderDataType::Float) exception("bad getData type"); - return getDataRange_helper(start, count); -} - -std::vector GLAttributeBuffer::getDataRange_double(size_t start, size_t count) { - std::vector floatValues = getDataRange_float(start, count); - std::vector values(count); - for (size_t i = 0; i < count; i++) { - values[i] = static_cast(floatValues[i]); - } - return values; -} - -std::vector GLAttributeBuffer::getDataRange_vec2(size_t start, size_t count) { - if (getType() != RenderDataType::Vector2Float) exception("bad getData type"); - return getDataRange_helper(start, count); -} -std::vector GLAttributeBuffer::getDataRange_vec3(size_t start, size_t count) { - if (getType() != RenderDataType::Vector3Float) exception("bad getData type"); - return getDataRange_helper(start, count); -} -std::vector GLAttributeBuffer::getDataRange_vec4(size_t start, size_t count) { - if (getType() != RenderDataType::Vector4Float) exception("bad getData type"); - return getDataRange_helper(start, count); -} -std::vector GLAttributeBuffer::getDataRange_int(size_t start, size_t count) { - if (getType() != RenderDataType::Int) exception("bad getData type"); - return getDataRange_helper(start, count); -} -std::vector GLAttributeBuffer::getDataRange_uint32(size_t start, size_t count) { - if (getType() != RenderDataType::UInt) exception("bad getData type"); - return getDataRange_helper(start, count); -} -std::vector GLAttributeBuffer::getDataRange_uvec2(size_t start, size_t count) { - if (getType() != RenderDataType::Vector2UInt) exception("bad getData type"); - return getDataRange_helper(start, count); -} -std::vector GLAttributeBuffer::getDataRange_uvec3(size_t start, size_t count) { - if (getType() != RenderDataType::Vector3UInt) exception("bad getData type"); - return getDataRange_helper(start, count); -} -std::vector GLAttributeBuffer::getDataRange_uvec4(size_t start, size_t count) { - if (getType() != RenderDataType::Vector4UInt) exception("bad getData type"); - bind(); - return getDataRange_helper(start, count); -} - - -uint32_t GLAttributeBuffer::getNativeBufferID() { return static_cast(VBOLoc); } - -// ============================================================= -// ==================== Texture buffer ========================= -// ============================================================= - - -// create a 1D texture from data -GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int size1D, const unsigned char* data) - : TextureBuffer(1, format_, size1D) { - - glEnable(GL_TEXTURE_1D); - - glGenTextures(1, &handle); - glBindTexture(GL_TEXTURE_1D, handle); - glTexImage1D(GL_TEXTURE_1D, 0, internalFormat(format), size1D, 0, formatF(format), GL_UNSIGNED_BYTE, data); - checkGLError(); - - setFilterMode(FilterMode::Nearest); -} - -GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int size1D, const float* data) - : TextureBuffer(1, format_, size1D) { - - glGenTextures(1, &handle); - glBindTexture(GL_TEXTURE_1D, handle); - glTexImage1D(GL_TEXTURE_1D, 0, internalFormat(format), size1D, 0, formatF(format), GL_FLOAT, data); - checkGLError(); - - setFilterMode(FilterMode::Nearest); -} - -// create a 2D texture from data -GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int sizeX_, unsigned int sizeY_, - const unsigned char* data) - : TextureBuffer(2, format_, sizeX_, sizeY_) { - - glGenTextures(1, &handle); - glBindTexture(GL_TEXTURE_2D, handle); - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat(format), sizeX, sizeY, 0, formatF(format), GL_UNSIGNED_BYTE, data); - checkGLError(); - - setFilterMode(FilterMode::Nearest); -} - -GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int sizeX_, unsigned int sizeY_, const float* data) - : TextureBuffer(2, format_, sizeX_, sizeY_) { - - glGenTextures(1, &handle); - glBindTexture(GL_TEXTURE_2D, handle); - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat(format), sizeX, sizeY, 0, formatF(format), GL_FLOAT, data); - checkGLError(); - - setFilterMode(FilterMode::Nearest); -} - -// create a 3D texture from data -GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int sizeX_, unsigned int sizeY_, unsigned int sizeZ_, - const unsigned char* data) - : TextureBuffer(3, format_, sizeX_, sizeY_, sizeZ_) { - - glGenTextures(1, &handle); - glBindTexture(GL_TEXTURE_3D, handle); - glTexImage3D(GL_TEXTURE_3D, 0, internalFormat(format), sizeX, sizeY, sizeZ, 0, formatF(format), GL_UNSIGNED_BYTE, - data); - checkGLError(); - - setFilterMode(FilterMode::Nearest); -} - -GLTextureBuffer::GLTextureBuffer(TextureFormat format_, unsigned int sizeX_, unsigned int sizeY_, unsigned int sizeZ_, - const float* data) - : TextureBuffer(3, format_, sizeX_, sizeY_, sizeZ_) { - - glGenTextures(1, &handle); - glBindTexture(GL_TEXTURE_3D, handle); - glTexImage3D(GL_TEXTURE_3D, 0, internalFormat(format), sizeX, sizeY, sizeZ, 0, formatF(format), GL_FLOAT, data); - checkGLError(); - - setFilterMode(FilterMode::Nearest); -} - -GLTextureBuffer::~GLTextureBuffer() { glDeleteTextures(1, &handle); } - -void GLTextureBuffer::resize(unsigned int newLen) { - - TextureBuffer::resize(newLen); - - bind(); - if (dim == 1) { - glTexImage1D(GL_TEXTURE_1D, 0, internalFormat(format), sizeX, 0, formatF(format), type(format), nullptr); - } else { - exception("OpenGL error: called 1D resize on not-1D texture"); - } - checkGLError(); -} - -void GLTextureBuffer::resize(unsigned int newX, unsigned int newY) { - - TextureBuffer::resize(newX, newY); - - bind(); - if (dim == 2) { - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat(format), sizeX, sizeY, 0, formatF(format), type(format), nullptr); - } else { - exception("OpenGL error: called 2D resize on not-2D texture"); - } - checkGLError(); -} - -void GLTextureBuffer::resize(unsigned int newX, unsigned int newY, unsigned int newZ) { - - TextureBuffer::resize(newX, newY, newZ); - - bind(); - if (dim == 3) { - glTexImage3D(GL_TEXTURE_3D, 0, internalFormat(format), sizeX, sizeY, sizeZ, 0, formatF(format), type(format), - nullptr); - } else { - exception("OpenGL error: called 3D resize on not-3D texture"); - } - checkGLError(); -} - -void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; -void GLTextureBuffer::setData(const std::vector& data) { - - bind(); - - if (data.size() != getTotalSize()) { - exception("OpenGL error: texture buffer data is not the right size."); - } - - switch (dim) { - case 1: - glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeX, formatF(format), type(format), &data.front().x); - break; - case 2: - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeX, sizeY, formatF(format), type(format), &data.front().x); - break; - case 3: - glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, formatF(format), type(format), &data.front().x); - break; - } - - checkGLError(); -}; - -void GLTextureBuffer::setData(const std::vector& data) { - - bind(); - - if (data.size() != getTotalSize()) { - exception("OpenGL error: texture buffer data is not the right size."); - } - - switch (dim) { - case 1: - glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeX, formatF(format), type(format), &data.front().x); - break; - case 2: - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeX, sizeY, formatF(format), type(format), &data.front().x); - break; - case 3: - glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, formatF(format), type(format), &data.front().x); - break; - } - - checkGLError(); -}; - -void GLTextureBuffer::setData(const std::vector& data) { - bind(); - - if (data.size() != getTotalSize()) { - exception("OpenGL error: texture buffer data is not the right size."); - } - - switch (dim) { - case 1: - glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeX, formatF(format), type(format), &data.front()); - break; - case 2: - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeX, sizeY, formatF(format), type(format), &data.front()); - break; - case 3: - glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, formatF(format), type(format), &data.front()); - break; - } - - checkGLError(); -}; - - -void GLTextureBuffer::setData(const std::vector& data) { - - // Convert to float - std::vector dataFloat(data.size()); - for (size_t i = 0; i < data.size(); i++) { - dataFloat[i] = static_cast(data[i]); - } - - bind(); - - if (data.size() != getTotalSize()) { - exception("OpenGL error: texture buffer data is not the right size."); - } - - switch (dim) { - case 1: - glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeX, formatF(format), type(format), &dataFloat.front()); - break; - case 2: - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeX, sizeY, formatF(format), type(format), &dataFloat.front()); - break; - case 3: - glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, formatF(format), type(format), &dataFloat.front()); - break; - } - - checkGLError(); -}; -void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; -void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; -void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; -void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; -void GLTextureBuffer::setData(const std::vector& data) { exception("not implemented"); }; -void GLTextureBuffer::setData(const std::vector>& data) { exception("not implemented"); }; -void GLTextureBuffer::setData(const std::vector>& data) { exception("not implemented"); }; -void GLTextureBuffer::setData(const std::vector>& data) { exception("not implemented"); }; - - -void GLTextureBuffer::setFilterMode(FilterMode newMode) { - - bind(); - - switch (newMode) { - case FilterMode::Nearest: - glTexParameteri(textureType(), GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(textureType(), GL_TEXTURE_MIN_FILTER, GL_NEAREST); - break; - case FilterMode::Linear: - glTexParameteri(textureType(), GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(textureType(), GL_TEXTURE_MIN_FILTER, GL_LINEAR); - break; - } - glTexParameteri(textureType(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - if (dim >= 2) { - glTexParameteri(textureType(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - if (dim >= 3) { - glTexParameteri(textureType(), GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - } - - checkGLError(); -} - -void* GLTextureBuffer::getNativeHandle() { return reinterpret_cast(getHandle()); } - -uint32_t GLTextureBuffer::getNativeBufferID() { return static_cast(getHandle()); }; - -std::vector GLTextureBuffer::getDataScalar() { - if (dimension(format) != 1) exception("called getDataScalar on texture which does not have a 1 dimensional format"); - - std::vector outData; - outData.resize(getTotalSize()); - - bind(); - glGetTexImage(textureType(), 0, formatF(format), GL_FLOAT, static_cast(&outData.front())); - checkGLError(); - - return outData; -} - -std::vector GLTextureBuffer::getDataVector2() { - if (dimension(format) != 2) exception("called getDataVector2 on texture which does not have a 2 dimensional format"); - - std::vector outData; - outData.resize(getTotalSize()); - - bind(); - glGetTexImage(textureType(), 0, formatF(format), GL_FLOAT, static_cast(&outData.front())); - checkGLError(); - - return outData; -} - -std::vector GLTextureBuffer::getDataVector3() { - if (dimension(format) != 3) exception("called getDataVector3 on texture which does not have a 3 dimensional format"); - exception("not implemented"); - - std::vector outData; - outData.resize(getTotalSize()); - - bind(); - glGetTexImage(textureType(), 0, formatF(format), GL_FLOAT, static_cast(&outData.front())); - checkGLError(); - - return outData; -} - -GLenum GLTextureBuffer::textureType() { - if (dim == 1) { - return GL_TEXTURE_1D; - } else if (dim == 2) { - return GL_TEXTURE_2D; - } else if (dim == 3) { - return GL_TEXTURE_3D; - } - exception("bad texture type"); - return GL_TEXTURE_1D; -} - -void GLTextureBuffer::bind() { - glBindTexture(textureType(), handle); - checkGLError(); -} - -// ============================================================= -// ===================== Render buffer ========================= -// ============================================================= - -GLRenderBuffer::GLRenderBuffer(RenderBufferType type_, unsigned int sizeX_, unsigned int sizeY_) - : RenderBuffer(type_, sizeX_, sizeY_) { - glGenRenderbuffers(1, &handle); - checkGLError(); - resize(sizeX, sizeY); -} - - -GLRenderBuffer::~GLRenderBuffer() { glDeleteRenderbuffers(1, &handle); } - -void GLRenderBuffer::resize(unsigned int newX, unsigned int newY) { - RenderBuffer::resize(newX, newY); - bind(); - - glRenderbufferStorage(GL_RENDERBUFFER, native(type), sizeX, sizeY); - checkGLError(); -} - -void GLRenderBuffer::bind() { - glBindRenderbuffer(GL_RENDERBUFFER, handle); - checkGLError(); -} - - -// ============================================================= -// ===================== Framebuffer =========================== -// ============================================================= - -GLFrameBuffer::GLFrameBuffer(unsigned int sizeX_, unsigned int sizeY_, bool isDefault) { - sizeX = sizeX_; - sizeY = sizeY_; - if (isDefault) { - handle = 0; - } else { - glGenFramebuffers(1, &handle); - glBindFramebuffer(GL_FRAMEBUFFER, handle); - } - checkGLError(); -}; - -GLFrameBuffer::~GLFrameBuffer() { - if (handle != 0) { - glDeleteFramebuffers(1, &handle); - } -} - -void GLFrameBuffer::bind() { - glBindFramebuffer(GL_FRAMEBUFFER, handle); - checkGLError(); -} - -void GLFrameBuffer::addColorBuffer(std::shared_ptr renderBufferIn) { - - // it _better_ be a GL buffer - std::shared_ptr renderBuffer = std::dynamic_pointer_cast(renderBufferIn); - if (!renderBuffer) exception("tried to bind to non-GL render buffer"); - - renderBuffer->bind(); - bind(); - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, colorAttachNum(nColorBuffers), GL_RENDERBUFFER, renderBuffer->getHandle()); - checkGLError(); - renderBuffersColor.push_back(renderBuffer); - nColorBuffers++; -} - -void GLFrameBuffer::addDepthBuffer(std::shared_ptr renderBufferIn) { - // it _better_ be a GL buffer - std::shared_ptr renderBuffer = std::dynamic_pointer_cast(renderBufferIn); - if (!renderBuffer) exception("tried to bind to non-GL render buffer"); - - renderBuffer->bind(); - bind(); - - // Sanity checks - // if (depthRenderBuffer != nullptr) exception("OpenGL error: already bound to render buffer"); - // if (depthTexture != nullptr) exception("OpenGL error: already bound to texture buffer"); - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer->getHandle()); - checkGLError(); - renderBuffersDepth.push_back(renderBuffer); -} - -void GLFrameBuffer::addColorBuffer(std::shared_ptr textureBufferIn) { - - // it _better_ be a GL buffer - std::shared_ptr textureBuffer = std::dynamic_pointer_cast(textureBufferIn); - if (!textureBuffer) exception("tried to bind to non-GL texture buffer"); - - textureBuffer->bind(); - bind(); - checkGLError(); - - glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachNum(nColorBuffers), GL_TEXTURE_2D, textureBuffer->getHandle(), 0); - - checkGLError(); - textureBuffersColor.push_back(textureBuffer); - nColorBuffers++; -} - -void GLFrameBuffer::addDepthBuffer(std::shared_ptr textureBufferIn) { - - // it _better_ be a GL buffer - std::shared_ptr textureBuffer = std::dynamic_pointer_cast(textureBufferIn); - if (!textureBuffer) exception("tried to bind to non-GL texture buffer"); - - textureBuffer->bind(); - bind(); - checkGLError(); - - // Sanity checks - // if (depthRenderBuffer != nullptr) exception("OpenGL error: already bound to render buffer"); - // if (depthTexture != nullptr) exception("OpenGL error: already bound to texture buffer"); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureBuffer->getHandle(), 0); - checkGLError(); - textureBuffersDepth.push_back(textureBuffer); -} - -void GLFrameBuffer::setDrawBuffers() { - bind(); - - std::vector buffs; - for (int i = 0; i < nColorBuffers; i++) { - buffs.push_back(GL_COLOR_ATTACHMENT0 + i); - } - if (nColorBuffers > 0) { - glDrawBuffers(nColorBuffers, &buffs.front()); - } - checkGLError(); -} - -bool GLFrameBuffer::bindForRendering() { - verifyBufferSizes(); - bind(); - - // Check if the frame buffer is okay - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - // it would be nice to error out here, but it seems that on some platforms this happens even during normal flow. - // For instance, on Windows we get an incomplete framebuffer when the application is minimized see - // https://github.com/nmwsharp/polyscope/issues/36 - - // exception("OpenGL error occurred: framebuffer not complete!"); - // std::cout << "OpenGL error occurred: framebuffer not complete!\n"; - return false; - } - - render::engine->currRenderFramebuffer = this; - - // Set the viewport - if (!viewportSet) { - exception("OpenGL error: viewport not set for framebuffer object. Call GLFrameBuffer::setViewport()"); - } - glViewport(viewportX, viewportY, viewportSizeX, viewportSizeY); - render::engine->setCurrentViewport({viewportX, viewportY, viewportSizeX, viewportSizeY}); - checkGLError(); - - // Enable depth testing - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LESS); - - // Enable blending - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - checkGLError(); - return true; -} - -void GLFrameBuffer::clear() { - if (!bindForRendering()) return; - - glClearColor(clearColor[0], clearColor[1], clearColor[2], clearAlpha); - glClearDepth(clearDepth); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); -} - -std::array GLFrameBuffer::readFloat4(int xPos, int yPos) { - - // if (colorRenderBuffer == nullptr || colorRenderBuffer->getType() != RenderBufferType::Float4) { - // exception("OpenGL error: buffer is not of right type to read float4 from"); - //} - - glFlush(); - glFinish(); - bind(); - - // Read from the buffer - std::array result; - glReadPixels(xPos, yPos, 1, 1, GL_RGBA, GL_FLOAT, &result); - - return result; -} - -float GLFrameBuffer::readDepth(int xPos, int yPos) { - - // TODO does no error checking for the case where no depth buffer is attached - - glFlush(); - glFinish(); - bind(); - - // Read from the buffer - float result; - glReadPixels(xPos, yPos, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &result); - - return result; -} - -std::vector GLFrameBuffer::readBuffer() { - - glFlush(); - glFinish(); - - bind(); - - int w = getSizeX(); - int h = getSizeY(); - - // Read from openGL - size_t buffSize = w * h * 4; - std::vector buff(buffSize); - glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, &(buff.front())); - - return buff; -} - -void GLFrameBuffer::blitTo(FrameBuffer* targetIn) { - - // it _better_ be a GL buffer - GLFrameBuffer* target = dynamic_cast(targetIn); - if (!target) exception("tried to blitTo() non-GL framebuffer"); - - // target->bindForRendering(); - bindForRendering(); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target->getHandle()); - - glBlitFramebuffer(0, 0, getSizeX(), getSizeY(), 0, 0, target->getSizeX(), target->getSizeY(), GL_COLOR_BUFFER_BIT, - GL_LINEAR); - checkGLError(); -} - -uint32_t GLFrameBuffer::getNativeBufferID() { return handle; } - -// ============================================================= -// ================== Shader Program ========================= -// ============================================================= - - -GLCompiledProgram::GLCompiledProgram(const std::vector& stages, DrawMode dm) : drawMode(dm) { - - // Collect attributes and uniforms from all of the shaders - for (const ShaderStageSpecification& s : stages) { - for (ShaderSpecUniform u : s.uniforms) { - addUniqueUniform(u); - } - for (ShaderSpecAttribute a : s.attributes) { - addUniqueAttribute(a); - } - for (ShaderSpecTexture t : s.textures) { - addUniqueTexture(t); - } - } - - if (attributes.size() == 0) { - throw std::invalid_argument("Uh oh... GLProgram has no attributes"); - } - - // Perform setup tasks - compileGLProgram(stages); - checkGLError(); - - setDataLocations(); - checkGLError(); -} - -GLCompiledProgram::~GLCompiledProgram() { glDeleteProgram(programHandle); } - -void GLCompiledProgram::compileGLProgram(const std::vector& stages) { - - - // Compile all of the shaders - std::vector handles; - for (const ShaderStageSpecification& s : stages) { - ShaderHandle h = glCreateShader(native(s.stage)); - std::array srcs = {s.src.c_str(), shaderCommonSource}; - glShaderSource(h, 2, &(srcs[0]), nullptr); - glCompileShader(h); - - // Catch the error here, so we can print shader source before re-throwing - try { - - GLint status; - glGetShaderiv(h, GL_COMPILE_STATUS, &status); - if (!status) { - printShaderInfoLog(h); - std::cout << "Program text:" << std::endl; - std::cout << s.src.c_str() << std::endl; - exception("[polyscope] GL shader compile failed"); - } - - if (options::verbosity > 2) { - printShaderInfoLog(h); - } - if (options::verbosity > 100) { - std::cout << "Program text:" << std::endl; - std::cout << s.src.c_str() << std::endl; - } - - checkGLError(); - } catch (...) { - std::cout << "GLError() after shader compilation! Program text:" << std::endl; - - // process shader line-by-line to print line numbers: - std::stringstream ss(s.src); - std::string line; - size_t lineNo = 1; - while (std::getline(ss, line, '\n')) { - std::cout << std::setw(4) << lineNo << ": " << line << std::endl; - lineNo++; - } - throw; - } - - handles.push_back(h); - } - - // Create the program and attach the shaders - programHandle = glCreateProgram(); - for (ShaderHandle h : handles) { - glAttachShader(programHandle, h); - } - - // Link the program - glLinkProgram(programHandle); - if (options::verbosity > 2) { - printProgramInfoLog(programHandle); - } - GLint status; - glGetProgramiv(programHandle, GL_LINK_STATUS, &status); - if (!status) { - printProgramInfoLog(programHandle); - exception("[polyscope] GL program compile failed"); - } - - // Delete the shaders we just compiled, they aren't used after link - for (ShaderHandle h : handles) { - glDeleteShader(h); - } - - checkGLError(); -} - -void GLCompiledProgram::setDataLocations() { - glUseProgram(programHandle); - - // Uniforms - for (GLShaderUniform& u : uniforms) { - u.location = glGetUniformLocation(programHandle, u.name.c_str()); - if (u.location == -1) { - if (options::verbosity > 3) { - info("failed to get location for uniform " + u.name); - } - } - } - - // Attributes - for (GLShaderAttribute& a : attributes) { - a.location = glGetAttribLocation(programHandle, a.name.c_str()); - if (a.location == -1) { - if (options::verbosity > 3) { - info("failed to get location for attribute " + a.name); - } - } - } - - // Textures - for (GLShaderTexture& t : textures) { - t.location = glGetUniformLocation(programHandle, t.name.c_str()); - if (t.location == -1) { - if (options::verbosity > 3) { - info("failed to get location for texture " + t.name); - } - } - } - - checkGLError(); -} - -void GLCompiledProgram::addUniqueAttribute(ShaderSpecAttribute newAttribute) { - for (GLShaderAttribute& a : attributes) { - if (a.name == newAttribute.name) { - - // if it occurs twice, confirm that the occurences match - if (a.type != newAttribute.type) - exception("attribute " + a.name + " appears twice in program with different types"); - - return; - } - } - attributes.push_back(GLShaderAttribute{newAttribute.name, newAttribute.type, newAttribute.arrayCount, -1, nullptr}); -} - -void GLCompiledProgram::addUniqueUniform(ShaderSpecUniform newUniform) { - for (GLShaderUniform& u : uniforms) { - if (u.name == newUniform.name) { - - // if it occurs twice, confirm that the occurences match - if (u.type != newUniform.type) exception("uniform " + u.name + " appears twice in program with different types"); - - return; - } - } - uniforms.push_back(GLShaderUniform{newUniform.name, newUniform.type, false, 777}); -} - -void GLCompiledProgram::addUniqueTexture(ShaderSpecTexture newTexture) { - for (GLShaderTexture& t : textures) { - if (t.name == newTexture.name) { - - // if it occurs twice, confirm that the occurences match - if (t.dim != newTexture.dim) - exception("texture " + t.name + " appears twice in program with different dimensions"); - - return; - } - } - textures.push_back(GLShaderTexture{newTexture.name, newTexture.dim, 777, false, nullptr, nullptr, 777}); -} - - -GLShaderProgram::GLShaderProgram(const std::shared_ptr& compiledProgram_) - : ShaderProgram(compiledProgram_->getDrawMode()), uniforms(compiledProgram_->getUniforms()), - attributes(compiledProgram_->getAttributes()), textures(compiledProgram_->getTextures()), - compiledProgram(compiledProgram_) { - - // Create a VAO - glGenVertexArrays(1, &vaoHandle); - checkGLError(); - - createBuffers(); // only handles texture & index things, attributes are lazily created - checkGLError(); -} - -GLShaderProgram::~GLShaderProgram() { glDeleteVertexArrays(1, &vaoHandle); } - -void GLShaderProgram::bindVAO() { glBindVertexArray(vaoHandle); } - -void GLShaderProgram::createBuffers() { - bindVAO(); - - // === Generate textures - - // Verify we have enough texture units - GLint nAvailTextureUnits; - glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &nAvailTextureUnits); - if ((int)textures.size() > nAvailTextureUnits) { - throw std::invalid_argument("Attempted to load more textures than the number of available texture " - "units (" + - std::to_string(nAvailTextureUnits) + ")."); - } - - // Set indices sequentially - uint32_t iTexture = 0; - for (GLShaderTexture& t : textures) { - t.index = iTexture++; - } - - checkGLError(); -} - -void GLShaderProgram::setAttribute(std::string name, std::shared_ptr externalBuffer) { - bindVAO(); - checkGLError(); - - for (GLShaderAttribute& a : attributes) { - if (a.name == name) { - - if (a.location == -1) return; // attributes which were optimized out or something, do nothing - - // check that types match - int compatCount = renderDataTypeCountCompatbility(a.type, externalBuffer->getType()); - if (compatCount == 0) - throw std::invalid_argument("Tried to set attribute " + name + " to incompatibile type. Attribute " + - renderDataTypeName(a.type) + " set with buffer of type " + - renderDataTypeName(externalBuffer->getType())); - - // check multiple-set errors (duplicates in externalBuffers list?) - if (a.buff) throw std::invalid_argument("attribute " + name + " is already set"); - - // cast to the engine type (booooooo) - std::shared_ptr engineExtBuff = std::dynamic_pointer_cast(externalBuffer); - if (!engineExtBuff) throw std::invalid_argument("attribute " + name + " external buffer engine type cast failed"); - - a.buff = engineExtBuff; - checkGLError(); - - a.buff->bind(); - checkGLError(); - - assignBufferToVAO(a); - checkGLError(); - return; - } - } - - throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); -} - -void GLShaderProgram::assignBufferToVAO(GLShaderAttribute& a) { - bindVAO(); - a.buff->bind(); - checkGLError(); - - // Choose the correct type for the buffer - for (int iArrInd = 0; iArrInd < a.arrayCount; iArrInd++) { - - glEnableVertexAttribArray(a.location + iArrInd); - - switch (a.type) { - case RenderDataType::Float: - glVertexAttribPointer(a.location + iArrInd, 1, GL_FLOAT, GL_FALSE, sizeof(float) * 1 * a.arrayCount, - reinterpret_cast(sizeof(float) * 1 * iArrInd)); - break; - case RenderDataType::Int: - glVertexAttribPointer(a.location + iArrInd, 1, GL_INT, GL_FALSE, sizeof(int) * 1 * a.arrayCount, - reinterpret_cast(sizeof(int) * 1 * iArrInd)); - break; - case RenderDataType::UInt: - glVertexAttribPointer(a.location + iArrInd, 1, GL_UNSIGNED_INT, GL_FALSE, sizeof(uint32_t) * 1 * a.arrayCount, - reinterpret_cast(sizeof(uint32_t) * 1 * iArrInd)); - break; - case RenderDataType::Vector2Float: - glVertexAttribPointer(a.location + iArrInd, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2 * a.arrayCount, - reinterpret_cast(sizeof(float) * 2 * iArrInd)); - break; - case RenderDataType::Vector3Float: - glVertexAttribPointer(a.location + iArrInd, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3 * a.arrayCount, - reinterpret_cast(sizeof(float) * 3 * iArrInd)); - break; - case RenderDataType::Vector4Float: - glVertexAttribPointer(a.location + iArrInd, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4 * a.arrayCount, - reinterpret_cast(sizeof(float) * 4 * iArrInd)); - break; - case RenderDataType::Vector2UInt: - glVertexAttribPointer(a.location + iArrInd, 2, GL_UNSIGNED_INT, GL_FALSE, sizeof(uint32_t) * 2 * a.arrayCount, - reinterpret_cast(sizeof(uint32_t) * 2 * iArrInd)); - break; - case RenderDataType::Vector3UInt: - glVertexAttribPointer(a.location + iArrInd, 3, GL_UNSIGNED_INT, GL_FALSE, sizeof(uint32_t) * 3 * a.arrayCount, - reinterpret_cast(sizeof(uint32_t) * 3 * iArrInd)); - break; - case RenderDataType::Vector4UInt: - glVertexAttribPointer(a.location + iArrInd, 4, GL_UNSIGNED_INT, GL_FALSE, sizeof(uint32_t) * 4 * a.arrayCount, - reinterpret_cast(sizeof(uint32_t) * 4 * iArrInd)); - break; - default: - throw std::invalid_argument("Unrecognized GLShaderAttribute type"); - break; - } - } - - checkGLError(); -} - -void GLShaderProgram::createBuffer(GLShaderAttribute& a) { - if (a.location == -1) return; - - // generate the buffer if needed - std::shared_ptr newBuff = glEngine->generateAttributeBuffer(a.type, a.arrayCount); - std::shared_ptr engineNewBuff = std::dynamic_pointer_cast(newBuff); - if (!engineNewBuff) throw std::invalid_argument("buffer type cast failed"); - a.buff = engineNewBuff; - - assignBufferToVAO(a); - - checkGLError(); -} - -void GLShaderProgram::ensureBufferExists(GLShaderAttribute& a) { - if (a.location != -1 && !a.buff) { - createBuffer(a); - } -} - -bool GLShaderProgram::hasUniform(std::string name) { - for (GLShaderUniform& u : uniforms) { - if (u.name == name && u.location != -1) { - return true; - } - } - return false; -} - -// Set an integer -void GLShaderProgram::setUniform(std::string name, int val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Int) { - glUniform1i(u.location, val); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set an unsigned integer -void GLShaderProgram::setUniform(std::string name, unsigned int val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::UInt) { - glUniform1ui(u.location, val); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a float -void GLShaderProgram::setUniform(std::string name, float val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Float) { - glUniform1f(u.location, val); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a double --- WARNING casts down to float -void GLShaderProgram::setUniform(std::string name, double val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Float) { - glUniform1f(u.location, static_cast(val)); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a 4x4 uniform matrix -// TODO why do we use a pointer here... makes no sense -void GLShaderProgram::setUniform(std::string name, float* val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Matrix44Float) { - glUniformMatrix4fv(u.location, 1, false, val); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a vector2 uniform -void GLShaderProgram::setUniform(std::string name, glm::vec2 val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Vector2Float) { - glUniform2f(u.location, val.x, val.y); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a vector3 uniform -void GLShaderProgram::setUniform(std::string name, glm::vec3 val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Vector3Float) { - glUniform3f(u.location, val.x, val.y, val.z); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a vector4 uniform -void GLShaderProgram::setUniform(std::string name, glm::vec4 val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Vector4Float) { - glUniform4f(u.location, val.x, val.y, val.z, val.w); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a vector3 uniform from a float array -void GLShaderProgram::setUniform(std::string name, std::array val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Vector3Float) { - glUniform3f(u.location, val[0], val[1], val[2]); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a vec4 uniform -void GLShaderProgram::setUniform(std::string name, float x, float y, float z, float w) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Vector4Float) { - glUniform4f(u.location, x, y, z, w); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a uint vector2 uniform -void GLShaderProgram::setUniform(std::string name, glm::uvec2 val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Vector2UInt) { - glUniform2ui(u.location, val.x, val.y); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a uint vector3 uniform -void GLShaderProgram::setUniform(std::string name, glm::uvec3 val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Vector3UInt) { - glUniform3ui(u.location, val.x, val.y, val.z); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -// Set a uint vector4 uniform -void GLShaderProgram::setUniform(std::string name, glm::uvec4 val) { - glUseProgram(compiledProgram->getHandle()); - - for (GLShaderUniform& u : uniforms) { - if (u.name == name) { - if (u.location == -1) return; - if (u.type == RenderDataType::Vector4UInt) { - glUniform4ui(u.location, val.x, val.y, val.z, val.w); - u.isSet = true; - } else { - throw std::invalid_argument("Tried to set GLShaderUniform with wrong type"); - } - return; - } - } - throw std::invalid_argument("Tried to set nonexistent uniform with name " + name); -} - -bool GLShaderProgram::hasAttribute(std::string name) { - for (GLShaderAttribute& a : attributes) { - if (a.name == name && a.location != -1) { - return true; - } - } - return false; -} - -bool GLShaderProgram::attributeIsSet(std::string name) { - for (GLShaderAttribute& a : attributes) { - if (a.name == name && a.location != -1) { - return a.buff->isSet(); - } - } - return false; -} +#ifdef POLYSCOPE_BACKEND_OPENGL3_EGL_ENABLED -std::shared_ptr GLShaderProgram::getAttributeBuffer(std::string name) { - // WARNING: may be null if the attribute was optimized out - for (GLShaderAttribute& a : attributes) { - if (a.name == name) { - return a.buff; - } - } - throw std::invalid_argument("No attribute with name " + name); - return nullptr; -}; - - -void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { - glBindVertexArray(vaoHandle); - - // pass-through to the buffer - for (GLShaderAttribute& a : attributes) { - if (a.name == name && a.location != -1) { - ensureBufferExists(a); - a.buff->setData(data); - return; - } - } - - throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); -} - -void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { - glBindVertexArray(vaoHandle); // TODO remove these? - - // pass-through to the buffer - for (GLShaderAttribute& a : attributes) { - if (a.name == name && a.location != -1) { - ensureBufferExists(a); - a.buff->setData(data); - return; - } - } - - throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); -} - -void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { - glBindVertexArray(vaoHandle); - - // pass-through to the buffer - for (GLShaderAttribute& a : attributes) { - if (a.name == name && a.location != -1) { - ensureBufferExists(a); - a.buff->setData(data); - return; - } - } - - throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); -} - -void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { - glBindVertexArray(vaoHandle); - - // pass-through to the buffer - for (GLShaderAttribute& a : attributes) { - if (a.name == name && a.location != -1) { - ensureBufferExists(a); - a.buff->setData(data); - return; - } - } - - throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); -} - -void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { - glBindVertexArray(vaoHandle); - - // pass-through to the buffer - for (GLShaderAttribute& a : attributes) { - if (a.name == name && a.location != -1) { - ensureBufferExists(a); - a.buff->setData(data); - return; - } - } - - throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); -} - -void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { - glBindVertexArray(vaoHandle); - - // pass-through to the buffer - for (GLShaderAttribute& a : attributes) { - if (a.name == name && a.location != -1) { - ensureBufferExists(a); - a.buff->setData(data); - return; - } - } - - throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); -} - -void GLShaderProgram::setAttribute(std::string name, const std::vector& data) { - glBindVertexArray(vaoHandle); - - // pass-through to the buffer - for (GLShaderAttribute& a : attributes) { - if (a.name == name && a.location != -1) { - ensureBufferExists(a); - a.buff->setData(data); - return; - } - } - - throw std::invalid_argument("Tried to set nonexistent attribute with name " + name); -} - -bool GLShaderProgram::hasTexture(std::string name) { - for (GLShaderTexture& t : textures) { - if (t.name == name && t.location != -1) { - return true; - } - } - return false; -} - -bool GLShaderProgram::textureIsSet(std::string name) { - for (GLShaderTexture& t : textures) { - if (t.name == name && t.location != -1) { - return t.isSet; - } - } - return false; -} - -void GLShaderProgram::setTexture1D(std::string name, unsigned char* texData, unsigned int length) { - throw std::invalid_argument("This code hasn't been testded yet."); - - // Find the right texture - for (GLShaderTexture& t : textures) { - if (t.name != name || t.location == -1) continue; - - if (t.isSet) { - throw std::invalid_argument("Attempted to set texture twice"); - } - - if (t.dim != 1) { - throw std::invalid_argument("Tried to use texture with mismatched dimension " + std::to_string(t.dim)); - } - - // Create a new texture object - t.textureBufferOwned.reset(new GLTextureBuffer(TextureFormat::RGB8, length, texData)); - t.textureBuffer = t.textureBufferOwned.get(); - - - // Set policies - // glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - // glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#include "polyscope/polyscope.h" - t.isSet = true; - return; - } +#include "polyscope/render/opengl/gl_engine_egl.h" - throw std::invalid_argument("No texture with name " + name); -} +#include "backends/imgui_impl_opengl3.h" +#include "polyscope/render/engine.h" -void GLShaderProgram::setTexture2D(std::string name, unsigned char* texData, unsigned int width, unsigned int height, - bool withAlpha, bool useMipMap, bool repeat) { - // Find the right texture - for (GLShaderTexture& t : textures) { - if (t.name != name || t.location == -1) continue; - - if (t.isSet) { - throw std::invalid_argument("Attempted to set texture twice"); - } - - if (t.dim != 2) { - throw std::invalid_argument("Tried to use texture with mismatched dimension " + std::to_string(t.dim)); - } - - if (withAlpha) { - t.textureBufferOwned.reset(new GLTextureBuffer(TextureFormat::RGBA8, width, height, texData)); - } else { - t.textureBufferOwned.reset(new GLTextureBuffer(TextureFormat::RGB8, width, height, texData)); - } - t.textureBuffer = t.textureBufferOwned.get(); - - - // Set policies - if (repeat) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // Use mip maps - if (useMipMap) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glGenerateMipmap(GL_TEXTURE_2D); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - - t.isSet = true; - return; - } +#include "stb_image.h" - throw std::invalid_argument("No texture with name " + name); -} +#include +#include -void GLShaderProgram::setTextureFromBuffer(std::string name, TextureBuffer* textureBuffer) { - glUseProgram(compiledProgram->getHandle()); +namespace polyscope { +namespace render { +namespace backend_openGL3 { - // Find the right texture - for (GLShaderTexture& t : textures) { - if (t.name != name || t.location == -1) continue; +GLEngineEGL* glEngineEGL = nullptr; // alias for global engine pointer +extern GLEngine* glEngine; // defined in gl_engine.h - if (t.dim != (int)textureBuffer->getDimension()) { - throw std::invalid_argument("Tried to use texture with mismatched dimension " + std::to_string(t.dim)); - } +namespace { // anonymous helpers - t.textureBuffer = dynamic_cast(textureBuffer); - if (!t.textureBuffer) { - throw std::invalid_argument("Bad texture in setTextureFromBuffer()"); - } +void checkEGLError(bool fatal = true) { - t.isSet = true; + if (!options::enableRenderErrorChecks) { return; } - throw std::invalid_argument("No texture with name " + name); -} - -void GLShaderProgram::setTextureFromColormap(std::string name, const std::string& colormapName, bool allowUpdate) { - const ValueColorMap& colormap = render::engine->getColorMap(colormapName); - - // TODO switch to global shared buffers from colormap - - // Find the right texture - for (GLShaderTexture& t : textures) { - if (t.name != name || t.location == -1) continue; - - if (t.isSet && !allowUpdate) { - throw std::invalid_argument("Attempted to set texture twice"); - } - - if (t.dim != 1) { - throw std::invalid_argument("Tried to use texture with mismatched dimension " + std::to_string(t.dim)); - } - - // Fill a buffer with the data - unsigned int dataLength = colormap.values.size() * 3; - std::vector colorBuffer(dataLength); - for (unsigned int i = 0; i < colormap.values.size(); i++) { - colorBuffer[3 * i + 0] = static_cast(colormap.values[i][0]); - colorBuffer[3 * i + 1] = static_cast(colormap.values[i][1]); - colorBuffer[3 * i + 2] = static_cast(colormap.values[i][2]); - } - - // glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, colormap.values.size(), 0, GL_RGB, GL_FLOAT, &(colorBuffer[0])); - t.textureBufferOwned = std::dynamic_pointer_cast( - engine->generateTextureBuffer(TextureFormat::RGB32F, colormap.values.size(), &(colorBuffer[0]))); - t.textureBufferOwned->setFilterMode(FilterMode::Linear); - t.textureBuffer = t.textureBufferOwned.get(); - - - t.isSet = true; - return; - } + // Map the GL error enums to strings + EGLint err = eglGetError(); - throw std::invalid_argument("No texture with name " + name); -} + if (err == EGL_SUCCESS) return; -void GLShaderProgram::setIndex(std::shared_ptr externalBuffer) { - if (!useIndex) { - throw std::invalid_argument("Tried to setIndex() when program drawMode does not use indexed " - "drawing"); - } + std::string errText; + switch (err) { - // cast to the engine type (booooooo) - std::shared_ptr engineExtBuff = std::dynamic_pointer_cast(externalBuffer); - if (!engineExtBuff) throw std::invalid_argument("index attribute external buffer engine type cast failed"); + case EGL_SUCCESS: + errText = "The last function succeeded without error."; + break; - switch (engineExtBuff->getType()) { - case RenderDataType::Int: - // NOTE: the render pass expects these to be unsigned.... but negative - // values don't make sense anyway, so I think it's okay to just let it slide - indexSizeMult = 1; + case EGL_NOT_INITIALIZED: + errText = "EGL is not initialized, or could not be initialized, for the specified EGL display connection."; break; - case RenderDataType::UInt: - indexSizeMult = 1; + + case EGL_BAD_ACCESS: + errText = "EGL cannot access a requested resource (for example a context is bound in another thread)."; break; - case RenderDataType::Vector2UInt: - indexSizeMult = 2; + + case EGL_BAD_ALLOC: + errText = "EGL failed to allocate resources for the requested operation."; break; - case RenderDataType::Vector3UInt: - indexSizeMult = 3; + + case EGL_BAD_ATTRIBUTE: + errText = "An unrecognized attribute or attribute value was passed in the attribute list."; break; - case RenderDataType::Vector4UInt: - indexSizeMult = 4; + + case EGL_BAD_CONTEXT: + errText = "An EGLContext argument does not name a valid EGL rendering context."; break; - case RenderDataType::Float: - case RenderDataType::Vector2Float: - case RenderDataType::Vector3Float: - case RenderDataType::Vector4Float: - case RenderDataType::Matrix44Float: - throw std::invalid_argument("index buffer should be integer type"); + + case EGL_BAD_CONFIG: + errText = "An EGLConfig argument does not name a valid EGL frame buffer configuration."; break; - } - indexBuffer = engineExtBuff; + case EGL_BAD_CURRENT_SURFACE: + errText = "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid."; + break; - // bind it as the VAOs index buffer - bindVAO(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, engineExtBuff->getHandle()); + case EGL_BAD_DISPLAY: + errText = "An EGLDisplay argument does not name a valid EGL display connection."; + break; - checkGLError(); -} + case EGL_BAD_SURFACE: + errText = "An EGLSurface argument does not name a valid surface (window, pixel buffer or pixmap) configured for GL " + "rendering."; + break; -// Check that uniforms and attributes are all set and of consistent size -void GLShaderProgram::validateData() { + case EGL_BAD_MATCH: + errText = + "Arguments are inconsistent (for example, a valid context requires buffers not supplied by a valid surface)."; + break; - // WARNING: this function is poorly named, it doesn't just do sanity checks, it also sets important values + case EGL_BAD_PARAMETER: + errText = "One or more argument values are invalid."; + break; - // Check uniforms - for (GLShaderUniform& u : uniforms) { - if (u.location == -1) continue; - if (!u.isSet) { - throw std::invalid_argument("Uniform " + u.name + " has not been set"); - } - } + case EGL_BAD_NATIVE_PIXMAP: + errText = "A NativePixmapType argument does not refer to a valid native pixmap."; + break; - // Check attributes - int64_t attributeSize = -1; - for (GLShaderAttribute a : attributes) { - if (a.location == -1) continue; - if (!a.buff) { - throw std::invalid_argument("Attribute " + a.name + " has no buffer attached"); - } - if (a.buff->getDataSize() < 0) { - throw std::invalid_argument("Attribute " + a.name + " has not been set"); - } - - int compatCount = renderDataTypeCountCompatbility(a.type, a.buff->getType()); - - if (attributeSize == -1) { // first one we've seen - attributeSize = a.buff->getDataSize() / (compatCount); - } else { // not the first one we've seen - if (a.buff->getDataSize() / (compatCount) != attributeSize) { - throw std::invalid_argument("Attributes have inconsistent size. One attribute has size " + - std::to_string(attributeSize) + " and " + a.name + " has size " + - std::to_string(a.buff->getDataSize())); - } - } - } + case EGL_BAD_NATIVE_WINDOW: + errText = "A NativeWindowType argument does not refer to a valid native window."; + break; - // Check textures - for (GLShaderTexture& t : textures) { - if (t.location == -1) continue; - if (!t.isSet) { - throw std::invalid_argument("Texture " + t.name + " has not been set"); - } - } + case EGL_CONTEXT_LOST: + errText = "A power management event has occurred. The application must destroy all contexts and reinitialise " + "OpenGL ES state and objects to continue rendering."; + break; - // Check index (if applicable) - if (useIndex && !indexBuffer) { - throw std::invalid_argument("Index buffer has not been filled"); + default: + errText = "Unknown error " + std::to_string(static_cast(err)); + break; } - // Set the size - if (useIndex) { - drawDataLength = static_cast(indexSizeMult * indexBuffer->getDataSize()); - } else { - drawDataLength = static_cast(attributeSize); + if (polyscope::options::verbosity > 0) { + std::cout << polyscope::options::printPrefix << "EGL Error! Type: " << errText << std::endl; } - - // Check instanced (if applicable) - if (drawMode == DrawMode::TrianglesInstanced || drawMode == DrawMode::TriangleStripInstanced) { - if (instanceCount == INVALID_IND_32) { - throw std::invalid_argument("Must set instance count to use instanced drawing"); - } + if (fatal) { + exception("EGL error occurred. Text: " + errText); } } +} // namespace -void GLShaderProgram::setPrimitiveRestartIndex(unsigned int restartIndex_) { - if (!usePrimitiveRestart) { - exception("setPrimitiveRestartIndex() called, but draw mode does not support restart indices."); - } - restartIndex = restartIndex_; - primitiveRestartIndexSet = true; -} +void initializeRenderEngine_egl() { -void GLShaderProgram::setInstanceCount(uint32_t instanceCount_) { instanceCount = instanceCount_; } + glEngineEGL = new GLEngineEGL(); // create the new global engine object -void GLShaderProgram::activateTextures() { - for (GLShaderTexture& t : textures) { - if (t.location == -1) continue; + engine = glEngineEGL; // we keep a few copies of this pointer with various types + glEngine = glEngineEGL; - glActiveTexture(GL_TEXTURE0 + t.index); - t.textureBuffer->bind(); - glUniform1i(t.location, t.index); - } + // initialize + glEngineEGL->initialize(); + engine->allocateGlobalBuffersAndPrograms(); + glEngineEGL->applyWindowSize(); } -void GLShaderProgram::draw() { - validateData(); +GLEngineEGL::GLEngineEGL() {} +GLEngineEGL::~GLEngineEGL() {} - glUseProgram(compiledProgram->getHandle()); - glBindVertexArray(vaoHandle); - - if (usePrimitiveRestart) { - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(restartIndex); - } +void GLEngineEGL::initialize() { - activateTextures(); + // === Initialize EGL - switch (drawMode) { - case DrawMode::Points: - glDrawArrays(GL_POINTS, 0, drawDataLength); - break; - case DrawMode::Triangles: - glDrawArrays(GL_TRIANGLES, 0, drawDataLength); - break; - case DrawMode::Lines: - glDrawArrays(GL_LINES, 0, drawDataLength); - break; - case DrawMode::TrianglesAdjacency: - glDrawArrays(GL_TRIANGLES_ADJACENCY, 0, drawDataLength); - break; - case DrawMode::LinesAdjacency: - glDrawArrays(GL_LINES_ADJACENCY, 0, drawDataLength); - break; - case DrawMode::IndexedLines: - // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); // TODO delete these - glDrawElements(GL_LINES, drawDataLength, GL_UNSIGNED_INT, 0); - break; - case DrawMode::IndexedLineStrip: - // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); - glDrawElements(GL_LINE_STRIP, drawDataLength, GL_UNSIGNED_INT, 0); - break; - case DrawMode::IndexedLinesAdjacency: - // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); - glDrawElements(GL_LINES_ADJACENCY, drawDataLength, GL_UNSIGNED_INT, 0); - break; - case DrawMode::IndexedLineStripAdjacency: - // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); - glDrawElements(GL_LINE_STRIP_ADJACENCY, drawDataLength, GL_UNSIGNED_INT, 0); - break; - case DrawMode::IndexedTriangles: - // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); - glDrawElements(GL_TRIANGLES, drawDataLength, GL_UNSIGNED_INT, 0); - break; - case DrawMode::TrianglesInstanced: - glDrawArraysInstanced(GL_TRIANGLES, 0, drawDataLength, instanceCount); - break; - case DrawMode::TriangleStripInstanced: - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, drawDataLength, instanceCount); - break; - } + // Get the default display + eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (usePrimitiveRestart) { - glDisable(GL_PRIMITIVE_RESTART); + // Configure + EGLint majorVer, minorVer; + bool success = eglInitialize(eglDisplay, &majorVer, &minorVer); + if (!success) { + exception("ERROR: Failed to initialize EGL"); } + checkEGLError(); - checkGLError(); -} - -GLEngine::GLEngine() {} -void GLEngine::initialize() { + // this has something to do with the EGL configuration, I don't understand exactly what + // clang-format off + const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_DEPTH_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, // this is important, it gets us openGL rather than openGL ES + EGL_NONE}; + // clang-format on - // initialize - EGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EGLint major, minor; - eglInitialize(eglDpy, &major, &minor); - checkEGLError(); - // choose config EGLint numConfigs; EGLConfig eglCfg; - eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs); + eglChooseConfig(eglDisplay, configAttribs, &eglCfg, 1, &numConfigs); checkEGLError(); - // create the surface - EGLSurface eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs); - checkEGLError(); - - // bind eglBindAPI(EGL_OPENGL_API); checkEGLError(); - // make a context - EGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL); - eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx); - checkEGLError(); + // requested context configuration (openGL 3.3 core profile) + // clang-format off + EGLint contextAttribs[] = { + EGL_CONTEXT_MAJOR_VERSION, 3, + EGL_CONTEXT_MINOR_VERSION, 3, + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_NONE }; + // clang-format on + eglContext = eglCreateContext(eglDisplay, eglCfg, EGL_NO_CONTEXT, contextAttribs); + checkEGLError(); - /* - // Small callback function for GLFW errors - auto error_print_callback = [](int error, const char* description) { - if (polyscope::options::verbosity > 0) { - std::cout << "GLFW emitted error: " << description << std::endl; - } - }; - - // === Initialize glfw - glfwSetErrorCallback(error_print_callback); - if (!glfwInit()) { - exception(options::printPrefix + "ERROR: Failed to initialize glfw"); - } - - // OpenGL version things - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - #if __APPLE__ - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); - #endif - - // Create the window with context - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); - glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_FALSE); - mainWindow = glfwCreateWindow(view::windowWidth, view::windowHeight, options::programName.c_str(), NULL, NULL); - glfwMakeContextCurrent(mainWindow); - glfwSetWindowPos(mainWindow, view::initWindowPosX, view::initWindowPosY); - - // Set initial window size - int newBufferWidth, newBufferHeight, newWindowWidth, newWindowHeight; - glfwGetFramebufferSize(mainWindow, &newBufferWidth, &newBufferHeight); - glfwGetWindowSize(mainWindow, &newWindowWidth, &newWindowHeight); - view::bufferWidth = newBufferWidth; - view::bufferHeight = newBufferHeight; - view::windowWidth = newWindowWidth; - view::windowHeight = newWindowHeight; - - setWindowResizable(view::windowResizable); - */ + makeContextCurrent(); view::bufferWidth = view::windowWidth; view::bufferHeight = view::windowHeight; + // === Initialize openGL // Load openGL functions (using GLAD) +// +// NOTE: right now this is using the same glad loader as the standard gl_engine_glfw.cpp uses, which was generated with +// the "OpenGL" specification setting rather than the "EGL" option. One would think we should use the "EGL" +// specification, however this one seems to work and that one does not. +// #ifndef __APPLE__ if (!gladLoadGL()) { exception(options::printPrefix + "ERROR: Failed to load openGL using GLAD"); } #endif if (options::verbosity > 0) { - std::cout << options::printPrefix << "Backend: openGL3_glfw_egl -- " + std::cout << options::printPrefix << "Backend: openGL3_egl -- " << "Loaded openGL version: " << glGetString(GL_VERSION) << std::endl; } -#ifdef __APPLE__ - // Hack to classify the process as interactive - glfwPollEvents(); -#endif - { // Manually create the screen frame buffer - GLFrameBuffer* glScreenBuffer = new GLFrameBuffer(view::bufferWidth, view::bufferHeight, true); - displayBuffer.reset(glScreenBuffer); - glScreenBuffer->bind(); + // NOTE: important difference here, we manually create both the framebuffer and and its render buffer, since + // headless EGL means we are not getting them from a window + displayBuffer = generateFrameBuffer(view::bufferWidth, view::bufferHeight); + displayBuffer->addColorBuffer( + generateRenderBuffer(RenderBufferType::Float4, view::bufferWidth, view::bufferHeight)); + displayBuffer->addDepthBuffer(generateRenderBuffer(RenderBufferType::Depth, view::bufferWidth, view::bufferHeight)); + displayBuffer->setDrawBuffers(); + checkError(); + + displayBuffer->bind(); glClearColor(1., 1., 1., 0.); - // glClearColor(0., 0., 0., 0.); - // glClearDepth(1.); + + checkError(); } populateDefaultShadersAndRules(); + checkError(); } -void GLEngine::initializeImGui() { - return; // TODO - - /* - bindDisplay(); - - ImGui::CreateContext(); // must call once at start +void GLEngineEGL::initializeImGui() { - // Set up ImGUI glfw bindings - ImGui_ImplGlfw_InitForOpenGL(mainWindow, true); - const char* glsl_version = "#version 150"; - ImGui_ImplOpenGL3_Init(glsl_version); + // headless mode uses the "null" imgui backed, which essentially does nothing and just passes-through inputs to all + // functions + ImGui::CreateContext(); configureImGui(); - */ -} - -void GLEngine::shutdownImGui() { - return; - - /* - // ImGui shutdown things - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); - */ -} - -void GLEngine::swapDisplayBuffers() { - return; - - /* - bindDisplay(); - glfwSwapBuffers(mainWindow); - */ -} - -std::vector GLEngine::readDisplayBuffer() { - // TODO do we need to bind here? - - glFlush(); - glFinish(); - - // Get buffer size - GLint viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - int w = viewport[2]; - int h = viewport[3]; - - // Read from openGL - size_t buffSize = w * h * 4; - std::vector buff(buffSize); - glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, &(buff.front())); - - return buff; -} - - -void GLEngine::checkError(bool fatal) { checkGLError(fatal); } - -void GLEngine::makeContextCurrent() { - glfwMakeContextCurrent(mainWindow); - glfwSwapInterval(options::enableVSync ? 1 : 0); } -void GLEngine::focusWindow() { glfwFocusWindow(mainWindow); } +void GLEngineEGL::shutdownImGui() { ImGui::DestroyContext(); } -void GLEngine::showWindow() { glfwShowWindow(mainWindow); } +void GLEngineEGL::ImGuiNewFrame() { -void GLEngine::hideWindow() { - glfwHideWindow(mainWindow); - glfwPollEvents(); // this shouldn't be necessary, but seems to be needed at least on macOS. Perhaps realted to a - // glfw bug? e.g. https://github.com/glfw/glfw/issues/1300 and related bugs -} - -void GLEngine::updateWindowSize(bool force) { - int newBufferWidth, newBufferHeight, newWindowWidth, newWindowHeight; - glfwGetFramebufferSize(mainWindow, &newBufferWidth, &newBufferHeight); - glfwGetWindowSize(mainWindow, &newWindowWidth, &newWindowHeight); - if (force || newBufferWidth != view::bufferWidth || newBufferHeight != view::bufferHeight || - newWindowHeight != view::windowHeight || newWindowWidth != view::windowWidth) { - // Basically a resize callback - requestRedraw(); - - // prevent any division by zero for e.g. aspect ratio calcs - if (newBufferHeight == 0) newBufferHeight = 1; - if (newWindowHeight == 0) newWindowHeight = 1; - - view::bufferWidth = newBufferWidth; - view::bufferHeight = newBufferHeight; - view::windowWidth = newWindowWidth; - view::windowHeight = newWindowHeight; - - render::engine->resizeScreenBuffers(); - render::engine->setScreenBufferViewports(); - } -} - - -void GLEngine::applyWindowSize() { - glfwSetWindowSize(mainWindow, view::windowWidth, view::windowHeight); - - // on some platform size changes are asynchonous, need to ensure it completes - // we don't want to just retry until the resize has happened, because it could be impossible - // TODO it seems like on X11 sometimes even this isn't enough? - glfwWaitEvents(); - - updateWindowSize(true); -} - - -void GLEngine::setWindowResizable(bool newVal) { - glfwSetWindowAttrib(mainWindow, GLFW_RESIZABLE, newVal ? GLFW_TRUE : GLFW_FALSE); -} - -bool GLEngine::getWindowResizable() { return glfwGetWindowAttrib(mainWindow, GLFW_RESIZABLE); } - -std::tuple GLEngine::getWindowPos() { - int x, y; - glfwGetWindowPos(mainWindow, &x, &y); - return std::tuple{x, y}; -} - -bool GLEngine::windowRequestsClose() { - bool shouldClose = glfwWindowShouldClose(mainWindow); - if (shouldClose) { - glfwSetWindowShouldClose(mainWindow, false); // un-set the state bit so we can close again - return true; - } - return false; -} - -void GLEngine::pollEvents() { glfwPollEvents(); } + // ImGUI has an error check which fires unless we do this + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize.x = view::bufferWidth; + io.DisplaySize.y = view::bufferHeight; -bool GLEngine::isKeyPressed(char c) { - if (c >= '0' && c <= '9') return ImGui::IsKeyPressed(GLFW_KEY_0 + (c - '0')); - if (c >= 'a' && c <= 'z') return ImGui::IsKeyPressed(GLFW_KEY_A + (c - 'a')); - if (c >= 'A' && c <= 'Z') return ImGui::IsKeyPressed(GLFW_KEY_A + (c - 'A')); - exception("keyPressed only supports 0-9, a-z, A-Z"); - return false; -} - -int GLEngine::getKeyCode(char c) { - if (c >= '0' && c <= '9') return static_cast(GLFW_KEY_0) + (c - '0'); - if (c >= 'a' && c <= 'z') return static_cast(GLFW_KEY_A) + (c - 'a'); - if (c >= 'A' && c <= 'Z') return static_cast(GLFW_KEY_A) + (c - 'A'); - exception("getKeyCode only supports 0-9, a-z, A-Z"); - return -1; + ImGui::NewFrame(); } -void GLEngine::ImGuiNewFrame() { - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); +void GLEngineEGL::ImGuiRender() { ImGui::Render(); } - // ImGui::ShowDemoWindow(); -} -void GLEngine::ImGuiRender() { - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); +void GLEngineEGL::swapDisplayBuffers() { + // not defined in headless mode } -void GLEngine::setDepthMode(DepthMode newMode) { - switch (newMode) { - case DepthMode::Less: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LESS); - glDepthMask(GL_TRUE); - break; - case DepthMode::LEqual: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - glDepthMask(GL_TRUE); - break; - case DepthMode::LEqualReadOnly: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - glDepthMask(GL_FALSE); - break; - case DepthMode::PassReadOnly: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - glDepthMask(GL_FALSE); - break; - case DepthMode::Greater: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_GREATER); - glDepthMask(GL_TRUE); - break; - case DepthMode::Disable: - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); // doesn't actually matter - break; - } +void GLEngineEGL::checkError(bool fatal) { + checkEGLError(fatal); + GLEngine::checkError(fatal); // call the parent version (checks openGL error) } -void GLEngine::setBlendMode(BlendMode newMode) { - switch (newMode) { - case BlendMode::AlphaOver: - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // for premultiplied alpha - break; - case BlendMode::OverNoWrite: - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); - break; - case BlendMode::AlphaUnder: - glEnable(GL_BLEND); - glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE); // for premultiplied alpha - break; - case BlendMode::Zero: - glEnable(GL_BLEND); - glBlendFunc(GL_ZERO, GL_ZERO); - break; - case BlendMode::WeightedAdd: - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE); - break; - case BlendMode::Add: - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE); - break; - case BlendMode::Source: - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ZERO); - break; - case BlendMode::Disable: - glDisable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // doesn't actually matter - break; - } +void GLEngineEGL::makeContextCurrent() { + eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, eglContext); + checkEGLError(); } -void GLEngine::setColorMask(std::array mask) { glColorMask(mask[0], mask[1], mask[2], mask[3]); } - -void GLEngine::setBackfaceCull(bool newVal) { - if (newVal) { - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - } else { - glDisable(GL_CULL_FACE); - } +void GLEngineEGL::focusWindow() { + // not defined in headless mode } -std::string GLEngine::getClipboardText() { - std::string clipboardData = ImGui::GetClipboardText(); - return clipboardData; +void GLEngineEGL::showWindow() { + // not defined in headless mode } -void GLEngine::setClipboardText(std::string text) { ImGui::SetClipboardText(text.c_str()); } - -void GLEngine::applyTransparencySettings() { - // Remove any old transparency-related rules - switch (transparencyMode) { - case TransparencyMode::None: { - setBlendMode(BlendMode::AlphaOver); - setDepthMode(DepthMode::Less); - break; - } - case TransparencyMode::Simple: { - setBlendMode(BlendMode::Add); - setDepthMode(DepthMode::Disable); - break; - } - case TransparencyMode::Pretty: { - setBlendMode(BlendMode::Disable); - setDepthMode(DepthMode::Less); - break; - } - } +void GLEngineEGL::hideWindow() { + // not defined in headless mode } -void GLEngine::setFrontFaceCCW(bool newVal) { - if (newVal == frontFaceCCW) return; - frontFaceCCW = newVal; - if (frontFaceCCW) { - glFrontFace(GL_CCW); - } else { - glFrontFace(GL_CW); - } +void GLEngineEGL::updateWindowSize(bool force) { + // does nothing + // (this function is for updating polyscope's internal window & buffer info in response to the user resizing the + // window at the OS level, but in headless mode there is no interactable window for the user to change size of) } -// == Factories - -std::shared_ptr GLEngine::generateAttributeBuffer(RenderDataType dataType_, int arrayCount_) { - GLAttributeBuffer* newA = new GLAttributeBuffer(dataType_, arrayCount_); - return std::shared_ptr(newA); -} +void GLEngineEGL::applyWindowSize() { + view::bufferWidth = view::windowWidth; + view::bufferHeight = view::windowHeight; -std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int size1D, - const unsigned char* data) { - GLTextureBuffer* newT = new GLTextureBuffer(format, size1D, data); - return std::shared_ptr(newT); + render::engine->resizeScreenBuffers(); + render::engine->setScreenBufferViewports(); } -std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int size1D, - const float* data) { - GLTextureBuffer* newT = new GLTextureBuffer(format, size1D, data); - return std::shared_ptr(newT); -} -std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int sizeX_, - unsigned int sizeY_, const unsigned char* data) { - GLTextureBuffer* newT = new GLTextureBuffer(format, sizeX_, sizeY_, data); - return std::shared_ptr(newT); -} -std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int sizeX_, - unsigned int sizeY_, const float* data) { - GLTextureBuffer* newT = new GLTextureBuffer(format, sizeX_, sizeY_, data); - return std::shared_ptr(newT); -} -std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int sizeX_, - unsigned int sizeY_, unsigned int sizeZ_, - const unsigned char* data) { - GLTextureBuffer* newT = new GLTextureBuffer(format, sizeX_, sizeY_, sizeZ_, data); - return std::shared_ptr(newT); -} -std::shared_ptr GLEngine::generateTextureBuffer(TextureFormat format, unsigned int sizeX_, - unsigned int sizeY_, unsigned int sizeZ_, - const float* data) { - GLTextureBuffer* newT = new GLTextureBuffer(format, sizeX_, sizeY_, sizeZ_, data); - return std::shared_ptr(newT); +void GLEngineEGL::setWindowResizable(bool newVal) { + // not defined in headless mode } -std::shared_ptr GLEngine::generateRenderBuffer(RenderBufferType type, unsigned int sizeX_, - unsigned int sizeY_) { - GLRenderBuffer* newR = new GLRenderBuffer(type, sizeX_, sizeY_); - return std::shared_ptr(newR); -} +bool GLEngineEGL::getWindowResizable() { return false; } -std::shared_ptr GLEngine::generateFrameBuffer(unsigned int sizeX_, unsigned int sizeY_) { - GLFrameBuffer* newF = new GLFrameBuffer(sizeX_, sizeY_); - return std::shared_ptr(newF); +std::tuple GLEngineEGL::getWindowPos() { + // not defined in headless mode + return std::tuple{-1, -1}; } -std::string GLEngine::programKeyFromRules(const std::string& programName, const std::vector& rules, - ShaderReplacementDefaults defaults) { - - std::stringstream builder; - - // program name comes first - builder << "$PROGRAMNAME: "; - builder << programName << "#"; - - // then rules - builder << " $RULES: "; - for (const std::string& s : rules) builder << s << "# "; - - // then rules from the defaults - builder << " $DEFAULTS: "; - switch (defaults) { - case ShaderReplacementDefaults::SceneObject: { - for (const std::string& s : defaultRules_sceneObject) builder << s << "# "; - break; - } - case ShaderReplacementDefaults::SceneObjectNoSlice: { - for (const std::string& s : defaultRules_sceneObject) { - if (s.rfind("SLICE_PLANE_", 0) != 0) { - builder << s << "# "; - } - } - break; - } - case ShaderReplacementDefaults::Pick: { - for (const std::string& s : defaultRules_pick) builder << s << "# "; - break; - } - case ShaderReplacementDefaults::Process: { - for (const std::string& s : defaultRules_process) builder << s << "# "; - break; - } - case ShaderReplacementDefaults::None: { - break; - } - } - - return builder.str(); +bool GLEngineEGL::windowRequestsClose() { + // not defined in headless mode + return false; } -std::shared_ptr GLEngine::getCompiledProgram(const std::string& programName, - const std::vector& customRules, - ShaderReplacementDefaults defaults) { - - // Build a cache key for the program - std::string progKey = programKeyFromRules(programName, customRules, defaults); - - - // If the cache doesn't already contain the program, create it and add to cache - if (compiledProgamCache.find(progKey) == compiledProgamCache.end()) { - - if (polyscope::options::verbosity > 3) polyscope::info("compiling shader program " + progKey); - - // == Compile the program - - // Get the list of shaders comprising the program from the global cache - if (registeredShaderPrograms.find(programName) == registeredShaderPrograms.end()) { - exception("No shader program with name [" + programName + "] registered."); - } - const std::vector& stages = registeredShaderPrograms[programName].first; - DrawMode dm = registeredShaderPrograms[programName].second; - - // Add in the default rules - std::vector fullCustomRules = customRules; - switch (defaults) { - case ShaderReplacementDefaults::SceneObject: { - fullCustomRules.insert(fullCustomRules.end(), defaultRules_sceneObject.begin(), defaultRules_sceneObject.end()); - break; - } - case ShaderReplacementDefaults::SceneObjectNoSlice: { - for (const std::string& rule : defaultRules_sceneObject) { - if (rule.rfind("SLICE_PLANE_", 0) != 0) { - fullCustomRules.insert(fullCustomRules.end(), rule); - } - } - break; - } - case ShaderReplacementDefaults::Pick: { - fullCustomRules.insert(fullCustomRules.end(), defaultRules_pick.begin(), defaultRules_pick.end()); - break; - } - case ShaderReplacementDefaults::Process: { - fullCustomRules.insert(fullCustomRules.end(), defaultRules_process.begin(), defaultRules_process.end()); - break; - } - case ShaderReplacementDefaults::None: { - break; - } - } - - // Prepare rule substitutions - std::vector rules; - for (auto it = fullCustomRules.begin(); it < fullCustomRules.end(); it++) { - std::string& ruleName = *it; - - // Empty rule is a no-op - if (ruleName == "") { - continue; - } - - // Only process each rule the first time it is seen - if (std::find(fullCustomRules.begin(), it, ruleName) != it) { - continue; - } - - if (registeredShaderRules.find(ruleName) == registeredShaderRules.end()) { - exception("No shader replacement rule with name [" + ruleName + "] registered."); - } - ShaderReplacementRule& thisRule = registeredShaderRules[ruleName]; - rules.push_back(thisRule); - } - - // Actually apply rule substitutions - std::vector updatedStages = applyShaderReplacements(stages, rules); - - // Create a new compiled program (GL work happens in the constructor) - compiledProgamCache[progKey] = std::shared_ptr(new GLCompiledProgram(updatedStages, dm)); - } - - // Now that the cache must contain the compiled program, just return it - return compiledProgamCache[progKey]; +void GLEngineEGL::pollEvents() { + // does nothing } -std::shared_ptr GLEngine::requestShader(const std::string& programName, - const std::vector& customRules, - ShaderReplacementDefaults defaults) { - GLShaderProgram* newP = new GLShaderProgram(getCompiledProgram(programName, customRules, defaults)); - return std::shared_ptr(newP); +bool GLEngineEGL::isKeyPressed(char c) { + // not defined in headless mode + return false; } - -void GLEngine::registerShaderProgram(const std::string& name, const std::vector& spec, - const DrawMode& dm) { - registeredShaderPrograms.insert({name, {spec, dm}}); +int GLEngineEGL::getKeyCode(char c) { + // not defined in headless mode + return -1; } -void GLEngine::registerShaderRule(const std::string& name, const ShaderReplacementRule& rule) { - registeredShaderRules.insert({name, rule}); +std::string GLEngineEGL::getClipboardText() { + // not defined in headless mode + return ""; } -void GLEngine::populateDefaultShadersAndRules() { - // clang-format off - - // == Load general base shaders - registerShaderProgram("MESH", {FLEX_MESH_VERT_SHADER, FLEX_MESH_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("INDEXED_MESH", {FLEX_MESH_VERT_SHADER, FLEX_MESH_FRAG_SHADER}, DrawMode::IndexedTriangles); - registerShaderProgram("SIMPLE_MESH", {SIMPLE_MESH_VERT_SHADER, SIMPLE_MESH_FRAG_SHADER}, DrawMode::IndexedTriangles); - registerShaderProgram("SLICE_TETS", {SLICE_TETS_VERT_SHADER, SLICE_TETS_GEOM_SHADER, SLICE_TETS_FRAG_SHADER}, DrawMode::Points); - registerShaderProgram("RAYCAST_SPHERE", {FLEX_SPHERE_VERT_SHADER, FLEX_SPHERE_GEOM_SHADER, FLEX_SPHERE_FRAG_SHADER}, DrawMode::Points); - registerShaderProgram("POINT_QUAD", {FLEX_POINTQUAD_VERT_SHADER, FLEX_POINTQUAD_GEOM_SHADER, FLEX_POINTQUAD_FRAG_SHADER}, DrawMode::Points); - registerShaderProgram("GRIDCUBE", {FLEX_GRIDCUBE_VERT_SHADER, FLEX_GRIDCUBE_GEOM_SHADER, FLEX_GRIDCUBE_FRAG_SHADER}, DrawMode::Points); - registerShaderProgram("GRIDCUBE_PLANE", {FLEX_GRIDCUBE_PLANE_VERT_SHADER, FLEX_GRIDCUBE_PLANE_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("RAYCAST_VECTOR", {FLEX_VECTOR_VERT_SHADER, FLEX_VECTOR_GEOM_SHADER, FLEX_VECTOR_FRAG_SHADER}, DrawMode::Points); - registerShaderProgram("RAYCAST_TANGENT_VECTOR", {FLEX_TANGENT_VECTOR_VERT_SHADER, FLEX_VECTOR_GEOM_SHADER, FLEX_VECTOR_FRAG_SHADER}, DrawMode::Points); - registerShaderProgram("RAYCAST_CYLINDER", {FLEX_CYLINDER_VERT_SHADER, FLEX_CYLINDER_GEOM_SHADER, FLEX_CYLINDER_FRAG_SHADER}, DrawMode::Points); - registerShaderProgram("HISTOGRAM", {HISTOGRAM_VERT_SHADER, HISTOGRAM_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("GROUND_PLANE_TILE", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_TILE_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("GROUND_PLANE_TILE_REFLECT", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_TILE_REFLECT_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("GROUND_PLANE_SHADOW", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_SHADOW_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("MAP_LIGHT", {TEXTURE_DRAW_VERT_SHADER, MAP_LIGHT_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("RIBBON", {RIBBON_VERT_SHADER, RIBBON_GEOM_SHADER, RIBBON_FRAG_SHADER}, DrawMode::IndexedLineStripAdjacency); - registerShaderProgram("SLICE_PLANE", {SLICE_PLANE_VERT_SHADER, SLICE_PLANE_FRAG_SHADER}, DrawMode::Triangles); - - registerShaderProgram("TEXTURE_DRAW_PLAIN", {TEXTURE_DRAW_VERT_SHADER, PLAIN_TEXTURE_DRAW_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("TEXTURE_DRAW_DOT3", {TEXTURE_DRAW_VERT_SHADER, DOT3_TEXTURE_DRAW_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("TEXTURE_DRAW_MAP3", {TEXTURE_DRAW_VERT_SHADER, MAP3_TEXTURE_DRAW_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("TEXTURE_DRAW_SPHEREBG", {SPHEREBG_DRAW_VERT_SHADER, SPHEREBG_DRAW_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("TEXTURE_DRAW_RENDERIMAGE_PLAIN", {TEXTURE_DRAW_VERT_SHADER, PLAIN_RENDERIMAGE_TEXTURE_DRAW_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("TEXTURE_DRAW_RAW_RENDERIMAGE_PLAIN", {TEXTURE_DRAW_VERT_SHADER, PLAIN_RAW_RENDERIMAGE_TEXTURE_DRAW_FRAG_SHADER}, DrawMode::Triangles); - registerShaderProgram("COMPOSITE_PEEL", {TEXTURE_DRAW_VERT_SHADER, COMPOSITE_PEEL}, DrawMode::Triangles); - registerShaderProgram("DEPTH_COPY", {TEXTURE_DRAW_VERT_SHADER, DEPTH_COPY}, DrawMode::Triangles); - registerShaderProgram("DEPTH_TO_MASK", {TEXTURE_DRAW_VERT_SHADER, DEPTH_TO_MASK}, DrawMode::Triangles); - registerShaderProgram("SCALAR_TEXTURE_COLORMAP", {TEXTURE_DRAW_VERT_SHADER, SCALAR_TEXTURE_COLORMAP}, DrawMode::Triangles); - registerShaderProgram("BLUR_RGB", {TEXTURE_DRAW_VERT_SHADER, BLUR_RGB}, DrawMode::Triangles); - registerShaderProgram("TRANSFORMATION_GIZMO_ROT", {TRANSFORMATION_GIZMO_ROT_VERT, TRANSFORMATION_GIZMO_ROT_FRAG}, DrawMode::Triangles); - - // === Load rules - - // Utility rules - registerShaderRule("GLSL_VERSION", GLSL_VERSION); - registerShaderRule("GLOBAL_FRAGMENT_FILTER", GLOBAL_FRAGMENT_FILTER); - registerShaderRule("DOWNSAMPLE_RESOLVE_1", DOWNSAMPLE_RESOLVE_1); - registerShaderRule("DOWNSAMPLE_RESOLVE_2", DOWNSAMPLE_RESOLVE_2); - registerShaderRule("DOWNSAMPLE_RESOLVE_3", DOWNSAMPLE_RESOLVE_3); - registerShaderRule("DOWNSAMPLE_RESOLVE_4", DOWNSAMPLE_RESOLVE_4); - - registerShaderRule("TRANSPARENCY_STRUCTURE", TRANSPARENCY_STRUCTURE); - registerShaderRule("TRANSPARENCY_RESOLVE_SIMPLE", TRANSPARENCY_RESOLVE_SIMPLE); - registerShaderRule("TRANSPARENCY_PEEL_STRUCTURE", TRANSPARENCY_PEEL_STRUCTURE); - registerShaderRule("TRANSPARENCY_PEEL_GROUND", TRANSPARENCY_PEEL_GROUND); - - registerShaderRule("GENERATE_VIEW_POS", GENERATE_VIEW_POS); - registerShaderRule("COMPUTE_SHADE_NORMAL_FROM_POSITION", COMPUTE_SHADE_NORMAL_FROM_POSITION); - registerShaderRule("PREMULTIPLY_LIT_COLOR", PREMULTIPLY_LIT_COLOR); - registerShaderRule("CULL_POS_FROM_VIEW", CULL_POS_FROM_VIEW); - registerShaderRule("PROJ_AND_INV_PROJ_MAT", PROJ_AND_INV_PROJ_MAT); - - // Lighting and shading things - registerShaderRule("LIGHT_MATCAP", LIGHT_MATCAP); - registerShaderRule("LIGHT_PASSTHRU", LIGHT_PASSTHRU); - registerShaderRule("SHADE_BASECOLOR", SHADE_BASECOLOR); - registerShaderRule("SHADE_COLOR", SHADE_COLOR); - registerShaderRule("SHADECOLOR_FROM_UNIFORM", SHADECOLOR_FROM_UNIFORM); - registerShaderRule("SHADE_COLORMAP_VALUE", SHADE_COLORMAP_VALUE); - registerShaderRule("SHADE_COLORMAP_ANGULAR2", SHADE_COLORMAP_ANGULAR2); - registerShaderRule("SHADE_GRID_VALUE2", SHADE_GRID_VALUE2); - registerShaderRule("SHADE_CHECKER_VALUE2", SHADE_CHECKER_VALUE2); - registerShaderRule("SHADE_CHECKER_CATEGORY", SHADE_CHECKER_CATEGORY); - registerShaderRule("SHADEVALUE_MAG_VALUE2", SHADEVALUE_MAG_VALUE2); - registerShaderRule("ISOLINE_STRIPE_VALUECOLOR", ISOLINE_STRIPE_VALUECOLOR); - registerShaderRule("CHECKER_VALUE2COLOR", CHECKER_VALUE2COLOR); - registerShaderRule("INVERSE_TONEMAP", INVERSE_TONEMAP); - - // Texture and image things - registerShaderRule("TEXTURE_ORIGIN_UPPERLEFT", TEXTURE_ORIGIN_UPPERLEFT); - registerShaderRule("TEXTURE_ORIGIN_LOWERLEFT", TEXTURE_ORIGIN_LOWERLEFT); - registerShaderRule("TEXTURE_SET_TRANSPARENCY", TEXTURE_SET_TRANSPARENCY); - registerShaderRule("TEXTURE_SET_TRANSPARENCY_PREMULTIPLIED", TEXTURE_SET_TRANSPARENCY_PREMULTIPLIED); - registerShaderRule("TEXTURE_PREMULTIPLY_OUT", TEXTURE_PREMULTIPLY_OUT); - registerShaderRule("TEXTURE_SHADE_COLOR", TEXTURE_SHADE_COLOR); - registerShaderRule("TEXTURE_SHADE_COLORALPHA", TEXTURE_SHADE_COLORALPHA); - registerShaderRule("TEXTURE_PROPAGATE_VALUE", TEXTURE_PROPAGATE_VALUE); - registerShaderRule("TEXTURE_PROPAGATE_COLOR", TEXTURE_PROPAGATE_COLOR); - registerShaderRule("TEXTURE_BILLBOARD_FROM_UNIFORMS", TEXTURE_BILLBOARD_FROM_UNIFORMS); - registerShaderRule("SHADE_NORMAL_FROM_TEXTURE", SHADE_NORMAL_FROM_TEXTURE); - registerShaderRule("SHADE_NORMAL_FROM_VIEWPOS_VAR", SHADE_NORMAL_FROM_VIEWPOS_VAR); - - // mesh things - registerShaderRule("MESH_WIREFRAME_FROM_BARY", MESH_WIREFRAME_FROM_BARY); - registerShaderRule("MESH_WIREFRAME", MESH_WIREFRAME); - registerShaderRule("MESH_WIREFRAME_ONLY", MESH_WIREFRAME_ONLY); - registerShaderRule("MESH_BACKFACE_NORMAL_FLIP", MESH_BACKFACE_NORMAL_FLIP); - registerShaderRule("MESH_BACKFACE_DIFFERENT", MESH_BACKFACE_DIFFERENT); - registerShaderRule("MESH_BACKFACE_DARKEN", MESH_BACKFACE_DARKEN); - registerShaderRule("MESH_PROPAGATE_VALUE", MESH_PROPAGATE_VALUE); - registerShaderRule("MESH_PROPAGATE_FLAT_VALUE", MESH_PROPAGATE_FLAT_VALUE); - registerShaderRule("MESH_PROPAGATE_VALUE2", MESH_PROPAGATE_VALUE2); - registerShaderRule("MESH_PROPAGATE_TCOORD", MESH_PROPAGATE_TCOORD); - registerShaderRule("MESH_PROPAGATE_COLOR", MESH_PROPAGATE_COLOR); - registerShaderRule("MESH_PROPAGATE_HALFEDGE_VALUE", MESH_PROPAGATE_HALFEDGE_VALUE); - registerShaderRule("MESH_PROPAGATE_CULLPOS", MESH_PROPAGATE_CULLPOS); - registerShaderRule("MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE", MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE); - registerShaderRule("MESH_PROPAGATE_PICK", MESH_PROPAGATE_PICK); - registerShaderRule("MESH_PROPAGATE_PICK_SIMPLE", MESH_PROPAGATE_PICK_SIMPLE); - - // volume gridcube things - registerShaderRule("GRIDCUBE_PROPAGATE_NODE_VALUE", GRIDCUBE_PROPAGATE_NODE_VALUE); - registerShaderRule("GRIDCUBE_PROPAGATE_CELL_VALUE", GRIDCUBE_PROPAGATE_CELL_VALUE); - registerShaderRule("GRIDCUBE_WIREFRAME", GRIDCUBE_WIREFRAME); - registerShaderRule("GRIDCUBE_CONSTANT_PICK", GRIDCUBE_CONSTANT_PICK); - registerShaderRule("GRIDCUBE_CULLPOS_FROM_CENTER", GRIDCUBE_CULLPOS_FROM_CENTER); - - // sphere things - registerShaderRule("SPHERE_PROPAGATE_VALUE", SPHERE_PROPAGATE_VALUE); - registerShaderRule("SPHERE_PROPAGATE_VALUE2", SPHERE_PROPAGATE_VALUE2); - registerShaderRule("SPHERE_PROPAGATE_COLOR", SPHERE_PROPAGATE_COLOR); - registerShaderRule("SPHERE_CULLPOS_FROM_CENTER", SPHERE_CULLPOS_FROM_CENTER); - registerShaderRule("SPHERE_CULLPOS_FROM_CENTER_QUAD", SPHERE_CULLPOS_FROM_CENTER_QUAD); - registerShaderRule("SPHERE_VARIABLE_SIZE", SPHERE_VARIABLE_SIZE); - - // vector things - registerShaderRule("VECTOR_PROPAGATE_COLOR", VECTOR_PROPAGATE_COLOR); - registerShaderRule("VECTOR_CULLPOS_FROM_TAIL", VECTOR_CULLPOS_FROM_TAIL); - registerShaderRule("TRANSFORMATION_GIZMO_VEC", TRANSFORMATION_GIZMO_VEC); - - // cylinder things - registerShaderRule("CYLINDER_PROPAGATE_VALUE", CYLINDER_PROPAGATE_VALUE); - registerShaderRule("CYLINDER_PROPAGATE_BLEND_VALUE", CYLINDER_PROPAGATE_BLEND_VALUE); - registerShaderRule("CYLINDER_PROPAGATE_COLOR", CYLINDER_PROPAGATE_COLOR); - registerShaderRule("CYLINDER_PROPAGATE_BLEND_COLOR", CYLINDER_PROPAGATE_BLEND_COLOR); - registerShaderRule("CYLINDER_PROPAGATE_PICK", CYLINDER_PROPAGATE_PICK); - registerShaderRule("CYLINDER_CULLPOS_FROM_MID", CYLINDER_CULLPOS_FROM_MID); - registerShaderRule("CYLINDER_VARIABLE_SIZE", CYLINDER_VARIABLE_SIZE); - - // marching tets things - registerShaderRule("SLICE_TETS_BASECOLOR_SHADE", SLICE_TETS_BASECOLOR_SHADE); - registerShaderRule("SLICE_TETS_PROPAGATE_VALUE", SLICE_TETS_PROPAGATE_VALUE); - registerShaderRule("SLICE_TETS_PROPAGATE_VECTOR", SLICE_TETS_PROPAGATE_VECTOR); - registerShaderRule("SLICE_TETS_VECTOR_COLOR", SLICE_TETS_VECTOR_COLOR); - registerShaderRule("SLICE_TETS_MESH_WIREFRAME", SLICE_TETS_MESH_WIREFRAME); - - // clang-format on -}; - -void GLEngine::createSlicePlaneFliterRule(std::string uniquePostfix) { - registeredShaderRules.insert({"SLICE_PLANE_CULL_" + uniquePostfix, generateSlicePlaneRule(uniquePostfix)}); - registeredShaderRules.insert( - {"SLICE_PLANE_VOLUMEGRID_CULL_" + uniquePostfix, generateVolumeGridSlicePlaneRule(uniquePostfix)}); +void GLEngineEGL::setClipboardText(std::string text) { + // not defined in headless mode } - -} // namespace backend_openGL3_glfw_egl +} // namespace backend_openGL3 } // namespace render } // namespace polyscope -#else - -#include +#else // POLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED #include "polyscope/messages.h" namespace polyscope { namespace render { -namespace backend_openGL3_glfw { -void initializeRenderEngine() { exception("Polyscope was not compiled with support for backend: openGL3_glfw"); } -} // namespace backend_openGL3_glfw +namespace backend_openGL3 { + +void initializeRenderEngine_egl() { exception("Polyscope was not compiled with support for backend: openGL3_egl"); } + + +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/gl_engine_glfw.cpp b/src/render/opengl/gl_engine_glfw.cpp index 5dd08a0b..0c8dcfca 100644 --- a/src/render/opengl/gl_engine_glfw.cpp +++ b/src/render/opengl/gl_engine_glfw.cpp @@ -5,8 +5,8 @@ #include "polyscope/render/opengl/gl_engine_glfw.h" #include "backends/imgui_impl_opengl3.h" -#include "polyscope/render/engine.h" #include "polyscope/polyscope.h" +#include "polyscope/render/engine.h" #include "stb_image.h" @@ -18,11 +18,11 @@ namespace render { namespace backend_openGL3 { GLEngineGLFW* glEngineGLFW = nullptr; // alias for global engine pointer -extern GLEngine* glEngine; // defined in gl_engine.h +extern GLEngine* glEngine; // defined in gl_engine.h -void initializeRenderEngine() { +void initializeRenderEngine_glfw() { - glEngineGLFW = new GLEngineGLFW();// create the new global engine object + glEngineGLFW = new GLEngineGLFW(); // create the new global engine object engine = glEngineGLFW; // we keep a few copies of this pointer with various types glEngine = glEngineGLFW; @@ -47,7 +47,7 @@ void GLEngineGLFW::initialize() { // === Initialize glfw glfwSetErrorCallback(error_print_callback); if (!glfwInit()) { - exception(options::printPrefix + "ERROR: Failed to initialize glfw"); + exception("ERROR: Failed to initialize glfw"); } // OpenGL version things @@ -80,7 +80,7 @@ void GLEngineGLFW::initialize() { // Load openGL functions (using GLAD) #ifndef __APPLE__ if (!gladLoadGL()) { - exception(options::printPrefix + "ERROR: Failed to load openGL using GLAD"); + exception("ERROR: Failed to load openGL using GLAD"); } #endif if (options::verbosity > 0) { @@ -143,6 +143,7 @@ void GLEngineGLFW::swapDisplayBuffers() { glfwSwapBuffers(mainWindow); } + void GLEngineGLFW::makeContextCurrent() { glfwMakeContextCurrent(mainWindow); glfwSwapInterval(options::enableVSync ? 1 : 0); @@ -252,7 +253,7 @@ namespace polyscope { namespace render { namespace backend_openGL3 { -void initializeRenderEngine() { exception("Polyscope was not compiled with support for backend: openGL3_glfw"); } +void initializeRenderEngine_glfw() { exception("Polyscope was not compiled with support for backend: openGL3_glfw"); } } // namespace backend_openGL3 } // namespace render diff --git a/src/render/opengl/shaders/common.cpp b/src/render/opengl/shaders/common.cpp index 8ccd6cf0..45f37fd6 100644 --- a/src/render/opengl/shaders/common.cpp +++ b/src/render/opengl/shaders/common.cpp @@ -5,7 +5,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { const char* shaderCommonSource = R"( diff --git a/src/render/opengl/shaders/cylinder_shaders.cpp b/src/render/opengl/shaders/cylinder_shaders.cpp index aea1adc8..6b579760 100644 --- a/src/render/opengl/shaders/cylinder_shaders.cpp +++ b/src/render/opengl/shaders/cylinder_shaders.cpp @@ -5,7 +5,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // clang-format off @@ -479,6 +479,6 @@ const ShaderReplacementRule CYLINDER_VARIABLE_SIZE ( // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/gizmo_shaders.cpp b/src/render/opengl/shaders/gizmo_shaders.cpp index 2df6f92b..7f6a6e76 100644 --- a/src/render/opengl/shaders/gizmo_shaders.cpp +++ b/src/render/opengl/shaders/gizmo_shaders.cpp @@ -4,7 +4,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // clang-format off @@ -280,6 +280,6 @@ R"( // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/grid_shaders.cpp b/src/render/opengl/shaders/grid_shaders.cpp index 7ca81c5c..8c858d95 100644 --- a/src/render/opengl/shaders/grid_shaders.cpp +++ b/src/render/opengl/shaders/grid_shaders.cpp @@ -5,7 +5,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // clang-format off diff --git a/src/render/opengl/shaders/ground_plane_shaders.cpp b/src/render/opengl/shaders/ground_plane_shaders.cpp index 5d6d7a60..d0782028 100644 --- a/src/render/opengl/shaders/ground_plane_shaders.cpp +++ b/src/render/opengl/shaders/ground_plane_shaders.cpp @@ -5,7 +5,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // clang-format off @@ -323,6 +323,6 @@ R"( // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/histogram_shaders.cpp b/src/render/opengl/shaders/histogram_shaders.cpp index db9ff280..eb6d2b34 100644 --- a/src/render/opengl/shaders/histogram_shaders.cpp +++ b/src/render/opengl/shaders/histogram_shaders.cpp @@ -5,7 +5,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // clang-format off @@ -87,6 +87,6 @@ R"( // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/lighting_shaders.cpp b/src/render/opengl/shaders/lighting_shaders.cpp index 5b0d1a71..b2e009ee 100644 --- a/src/render/opengl/shaders/lighting_shaders.cpp +++ b/src/render/opengl/shaders/lighting_shaders.cpp @@ -8,7 +8,7 @@ namespace polyscope { namespace render{ -namespace backend_openGL3_glfw { +namespace backend_openGL3 { const ShaderStageSpecification MAP_LIGHT_FRAG_SHADER = { @@ -304,6 +304,6 @@ const ShaderReplacementRule TRANSPARENCY_PEEL_GROUND ( // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/ribbon_shaders.cpp b/src/render/opengl/shaders/ribbon_shaders.cpp index 681013e4..f4b79a53 100644 --- a/src/render/opengl/shaders/ribbon_shaders.cpp +++ b/src/render/opengl/shaders/ribbon_shaders.cpp @@ -5,7 +5,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // clang-format off @@ -204,6 +204,6 @@ R"( // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/rules.cpp b/src/render/opengl/shaders/rules.cpp index 42ac462a..da510e4c 100644 --- a/src/render/opengl/shaders/rules.cpp +++ b/src/render/opengl/shaders/rules.cpp @@ -4,7 +4,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // clang-format off @@ -465,6 +465,6 @@ ShaderReplacementRule generateVolumeGridSlicePlaneRule(std::string uniquePostfix // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/sphere_shaders.cpp b/src/render/opengl/shaders/sphere_shaders.cpp index 69df2776..b140a95b 100644 --- a/src/render/opengl/shaders/sphere_shaders.cpp +++ b/src/render/opengl/shaders/sphere_shaders.cpp @@ -5,7 +5,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // clang-format off @@ -522,6 +522,6 @@ const ShaderReplacementRule SPHERE_VARIABLE_SIZE ( // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/surface_mesh_shaders.cpp b/src/render/opengl/shaders/surface_mesh_shaders.cpp index 2469f088..97643655 100644 --- a/src/render/opengl/shaders/surface_mesh_shaders.cpp +++ b/src/render/opengl/shaders/surface_mesh_shaders.cpp @@ -5,7 +5,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // clang-format off @@ -646,6 +646,6 @@ const ShaderReplacementRule MESH_PROPAGATE_PICK_SIMPLE ( // this one does faces // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/texture_draw_shaders.cpp b/src/render/opengl/shaders/texture_draw_shaders.cpp index 27bb3f06..28742ec0 100644 --- a/src/render/opengl/shaders/texture_draw_shaders.cpp +++ b/src/render/opengl/shaders/texture_draw_shaders.cpp @@ -8,7 +8,7 @@ namespace polyscope { namespace render{ -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // this uses the default openGL convention of origin in the lower left const ShaderStageSpecification TEXTURE_DRAW_VERT_SHADER = { @@ -790,6 +790,6 @@ const ShaderReplacementRule SHADE_NORMAL_FROM_VIEWPOS_VAR ( // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/vector_shaders.cpp b/src/render/opengl/shaders/vector_shaders.cpp index 4e963740..61e0f3bc 100644 --- a/src/render/opengl/shaders/vector_shaders.cpp +++ b/src/render/opengl/shaders/vector_shaders.cpp @@ -5,7 +5,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { // clang-format off @@ -333,6 +333,6 @@ const ShaderReplacementRule VECTOR_CULLPOS_FROM_TAIL( // clang-format on -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/volume_mesh_shaders.cpp b/src/render/opengl/shaders/volume_mesh_shaders.cpp index 1a0d9f00..24394749 100644 --- a/src/render/opengl/shaders/volume_mesh_shaders.cpp +++ b/src/render/opengl/shaders/volume_mesh_shaders.cpp @@ -4,7 +4,7 @@ namespace polyscope { namespace render { -namespace backend_openGL3_glfw { +namespace backend_openGL3 { const ShaderStageSpecification SLICE_TETS_VERT_SHADER = { @@ -391,6 +391,6 @@ const ShaderReplacementRule SLICE_TETS_PROPAGATE_VALUE( }, /* textures */ {}); -} // namespace backend_openGL3_glfw +} // namespace backend_openGL3 } // namespace render }; // namespace polyscope From bdc808d693a5daeb260853fefb6c9baf36f9037a Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sat, 16 Mar 2024 23:28:47 -0700 Subject: [PATCH 04/18] test updates --- test/include/polyscope_test.h | 2 +- test/src/basics_test.cpp | 3 +++ test/src/volume_grid_test.cpp | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/test/include/polyscope_test.h b/test/include/polyscope_test.h index 42fc1634..c5b4a6e9 100644 --- a/test/include/polyscope_test.h +++ b/test/include/polyscope_test.h @@ -29,10 +29,10 @@ class PolyscopeTest : public ::testing::Test { // Called before the first test in this test suite. // Can be omitted if not needed. static void SetUpTestSuite() { - polyscope::init(testBackend); polyscope::options::enableRenderErrorChecks = true; polyscope::options::errorsThrowExceptions = true; polyscope::options::hideWindowAfterShow = false; + polyscope::init(testBackend); } // Per-test-suite tear-down. diff --git a/test/src/basics_test.cpp b/test/src/basics_test.cpp index fb66fd75..9c1efb58 100644 --- a/test/src/basics_test.cpp +++ b/test/src/basics_test.cpp @@ -99,6 +99,9 @@ TEST_F(PolyscopeTest, EmptyBuffer) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, Screenshot) { + polyscope::screenshot("test_screeshot.png"); +} // ============================================================ // =============== Ground plane tests diff --git a/test/src/volume_grid_test.cpp b/test/src/volume_grid_test.cpp index c10d4a23..b05a5627 100644 --- a/test/src/volume_grid_test.cpp +++ b/test/src/volume_grid_test.cpp @@ -1,5 +1,6 @@ // Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run +#include "polyscope/slice_plane.h" #include "polyscope_test.h" @@ -176,5 +177,6 @@ TEST_F(PolyscopeTest, VolumeGridScalarIsosurfaceAndOpts) { q->registerIsosurfaceAsMesh(); polyscope::show(3); + polyscope::removeLastSceneSlicePlane(); polyscope::removeAllStructures(); } From a69420b4faec6a72ff4e9ff466c602546d3c4c00 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 17 Mar 2024 00:27:28 -0700 Subject: [PATCH 05/18] cleanup after rebasing onto recent --- CMakeLists.txt | 2 +- include/polyscope/render/opengl/gl_engine.h | 4 ++ src/CMakeLists.txt | 77 ++++++++++++--------- src/render/opengl/gl_engine_egl.cpp | 2 +- src/render/opengl/gl_engine_glfw.cpp | 12 ++-- 5 files changed, 56 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 39d3c943..c37065f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(POLYSCOPE_BACKEND_OPENGL_MOCK "ON" CACHE BOOL "Enable openGL_mock backend") if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") # the EGL backend is enabled by default when building on linux, and - # disabled by default otherwisek + # disabled by default otherwise set(POLYSCOPE_BACKEND_OPENGL3_EGL "ON" CACHE BOOL "Enable openGL3_egl backend") else() set(POLYSCOPE_BACKEND_OPENGL3_EGL "OFF" CACHE BOOL "Enable openGL3_egl backend") diff --git a/include/polyscope/render/opengl/gl_engine.h b/include/polyscope/render/opengl/gl_engine.h index 82cbee01..ba57b9bd 100644 --- a/include/polyscope/render/opengl/gl_engine.h +++ b/include/polyscope/render/opengl/gl_engine.h @@ -12,6 +12,10 @@ // be used to construct an instance of Engine. engine.h gives the render API, all render calls should pass through that. #ifdef __APPLE__ +// this means this file can only compile openGL on apple if we are also using glfw, but that's fine, because we only +// ever use the two together on apple +#define GLFW_INCLUDE_GLCOREARB +#include "GLFW/glfw3.h" #else #include "glad/glad.h" #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 76477c5d..8a4ac380 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,21 +5,23 @@ endif() SET(INCLUDE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../include/polyscope/") +# We need the shared openGL backend if either of these backends are enabled +if(POLYSCOPE_BACKEND_OPENGL3_GLFW OR POLYSCOPE_BACKEND_OPENGL3_EGL) + SET(POLYSCOPE_BACKEND_OPENGL3 TRUE) + add_definitions(-DPOLYSCOPE_BACKEND_OPENGL3_ENABLED) +else() + SET(POLYSCOPE_BACKEND_OPENGL3 FALSE) +endif() + # Lists that will be populated for the render backend # Add the main _engine file no matter what. All of the interesting parts are ifdef'd out, and this # allows us to resolve stubs. list (APPEND BACKEND_SRCS render/opengl/gl_engine.cpp + render/opengl/gl_engine_glfw.cpp + render/opengl/gl_engine_egl.cpp render/mock_opengl/mock_gl_engine.cpp -) - -# Configure the render backend -if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") - message("Polyscope backend openGL3_glfw enabled") - - list (APPEND BACKEND_SRCS - render/opengl/gl_engine.cpp render/opengl/shaders/texture_draw_shaders.cpp render/opengl/shaders/lighting_shaders.cpp render/opengl/shaders/grid_shaders.cpp @@ -34,10 +36,14 @@ if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") render/opengl/shaders/cylinder_shaders.cpp render/opengl/shaders/rules.cpp render/opengl/shaders/common.cpp - ) +) + +# Configure the render backend(s) +if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") + message("Polyscope backend openGL3_glfw enabled") list(APPEND BACKEND_HEADERS - ${INCLUDE_ROOT}render/opengl/gl_engine.h + ${INCLUDE_ROOT}render/opengl/gl_engine_glfw.h ${INCLUDE_ROOT}render/opengl/shaders/common.h ) @@ -69,37 +75,42 @@ if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") add_definitions(-DPOLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED) endif() -if("${POLYSCOPE_BACKEND_OPENGL_MOCK}") - message("Polyscope backend openGL_mock enabled") +if("${POLYSCOPE_BACKEND_OPENGL3_EGL}") + message("Polyscope backend openGL3_egl enabled") - list (APPEND BACKEND_SRCS - render/mock_opengl/mock_gl_engine.cpp - render/opengl/shaders/texture_draw_shaders.cpp - render/opengl/shaders/lighting_shaders.cpp - render/opengl/shaders/grid_shaders.cpp - render/opengl/shaders/ground_plane_shaders.cpp - render/opengl/shaders/gizmo_shaders.cpp - render/opengl/shaders/histogram_shaders.cpp - render/opengl/shaders/surface_mesh_shaders.cpp - render/opengl/shaders/volume_mesh_shaders.cpp - render/opengl/shaders/vector_shaders.cpp - render/opengl/shaders/sphere_shaders.cpp - render/opengl/shaders/ribbon_shaders.cpp - render/opengl/shaders/cylinder_shaders.cpp - render/opengl/shaders/rules.cpp - render/opengl/shaders/common.cpp + list(APPEND BACKEND_HEADERS + ${INCLUDE_ROOT}render/opengl/gl_engine_egl.h + ${INCLUDE_ROOT}render/opengl/shaders/common.h ) + if(APPLE) + message(FATAL_ERROR "Compiling EGL backed on APPLE is not supported. Set POLYSCOPE_BACKEND_OPENGL3_EGL=False") + else() + + # On Windows/Linux, use the glad openGL loader + list(APPEND BACKEND_LIBS glad) + + if(WIN32) + message(FATAL_ERROR "Compiling EGL backed on Windows is not supported. Set POLYSCOPE_BACKEND_OPENGL3_EGL=False") + else() # linux + # only linux is actually supported for EGL + list(APPEND BACKEND_LIBS EGL) + endif() + + endif() + + add_definitions(-DPOLYSCOPE_BACKEND_OPENGL3_EGL_ENABLED) +endif() + +if("${POLYSCOPE_BACKEND_OPENGL_MOCK}") + message("Polyscope backend openGL_mock enabled") + list(APPEND BACKEND_HEADERS ${INCLUDE_ROOT}render/mock_opengl/mock_gl_engine.h ${INCLUDE_ROOT}render/opengl/shaders/common.h ) - # Link settings - list(APPEND BACKEND_LIBS - ) - -add_definitions(-DPOLYSCOPE_BACKEND_OPENGL_MOCK_ENABLED) + add_definitions(-DPOLYSCOPE_BACKEND_OPENGL_MOCK_ENABLED) endif() diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 3bf9f3fe..f7ec923c 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -332,7 +332,7 @@ void GLEngineEGL::setClipboardText(std::string text) { } // namespace render } // namespace polyscope -#else // POLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED +#else // POLYSCOPE_BACKEND_OPENGL3_EGL_ENABLED #include "polyscope/messages.h" diff --git a/src/render/opengl/gl_engine_glfw.cpp b/src/render/opengl/gl_engine_glfw.cpp index 0c8dcfca..4eede7aa 100644 --- a/src/render/opengl/gl_engine_glfw.cpp +++ b/src/render/opengl/gl_engine_glfw.cpp @@ -219,17 +219,17 @@ bool GLEngineGLFW::windowRequestsClose() { void GLEngineGLFW::pollEvents() { glfwPollEvents(); } bool GLEngineGLFW::isKeyPressed(char c) { - if (c >= '0' && c <= '9') return ImGui::IsKeyPressed(GLFW_KEY_0 + (c - '0')); - if (c >= 'a' && c <= 'z') return ImGui::IsKeyPressed(GLFW_KEY_A + (c - 'a')); - if (c >= 'A' && c <= 'Z') return ImGui::IsKeyPressed(GLFW_KEY_A + (c - 'A')); + if (c >= '0' && c <= '9') return ImGui::IsKeyPressed(static_cast(ImGuiKey_0 + (c - '0'))); + if (c >= 'a' && c <= 'z') return ImGui::IsKeyPressed(static_cast(ImGuiKey_A + (c - 'a'))); + if (c >= 'A' && c <= 'Z') return ImGui::IsKeyPressed(static_cast(ImGuiKey_A + (c - 'A'))); exception("keyPressed only supports 0-9, a-z, A-Z"); return false; } int GLEngineGLFW::getKeyCode(char c) { - if (c >= '0' && c <= '9') return static_cast(GLFW_KEY_0) + (c - '0'); - if (c >= 'a' && c <= 'z') return static_cast(GLFW_KEY_A) + (c - 'a'); - if (c >= 'A' && c <= 'Z') return static_cast(GLFW_KEY_A) + (c - 'A'); + if (c >= '0' && c <= '9') return static_cast(ImGuiKey_0) + (c - '0'); + if (c >= 'a' && c <= 'z') return static_cast(ImGuiKey_A) + (c - 'a'); + if (c >= 'A' && c <= 'Z') return static_cast(ImGuiKey_A) + (c - 'A'); exception("getKeyCode only supports 0-9, a-z, A-Z"); return -1; } From 787ee84c0158d40dee9a81e04a4e06ac2ba122d9 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 17 Mar 2024 00:45:48 -0700 Subject: [PATCH 06/18] update init code for new auto policy --- src/render/initialize_backend.cpp | 66 ++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/src/render/initialize_backend.cpp b/src/render/initialize_backend.cpp index ea5aa6a9..e555c52f 100644 --- a/src/render/initialize_backend.cpp +++ b/src/render/initialize_backend.cpp @@ -1,8 +1,11 @@ // Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run #include "polyscope/messages.h" +#include "polyscope/options.h" #include "polyscope/render/engine.h" +#include + namespace polyscope { namespace render { @@ -25,21 +28,8 @@ void initializeRenderEngine(); void initializeRenderEngine(std::string backend) { // Handle default backends - // (the string is getting overwritten, so lower on the list means higher priority) if (backend == "") { - -#ifdef POLYSCOPE_BACKEND_OPENGL_MOCK_ENABLED - // Don't set it one by default, since it's probably a mistake; better to throw the exception below. - // backend = "mock_openGL"; -#endif - -#ifdef POLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED - backend = "openGL3_glfw"; -#endif - - if (backend == "") { - exception("no Polyscope backends available"); - } + backend = "auto"; // treat "" as "auto" } engineBackendName = backend; @@ -51,6 +41,54 @@ void initializeRenderEngine(std::string backend) { backend_openGL3::initializeRenderEngine_egl(); } else if (backend == "openGL_mock") { backend_openGL_mock::initializeRenderEngine(); + } else if (backend == "auto") { + + // Attempt to automatically initialize by trynig + + bool initSucces = false; + +#ifdef POLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED + // First try GLFW, if available + backend = "openGL3_glfw"; + try { + backend_openGL3::initializeRenderEngine_glfw(); + initSucces = true; + } catch (const std::exception& e) { + if (options::verbosity > 0) { + info("Attempting automatic initialization. Could not initialize backend [openGL3_glfw]. Message: " + + std::string(e.what())); + } + } + if (initSucces) return; +#endif + +#ifdef POLYSCOPE_BACKEND_OPENGL3_EGL_ENABLED + // Then, try EGL if available + backend = "openGL3_egl"; + try { + backend_openGL3::initializeRenderEngine_egl(); + initSucces = true; + } catch (const std::exception& e) { + if (options::verbosity > 0) { + info("Attempting automatic initialization. Could not initialize backend [openGL3_egl]. Message: " + e.what()); + } + } + if (initSucces) { + if (options::verbosity > 0) { + info("Automatic initialization could not create an interactive backend, and created a headless backend " + "instead. This likely means no displays are available. With the headless backend, you can still run " + "Polyscope and even render, for instance to record screenshots. However no interactive windows can be " + "created.") + } + return; + } +#endif + + // Don't bother trying the 'mock' backend, it is unlikely to be what the user wants from the 'auto' option + + // Failure + exception("Automatic initialization: no Polyscope backends could be initialized successfully."); + } else { exception("unrecognized Polyscope backend " + backend); } From f532fcdc614f9927d4538aac4f5c17636427a135 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 17 Mar 2024 00:49:27 -0700 Subject: [PATCH 07/18] add egl test on linux --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8f7b2b00..d61480fc 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -51,4 +51,4 @@ jobs: run: cd test/build && make - name: run test - run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock + run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL3_egl From 6f91aa57eb2cd33102cf0fefe153149c4c237f81 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 17 Mar 2024 00:52:45 -0700 Subject: [PATCH 08/18] fix exception compile error --- src/render/initialize_backend.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/render/initialize_backend.cpp b/src/render/initialize_backend.cpp index e555c52f..a0b79b82 100644 --- a/src/render/initialize_backend.cpp +++ b/src/render/initialize_backend.cpp @@ -70,7 +70,8 @@ void initializeRenderEngine(std::string backend) { initSucces = true; } catch (const std::exception& e) { if (options::verbosity > 0) { - info("Attempting automatic initialization. Could not initialize backend [openGL3_egl]. Message: " + e.what()); + info("Attempting automatic initialization. Could not initialize backend [openGL3_egl]. Message: " + + std::string(e.what())); } } if (initSucces) { From b4d2b0a8aa4ad47e57fcf420e49012ab92ab4fa3 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 17 Mar 2024 00:55:38 -0700 Subject: [PATCH 09/18] fix compile --- src/render/initialize_backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/initialize_backend.cpp b/src/render/initialize_backend.cpp index a0b79b82..b90557fe 100644 --- a/src/render/initialize_backend.cpp +++ b/src/render/initialize_backend.cpp @@ -79,7 +79,7 @@ void initializeRenderEngine(std::string backend) { info("Automatic initialization could not create an interactive backend, and created a headless backend " "instead. This likely means no displays are available. With the headless backend, you can still run " "Polyscope and even render, for instance to record screenshots. However no interactive windows can be " - "created.") + "created."); } return; } From 5a0021697a0359e4fc96a01ad8b3dc45163c279d Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 17 Mar 2024 01:10:10 -0700 Subject: [PATCH 10/18] add egl test to static build too --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d61480fc..402c78e0 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -28,7 +28,7 @@ jobs: run: cd test/build && make - name: run test - run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock + run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL3_egl build_shared: strategy: From 1eb2f2ed1999e06f727ace5276bb063c6917b46c Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 17 Mar 2024 01:19:03 -0700 Subject: [PATCH 11/18] add error check to egl initialize --- src/render/opengl/gl_engine_egl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index f7ec923c..4540321f 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -141,6 +141,7 @@ void GLEngineEGL::initialize() { EGLint majorVer, minorVer; bool success = eglInitialize(eglDisplay, &majorVer, &minorVer); if (!success) { + checkEGLError(false); exception("ERROR: Failed to initialize EGL"); } checkEGLError(); From 2f93cbdfccab4c58a4c6b85955d55178818c3c95 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 17 Mar 2024 12:03:23 -0700 Subject: [PATCH 12/18] improve egl error messaging --- include/polyscope/render/opengl/gl_engine_egl.h | 1 - src/render/opengl/gl_engine_egl.cpp | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/polyscope/render/opengl/gl_engine_egl.h b/include/polyscope/render/opengl/gl_engine_egl.h index 40b0005a..471becb2 100644 --- a/include/polyscope/render/opengl/gl_engine_egl.h +++ b/include/polyscope/render/opengl/gl_engine_egl.h @@ -76,7 +76,6 @@ class GLEngineEGL : public GLEngine { protected: // Internal windowing and engine details EGLDisplay eglDisplay; - // EGLSurface eglSurface; EGLContext eglContext; }; diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 4540321f..9fe38de8 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -128,7 +128,9 @@ void initializeRenderEngine_egl() { } GLEngineEGL::GLEngineEGL() {} -GLEngineEGL::~GLEngineEGL() {} +GLEngineEGL::~GLEngineEGL() { + // eglTerminate(eglDisplay) // TODO +} void GLEngineEGL::initialize() { @@ -136,6 +138,10 @@ void GLEngineEGL::initialize() { // Get the default display eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (eglDisplay == EGL_NO_DISPLAY) { + checkEGLError(false); + exception("ERROR: Failed to initialize EGL, could not get default display"); + } // Configure EGLint majorVer, minorVer; @@ -200,7 +206,8 @@ void GLEngineEGL::initialize() { #endif if (options::verbosity > 0) { std::cout << options::printPrefix << "Backend: openGL3_egl -- " - << "Loaded openGL version: " << glGetString(GL_VERSION) << std::endl; + << "Loaded openGL version: " << glGetString(GL_VERSION) << " -- " + << "EGL version: " << majorVer << "." << minorVer << std::endl; } { // Manually create the screen frame buffer From c16c3483e4a76af27cc83828f2b9df4e04320448 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 17 Mar 2024 12:10:54 -0700 Subject: [PATCH 13/18] remove premature error check --- src/render/opengl/gl_engine_egl.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 9fe38de8..eb236378 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -139,7 +139,6 @@ void GLEngineEGL::initialize() { // Get the default display eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (eglDisplay == EGL_NO_DISPLAY) { - checkEGLError(false); exception("ERROR: Failed to initialize EGL, could not get default display"); } From 9406d2917b0b30f7f4fa10453778857e99726b65 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 17 Mar 2024 16:53:20 -0700 Subject: [PATCH 14/18] disable EGL backend by default --- .github/workflows/linux.yml | 4 ++-- CMakeLists.txt | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 402c78e0..8f7b2b00 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -28,7 +28,7 @@ jobs: run: cd test/build && make - name: run test - run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL3_egl + run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock build_shared: strategy: @@ -51,4 +51,4 @@ jobs: run: cd test/build && make - name: run test - run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL3_egl + run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock diff --git a/CMakeLists.txt b/CMakeLists.txt index c37065f2..120bda6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,14 +10,7 @@ cmake_policy(SET CMP0054 NEW) # don't implicitly dereference inside if() # Backend set(POLYSCOPE_BACKEND_OPENGL3_GLFW "ON" CACHE BOOL "Enable openGL3_glfw backend") set(POLYSCOPE_BACKEND_OPENGL_MOCK "ON" CACHE BOOL "Enable openGL_mock backend") - -if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - # the EGL backend is enabled by default when building on linux, and - # disabled by default otherwise - set(POLYSCOPE_BACKEND_OPENGL3_EGL "ON" CACHE BOOL "Enable openGL3_egl backend") -else() - set(POLYSCOPE_BACKEND_OPENGL3_EGL "OFF" CACHE BOOL "Enable openGL3_egl backend") -endif() +set(POLYSCOPE_BACKEND_OPENGL3_EGL "OFF" CACHE BOOL "Enable openGL3_egl backend") # disable EGL by default, for now ### Do anything needed for dependencies and bring their stuff in to scope add_subdirectory(deps) From 03127ecf142001ec12dfa8f60e04e60c63a75b51 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 24 Mar 2024 15:02:23 -0700 Subject: [PATCH 15/18] add EGL note --- src/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8a4ac380..8e9ebf20 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,6 +78,11 @@ endif() if("${POLYSCOPE_BACKEND_OPENGL3_EGL}") message("Polyscope backend openGL3_egl enabled") + # this only supports headless rendering, and as of Mar 2024 headless rendering seems to only be available on nvidia drivers. + # + # This adds an additional requirement for EGL.h headers, get them on Ubuntu & friends via + # sudo apt-get install libegl1-mesa-dev + list(APPEND BACKEND_HEADERS ${INCLUDE_ROOT}render/opengl/gl_engine_egl.h ${INCLUDE_ROOT}render/opengl/shaders/common.h From 53016e704afcde1a59c921701a60a0f73be9d72c Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 24 Mar 2024 15:06:26 -0700 Subject: [PATCH 16/18] remove comment --- include/polyscope/render/opengl/gl_engine_egl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/polyscope/render/opengl/gl_engine_egl.h b/include/polyscope/render/opengl/gl_engine_egl.h index 471becb2..5eb98016 100644 --- a/include/polyscope/render/opengl/gl_engine_egl.h +++ b/include/polyscope/render/opengl/gl_engine_egl.h @@ -7,7 +7,6 @@ #include "polyscope/utilities.h" #ifdef __APPLE__ -// TODO should we IFDEF this away instead? #error "EGL backend is not supported on macOS. Disable it in the CMake build options." #else #include "glad/glad.h" From 02c1023ba943afc90d648fc03152f03e7aceaf86 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 24 Mar 2024 22:50:34 -0700 Subject: [PATCH 17/18] change cmake setup to use auto option --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 120bda6a..77dc97b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ cmake_policy(SET CMP0054 NEW) # don't implicitly dereference inside if() # Backend set(POLYSCOPE_BACKEND_OPENGL3_GLFW "ON" CACHE BOOL "Enable openGL3_glfw backend") set(POLYSCOPE_BACKEND_OPENGL_MOCK "ON" CACHE BOOL "Enable openGL_mock backend") -set(POLYSCOPE_BACKEND_OPENGL3_EGL "OFF" CACHE BOOL "Enable openGL3_egl backend") # disable EGL by default, for now +set(POLYSCOPE_BACKEND_OPENGL3_EGL "AUTO" CACHE STRING "Enable openGL3_egl backend") # 'AUTO' means "if we're on linux and EGL.h is available" ### Do anything needed for dependencies and bring their stuff in to scope add_subdirectory(deps) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e9ebf20..9d03d967 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,13 +75,43 @@ if("${POLYSCOPE_BACKEND_OPENGL3_GLFW}") add_definitions(-DPOLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED) endif() +## some logic to decide whether or not EGL is available and whether we want to build with it +include(CheckIncludeFileCXX) +CHECK_INCLUDE_FILE_CXX("EGL/egl.h" HAVE_EGL_LIB) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(IS_LINUX TRUE) +else() + set(IS_LINUX FALSE) +endif() + +if(POLYSCOPE_BACKEND_OPENGL3_EGL STREQUAL "AUTO") + if(IS_LINUX AND HAVE_EGL_LIB) # "AUTO" policy: only enable if we're on linux and EGL libs seem to be present + set(POLYSCOPE_BACKEND_OPENGL3_EGL ON) + else() + set(POLYSCOPE_BACKEND_OPENGL3_EGL OFF) + endif() +elseif(POLYSCOPE_BACKEND_OPENGL3_EGL) # if "TRUE" (or "ON" or "YES" etc etc) + # sanity check that we can actually use the EGL backend + if(NOT IS_LINUX) + message(FATAL_ERROR "EGL backend is only supported on linux. Disable it with POLYSCOPE_BACKEND_OPENGL3_EGL=False") + endif() + if(NOT HAVE_EGL_LIB) + message(FATAL_ERROR "EGL backend requires EGL headers, but 'EGL/egl.h' was not found. On Ubuntu, try 'apt-get install libegl1-mesa-dev'") + endif() + + # we should be good-to-go for EGL + +else() + # nothing to do, already disabled +endif() + + + + if("${POLYSCOPE_BACKEND_OPENGL3_EGL}") message("Polyscope backend openGL3_egl enabled") # this only supports headless rendering, and as of Mar 2024 headless rendering seems to only be available on nvidia drivers. - # - # This adds an additional requirement for EGL.h headers, get them on Ubuntu & friends via - # sudo apt-get install libegl1-mesa-dev list(APPEND BACKEND_HEADERS ${INCLUDE_ROOT}render/opengl/gl_engine_egl.h From 99ee141184ff9fac70337d75374b9f9e3463ab1e Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Wed, 27 Mar 2024 17:14:21 -0700 Subject: [PATCH 18/18] cleanup --- include/polyscope/render/opengl/gl_engine_egl.h | 3 --- src/render/opengl/gl_engine_egl.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/polyscope/render/opengl/gl_engine_egl.h b/include/polyscope/render/opengl/gl_engine_egl.h index 5eb98016..24d93ef3 100644 --- a/include/polyscope/render/opengl/gl_engine_egl.h +++ b/include/polyscope/render/opengl/gl_engine_egl.h @@ -16,9 +16,6 @@ #include "imgui.h" -#define IMGUI_IMPL_OPENGL_LOADER_GLAD -#include "backends/imgui_impl_glfw.h" // TODO -#include "backends/imgui_impl_opengl3.h" #include "polyscope/render/opengl/gl_engine.h" diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index eb236378..76a957a0 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -129,7 +129,7 @@ void initializeRenderEngine_egl() { GLEngineEGL::GLEngineEGL() {} GLEngineEGL::~GLEngineEGL() { - // eglTerminate(eglDisplay) // TODO + // eglTerminate(eglDisplay) // TODO handle termination } void GLEngineEGL::initialize() {