blob: 9c615a19ecfdcd044faea484e367684baf093f2e [file] [log] [blame]
#include "fiddle_context.hpp"
#ifdef RIVE_TOOLS_NO_GLFW
std::unique_ptr<FiddleContext> 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"
#ifdef RIVE_WEBGL
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
#endif
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
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 error has been generated"))
{
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<uint8_t>* 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);
}
}
protected:
virtual void onEnd(std::vector<uint8_t>* 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<GLADloadfunc>(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<RenderContextGLImpl>();
}
RenderTarget* renderTargetOrNull() final { return m_renderTarget.get(); }
void onSizeChanged(GLFWwindow* window,
int width,
int height,
uint32_t sampleCount) final
{
m_renderTarget =
make_rcp<FramebufferRenderTargetGL>(width, height, 0, sampleCount);
glViewport(0, 0, width, height);
}
std::unique_ptr<Renderer> makeRenderer(int width, int height) final
{
return std::make_unique<RiveRenderer>(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<uint8_t>* 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<RenderContext> m_renderContext;
rcp<RenderTargetGL> m_renderTarget;
};
std::unique_ptr<FiddleContext> FiddleContext::MakeGLPLS(
FiddleContextOptions options)
{
return std::make_unique<FiddleContextGL>(options);
}
#ifndef RIVE_SKIA
std::unique_ptr<FiddleContext> 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<Renderer> 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<SkiaRenderer>(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<uint8_t>* pixelData) final
{
m_skSurface->getCanvas()->restore();
m_skSurface->flush();
}
void flushPLSContext(RenderTarget* offscreenRenderTarget) final {}
private:
SkiaFactory m_factory;
const sk_sp<GrDirectContext> m_grContext;
sk_sp<SkSurface> m_skSurface;
};
std::unique_ptr<FiddleContext> FiddleContext::MakeGLSkia()
{
return std::make_unique<FiddleContextGLSkia>();
}
#endif
#endif