3902628e35
There is some logic in here for 2.0 as well, just as a "as long as I was looking at the specs", but only 1.0 is really supported. This seems to resolve the bug where some GPUs weren't advertising correctly that they had vertex array object support, by checking for both extension names (with and without "GL_" prefix) Of note, this saves about 18 Kb (5.5 Kb gzipped) of code size by compiling out the unneeded GLES checks/functionality. Bug: skia:8378 Change-Id: I773bf4dbf231b991051d2a9f640b8047a9010e7d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/203461 Commit-Queue: Kevin Lubick <kjlubick@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
484 lines
17 KiB
C++
484 lines
17 KiB
C++
/*
|
|
* Copyright 2017 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "GLTestAtlasTextRenderer.h"
|
|
#include "../gl/GLTestContext.h"
|
|
#include "SkBitmap.h"
|
|
#include "TestAtlasTextRenderer.h"
|
|
#include "gl/GrGLDefines.h"
|
|
#include "gl/GrGLUtil.h"
|
|
|
|
using sk_gpu_test::GLTestContext;
|
|
|
|
namespace {
|
|
|
|
class GLTestAtlasTextRenderer : public sk_gpu_test::TestAtlasTextRenderer {
|
|
public:
|
|
GLTestAtlasTextRenderer(std::unique_ptr<GLTestContext>);
|
|
|
|
void* createTexture(AtlasFormat, int width, int height) override;
|
|
|
|
void deleteTexture(void* textureHandle) override;
|
|
|
|
void setTextureData(void* textureHandle, const void* data, int x, int y, int width, int height,
|
|
size_t rowBytes) override;
|
|
|
|
void drawSDFGlyphs(void* targetHandle, void* textureHandle, const SDFVertex vertices[],
|
|
int quadCnt) override;
|
|
|
|
void* makeTargetHandle(int width, int height) override;
|
|
|
|
void targetDeleted(void* targetHandle) override;
|
|
|
|
SkBitmap readTargetHandle(void* targetHandle) override;
|
|
|
|
void clearTarget(void* targetHandle, uint32_t color) override;
|
|
|
|
bool initialized() const { return 0 != fProgram; }
|
|
|
|
private:
|
|
struct AtlasTexture {
|
|
GrGLuint fID;
|
|
AtlasFormat fFormat;
|
|
int fWidth;
|
|
int fHeight;
|
|
};
|
|
|
|
struct Target {
|
|
GrGLuint fFBOID;
|
|
GrGLuint fRBID;
|
|
int fWidth;
|
|
int fHeight;
|
|
};
|
|
|
|
std::unique_ptr<GLTestContext> fContext;
|
|
GrGLuint fProgram = 0;
|
|
GrGLint fDstScaleAndTranslateLocation = 0;
|
|
GrGLint fAtlasInvSizeLocation = 0;
|
|
GrGLint fSamplerLocation = 0;
|
|
};
|
|
|
|
#define callgl(NAME, ...) fContext->gl()->fFunctions.f##NAME(__VA_ARGS__)
|
|
#define checkgl() \
|
|
do { \
|
|
static constexpr auto line = __LINE__; \
|
|
auto error = fContext->gl()->fFunctions.fGetError(); \
|
|
if (error != GR_GL_NO_ERROR) { \
|
|
SkDebugf("GL ERROR: 0x%x, line %d\n", error, line); \
|
|
} \
|
|
} while (false)
|
|
|
|
GLTestAtlasTextRenderer::GLTestAtlasTextRenderer(std::unique_ptr<GLTestContext> context)
|
|
: fContext(std::move(context)) {
|
|
auto restore = fContext->makeCurrentAndAutoRestore();
|
|
|
|
// First check whether the GL is supported so we can avoid spammy failures on systems
|
|
// where the GL simply doesn't work with this class.
|
|
const char* versionStr = reinterpret_cast<const char*>(callgl(GetString, GR_GL_VERSION));
|
|
auto version = GrGLGetVersionFromString(versionStr);
|
|
auto standard = GrGLGetStandardInUseFromString(versionStr);
|
|
switch (standard) {
|
|
case kWebGL_GrGLStandard:
|
|
case kNone_GrGLStandard:
|
|
return;
|
|
case kGLES_GrGLStandard:
|
|
if (version < GR_GL_VER(3, 0)) {
|
|
return;
|
|
}
|
|
break;
|
|
case kGL_GrGLStandard: {
|
|
if (version < GR_GL_VER(4, 3)) {
|
|
return;
|
|
}
|
|
GrGLint profileMask;
|
|
callgl(GetIntegerv, GR_GL_CONTEXT_PROFILE_MASK, &profileMask);
|
|
if (profileMask & GR_GL_CONTEXT_CORE_PROFILE_BIT) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto vs = callgl(CreateShader, GR_GL_VERTEX_SHADER);
|
|
static constexpr char kGLVersionString[] = "#version 430 compatibility";
|
|
static constexpr char kGLESVersionString[] = "#version 300 es";
|
|
GrGLint lengths[2];
|
|
const GrGLchar* strings[2];
|
|
switch (fContext->gl()->fStandard) {
|
|
case kGL_GrGLStandard:
|
|
strings[0] = kGLVersionString;
|
|
lengths[0] = static_cast<GrGLint>(SK_ARRAY_COUNT(kGLVersionString)) - 1;
|
|
break;
|
|
case kGLES_GrGLStandard:
|
|
strings[0] = kGLESVersionString;
|
|
lengths[0] = static_cast<GrGLint>(SK_ARRAY_COUNT(kGLESVersionString)) - 1;
|
|
break;
|
|
default:
|
|
strings[0] = nullptr;
|
|
lengths[0] = 0;
|
|
break;
|
|
}
|
|
|
|
static constexpr const char kVS[] = R"(
|
|
uniform vec4 uDstScaleAndTranslate;
|
|
uniform vec2 uAtlasInvSize;
|
|
|
|
layout (location = 0) in vec3 inPosition;
|
|
layout (location = 1) in vec4 inColor;
|
|
layout (location = 2) in uvec2 inTextureCoords;
|
|
|
|
out vec2 vTexCoord;
|
|
out vec4 vColor;
|
|
out vec2 vIntTexCoord;
|
|
|
|
void main() {
|
|
vec2 intCoords;
|
|
// floor(vec2) doesn't seem to work on some ES devices.
|
|
intCoords.x = floor(float(inTextureCoords.x));
|
|
intCoords.y = floor(float(inTextureCoords.y));
|
|
vTexCoord = intCoords * uAtlasInvSize;
|
|
vIntTexCoord = intCoords;
|
|
vColor = inColor;
|
|
gl_Position = vec4(inPosition.x * uDstScaleAndTranslate.x + uDstScaleAndTranslate.y,
|
|
inPosition.y * uDstScaleAndTranslate.z + uDstScaleAndTranslate.w,
|
|
0.0, inPosition.z);
|
|
}
|
|
)";
|
|
strings[1] = kVS;
|
|
lengths[1] = SK_ARRAY_COUNT(kVS) - 1;
|
|
callgl(ShaderSource, vs, 2, strings, lengths);
|
|
callgl(CompileShader, vs);
|
|
GrGLint compileStatus;
|
|
callgl(GetShaderiv, vs, GR_GL_COMPILE_STATUS, &compileStatus);
|
|
if (compileStatus == GR_GL_FALSE) {
|
|
GrGLint logLength;
|
|
callgl(GetShaderiv, vs, GR_GL_INFO_LOG_LENGTH, &logLength);
|
|
std::unique_ptr<GrGLchar[]> log(new GrGLchar[logLength + 1]);
|
|
log[logLength] = '\0';
|
|
callgl(GetShaderInfoLog, vs, logLength, &logLength, log.get());
|
|
SkDebugf("Vertex Shader failed to compile\n%s", log.get());
|
|
callgl(DeleteShader, vs);
|
|
return;
|
|
}
|
|
|
|
auto fs = callgl(CreateShader, GR_GL_FRAGMENT_SHADER);
|
|
static constexpr const char kFS[] = R"(
|
|
uniform sampler2D uSampler;
|
|
|
|
in vec2 vTexCoord;
|
|
in vec4 vColor;
|
|
in vec2 vIntTexCoord;
|
|
|
|
layout (location = 0) out vec4 outColor;
|
|
|
|
void main() {
|
|
float sdfValue = texture(uSampler, vTexCoord).r;
|
|
float distance = 7.96875 * (sdfValue - 0.50196078431000002);
|
|
vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));
|
|
vec2 Jdx = dFdx(vIntTexCoord);
|
|
vec2 Jdy = dFdy(vIntTexCoord);
|
|
float dg_len2 = dot(dist_grad, dist_grad);
|
|
if (dg_len2 < 0.0001) {
|
|
dist_grad = vec2(0.7071, 0.7071);
|
|
} else {
|
|
dist_grad = dist_grad * inversesqrt(dg_len2);
|
|
}
|
|
vec2 grad = vec2(dist_grad.x * Jdx.x + dist_grad.y * Jdy.x,
|
|
dist_grad.x * Jdx.y + dist_grad.y * Jdy.y);
|
|
float afwidth = abs(0.65000000000000002 * length(grad));
|
|
float value = smoothstep(-afwidth, afwidth, distance);
|
|
outColor = value * vec4(vColor.rgb * vColor.a, vColor.a);
|
|
}
|
|
)";
|
|
strings[1] = kFS;
|
|
lengths[1] = SK_ARRAY_COUNT(kFS) - 1;
|
|
callgl(ShaderSource, fs, 2, strings, lengths);
|
|
callgl(CompileShader, fs);
|
|
callgl(GetShaderiv, fs, GR_GL_COMPILE_STATUS, &compileStatus);
|
|
if (compileStatus == GR_GL_FALSE) {
|
|
GrGLint logLength;
|
|
callgl(GetShaderiv, fs, GR_GL_INFO_LOG_LENGTH, &logLength);
|
|
std::unique_ptr<GrGLchar[]> log(new GrGLchar[logLength + 1]);
|
|
log[logLength] = '\0';
|
|
callgl(GetShaderInfoLog, fs, logLength, &logLength, log.get());
|
|
SkDebugf("Fragment Shader failed to compile\n%s", log.get());
|
|
callgl(DeleteShader, vs);
|
|
callgl(DeleteShader, fs);
|
|
return;
|
|
}
|
|
|
|
fProgram = callgl(CreateProgram);
|
|
if (!fProgram) {
|
|
callgl(DeleteShader, vs);
|
|
callgl(DeleteShader, fs);
|
|
return;
|
|
}
|
|
|
|
callgl(AttachShader, fProgram, vs);
|
|
callgl(AttachShader, fProgram, fs);
|
|
callgl(LinkProgram, fProgram);
|
|
GrGLint linkStatus;
|
|
callgl(GetProgramiv, fProgram, GR_GL_LINK_STATUS, &linkStatus);
|
|
if (linkStatus == GR_GL_FALSE) {
|
|
GrGLint logLength = 0;
|
|
callgl(GetProgramiv, vs, GR_GL_INFO_LOG_LENGTH, &logLength);
|
|
std::unique_ptr<GrGLchar[]> log(new GrGLchar[logLength + 1]);
|
|
log[logLength] = '\0';
|
|
callgl(GetProgramInfoLog, vs, logLength, &logLength, log.get());
|
|
SkDebugf("Program failed to link\n%s", log.get());
|
|
callgl(DeleteShader, vs);
|
|
callgl(DeleteShader, fs);
|
|
callgl(DeleteProgram, fProgram);
|
|
fProgram = 0;
|
|
return;
|
|
}
|
|
fDstScaleAndTranslateLocation = callgl(GetUniformLocation, fProgram, "uDstScaleAndTranslate");
|
|
fAtlasInvSizeLocation = callgl(GetUniformLocation, fProgram, "uAtlasInvSize");
|
|
fSamplerLocation = callgl(GetUniformLocation, fProgram, "uSampler");
|
|
if (fDstScaleAndTranslateLocation < 0 || fAtlasInvSizeLocation < 0 || fSamplerLocation < 0) {
|
|
callgl(DeleteShader, vs);
|
|
callgl(DeleteShader, fs);
|
|
callgl(DeleteProgram, fProgram);
|
|
fProgram = 0;
|
|
}
|
|
|
|
checkgl();
|
|
}
|
|
|
|
inline bool atlas_format_to_gl_types(SkAtlasTextRenderer::AtlasFormat format,
|
|
GrGLenum* internalFormat, GrGLenum* externalFormat,
|
|
GrGLenum* type) {
|
|
switch (format) {
|
|
case SkAtlasTextRenderer::AtlasFormat::kA8:
|
|
*internalFormat = GR_GL_R8;
|
|
*externalFormat = GR_GL_RED;
|
|
*type = GR_GL_UNSIGNED_BYTE;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline int atlas_format_bytes_per_pixel(SkAtlasTextRenderer::AtlasFormat format) {
|
|
switch (format) {
|
|
case SkAtlasTextRenderer::AtlasFormat::kA8:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void* GLTestAtlasTextRenderer::createTexture(AtlasFormat format, int width, int height) {
|
|
GrGLenum internalFormat;
|
|
GrGLenum externalFormat;
|
|
GrGLenum type;
|
|
if (!atlas_format_to_gl_types(format, &internalFormat, &externalFormat, &type)) {
|
|
return nullptr;
|
|
}
|
|
auto restore = fContext->makeCurrentAndAutoRestore();
|
|
|
|
GrGLuint id;
|
|
callgl(GenTextures, 1, &id);
|
|
if (!id) {
|
|
return nullptr;
|
|
}
|
|
|
|
callgl(BindTexture, GR_GL_TEXTURE_2D, id);
|
|
callgl(TexImage2D, GR_GL_TEXTURE_2D, 0, internalFormat, width, height, 0, externalFormat, type,
|
|
nullptr);
|
|
checkgl();
|
|
|
|
AtlasTexture* atlas = new AtlasTexture;
|
|
atlas->fID = id;
|
|
atlas->fFormat = format;
|
|
atlas->fWidth = width;
|
|
atlas->fHeight = height;
|
|
return atlas;
|
|
}
|
|
|
|
void GLTestAtlasTextRenderer::deleteTexture(void* textureHandle) {
|
|
auto restore = fContext->makeCurrentAndAutoRestore();
|
|
|
|
auto* atlasTexture = reinterpret_cast<const AtlasTexture*>(textureHandle);
|
|
|
|
callgl(DeleteTextures, 1, &atlasTexture->fID);
|
|
checkgl();
|
|
|
|
delete atlasTexture;
|
|
}
|
|
|
|
void GLTestAtlasTextRenderer::setTextureData(void* textureHandle, const void* data, int x, int y,
|
|
int width, int height, size_t rowBytes) {
|
|
auto restore = fContext->makeCurrentAndAutoRestore();
|
|
|
|
auto atlasTexture = reinterpret_cast<const AtlasTexture*>(textureHandle);
|
|
|
|
GrGLenum internalFormat;
|
|
GrGLenum externalFormat;
|
|
GrGLenum type;
|
|
if (!atlas_format_to_gl_types(atlasTexture->fFormat, &internalFormat, &externalFormat, &type)) {
|
|
return;
|
|
}
|
|
int bpp = atlas_format_bytes_per_pixel(atlasTexture->fFormat);
|
|
GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp);
|
|
if (static_cast<size_t>(rowLength * bpp) != rowBytes) {
|
|
return;
|
|
}
|
|
callgl(PixelStorei, GR_GL_UNPACK_ALIGNMENT, 1);
|
|
callgl(PixelStorei, GR_GL_UNPACK_ROW_LENGTH, rowLength);
|
|
callgl(BindTexture, GR_GL_TEXTURE_2D, atlasTexture->fID);
|
|
callgl(TexSubImage2D, GR_GL_TEXTURE_2D, 0, x, y, width, height, externalFormat, type, data);
|
|
checkgl();
|
|
}
|
|
|
|
void GLTestAtlasTextRenderer::drawSDFGlyphs(void* targetHandle, void* textureHandle,
|
|
const SDFVertex vertices[], int quadCnt) {
|
|
auto restore = fContext->makeCurrentAndAutoRestore();
|
|
|
|
auto target = reinterpret_cast<const Target*>(targetHandle);
|
|
auto atlas = reinterpret_cast<const AtlasTexture*>(textureHandle);
|
|
|
|
callgl(UseProgram, fProgram);
|
|
|
|
callgl(ActiveTexture, GR_GL_TEXTURE0);
|
|
callgl(BindTexture, GR_GL_TEXTURE_2D, atlas->fID);
|
|
callgl(TexParameteri, GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MAG_FILTER, GR_GL_LINEAR);
|
|
callgl(TexParameteri, GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MIN_FILTER, GR_GL_LINEAR);
|
|
|
|
float uniformScaleAndTranslate[4] = {2.f / target->fWidth, -1.f, 2.f / target->fHeight, -1.f};
|
|
callgl(Uniform4fv, fDstScaleAndTranslateLocation, 1, uniformScaleAndTranslate);
|
|
callgl(Uniform2f, fAtlasInvSizeLocation, 1.f / atlas->fWidth, 1.f / atlas->fHeight);
|
|
callgl(Uniform1i, fSamplerLocation, 0);
|
|
|
|
callgl(BindFramebuffer, GR_GL_FRAMEBUFFER, target->fFBOID);
|
|
callgl(Viewport, 0, 0, target->fWidth, target->fHeight);
|
|
|
|
callgl(Enable, GR_GL_BLEND);
|
|
callgl(BlendFunc, GR_GL_ONE, GR_GL_ONE_MINUS_SRC_ALPHA);
|
|
callgl(Disable, GR_GL_DEPTH_TEST);
|
|
|
|
callgl(BindVertexArray, 0);
|
|
callgl(BindBuffer, GR_GL_ARRAY_BUFFER, 0);
|
|
callgl(BindBuffer, GR_GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
callgl(VertexAttribPointer, 0, 3, GR_GL_FLOAT, GR_GL_FALSE, sizeof(SDFVertex), vertices);
|
|
size_t colorOffset = 3 * sizeof(float);
|
|
callgl(VertexAttribPointer, 1, 4, GR_GL_UNSIGNED_BYTE, GR_GL_TRUE, sizeof(SDFVertex),
|
|
reinterpret_cast<const char*>(vertices) + colorOffset);
|
|
size_t texOffset = colorOffset + sizeof(uint32_t);
|
|
callgl(VertexAttribIPointer, 2, 2, GR_GL_UNSIGNED_SHORT, sizeof(SDFVertex),
|
|
reinterpret_cast<const char*>(vertices) + texOffset);
|
|
callgl(EnableVertexAttribArray, 0);
|
|
callgl(EnableVertexAttribArray, 1);
|
|
callgl(EnableVertexAttribArray, 2);
|
|
|
|
std::unique_ptr<uint16_t[]> indices(new uint16_t[quadCnt * 6]);
|
|
for (int q = 0; q < quadCnt; ++q) {
|
|
indices[q * 6 + 0] = 0 + 4 * q;
|
|
indices[q * 6 + 1] = 1 + 4 * q;
|
|
indices[q * 6 + 2] = 2 + 4 * q;
|
|
indices[q * 6 + 3] = 2 + 4 * q;
|
|
indices[q * 6 + 4] = 1 + 4 * q;
|
|
indices[q * 6 + 5] = 3 + 4 * q;
|
|
}
|
|
callgl(DrawElements, GR_GL_TRIANGLES, 6 * quadCnt, GR_GL_UNSIGNED_SHORT, indices.get());
|
|
checkgl();
|
|
}
|
|
|
|
void* GLTestAtlasTextRenderer::makeTargetHandle(int width, int height) {
|
|
auto restore = fContext->makeCurrentAndAutoRestore();
|
|
|
|
GrGLuint fbo;
|
|
callgl(GenFramebuffers, 1, &fbo);
|
|
if (!fbo) {
|
|
return nullptr;
|
|
}
|
|
GrGLuint rb;
|
|
callgl(GenRenderbuffers, 1, &rb);
|
|
if (!rb) {
|
|
callgl(DeleteFramebuffers, 1, &fbo);
|
|
return nullptr;
|
|
}
|
|
callgl(BindFramebuffer, GR_GL_FRAMEBUFFER, fbo);
|
|
callgl(BindRenderbuffer, GR_GL_RENDERBUFFER, rb);
|
|
callgl(RenderbufferStorage, GR_GL_RENDERBUFFER, GR_GL_RGBA8, width, height);
|
|
callgl(FramebufferRenderbuffer, GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0, GR_GL_RENDERBUFFER,
|
|
rb);
|
|
GrGLenum status = callgl(CheckFramebufferStatus, GR_GL_FRAMEBUFFER);
|
|
if (GR_GL_FRAMEBUFFER_COMPLETE != status) {
|
|
callgl(DeleteFramebuffers, 1, &fbo);
|
|
callgl(DeleteRenderbuffers, 1, &rb);
|
|
return nullptr;
|
|
}
|
|
callgl(Disable, GR_GL_SCISSOR_TEST);
|
|
callgl(ClearColor, 0, 0, 0, 0.0);
|
|
callgl(Clear, GR_GL_COLOR_BUFFER_BIT);
|
|
checkgl();
|
|
Target* target = new Target;
|
|
target->fFBOID = fbo;
|
|
target->fRBID = rb;
|
|
target->fWidth = width;
|
|
target->fHeight = height;
|
|
return target;
|
|
}
|
|
|
|
void GLTestAtlasTextRenderer::targetDeleted(void* targetHandle) {
|
|
auto restore = fContext->makeCurrentAndAutoRestore();
|
|
|
|
Target* target = reinterpret_cast<Target*>(targetHandle);
|
|
callgl(DeleteFramebuffers, 1, &target->fFBOID);
|
|
callgl(DeleteRenderbuffers, 1, &target->fRBID);
|
|
delete target;
|
|
}
|
|
|
|
SkBitmap GLTestAtlasTextRenderer::readTargetHandle(void* targetHandle) {
|
|
auto restore = fContext->makeCurrentAndAutoRestore();
|
|
|
|
Target* target = reinterpret_cast<Target*>(targetHandle);
|
|
|
|
auto info =
|
|
SkImageInfo::Make(target->fWidth, target->fHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
|
SkBitmap bmp;
|
|
bmp.setInfo(info, sizeof(uint32_t) * target->fWidth);
|
|
bmp.allocPixels();
|
|
|
|
callgl(BindFramebuffer, GR_GL_FRAMEBUFFER, target->fFBOID);
|
|
callgl(ReadPixels, 0, 0, target->fWidth, target->fHeight, GR_GL_RGBA, GR_GL_UNSIGNED_BYTE,
|
|
bmp.getPixels());
|
|
checkgl();
|
|
return bmp;
|
|
}
|
|
|
|
void GLTestAtlasTextRenderer::clearTarget(void* targetHandle, uint32_t color) {
|
|
auto restore = fContext->makeCurrentAndAutoRestore();
|
|
|
|
Target* target = reinterpret_cast<Target*>(targetHandle);
|
|
callgl(BindFramebuffer, GR_GL_FRAMEBUFFER, target->fFBOID);
|
|
callgl(Disable, GR_GL_SCISSOR_TEST);
|
|
float r = ((color >> 0) & 0xff) / 255.f;
|
|
float g = ((color >> 8) & 0xff) / 255.f;
|
|
float b = ((color >> 16) & 0xff) / 255.f;
|
|
float a = ((color >> 24) & 0xff) / 255.f;
|
|
callgl(ClearColor, r, g, b, a);
|
|
callgl(Clear, GR_GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
namespace sk_gpu_test {
|
|
|
|
sk_sp<TestAtlasTextRenderer> MakeGLTestAtlasTextRenderer() {
|
|
std::unique_ptr<GLTestContext> context(CreatePlatformGLTestContext(kGL_GrGLStandard));
|
|
if (!context) {
|
|
context.reset(CreatePlatformGLTestContext(kGLES_GrGLStandard));
|
|
}
|
|
if (!context) {
|
|
return nullptr;
|
|
}
|
|
auto restorer = context->makeCurrentAndAutoRestore();
|
|
auto renderer = sk_make_sp<GLTestAtlasTextRenderer>(std::move(context));
|
|
return renderer->initialized() ? std::move(renderer) : nullptr;
|
|
}
|
|
|
|
} // namespace sk_gpu_test
|