blob: 70c0e0100758860d735ea2a06888aad6622ebf2f [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#include "rive/pls/gl/pls_render_context_gl_impl.hpp"
#include "buffer_ring_gl.hpp"
#include "gl_utils.hpp"
#include "pls_path.hpp"
#include "pls_paint.hpp"
#include <sstream>
#include "../out/obj/generated/advanced_blend.glsl.hpp"
#include "../out/obj/generated/color_ramp.glsl.hpp"
#include "../out/obj/generated/common.glsl.hpp"
#include "../out/obj/generated/draw.glsl.hpp"
#include "../out/obj/generated/tessellate.glsl.hpp"
// Offset all texture indices by 1 so we, and others who share our GL context, can use GL_TEXTURE0
// as a scratch texture index.
constexpr static int kGLTexIdxOffset = 1;
namespace rive::pls
{
#ifdef RIVE_WASM
EM_JS(void, set_provoking_vertex_webgl, (GLenum convention), {
const ext = Module["ctx"].getExtension("WEBGL_provoking_vertex");
if (ext)
{
ext.provokingVertexWEBGL(convention);
}
});
#endif
PLSRenderContextGLImpl::PLSRenderContextGLImpl(const PlatformFeatures& platformFeatures,
GLExtensions extensions,
std::unique_ptr<PLSImpl> plsImpl) :
PLSRenderContextBufferRingImpl(platformFeatures),
m_extensions(extensions),
m_plsImpl(std::move(plsImpl))
{
m_shaderVersionString[kShaderVersionStringBuffSize - 1] = '\0';
#ifdef _WIN32
strcpy_s(m_shaderVersionString, kShaderVersionStringBuffSize, "#version 300 es\n");
#else
strncpy(m_shaderVersionString, "#version 300 es\n", kShaderVersionStringBuffSize - 1);
#endif
#ifdef RIVE_DESKTOP_GL
if (!GLAD_GL_version_es && GLAD_IS_GL_VERSION_AT_LEAST(4, 0))
{
snprintf(m_shaderVersionString,
kShaderVersionStringBuffSize,
"#version %d%d0\n",
GLAD_GL_version_major,
GLAD_GL_version_minor);
}
#endif
m_colorRampProgram = glCreateProgram();
const char* colorRampSources[] = {glsl::common, glsl::color_ramp};
glutils::CompileAndAttachShader(m_colorRampProgram,
GL_VERTEX_SHADER,
nullptr,
0,
colorRampSources,
2,
m_extensions,
m_shaderVersionString);
glutils::CompileAndAttachShader(m_colorRampProgram,
GL_FRAGMENT_SHADER,
nullptr,
0,
colorRampSources,
2,
m_extensions,
m_shaderVersionString);
glutils::LinkProgram(m_colorRampProgram);
glUniformBlockBinding(m_colorRampProgram,
glGetUniformBlockIndex(m_colorRampProgram, GLSL_Uniforms),
0);
glGenVertexArrays(1, &m_colorRampVAO);
bindVAO(m_colorRampVAO);
glEnableVertexAttribArray(0);
glVertexAttribDivisor(0, 1);
glGenFramebuffers(1, &m_colorRampFBO);
m_tessellateProgram = glCreateProgram();
const char* tessellateSources[] = {glsl::common, glsl::tessellate};
glutils::CompileAndAttachShader(m_tessellateProgram,
GL_VERTEX_SHADER,
nullptr,
0,
tessellateSources,
2,
m_extensions,
m_shaderVersionString);
glutils::CompileAndAttachShader(m_tessellateProgram,
GL_FRAGMENT_SHADER,
nullptr,
0,
tessellateSources,
2,
m_extensions,
m_shaderVersionString);
glutils::LinkProgram(m_tessellateProgram);
bindProgram(m_tessellateProgram);
glUniformBlockBinding(m_tessellateProgram,
glGetUniformBlockIndex(m_tessellateProgram, GLSL_Uniforms),
0);
glUniform1i(glGetUniformLocation(m_tessellateProgram, GLSL_pathTexture),
kGLTexIdxOffset + kPathTextureIdx);
glUniform1i(glGetUniformLocation(m_tessellateProgram, GLSL_contourTexture),
kGLTexIdxOffset + kContourTextureIdx);
glGenVertexArrays(1, &m_tessellateVAO);
bindVAO(m_tessellateVAO);
for (int i = 0; i < 4; ++i)
{
glEnableVertexAttribArray(i);
// Draw two instances per TessVertexSpan: one normal and one optional reflection.
glVertexAttribDivisor(i, 2);
}
glGenFramebuffers(1, &m_tessellateFBO);
glGenVertexArrays(1, &m_drawVAO);
bindVAO(m_drawVAO);
PatchVertex patchVertices[kPatchVertexBufferCount];
uint16_t patchIndices[kPatchIndexBufferCount];
GeneratePatchBufferData(patchVertices, patchIndices);
glGenBuffers(1, &m_patchVerticesBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_patchVerticesBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(patchVertices), patchVertices, GL_STATIC_DRAW);
glGenBuffers(1, &m_patchIndicesBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_patchIndicesBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(patchIndices), patchIndices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(PatchVertex), nullptr);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1,
4,
GL_FLOAT,
GL_FALSE,
sizeof(PatchVertex),
reinterpret_cast<void*>(sizeof(float) * 4));
glGenVertexArrays(1, &m_interiorTrianglesVAO);
bindVAO(m_interiorTrianglesVAO);
glEnableVertexAttribArray(0);
glFrontFace(GL_CW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
// ANGLE_shader_pixel_local_storage doesn't allow dither.
glDisable(GL_DITHER);
// D3D and Metal both have a provoking vertex convention of "first" for flat varyings, and it's
// very costly for ANGLE to implement the OpenGL convention of "last" on these backends. To
// workaround this, ANGLE provides the ANGLE_provoking_vertex extension. When this extension is
// present, we can just set the provoking vertex to "first" and trust that it will be fast.
#ifdef RIVE_WASM
set_provoking_vertex_webgl(GL_FIRST_VERTEX_CONVENTION_WEBGL);
#elif defined(RIVE_DESKTOP_GL)
if (m_extensions.ANGLE_provoking_vertex)
{
glProvokingVertexANGLE(GL_FIRST_VERTEX_CONVENTION_ANGLE);
}
#endif
}
PLSRenderContextGLImpl::~PLSRenderContextGLImpl()
{
glDeleteProgram(m_colorRampProgram);
glDeleteVertexArrays(1, &m_colorRampVAO);
glDeleteFramebuffers(1, &m_colorRampFBO);
glDeleteTextures(1, &m_gradientTexture);
glDeleteProgram(m_tessellateProgram);
glDeleteVertexArrays(1, &m_tessellateVAO);
glDeleteFramebuffers(1, &m_tessellateFBO);
glDeleteTextures(1, &m_tessVertexTexture);
glDeleteVertexArrays(1, &m_drawVAO);
glDeleteBuffers(1, &m_patchVerticesBuffer);
glDeleteBuffers(1, &m_patchIndicesBuffer);
}
std::unique_ptr<BufferRingImpl> PLSRenderContextGLImpl::makeVertexBufferRing(size_t capacity,
size_t itemSizeInBytes)
{
return std::make_unique<BufferGL>(GL_ARRAY_BUFFER, capacity, itemSizeInBytes);
}
std::unique_ptr<TexelBufferRing> PLSRenderContextGLImpl::makeTexelBufferRing(
TexelBufferRing::Format format,
size_t widthInItems,
size_t height,
size_t texelsPerItem,
int textureIdx,
TexelBufferRing::Filter filter)
{
return std::make_unique<TexelBufferGL>(format,
widthInItems,
height,
texelsPerItem,
GL_TEXTURE0 + kGLTexIdxOffset + textureIdx,
filter);
}
std::unique_ptr<BufferRingImpl> PLSRenderContextGLImpl::makePixelUnpackBufferRing(
size_t capacity,
size_t itemSizeInBytes)
{
return std::make_unique<BufferGL>(GL_PIXEL_UNPACK_BUFFER, capacity, itemSizeInBytes);
}
std::unique_ptr<BufferRingImpl> PLSRenderContextGLImpl::makeUniformBufferRing(size_t sizeInBytes)
{
return std::make_unique<BufferGL>(GL_UNIFORM_BUFFER, 1, sizeInBytes);
}
void PLSRenderContextGLImpl::allocateGradientTexture(size_t height)
{
glDeleteTextures(1, &m_gradientTexture);
glGenTextures(1, &m_gradientTexture);
glActiveTexture(GL_TEXTURE0 + kGLTexIdxOffset + kGradTextureIdx);
glBindTexture(GL_TEXTURE_2D, m_gradientTexture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kGradTextureWidth, height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, m_colorRampFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
m_gradientTexture,
0);
}
void PLSRenderContextGLImpl::allocateTessellationTexture(size_t height)
{
glDeleteTextures(1, &m_tessVertexTexture);
glGenTextures(1, &m_tessVertexTexture);
glActiveTexture(GL_TEXTURE0 + kGLTexIdxOffset + kTessVertexTextureIdx);
glBindTexture(GL_TEXTURE_2D, m_tessVertexTexture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32UI, kTessTextureWidth, height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, m_tessellateFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
m_tessVertexTexture,
0);
}
// Wraps a compiled GL shader of draw.glsl, either vertex or fragment, with a specific set of
// features enabled via #define. The set of features to enable is dictated by ShaderFeatures.
class PLSRenderContextGLImpl::DrawShader
{
public:
DrawShader(const DrawShader&) = delete;
DrawShader& operator=(const DrawShader&) = delete;
DrawShader(PLSRenderContextGLImpl* context,
GLenum shaderType,
DrawType drawType,
const ShaderFeatures& shaderFeatures)
{
auto sourceType =
shaderType == GL_VERTEX_SHADER ? SourceType::vertexOnly : SourceType::wholeProgram;
std::vector<const char*> defines;
defines.push_back(context->m_plsImpl->shaderDefineName());
uint64_t shaderFeatureDefines = shaderFeatures.getPreprocessorDefines(sourceType);
if (drawType == DrawType::interiorTriangulation)
{
defines.push_back(GLSL_DRAW_INTERIOR_TRIANGLES);
}
if (shaderFeatureDefines & ShaderFeatures::PreprocessorDefines::ENABLE_ADVANCED_BLEND)
{
defines.push_back(GLSL_ENABLE_ADVANCED_BLEND);
}
if (shaderFeatureDefines & ShaderFeatures::PreprocessorDefines::ENABLE_PATH_CLIPPING)
{
defines.push_back(GLSL_ENABLE_PATH_CLIPPING);
}
if (shaderFeatureDefines & ShaderFeatures::PreprocessorDefines::ENABLE_EVEN_ODD)
{
defines.push_back(GLSL_ENABLE_EVEN_ODD);
}
if (shaderFeatureDefines & ShaderFeatures::PreprocessorDefines::ENABLE_HSL_BLEND_MODES)
{
defines.push_back(GLSL_ENABLE_HSL_BLEND_MODES);
}
std::vector<const char*> sources;
sources.push_back(glsl::common);
if (sourceType != SourceType::vertexOnly)
{
if (shaderFeatures.programFeatures.blendTier > BlendTier::srcOver)
{
sources.push_back(glsl::advanced_blend);
}
}
if (context->platformFeatures().avoidFlatVaryings)
{
sources.push_back("#define " GLSL_OPTIONALLY_FLAT "\n");
}
else
{
sources.push_back("#define " GLSL_OPTIONALLY_FLAT " flat\n");
}
sources.push_back(glsl::draw);
m_id = glutils::CompileShader(shaderType,
defines.data(),
defines.size(),
sources.data(),
sources.size(),
context->m_extensions,
context->m_shaderVersionString);
}
~DrawShader() { glDeleteShader(m_id); }
GLuint id() const { return m_id; }
private:
GLuint m_id;
};
PLSRenderContextGLImpl::DrawProgram::DrawProgram(PLSRenderContextGLImpl* context,
DrawType drawType,
const ShaderFeatures& shaderFeatures)
{
m_id = glCreateProgram();
// Not every vertex shader is unique. Cache them by just the vertex features and reuse when
// possible.
uint32_t vertexShaderKey = ShaderUniqueKey(SourceType::vertexOnly, drawType, shaderFeatures);
const DrawShader& vertexShader =
context->m_vertexShaders
.try_emplace(vertexShaderKey, context, GL_VERTEX_SHADER, drawType, shaderFeatures)
.first->second;
glAttachShader(m_id, vertexShader.id());
// Every fragment shader is unique.
DrawShader fragmentShader(context, GL_FRAGMENT_SHADER, drawType, shaderFeatures);
glAttachShader(m_id, fragmentShader.id());
glutils::LinkProgram(m_id);
context->bindProgram(m_id);
glUniformBlockBinding(m_id, glGetUniformBlockIndex(m_id, GLSL_Uniforms), 0);
glUniform1i(glGetUniformLocation(m_id, GLSL_tessVertexTexture),
kGLTexIdxOffset + kTessVertexTextureIdx);
glUniform1i(glGetUniformLocation(m_id, GLSL_pathTexture), kGLTexIdxOffset + kPathTextureIdx);
glUniform1i(glGetUniformLocation(m_id, GLSL_contourTexture),
kGLTexIdxOffset + kContourTextureIdx);
glUniform1i(glGetUniformLocation(m_id, GLSL_gradTexture), kGLTexIdxOffset + kGradTextureIdx);
if (!context->m_extensions.ANGLE_base_vertex_base_instance_shader_builtin)
{
m_baseInstancePolyfillLocation = glGetUniformLocation(m_id, GLSL_baseInstancePolyfill);
}
}
PLSRenderContextGLImpl::DrawProgram::~DrawProgram() { glDeleteProgram(m_id); }
static GLuint gl_buffer_id(const BufferRingImpl* bufferRing)
{
return static_cast<const BufferGL*>(bufferRing)->submittedBufferID();
}
void PLSRenderContextGLImpl::flush(const PLSRenderContext::FlushDescriptor& desc)
{
// All programs use the same set of per-flush uniforms.
glBindBufferBase(GL_UNIFORM_BUFFER, 0, gl_buffer_id(uniformBufferRing()));
// Render the complex color ramps into the gradient texture.
if (desc.complexGradSpanCount > 0)
{
glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id(gradSpanBufferRing()));
bindVAO(m_colorRampVAO);
glVertexAttribIPointer(0, 4, GL_UNSIGNED_INT, 0, nullptr);
glViewport(0, desc.complexGradRowsTop, kGradTextureWidth, desc.complexGradRowsHeight);
glBindFramebuffer(GL_FRAMEBUFFER, m_colorRampFBO);
bindProgram(m_colorRampProgram);
GLenum colorAttachment0 = GL_COLOR_ATTACHMENT0;
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, &colorAttachment0);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, desc.complexGradSpanCount);
}
// Copy the simple color ramps to the gradient texture.
if (desc.simpleGradTexelsHeight > 0)
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_buffer_id(simpleColorRampsBufferRing()));
glActiveTexture(GL_TEXTURE0 + kGLTexIdxOffset + kGradTextureIdx);
glTexSubImage2D(GL_TEXTURE_2D,
0,
0,
0,
desc.simpleGradTexelsWidth,
desc.simpleGradTexelsHeight,
GL_RGBA,
GL_UNSIGNED_BYTE,
nullptr);
}
// Tessellate all curves into vertices in the tessellation texture.
if (desc.tessVertexSpanCount > 0)
{
glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id(tessSpanBufferRing()));
bindVAO(m_tessellateVAO);
for (uintptr_t i = 0; i < 3; ++i)
{
glVertexAttribPointer(i,
4,
GL_FLOAT,
GL_FALSE,
sizeof(TessVertexSpan),
reinterpret_cast<const void*>(i * 4 * 4));
}
glVertexAttribIPointer(3,
4,
GL_UNSIGNED_INT,
sizeof(TessVertexSpan),
reinterpret_cast<const void*>(offsetof(TessVertexSpan, x0x1)));
glViewport(0, 0, kTessTextureWidth, desc.tessDataHeight);
glBindFramebuffer(GL_FRAMEBUFFER, m_tessellateFBO);
bindProgram(m_tessellateProgram);
GLenum colorAttachment0 = GL_COLOR_ATTACHMENT0;
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, &colorAttachment0);
// Draw two instances per TessVertexSpan: one normal and one optional reflection.
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, desc.tessVertexSpanCount * 2);
}
// Compile the draw programs before activating pixel local storage.
// (ANGLE_shader_pixel_local_storage doesn't allow shader compilation while active.)
for (const Draw& draw : *desc.drawList)
{
// Compile the draw program before activating pixel local storage.
// Cache specific compilations of draw.glsl by ShaderFeatures.
uint32_t fragmentShaderKey =
ShaderUniqueKey(SourceType::wholeProgram, draw.drawType, draw.shaderFeatures);
m_drawPrograms.try_emplace(fragmentShaderKey, this, draw.drawType, draw.shaderFeatures);
}
// Bind the currently-submitted buffer in the triangleBufferRing to its vertex array.
if (desc.hasTriangleVertices)
{
bindVAO(m_interiorTrianglesVAO);
glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id(triangleBufferRing()));
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
}
auto renderTarget = static_cast<const PLSRenderTargetGL*>(desc.renderTarget);
glViewport(0, 0, renderTarget->width(), renderTarget->height());
#ifdef RIVE_DESKTOP_GL
if (m_extensions.ANGLE_polygon_mode && desc.wireframe)
{
glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_LINE_ANGLE);
glLineWidth(2);
}
#endif
m_plsImpl->activatePixelLocalStorage(this, desc);
// Execute the DrawList.
for (const Draw& draw : *desc.drawList)
{
if (draw.vertexOrInstanceCount == 0)
{
continue;
}
uint32_t fragmentShaderKey =
ShaderUniqueKey(SourceType::wholeProgram, draw.drawType, draw.shaderFeatures);
const DrawProgram& drawProgram = m_drawPrograms.find(fragmentShaderKey)->second;
bindProgram(drawProgram.id());
switch (DrawType drawType = draw.drawType)
{
case DrawType::midpointFanPatches:
case DrawType::outerCurvePatches:
{
// Draw PLS patches that connect the tessellation vertices.
m_plsImpl->ensureRasterOrderingEnabled(true);
bindVAO(m_drawVAO);
uint32_t indexCount = PatchIndexCount(drawType);
uint32_t baseIndex = PatchBaseIndex(drawType);
void* indexOffset = reinterpret_cast<void*>(baseIndex * sizeof(uint16_t));
if (m_extensions.ANGLE_base_vertex_base_instance_shader_builtin)
{
glDrawElementsInstancedBaseInstanceEXT(GL_TRIANGLES,
indexCount,
GL_UNSIGNED_SHORT,
indexOffset,
draw.vertexOrInstanceCount,
draw.baseVertexOrInstance);
}
else
{
glUniform1i(drawProgram.baseInstancePolyfillLocation(),
draw.baseVertexOrInstance);
glDrawElementsInstanced(GL_TRIANGLES,
indexCount,
GL_UNSIGNED_SHORT,
indexOffset,
draw.vertexOrInstanceCount);
}
break;
}
case DrawType::interiorTriangulation:
// Draw generic triangles.
m_plsImpl->ensureRasterOrderingEnabled(false);
bindVAO(m_interiorTrianglesVAO);
glDrawArrays(GL_TRIANGLES, draw.baseVertexOrInstance, draw.vertexOrInstanceCount);
m_plsImpl->barrier();
break;
}
}
m_plsImpl->deactivatePixelLocalStorage(this);
#ifdef RIVE_DESKTOP_GL
if (m_extensions.ANGLE_polygon_mode && desc.wireframe)
{
glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_FILL_ANGLE);
}
#endif
}
void PLSRenderContextGLImpl::bindProgram(GLuint programID)
{
if (programID != m_boundProgramID)
{
glUseProgram(programID);
m_boundProgramID = programID;
}
}
void PLSRenderContextGLImpl::bindVAO(GLuint vao)
{
if (vao != m_boundVAO)
{
glBindVertexArray(vao);
m_boundVAO = vao;
}
}
std::unique_ptr<PLSRenderContextGLImpl> PLSRenderContextGLImpl::Make()
{
GLExtensions extensions{};
GLint extensionCount;
glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);
for (int i = 0; i < extensionCount; ++i)
{
auto* ext = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i));
if (strcmp(ext, "GL_ANGLE_base_vertex_base_instance_shader_builtin") == 0)
{
extensions.ANGLE_base_vertex_base_instance_shader_builtin = true;
}
if (strcmp(ext, "GL_ANGLE_shader_pixel_local_storage") == 0)
{
extensions.ANGLE_shader_pixel_local_storage = true;
}
else if (strcmp(ext, "GL_ANGLE_shader_pixel_local_storage_coherent") == 0)
{
extensions.ANGLE_shader_pixel_local_storage_coherent = true;
}
else if (strcmp(ext, "GL_ANGLE_provoking_vertex") == 0)
{
extensions.ANGLE_provoking_vertex = true;
}
else if (strcmp(ext, "GL_ANGLE_polygon_mode") == 0)
{
extensions.ANGLE_polygon_mode = true;
}
else if (strcmp(ext, "GL_ARM_shader_framebuffer_fetch") == 0)
{
extensions.ARM_shader_framebuffer_fetch = true;
}
else if (strcmp(ext, "GL_ARB_fragment_shader_interlock") == 0)
{
extensions.ARB_fragment_shader_interlock = true;
}
else if (strcmp(ext, "GL_EXT_base_instance") == 0)
{
extensions.EXT_base_instance = true;
}
else if (strcmp(ext, "GL_INTEL_fragment_shader_ordering") == 0)
{
extensions.INTEL_fragment_shader_ordering = true;
}
else if (strcmp(ext, "GL_EXT_shader_framebuffer_fetch") == 0)
{
extensions.EXT_shader_framebuffer_fetch = true;
}
else if (strcmp(ext, "GL_EXT_shader_pixel_local_storage") == 0)
{
extensions.EXT_shader_pixel_local_storage = true;
}
else if (strcmp(ext, "GL_QCOM_shader_framebuffer_fetch_noncoherent") == 0)
{
extensions.QCOM_shader_framebuffer_fetch_noncoherent = true;
}
}
#ifdef RIVE_DESKTOP_GL
// We implement some ES extensions with core Desktop GL in glad_custom.c.
if (GLAD_GL_ANGLE_base_vertex_base_instance_shader_builtin)
{
extensions.ANGLE_base_vertex_base_instance_shader_builtin = true;
}
if (GLAD_GL_ANGLE_polygon_mode)
{
extensions.ANGLE_polygon_mode = true;
}
if (GLAD_GL_EXT_base_instance)
{
extensions.EXT_base_instance = true;
}
#endif
PlatformFeatures platformFeatures;
GLenum rendererToken = GL_RENDERER;
#ifdef RIVE_WASM
if (emscripten_webgl_enable_extension(emscripten_webgl_get_current_context(),
"WEBGL_debug_renderer_info"))
{
rendererToken = GL_UNMASKED_RENDERER_WEBGL;
}
#endif
const char* rendererString = reinterpret_cast<const char*>(glGetString(rendererToken));
if (strstr(rendererString, "Apple") && strstr(rendererString, "Metal"))
{
// In Metal, non-flat varyings preserve their exact value if all vertices in the triangle
// emit the same value, and we also see a small (5-10%) improvement from not using flat
// varyings.
platformFeatures.avoidFlatVaryings = true;
}
if (strstr(rendererString, "Direct3D"))
{
// This extension is polyfilled on D3D anyway. Just don't use it so we can make sure to test
// our own fallback.
extensions.ANGLE_base_vertex_base_instance_shader_builtin = false;
}
#ifdef RIVE_GLES
loadGLESExtensions(extensions); // Android doesn't load extension functions for us.
if (extensions.EXT_shader_pixel_local_storage &&
(extensions.ARM_shader_framebuffer_fetch || extensions.EXT_shader_framebuffer_fetch))
{
return std::unique_ptr<PLSRenderContextGLImpl>(
new PLSRenderContextGLImpl(platformFeatures,
extensions,
MakePLSImplEXTNative(extensions)));
}
if (extensions.EXT_shader_framebuffer_fetch)
{
return std::unique_ptr<PLSRenderContextGLImpl>(
new PLSRenderContextGLImpl(platformFeatures,
extensions,
MakePLSImplFramebufferFetch(extensions)));
}
#endif
#ifdef RIVE_DESKTOP_GL
if (extensions.ANGLE_shader_pixel_local_storage_coherent)
{
return std::unique_ptr<PLSRenderContextGLImpl>(
new PLSRenderContextGLImpl(platformFeatures, extensions, MakePLSImplWebGL()));
}
if (extensions.ARB_fragment_shader_interlock || extensions.INTEL_fragment_shader_ordering)
{
return std::unique_ptr<PLSRenderContextGLImpl>(
new PLSRenderContextGLImpl(platformFeatures, extensions, MakePLSImplRWTexture()));
}
#endif
#ifdef RIVE_WASM
if (emscripten_webgl_enable_WEBGL_shader_pixel_local_storage(
emscripten_webgl_get_current_context()) &&
emscripten_webgl_shader_pixel_local_storage_is_coherent())
{
return std::unique_ptr<PLSRenderContextGLImpl>(
new PLSRenderContextGLImpl(platformFeatures, extensions, MakePLSImplWebGL()));
}
#endif
fprintf(stderr, "Pixel local storage is not supported.\n");
return nullptr;
}
} // namespace rive::pls