blob: e7bfbdac8a0cf169ec1bc5ccdb422b2753c61a16 [file] [log] [blame]
#include "opengl/opengl_renderer.hpp"
#include "opengl/opengl_render_path.hpp"
#include "opengl/opengl_render_paint.hpp"
#include "opengl_shaders.cpp"
#include <cassert>
using namespace rive;
GLuint createAndCompileShader(GLuint type, const char* source);
OpenGLRenderer::OpenGLRenderer() {}
OpenGLRenderer::~OpenGLRenderer()
{
glDeleteProgram(m_Program);
glDeleteShader(m_VertexShader);
glDeleteShader(m_FragmentShader);
glDeleteBuffers(1, &m_IndexBuffer);
glDeleteBuffers(1, &m_BlitBuffer);
glDeleteVertexArrays(1, &m_VertexArray);
}
bool OpenGLRenderer::initialize(void* data)
{
assert(m_VertexShader == 0 && m_FragmentShader == 0 && m_Program == 0);
m_VertexShader =
createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
if (m_VertexShader == 0)
{
return false;
}
m_FragmentShader =
createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
if (m_FragmentShader == 0)
{
return false;
}
m_Program = glCreateProgram();
glAttachShader(m_Program, m_VertexShader);
glAttachShader(m_Program, m_FragmentShader);
glLinkProgram(m_Program);
GLint isLinked = 0;
glGetProgramiv(m_Program, GL_LINK_STATUS, (int*)&isLinked);
if (isLinked == GL_FALSE)
{
GLint maxLength = 0;
glGetProgramiv(m_Program, GL_INFO_LOG_LENGTH, &maxLength);
std::vector<GLchar> infoLog(maxLength);
glGetProgramInfoLog(m_Program, maxLength, &maxLength, &infoLog[0]);
fprintf(stderr, "Failed to link program %s\n", &infoLog[0]);
return false;
}
// Create index buffer which we'll grow and populate as necessary.
glGenBuffers(1, &m_IndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBuffer);
// Create vertex buffer for blitting to full viewport coordinates.
float blitBuffer[8] = {
-1.0f,
1.0f,
1.0f,
1.0f,
1.0f,
-1.0f,
-1.0f,
-1.0f,
};
glGenBuffers(1, &m_BlitBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_BlitBuffer);
glBufferData(
GL_ARRAY_BUFFER, 8 * sizeof(float), &blitBuffer[0], GL_STATIC_DRAW);
// Two triangles for bounds.
m_Indices.emplace_back(0);
m_Indices.emplace_back(1);
m_Indices.emplace_back(2);
m_Indices.emplace_back(2);
m_Indices.emplace_back(3);
m_Indices.emplace_back(0);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
m_Indices.size() * sizeof(unsigned short),
&m_Indices[0],
GL_STATIC_DRAW);
glGenVertexArrays(1, &m_VertexArray);
glBindVertexArray(m_VertexArray);
glUseProgram(m_Program);
m_ProjectionUniformIndex = glGetUniformLocation(m_Program, "projection");
m_TransformUniformIndex = glGetUniformLocation(m_Program, "transform");
m_FillTypeUniformIndex = glGetUniformLocation(m_Program, "fillType");
m_StopCountUniformIndex = glGetUniformLocation(m_Program, "count");
m_StopColorsUniformIndex = glGetUniformLocation(m_Program, "colors");
m_StopsUniformIndex = glGetUniformLocation(m_Program, "stops");
m_ColorUniformIndex = glGetUniformLocation(m_Program, "color");
m_GradientPositionUniformIndex =
glGetUniformLocation(m_Program, "position");
m_ShapeTransformUniformIndex =
glGetUniformLocation(m_Program, "localTransform");
float projection[16] = {0.0f};
orthographicProjection(projection, 0.0f, 800, 800, 0.0f, 0.0f, 1.0f);
modelViewProjection(projection);
return true;
}
void OpenGLRenderer::drawPath(RenderPath* path, RenderPaint* paint)
{
auto glPaint = static_cast<OpenGLRenderPaint*>(paint);
// if (glPaint->style() == RenderPaintStyle::stroke || !glPaint->doesDraw())
if (!glPaint->doesDraw())
{
return;
}
bool needsStencil = glPaint->style() == RenderPaintStyle::fill;
glColorMask(false, false, false, false);
// Set fill type to 0 so we don't perform any gradient fragment calcs.
glUniform1i(fillTypeUniformIndex(), 0);
if (isClippingDirty())
{
if (m_IsClipping)
{
// Clear previous clip.
glStencilMask(0xFF);
glClear(GL_STENCIL_BUFFER_BIT);
// TODO: instead of clearing the entire buffer, as we clip we could
// compute the combined clipping area set and clear that here.
}
auto clipLength = m_ClipPaths.size();
if (clipLength > 0)
{
m_IsClipping = true;
SubPath& firstClipPath = m_ClipPaths[0];
glStencilMask(0xFF);
glStencilFunc(GL_ALWAYS, 0x0, 0xFF);
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
static_cast<OpenGLRenderPath*>(firstClipPath.path())
->stencil(this, firstClipPath.transform());
// Fail when not equal to 0 and replace with 0x80 (mark high bit as
// included in clip). Require stencil mask (write mask) of 0xFF and
// stencil func mask of 0x7F such that the comparison looks for 0
// but write 0x80.
glStencilMask(0xFF);
glStencilFunc(GL_NOTEQUAL, 0x80, 0x7F);
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
glBindBuffer(GL_ARRAY_BUFFER, m_BlitBuffer);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * 4, (void*)0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBuffer);
float m4[16] = {1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0};
glUniformMatrix4fv(transformUniformIndex(), 1, GL_FALSE, m4);
glUniformMatrix4fv(m_ProjectionUniformIndex, 1, GL_FALSE, m4);
// Draw bounds.
glDrawElements(GL_TRIANGLES, 2 * 3, GL_UNSIGNED_SHORT, (void*)(0));
glUniformMatrix4fv(
m_ProjectionUniformIndex, 1, GL_FALSE, m_ModelViewProjection);
for (int i = 1; i < clipLength; i++)
// for (int i = 1; i < 0; i++)
{
// When already clipping we want to write only to the last/lower
// 7 bits as our high 8th bit is used to mark clipping
// inclusion.
glStencilMask(0x7F);
// Pass only if that 8th bit is set. This allows us to write our
// new winding into the lower 7 bits.
glStencilFunc(GL_EQUAL, 0x80, 0x80);
SubPath& nextClipPath = m_ClipPaths[i];
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
static_cast<OpenGLRenderPath*>(nextClipPath.path())
->stencil(this, nextClipPath.transform());
// Fail when not equal to 0 and replace with 0x80 (mark high bit
// as included in clip). Require stencil mask (write mask) of
// 0xFF and stencil func mask of 0x7F such that the comparison
// looks for 0 but write 0x80.
glStencilMask(0xFF);
glStencilFunc(GL_NOTEQUAL, 0x80, 0x7F);
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
glBindBuffer(GL_ARRAY_BUFFER, m_BlitBuffer);
glEnableVertexAttribArray(0);
glVertexAttribPointer(
0, 2, GL_FLOAT, GL_FALSE, 2 * 4, (void*)0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBuffer);
glUniformMatrix4fv(transformUniformIndex(), 1, GL_FALSE, m4);
glUniformMatrix4fv(m_ProjectionUniformIndex, 1, GL_FALSE, m4);
// Draw bounds.
glDrawElements(
GL_TRIANGLES, 2 * 3, GL_UNSIGNED_SHORT, (void*)(0));
glUniformMatrix4fv(m_ProjectionUniformIndex,
1,
GL_FALSE,
m_ModelViewProjection);
}
}
else
{
m_IsClipping = false;
}
}
auto glPath = static_cast<OpenGLRenderPath*>(path);
if (needsStencil)
{
// Set up stencil buffer.
if (m_IsClipping)
{
glStencilMask(0x7F);
glStencilFunc(GL_EQUAL, 0x80, 0x80);
}
else
{
glStencilMask(0xFF);
glStencilFunc(GL_ALWAYS, 0x0, 0xFF);
}
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
auto xform = transform();
glPath->stencil(this, xform);
glColorMask(true, true, true, true);
glStencilFunc(GL_NOTEQUAL, 0, m_IsClipping ? 0x7F : 0xFF);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
}
else
{
if (m_IsClipping)
{
glStencilMask(0x7F);
glStencilFunc(GL_EQUAL, 0x80, 0x80);
}
else
{
glStencilMask(0xFF);
glStencilFunc(GL_ALWAYS, 0x0, 0xFF);
}
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_KEEP);
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP);
glColorMask(true, true, true, true);
// glStencilFunc(GL_ALWAYS, 0x0, 0xFF);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
}
glPaint->draw(this, transform(), glPath);
// glPath->cover(this, transform());
}
void OpenGLRenderer::startFrame()
{
LowLevelRenderer::startFrame();
glUseProgram(m_Program);
glEnableVertexAttribArray(0);
glUniformMatrix4fv(
m_ProjectionUniformIndex, 1, GL_FALSE, m_ModelViewProjection);
glEnable(GL_STENCIL_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void OpenGLRenderer::endFrame() {}
RenderPaint* OpenGLRenderer::makeRenderPaint()
{
return new OpenGLRenderPaint();
}
RenderPath* OpenGLRenderer::makeRenderPath() { return new OpenGLRenderPath(); }
void OpenGLRenderer::updateIndexBuffer(std::size_t contourLength)
{
if (contourLength < 2)
{
return;
}
auto edgeCount = (m_Indices.size() - 6) / 3;
auto targetEdgeCount = contourLength - 2;
if (edgeCount < targetEdgeCount)
{
while (edgeCount < targetEdgeCount)
{
m_Indices.push_back(3);
m_Indices.push_back(edgeCount + 4);
m_Indices.push_back(edgeCount + 5);
edgeCount++;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
m_Indices.size() * sizeof(unsigned short),
&m_Indices[0],
GL_STATIC_DRAW);
}
}
GLuint createAndCompileShader(GLuint type, const char* source)
{
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
GLint isCompiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
if (isCompiled == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
std::vector<GLchar> infoLog(maxLength);
glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]);
fprintf(stderr, "Failed to compile shader %s\n", &infoLog[0]);
glDeleteShader(shader);
return 0;
}
return shader;
}