#include "fiddle_context.hpp" #ifdef RIVE_TOOLS_NO_GLFW std::unique_ptr FiddleContext::MakeGLPLS(FiddleContextOptions) { return nullptr; } #else #include "path_fiddle.hpp" #include "rive/renderer/rive_renderer.hpp" #include "rive/renderer/gl/render_context_gl_impl.hpp" #include "rive/renderer/gl/render_target_gl.hpp" #include "rive/profiler/profiler_macros.h" #ifdef RIVE_WEBGL #include #include #endif #define GLFW_INCLUDE_NONE #include using namespace rive; using namespace rive::gpu; #ifdef RIVE_DESKTOP_GL #ifdef DEBUG static void GLAPIENTRY err_msg_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { if (type == GL_DEBUG_TYPE_ERROR_KHR) { printf("GL ERROR: %s\n", message); fflush(stdout); // Don't abort if it's a shader compile error; let our internal handlers // print the source (for debugging) and exit on their own. if (strstr(message, "SHADER_ID_COMPILE") == nullptr && strstr(message, "SHADER_ID_LINK") == nullptr) { assert(0); } } else if (type == GL_DEBUG_TYPE_PERFORMANCE_KHR) { if (strstr( message, "API_ID_REDUNDANT_FBO performance warning has been generated. " "Redundant state change in glBindFramebuffer API call, FBO")) { return; } if (strstr(message, "is being recompiled based on GL state") || strstr(message, "shader recompiled due to state change")) { return; } if (strcmp(message, "Pixel-path performance warning: Pixel transfer is " "synchronized with 3D rendering.") == 0) { return; } printf("GL PERF: %s\n", message); fflush(stdout); } } #endif #endif class FiddleContextGLBase : public FiddleContext { public: ~FiddleContextGLBase() override { glDeleteFramebuffers(1, &m_zoomWindowFBO); } float dpiScale(GLFWwindow*) const override { #if defined(__APPLE__) || defined(RIVE_WEBGL) return 2; #else return 1; #endif } void toggleZoomWindow() override { if (m_zoomWindowFBO) { glDeleteFramebuffers(1, &m_zoomWindowFBO); m_zoomWindowFBO = 0; } else { GLuint tex; glGenTextures(1, &tex); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, kZoomWindowWidth, kZoomWindowHeight); glGenFramebuffers(1, &m_zoomWindowFBO); glBindFramebuffer(GL_FRAMEBUFFER, m_zoomWindowFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteTextures(1, &tex); } } void end(GLFWwindow* window, std::vector* pixelData) final { onEnd(pixelData); if (m_zoomWindowFBO) { // Blit the zoom window. double xd, yd; glfwGetCursorPos(window, &xd, &yd); xd *= dpiScale(window); yd *= dpiScale(window); int width = 0, height = 0; glfwGetFramebufferSize(window, &width, &height); int x = xd, y = height - yd; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_zoomWindowFBO); glBlitFramebuffer(x - kZoomWindowWidth / 2, y - kZoomWindowHeight / 2, x + kZoomWindowWidth / 2, y + kZoomWindowHeight / 2, 0, 0, kZoomWindowWidth, kZoomWindowHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_FRAMEBUFFER, 0); glEnable(GL_SCISSOR_TEST); glScissor(0, 0, kZoomWindowWidth * kZoomWindowScale + 2, kZoomWindowHeight * kZoomWindowScale + 2); glClearColor(.6f, .6f, .6f, 1); glClear(GL_COLOR_BUFFER_BIT); glBindFramebuffer(GL_READ_FRAMEBUFFER, m_zoomWindowFBO); glBlitFramebuffer(0, 0, kZoomWindowWidth, kZoomWindowHeight, 0, 0, kZoomWindowWidth * kZoomWindowScale, kZoomWindowHeight * kZoomWindowScale, GL_COLOR_BUFFER_BIT, GL_NEAREST); glDisable(GL_SCISSOR_TEST); } RIVE_PROF_ENDFRAME() } protected: virtual void onEnd(std::vector* pixelData) = 0; private: GLuint m_zoomWindowFBO = 0; }; class FiddleContextGL : public FiddleContextGLBase { public: FiddleContextGL(FiddleContextOptions options) { #ifdef RIVE_DESKTOP_GL // Load the OpenGL API using glad. if (!gladLoadCustomLoader( reinterpret_cast(glfwGetProcAddress))) { fprintf(stderr, "Failed to initialize glad.\n"); abort(); } #endif printf("==== GL GPU: %s ====\n", glGetString(GL_RENDERER)); #if 0 int n; glGetIntegerv(GL_NUM_EXTENSIONS, &n); for (size_t i = 0; i < n; ++i) { printf(" %s\n", glGetStringi(GL_EXTENSIONS, i)); } #endif #ifdef RIVE_DESKTOP_GL #ifdef DEBUG if (GLAD_GL_KHR_debug) { glEnable(GL_DEBUG_OUTPUT_KHR); glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); glDebugMessageCallbackKHR(&err_msg_callback, nullptr); } #endif #endif m_renderContext = RenderContextGLImpl::MakeContext({ .shaderCompilationMode = options.shaderCompilationMode, .disableFragmentShaderInterlock = options.disableRasterOrdering, }); if (!m_renderContext) { fprintf(stderr, "Failed to create a RiveRenderContext for GL.\n"); abort(); } } rive::Factory* factory() final { return m_renderContext.get(); } RenderContext* renderContextOrNull() final { return m_renderContext.get(); } RenderContextGLImpl* renderContextGLImpl() const final { return m_renderContext->static_impl_cast(); } RenderTarget* renderTargetOrNull() final { return m_renderTarget.get(); } void onSizeChanged(GLFWwindow* window, int width, int height, uint32_t sampleCount) final { m_renderTarget = make_rcp(width, height, 0, sampleCount); glViewport(0, 0, width, height); } std::unique_ptr makeRenderer(int width, int height) final { return std::make_unique(m_renderContext.get()); } void begin(const RenderContext::FrameDescriptor& frameDescriptor) final { renderContextGLImpl()->invalidateGLState(); m_renderContext->beginFrame(frameDescriptor); } void flushPLSContext(RenderTarget* offscreenRenderTarget) final { m_renderContext->flush({ .renderTarget = offscreenRenderTarget != nullptr ? offscreenRenderTarget : m_renderTarget.get(), }); } void onEnd(std::vector* pixelData) final { flushPLSContext(nullptr); renderContextGLImpl()->unbindGLInternalResources(); glBindFramebuffer(GL_FRAMEBUFFER, 0); if (pixelData) { pixelData->resize(m_renderTarget->height() * m_renderTarget->width() * 4); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glReadPixels(0, 0, m_renderTarget->width(), m_renderTarget->height(), GL_RGBA, GL_UNSIGNED_BYTE, pixelData->data()); } } private: std::unique_ptr m_renderContext; rcp m_renderTarget; }; std::unique_ptr FiddleContext::MakeGLPLS( FiddleContextOptions options) { return std::make_unique(options); } #ifndef RIVE_SKIA std::unique_ptr FiddleContext::MakeGLSkia() { return nullptr; } #else #include "skia_factory.hpp" #include "skia_renderer.hpp" #include "skia/include/core/SkCanvas.h" #include "skia/include/core/SkSurface.h" #include "skia/include/gpu/GrDirectContext.h" #include "skia/include/gpu/gl/GrGLAssembleInterface.h" #include "skia/include/gpu/gl/GrGLInterface.h" #include "include/effects/SkImageFilters.h" static GrGLFuncPtr get_skia_gl_proc_address(void* ctx, const char name[]) { return glfwGetProcAddress(name); } class FiddleContextGLSkia : public FiddleContextGLBase { public: FiddleContextGLSkia() : m_grContext(GrDirectContext::MakeGL( GrGLMakeAssembledInterface(nullptr, get_skia_gl_proc_address))) { if (!m_grContext) { fprintf(stderr, "GrDirectContext::MakeGL failed.\n"); abort(); } } rive::Factory* factory() final { return &m_factory; } RenderContext* renderContextOrNull() final { return nullptr; } RenderTarget* renderTargetOrNull() final { return nullptr; } std::unique_ptr makeRenderer(int width, int height) final { GrBackendRenderTarget backendRT(width, height, 1 /*samples*/, 0 /*stencilBits*/, {0 /*fbo 0*/, GL_RGBA8}); SkSurfaceProps surfProps(0, kUnknown_SkPixelGeometry); m_skSurface = SkSurface::MakeFromBackendRenderTarget(m_grContext.get(), backendRT, kBottomLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, nullptr, &surfProps); if (!m_skSurface) { fprintf(stderr, "SkSurface::MakeFromBackendRenderTarget failed.\n"); abort(); } return std::make_unique(m_skSurface->getCanvas()); } void begin(const RenderContext::FrameDescriptor& frameDescriptor) final { m_skSurface->getCanvas()->clear(frameDescriptor.clearColor); m_grContext->resetContext(); m_skSurface->getCanvas()->save(); } void onEnd(std::vector* pixelData) final { m_skSurface->getCanvas()->restore(); m_skSurface->flush(); } void flushPLSContext(RenderTarget* offscreenRenderTarget) final {} private: SkiaFactory m_factory; const sk_sp m_grContext; sk_sp m_skSurface; }; std::unique_ptr FiddleContext::MakeGLSkia() { return std::make_unique(); } #endif #endif