diff options
author | Brian Salomon <bsalomon@google.com> | 2017-11-19 13:20:13 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-11-19 18:55:18 +0000 |
commit | cbcb0a12ad0068b820c28178e8aa141166febd1f (patch) | |
tree | 120917b5961b8a043894b95811eec1f8f6379b25 | |
parent | b07b06e14819c7bfb9da87dd754aca1239045af4 (diff) |
Revert "Revert "Add Atlas Text interface for rendering SDF glyphs.""
This reverts commit 9c2202ffc22b4293b48a4edeafa1b5d2bab8bb83.
Bug: skia:
Change-Id: I482ddf74f8e40d3d0908c840ba5c6ff981ccefbd
Reviewed-on: https://skia-review.googlesource.com/73345
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
24 files changed, 1331 insertions, 84 deletions
@@ -52,6 +52,7 @@ declare_args() { declare_args() { skia_use_dng_sdk = !is_fuchsia && skia_use_libjpeg_turbo && skia_use_zlib skia_use_sfntly = skia_use_icu + skia_enable_atlas_text = is_skia_dev_build && skia_enable_gpu if (is_android) { skia_use_vulkan = defined(ndk_api) && ndk_api >= 24 @@ -92,6 +93,7 @@ skia_public_includes = [ "include/encode", "include/gpu", "include/gpu/gl", + "include/atlastext", "include/pathops", "include/ports", "include/svg", @@ -102,6 +104,9 @@ skia_public_includes = [ if (skia_use_vulkan) { skia_public_includes += [ "include/gpu/vk" ] } +if (skia_enable_atlas_text) { + skia_public_includes += [ "include/atlastext" ] +} if (skia_use_metal) { skia_public_includes += [ "include/gpu/mtl" ] } @@ -125,6 +130,9 @@ config("skia_public") { if (!skia_enable_gpu) { defines += [ "SK_SUPPORT_GPU=0" ] } + if (skia_enable_atlas_text) { + defines += [ "SK_SUPPORT_ATLAS_TEXT=1" ] + } } # Skia internal APIs, used by Skia itself and a few test tools. @@ -586,6 +594,10 @@ optional("gpu") { libs += [ "Metal.framework" ] cflags_objcc += [ "-fobjc-arc" ] } + + if (skia_enable_atlas_text) { + sources += skia_atlas_text_sources + } } optional("heif") { @@ -992,6 +1004,7 @@ if (skia_enable_tools) { "tools/gpu/GrContextFactory.cpp", "tools/gpu/GrTest.cpp", "tools/gpu/TestContext.cpp", + "tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp", "tools/gpu/gl/GLTestContext.cpp", "tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp", "tools/gpu/gl/debug/DebugGLTestContext.cpp", diff --git a/gm/atlastext.cpp b/gm/atlastext.cpp new file mode 100644 index 0000000000..b8acb0fcc7 --- /dev/null +++ b/gm/atlastext.cpp @@ -0,0 +1,123 @@ +/* + * 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 "gm.h" + +#if SK_SUPPORT_ATLAS_TEXT + +#include "SkAtlasTextContext.h" +#include "SkAtlasTextFont.h" +#include "SkAtlasTextTarget.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkTypeface.h" +#include "gpu/TestContext.h" +#include "gpu/atlastext/GLTestAtlasTextRenderer.h" +#include "gpu/atlastext/TestAtlasTextRenderer.h" +#include "sk_tool_utils.h" + +// GM that draws text using the Atlas Text interface offscreen and then blits that to the canvas. + +static SkScalar draw_string(SkAtlasTextTarget* target, const SkString& text, SkScalar x, SkScalar y, + uint32_t color, sk_sp<SkTypeface> typeface, float size) { + auto font = SkAtlasTextFont::Make(std::move(typeface), size); + target->drawText(text.c_str(), text.size(), x, y, color, *font); + SkPaint paint; + paint.setTextSize(size); + return x + paint.measureText(text.c_str(), text.size()); +} + +class AtlasTextGM : public skiagm::GM { +public: + AtlasTextGM() = default; + +protected: + SkString onShortName() override { return SkString("atlastext"); } + + SkISize onISize() override { return SkISize::Make(kSize, kSize); } + + void onOnceBeforeDraw() override { + fRenderer = sk_gpu_test::MakeGLTestAtlasTextRenderer(); + if (!fRenderer) { + return; + } + fContext = SkAtlasTextContext::Make(fRenderer); + auto targetHandle = fRenderer->makeTargetHandle(kSize, kSize); + fTarget = SkAtlasTextTarget::Make(fContext, kSize, kSize, targetHandle); + + fTypefaces[0] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Italic()); + fTypefaces[1] = + sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Italic()); + fTypefaces[2] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Normal()); + fTypefaces[3] = + sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Normal()); + fTypefaces[4] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Bold()); + fTypefaces[5] = sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Bold()); + } + + void onDraw(SkCanvas* canvas) override { + if (!fRenderer) { + canvas->clear(SK_ColorRED); + return; + } + auto bmp = this->drawText(); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + canvas->drawBitmap(bmp, 0, 0); + } + +private: + SkBitmap drawText() { + static const int kSizes[] = {8, 13, 18, 23, 30}; + + static const SkString kTexts[] = {SkString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), + SkString("abcdefghijklmnopqrstuvwxyz"), + SkString("0123456789"), + SkString("!@#$%^&*()<>[]{}")}; + SkScalar x = 0; + SkScalar y = 10; + + SkRandom random; + do { + for (auto s : kSizes) { + auto size = 2 * s; + for (const auto& typeface : fTypefaces) { + for (const auto& text : kTexts) { + uint32_t color = random.nextU(); + x = size + draw_string(fTarget.get(), text, x, y, color, typeface, size); + x = SkScalarCeilToScalar(x); + if (x + 100 > kSize) { + x = 0; + y += SkScalarCeilToScalar(size + 3); + if (y > kSize) { + fTarget->flush(); + return fRenderer->readTargetHandle(fTarget->handle()); + } + } + } + } + } + } while (true); + } + + static constexpr int kSize = 1280; + + sk_sp<SkTypeface> fTypefaces[6]; + sk_sp<sk_gpu_test::TestAtlasTextRenderer> fRenderer; + std::unique_ptr<SkAtlasTextTarget> fTarget; + sk_sp<SkAtlasTextContext> fContext; + + typedef GM INHERITED; +}; + +constexpr int AtlasTextGM::kSize; + +////////////////////////////////////////////////////////////////////////////// + +DEF_GM(return new AtlasTextGM;) + +#endif diff --git a/gn/core.gni b/gn/core.gni index 186e5af454..259cd191f4 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -134,6 +134,7 @@ skia_core_sources = [ "$_src/core/SkFindAndPlaceGlyph.h", "$_src/core/SkArenaAlloc.cpp", "$_src/core/SkArenaAlloc.h", + "$_src/core/SkArenaAllocList.h", "$_src/core/SkGaussFilter.cpp", "$_src/core/SkGaussFilter.h", "$_src/core/SkFlattenable.cpp", @@ -22,6 +22,7 @@ gm_sources = [ "$_gm/arcofzorro.cpp", "$_gm/arcto.cpp", "$_gm/arithmode.cpp", + "$_gm/atlastext.cpp", "$_gm/badpaint.cpp", "$_gm/beziereffects.cpp", "$_gm/beziers.cpp", diff --git a/gn/gpu.gni b/gn/gpu.gni index e584bf5187..a26111dbc3 100644 --- a/gn/gpu.gni +++ b/gn/gpu.gni @@ -628,3 +628,14 @@ skia_native_gpu_sources = [ "$_src/gpu/gl/iOS/GrGLCreateNativeInterface_iOS.cpp", "$_src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp", ] + +skia_atlas_text_sources = [ + "$_include/atlastext/SkAtlasTextContext.h", + "$_include/atlastext/SkAtlasTextFont.h", + "$_include/atlastext/SkAtlasTextRenderer.h", + "$_include/atlastext/SkAtlasTextTarget.h", + + "$_src/atlastext/SkAtlasTextContext.cpp", + "$_src/atlastext/SkAtlasTextTarget.cpp", + "$_src/atlastext/SkInternalAtlasTextContext.cpp", +] diff --git a/include/atlastext/SkAtlasTextContext.h b/include/atlastext/SkAtlasTextContext.h new file mode 100644 index 0000000000..bb5de52992 --- /dev/null +++ b/include/atlastext/SkAtlasTextContext.h @@ -0,0 +1,42 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAtlasTextContext_DEFINED +#define SkAtlasTextContext_DEFINED + +#include "SkRefCnt.h" + +class SkAtlasTextRenderer; +class SkInternalAtlasTextContext; + +SkAtlasTextRenderer* SkGetAtlasTextRendererFromInternalContext(class SkInternalAtlasTextContext&); + +/** + * Class that Atlas Text client uses to register their SkAtlasTextRenderer implementation and + * to create one or more SkAtlasTextTargets (destination surfaces for text rendering). + */ +class SK_API SkAtlasTextContext : public SkRefCnt { +public: + static sk_sp<SkAtlasTextContext> Make(sk_sp<SkAtlasTextRenderer>); + + SkAtlasTextRenderer* renderer() const { + return SkGetAtlasTextRendererFromInternalContext(*fInternalContext); + } + + SkInternalAtlasTextContext& internal() { return *fInternalContext; } + +private: + SkAtlasTextContext() = delete; + SkAtlasTextContext(const SkAtlasTextContext&) = delete; + SkAtlasTextContext& operator=(const SkAtlasTextContext&) = delete; + + SkAtlasTextContext(sk_sp<SkAtlasTextRenderer>); + + std::unique_ptr<SkInternalAtlasTextContext> fInternalContext; +}; + +#endif diff --git a/include/atlastext/SkAtlasTextFont.h b/include/atlastext/SkAtlasTextFont.h new file mode 100644 index 0000000000..a9e641f7ad --- /dev/null +++ b/include/atlastext/SkAtlasTextFont.h @@ -0,0 +1,35 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAtlasTextFont_DEFINED +#define SkAtlasTextFont_DEFINED + +#include "SkRefCnt.h" +#include "SkTypeface.h" + +/** Represents a font at a size. TODO: What else do we need here (skewX, scaleX, vertical, ...)? */ +class SK_API SkAtlasTextFont : public SkRefCnt { +public: + static sk_sp<SkAtlasTextFont> Make(sk_sp<SkTypeface> typeface, SkScalar size) { + return sk_sp<SkAtlasTextFont>(new SkAtlasTextFont(std::move(typeface), size)); + } + + SkTypeface* typeface() const { return fTypeface.get(); } + + sk_sp<SkTypeface> refTypeface() const { return fTypeface; } + + SkScalar size() const { return fSize; } + +private: + SkAtlasTextFont(sk_sp<SkTypeface> typeface, SkScalar size) + : fTypeface(std::move(typeface)), fSize(size) {} + + sk_sp<SkTypeface> fTypeface; + SkScalar fSize; +}; + +#endif diff --git a/include/atlastext/SkAtlasTextRenderer.h b/include/atlastext/SkAtlasTextRenderer.h new file mode 100644 index 0000000000..a78e270edb --- /dev/null +++ b/include/atlastext/SkAtlasTextRenderer.h @@ -0,0 +1,71 @@ +/* + * 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 "SkPoint.h" +#include "SkRefCnt.h" + +#ifndef SkAtlasTextRenderer_DEFINED +#define SkAtlasTextRenderer_DEFINED + +/** + * This is the base class for a renderer implemented by the SkAtlasText client. The + * SkAtlasTextContext issues texture creations, deletions, uploads, and vertex draws to the + * renderer. The renderer must perform those actions in the order called to correctly render + * the text drawn to SkAtlasTextTargets. + */ +class SK_API SkAtlasTextRenderer : public SkRefCnt { +public: + enum class AtlasFormat { + /** Unsigned normalized 8 bit single channel format. */ + kA8 + }; + + struct SDFVertex { + /** Position in device space (not normalized). */ + SkPoint fPosition; + /** Color, same value for all four corners of a glyph quad. */ + uint32_t fColor; + /** Texture coordinate (in texel units, not normalized). */ + SkIPoint16 fTextureCoord; + }; + + virtual ~SkAtlasTextRenderer() = default; + + /** + * Create a texture of the provided format with dimensions 'width' x 'height' + * and return a unique handle. + */ + virtual void* createTexture(AtlasFormat, int width, int height) = 0; + + /** + * Delete the texture with the passed handle. + */ + virtual void deleteTexture(void* textureHandle) = 0; + + /** + * Place the pixel data specified by 'data' in the texture with handle + * 'textureHandle' in the rectangle ['x', 'x' + 'width') x ['y', 'y' + 'height'). + * 'rowBytes' specifies the byte offset between successive rows in 'data' and will always be + * a multiple of the number of bytes per pixel. + * The pixel format of data is the same as that of 'textureHandle'. + */ + virtual void setTextureData(void* textureHandle, const void* data, int x, int y, int width, + int height, size_t rowBytes) = 0; + + /** + * Draws glyphs using SDFs. The SDF data resides in 'textureHandle'. The array + * 'vertices' provides interleaved device-space positions, colors, and + * texture coordinates. There are are 4 * 'quadCnt' entries in 'vertices'. + */ + virtual void drawSDFGlyphs(void* targetHandle, void* textureHandle, const SDFVertex vertices[], + int quadCnt) = 0; + + /** Called when a SkAtlasTextureTarget is destroyed. */ + virtual void targetDeleted(void* targetHandle) = 0; +}; + +#endif diff --git a/include/atlastext/SkAtlasTextTarget.h b/include/atlastext/SkAtlasTextTarget.h new file mode 100644 index 0000000000..0859afde39 --- /dev/null +++ b/include/atlastext/SkAtlasTextTarget.h @@ -0,0 +1,61 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAtlasTextTarget_DEFINED +#define SkAtlasTextTarget_DEFINED + +#include <memory> +#include "SkRefCnt.h" +#include "SkScalar.h" + +class SkAtlasTextContext; +class SkAtlasTextFont; + +/** Represents a client-created renderable surface and is used to draw text into the surface. */ +class SK_API SkAtlasTextTarget { +public: + virtual ~SkAtlasTextTarget(); + + /** + * Creates a text drawing target. ‘handle’ is used to identify this rendering surface when + * draws are flushed to the SkAtlasTextContext's SkAtlasTextRenderer. + */ + static std::unique_ptr<SkAtlasTextTarget> Make(sk_sp<SkAtlasTextContext>, int width, int height, + void* handle); + + /** + * Enqueues a text draw in the target. The meaning of 'color' here is interpreted by the + * client's SkAtlasTextRenderer when it actually renders the text. + */ + virtual void drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, + uint32_t color, const SkAtlasTextFont& font) = 0; + + /** Issues all queued text draws to SkAtlasTextRenderer. */ + virtual void flush() = 0; + + int width() const { return fWidth; } + int height() const { return fHeight; } + + void* handle() const { return fHandle; } + + SkAtlasTextContext* context() const { return fContext.get(); } + +protected: + SkAtlasTextTarget(sk_sp<SkAtlasTextContext>, int width, int height, void* handle); + + void* const fHandle; + const sk_sp<SkAtlasTextContext> fContext; + const int fWidth; + const int fHeight; + +private: + SkAtlasTextTarget() = delete; + SkAtlasTextTarget(const SkAtlasTextContext&) = delete; + SkAtlasTextTarget& operator=(const SkAtlasTextContext&) = delete; +}; + +#endif diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h index 3a39f66c8f..ce81904452 100644 --- a/include/core/SkPostConfig.h +++ b/include/core/SkPostConfig.h @@ -89,6 +89,12 @@ # define SK_SUPPORT_GPU 1 #endif +#if !defined(SK_SUPPORT_ATLAS_TEXT) +# define SK_SUPPORT_ATLAS_TEXT 0 +#elif SK_SUPPORT_ATLAS_TEXT && !SK_SUPPORT_GPU +# error "SK_SUPPORT_ATLAS_TEXT requires SK_SUPPORT_GPU" +#endif + /** * The clang static analyzer likes to know that when the program is not * expected to continue (crash, assertion failure, etc). It will notice that diff --git a/public.bzl b/public.bzl index f455981485..dfc163b187 100644 --- a/public.bzl +++ b/public.bzl @@ -255,6 +255,9 @@ BASE_SRCS_ALL = struct( # Only used to regenerate the lexer "src/sksl/lex/*", + + # Atlas text + "src/atlastext/*", ], ) diff --git a/src/atlastext/SkAtlasTextContext.cpp b/src/atlastext/SkAtlasTextContext.cpp new file mode 100644 index 0000000000..85e79118ee --- /dev/null +++ b/src/atlastext/SkAtlasTextContext.cpp @@ -0,0 +1,17 @@ +/* + * 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 "SkAtlasTextContext.h" +#include "SkAtlasTextRenderer.h" +#include "SkInternalAtlasTextContext.h" + +sk_sp<SkAtlasTextContext> SkAtlasTextContext::Make(sk_sp<SkAtlasTextRenderer> renderer) { + return sk_sp<SkAtlasTextContext>(new SkAtlasTextContext(std::move(renderer))); +} + +SkAtlasTextContext::SkAtlasTextContext(sk_sp<SkAtlasTextRenderer> renderer) + : fInternalContext(SkInternalAtlasTextContext::Make(std::move(renderer))) {} diff --git a/src/atlastext/SkAtlasTextTarget.cpp b/src/atlastext/SkAtlasTextTarget.cpp new file mode 100644 index 0000000000..57ece37b4f --- /dev/null +++ b/src/atlastext/SkAtlasTextTarget.cpp @@ -0,0 +1,143 @@ +/* + * 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 "SkAtlasTextTarget.h" +#include "GrClip.h" +#include "SkAtlasTextContext.h" +#include "SkAtlasTextFont.h" +#include "SkAtlasTextRenderer.h" +#include "SkGr.h" +#include "SkInternalAtlasTextContext.h" +#include "ops/GrAtlasTextOp.h" +#include "text/GrAtlasTextContext.h" + +SkAtlasTextTarget::SkAtlasTextTarget(sk_sp<SkAtlasTextContext> context, int width, int height, + void* handle) + : fHandle(handle), fContext(std::move(context)), fWidth(width), fHeight(height) {} + +SkAtlasTextTarget::~SkAtlasTextTarget() { fContext->renderer()->targetDeleted(fHandle); } + +////////////////////////////////////////////////////////////////////////////// + +static const GrColorSpaceInfo kColorSpaceInfo(nullptr, kRGBA_8888_GrPixelConfig); + +////////////////////////////////////////////////////////////////////////////// + +class SkInternalAtlasTextTarget : public GrTextUtils::Target, public SkAtlasTextTarget { +public: + SkInternalAtlasTextTarget(sk_sp<SkAtlasTextContext> context, int width, int height, + void* handle) + : GrTextUtils::Target(width, height, kColorSpaceInfo) + , SkAtlasTextTarget(std::move(context), width, height, handle) {} + + /** GrTextUtils::Target overrides */ + + void addDrawOp(const GrClip&, std::unique_ptr<GrAtlasTextOp> op) override; + + void drawPath(const GrClip&, const SkPath&, const SkPaint&, const SkMatrix& viewMatrix, + const SkMatrix* pathMatrix, const SkIRect& clipBounds) override { + SkDebugf("Path glyph??"); + } + + void makeGrPaint(GrMaskFormat, const SkPaint& skPaint, const SkMatrix&, + GrPaint* grPaint) override { + grPaint->setColor4f(SkColorToPremulGrColor4fLegacy(skPaint.getColor())); + } + + /** SkAtlasTextTarget overrides */ + + void drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, uint32_t color, + const SkAtlasTextFont&) override; + void flush() override; + +private: + uint32_t fColor; + using SkAtlasTextTarget::fWidth; + using SkAtlasTextTarget::fHeight; + struct RecordedOp { + std::unique_ptr<GrAtlasTextOp> fOp; + uint32_t fColor; + }; + SkTArray<RecordedOp, true> fOps; +}; + +////////////////////////////////////////////////////////////////////////////// + +std::unique_ptr<SkAtlasTextTarget> SkAtlasTextTarget::Make(sk_sp<SkAtlasTextContext> context, + int width, int height, void* handle) { + return std::unique_ptr<SkAtlasTextTarget>( + new SkInternalAtlasTextTarget(std::move(context), width, height, handle)); +} + +////////////////////////////////////////////////////////////////////////////// + +#include "GrContextPriv.h" +#include "GrDrawingManager.h" + +void SkInternalAtlasTextTarget::drawText(const void* text, size_t byteLength, SkScalar x, + SkScalar y, uint32_t color, const SkAtlasTextFont& font) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setTypeface(font.refTypeface()); + paint.setTextSize(font.size()); + paint.setStyle(SkPaint::kFill_Style); + + // TODO: Figure out what if anything to do with these: + // Paint setTextEncoding? Font isEnableByteCodeHints()? Font isUseNonLinearMetrics()? + + // The atlas text context does munging of the paint color. We store the client's color here + // and the context will write it into the final vertices given to the client's renderer. + fColor = color; + + // The pixel geometry here is arbitrary. We don't draw LCD text. + SkSurfaceProps props(SkSurfaceProps::kUseDistanceFieldFonts_Flag, kUnknown_SkPixelGeometry); + auto* grContext = this->context()->internal().grContext(); + auto bounds = SkIRect::MakeWH(fWidth, fHeight); + auto atlasTextContext = grContext->contextPriv().drawingManager()->getAtlasTextContext(); + atlasTextContext->drawText(grContext, this, GrNoClip(), paint, SkMatrix::I(), props, + (const char*)text, byteLength, x, y, bounds); +} + +void SkInternalAtlasTextTarget::addDrawOp(const GrClip& clip, std::unique_ptr<GrAtlasTextOp> op) { + SkASSERT(clip.quickContains(SkRect::MakeIWH(fWidth, fHeight))); + // The SkAtlasTextRenderer currently only handles grayscale SDF glyphs. + if (op->maskType() != GrAtlasTextOp::kGrayscaleDistanceField_MaskType) { + return; + } + // TODO: batch ops here. + op->visitProxies([](GrSurfaceProxy*) {}); + fOps.emplace_back(RecordedOp{std::move(op), fColor}); +} + +void SkInternalAtlasTextTarget::flush() { + for (int i = 0; i < fOps.count(); ++i) { + fOps[i].fOp->executeForTextTarget(this, fOps[i].fColor); + } + this->context()->internal().flush(); + fOps.reset(); +} + +void GrAtlasTextOp::executeForTextTarget(SkAtlasTextTarget* target, uint32_t color) { + FlushInfo flushInfo; + SkAutoGlyphCache glyphCache; + auto& context = target->context()->internal(); + auto* atlasGlyphCache = context.grContext()->getAtlasGlyphCache(); + for (int i = 0; i < fGeoCount; ++i) { + GrAtlasTextBlob::VertexRegenerator regenerator( + fGeoData[i].fBlob, fGeoData[i].fRun, fGeoData[i].fSubRun, fGeoData[i].fViewMatrix, + fGeoData[i].fX, fGeoData[i].fY, color, &context, atlasGlyphCache, &glyphCache); + GrAtlasTextBlob::VertexRegenerator::Result result; + do { + result = regenerator.regenerate(); + context.recordDraw(result.fFirstVertex, result.fGlyphsRegenerated, target->handle()); + if (!result.fFinished) { + // Make space in the atlas so we can continue generating vertices. + context.flush(); + } + } while (!result.fFinished); + } +} diff --git a/src/atlastext/SkInternalAtlasTextContext.cpp b/src/atlastext/SkInternalAtlasTextContext.cpp new file mode 100644 index 0000000000..8be7e1fb67 --- /dev/null +++ b/src/atlastext/SkInternalAtlasTextContext.cpp @@ -0,0 +1,117 @@ +/* + * 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 "SkInternalAtlasTextContext.h" +#include "GrContext.h" +#include "SkAtlasTextContext.h" +#include "SkAtlasTextRenderer.h" +#include "text/GrAtlasGlyphCache.h" + +SkAtlasTextRenderer* SkGetAtlasTextRendererFromInternalContext( + class SkInternalAtlasTextContext& internal) { + return internal.renderer(); +} + +////////////////////////////////////////////////////////////////////////////// + +std::unique_ptr<SkInternalAtlasTextContext> SkInternalAtlasTextContext::Make( + sk_sp<SkAtlasTextRenderer> renderer) { + return std::unique_ptr<SkInternalAtlasTextContext>( + new SkInternalAtlasTextContext(std::move(renderer))); +} + +SkInternalAtlasTextContext::SkInternalAtlasTextContext(sk_sp<SkAtlasTextRenderer> renderer) + : fRenderer(std::move(renderer)) { + GrContextOptions options; + options.fAllowMultipleGlyphCacheTextures = GrContextOptions::Enable::kNo; + options.fMinDistanceFieldFontSize = 0.f; + options.fGlyphsAsPathsFontSize = SK_ScalarInfinity; + fGrContext = GrContext::MakeMock(nullptr, options); +} + +SkInternalAtlasTextContext::~SkInternalAtlasTextContext() { + if (fDistanceFieldAtlas.fProxy) { + SkASSERT(1 == fGrContext->getAtlasGlyphCache()->getAtlasPageCount(kA8_GrMaskFormat)); + fRenderer->deleteTexture(fDistanceFieldAtlas.fTextureHandle); + } +} + +GrAtlasGlyphCache* SkInternalAtlasTextContext::atlasGlyphCache() { + return fGrContext->getAtlasGlyphCache(); +} + +GrTextBlobCache* SkInternalAtlasTextContext::textBlobCache() { + return fGrContext->getTextBlobCache(); +} + +GrDeferredUploadToken SkInternalAtlasTextContext::addInlineUpload( + GrDeferredTextureUploadFn&& upload) { + auto token = this->nextDrawToken(); + fInlineUploads.append(&fArena, InlineUpload{std::move(upload), token}); + return token; +} + +GrDeferredUploadToken SkInternalAtlasTextContext::addASAPUpload( + GrDeferredTextureUploadFn&& upload) { + fASAPUploads.append(&fArena, std::move(upload)); + return this->nextTokenToFlush(); +} + +void SkInternalAtlasTextContext::recordDraw(const void* srcVertexData, int glyphCnt, + void* targetHandle) { + auto vertexDataSize = sizeof(SkAtlasTextRenderer::SDFVertex) * 4 * glyphCnt; + auto vertexData = fArena.makeArrayDefault<char>(vertexDataSize); + memcpy(vertexData, srcVertexData, vertexDataSize); + for (int i = 0; i < 4 * glyphCnt; ++i) { + auto* vertex = reinterpret_cast<SkAtlasTextRenderer::SDFVertex*>(vertexData) + i; + // GrAtlasTextContext encodes a texture index into the lower bit of each texture coord. + // This isn't expected by SkAtlasTextRenderer subclasses. + vertex->fTextureCoord.fX /= 2; + vertex->fTextureCoord.fY /= 2; + } + fDraws.append(&fArena, Draw{glyphCnt, this->issueDrawToken(), targetHandle, vertexData}); +} + +void SkInternalAtlasTextContext::flush() { + auto* atlasGlyphCache = fGrContext->getAtlasGlyphCache(); + if (!fDistanceFieldAtlas.fProxy) { + SkASSERT(1 == atlasGlyphCache->getAtlasPageCount(kA8_GrMaskFormat)); + fDistanceFieldAtlas.fProxy = atlasGlyphCache->getProxies(kA8_GrMaskFormat)->get(); + fDistanceFieldAtlas.fTextureHandle = + fRenderer->createTexture(SkAtlasTextRenderer::AtlasFormat::kA8, + fDistanceFieldAtlas.fProxy->width(), + fDistanceFieldAtlas.fProxy->height()); + } + GrDeferredTextureUploadWritePixelsFn writePixelsFn = + [this](GrTextureProxy* proxy, int left, int top, int width, int height, + GrPixelConfig config, const void* data, size_t rowBytes) -> bool { + SkASSERT(kAlpha_8_GrPixelConfig == config); + SkASSERT(proxy == this->fDistanceFieldAtlas.fProxy); + void* handle = fDistanceFieldAtlas.fTextureHandle; + this->fRenderer->setTextureData(handle, data, left, top, width, height, rowBytes); + return true; + }; + for (const auto& upload : fASAPUploads) { + upload(writePixelsFn); + } + auto draw = fDraws.begin(); + auto inlineUpload = fInlineUploads.begin(); + while (draw != fDraws.end()) { + while (inlineUpload != fInlineUploads.end() && inlineUpload->fToken == draw->fToken) { + inlineUpload->fUpload(writePixelsFn); + ++inlineUpload; + } + auto vertices = reinterpret_cast<const SkAtlasTextRenderer::SDFVertex*>(draw->fVertexData); + fRenderer->drawSDFGlyphs(draw->fTargetHandle, fDistanceFieldAtlas.fTextureHandle, vertices, + draw->fGlyphCnt); + ++draw; + } + fASAPUploads.reset(); + fInlineUploads.reset(); + fDraws.reset(); + fArena.reset(); +} diff --git a/src/atlastext/SkInternalAtlasTextContext.h b/src/atlastext/SkInternalAtlasTextContext.h new file mode 100644 index 0000000000..1bb12cee5a --- /dev/null +++ b/src/atlastext/SkInternalAtlasTextContext.h @@ -0,0 +1,80 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkInternalAtlasTextContext_DEFINED +#define SkInternalAtlasTextContext_DEFINED + +#include "GrDeferredUpload.h" +#include "SkArenaAlloc.h" +#include "SkArenaAllocList.h" +#include "SkRefCnt.h" + +class SkAtlasTextRenderer; +class GrContext; +class GrAtlasGlyphCache; +class GrTextBlobCache; + +/** + * The implementation of SkAtlasTextContext. This exists to hide the details from the public class. + * and to be able to use other private types. + */ +class SkInternalAtlasTextContext : public GrDeferredUploadTarget { +public: + static std::unique_ptr<SkInternalAtlasTextContext> Make(sk_sp<SkAtlasTextRenderer>); + + ~SkInternalAtlasTextContext() override; + + SkAtlasTextRenderer* renderer() const { return fRenderer.get(); } + + GrContext* grContext() const { return fGrContext.get(); } + GrAtlasGlyphCache* atlasGlyphCache(); + GrTextBlobCache* textBlobCache(); + + GrDeferredUploadToken addInlineUpload(GrDeferredTextureUploadFn&&) override; + + GrDeferredUploadToken addASAPUpload(GrDeferredTextureUploadFn&&) override; + + void recordDraw(const void* vertexData, int glyphCnt, void* targetHandle); + + void flush(); + +private: + class DeferredUploader; + SkInternalAtlasTextContext() = delete; + SkInternalAtlasTextContext(const SkInternalAtlasTextContext&) = delete; + SkInternalAtlasTextContext& operator=(const SkInternalAtlasTextContext&) = delete; + + SkInternalAtlasTextContext(sk_sp<SkAtlasTextRenderer>); + + sk_sp<SkAtlasTextRenderer> fRenderer; + + struct AtlasTexture { + void* fTextureHandle = nullptr; + GrTextureProxy* fProxy = nullptr; + }; + + struct Draw { + int fGlyphCnt; + GrDeferredUploadToken fToken; + void* fTargetHandle; + const void* fVertexData; + }; + + struct InlineUpload { + GrDeferredTextureUploadFn fUpload; + GrDeferredUploadToken fToken; + }; + + SkArenaAllocList<InlineUpload> fInlineUploads; + SkArenaAllocList<Draw> fDraws; + SkArenaAllocList<GrDeferredTextureUploadFn> fASAPUploads; + SkArenaAlloc fArena{1024 * 40}; + sk_sp<GrContext> fGrContext; + AtlasTexture fDistanceFieldAtlas; +}; + +#endif diff --git a/src/core/SkArenaAlloc.h b/src/core/SkArenaAlloc.h index 9e971610a6..2f23382ac1 100644 --- a/src/core/SkArenaAlloc.h +++ b/src/core/SkArenaAlloc.h @@ -5,8 +5,8 @@ * found in the LICENSE file. */ -#ifndef SkFixedAlloc_DEFINED -#define SkFixedAlloc_DEFINED +#ifndef SkArenaAlloc_DEFINED +#define SkArenaAlloc_DEFINED #include "SkRefCnt.h" #include "SkTFitsIn.h" @@ -240,4 +240,4 @@ private: using INHERITED = SkArenaAlloc; }; -#endif//SkFixedAlloc_DEFINED +#endif // SkArenaAlloc_DEFINED diff --git a/src/core/SkArenaAllocList.h b/src/core/SkArenaAllocList.h new file mode 100644 index 0000000000..b4e442b0bd --- /dev/null +++ b/src/core/SkArenaAllocList.h @@ -0,0 +1,79 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkArenaAllocList_DEFINED +#define SkArenaAllocList_DEFINED + +#include "SkArenaAlloc.h" + +/** + * A singly linked list of Ts stored in a SkArenaAlloc. The arena rather than the list owns + * the elements. This supports forward iteration and range based for loops. + */ +template <typename T> +class SkArenaAllocList { +private: + struct Node; + +public: + SkArenaAllocList() = default; + + void reset() { fHead = fTail = nullptr; } + + template <typename... Args> + inline T& append(SkArenaAlloc* arena, Args... args); + + class Iter { + public: + Iter() = default; + inline Iter& operator++(); + T& operator*() const { return fCurr->fT; } + T* operator->() const { return &fCurr->fT; } + bool operator==(const Iter& that) const { return fCurr == that.fCurr; } + bool operator!=(const Iter& that) const { return !(*this == that); } + + private: + friend class SkArenaAllocList; + explicit Iter(Node* node) : fCurr(node) {} + Node* fCurr = nullptr; + }; + + Iter begin() { return Iter(fHead); } + Iter end() { return Iter(); } + Iter tail() { return Iter(fTail); } + +private: + struct Node { + template <typename... Args> + Node(Args... args) : fT(std::forward<Args>(args)...) {} + T fT; + Node* fNext = nullptr; + }; + Node* fHead = nullptr; + Node* fTail = nullptr; +}; + +template <typename T> +template <typename... Args> +T& SkArenaAllocList<T>::append(SkArenaAlloc* arena, Args... args) { + SkASSERT(!fHead == !fTail); + auto* n = arena->make<Node>(std::forward<Args>(args)...); + if (!fTail) { + fHead = fTail = n; + } else { + fTail = fTail->fNext = n; + } + return fTail->fT; +} + +template <typename T> +typename SkArenaAllocList<T>::Iter& SkArenaAllocList<T>::Iter::operator++() { + fCurr = fCurr->fNext; + return *this; +} + +#endif diff --git a/src/gpu/GrOpFlushState.cpp b/src/gpu/GrOpFlushState.cpp index 6ac5d13430..5245a9d9b2 100644 --- a/src/gpu/GrOpFlushState.cpp +++ b/src/gpu/GrOpFlushState.cpp @@ -12,25 +12,6 @@ #include "GrResourceProvider.h" #include "GrTexture.h" -template <typename T> -template <typename... Args> -T& GrOpFlushState::List<T>::append(SkArenaAlloc* arena, Args... args) { - SkASSERT(!fHead == !fTail); - auto* n = arena->make<Node>(std::forward<Args>(args)...); - if (!fTail) { - fHead = fTail = n; - } else { - fTail = fTail->fNext = n; - } - return fTail->fT; -} - -template <typename T> -typename GrOpFlushState::List<T>::Iter& GrOpFlushState::List<T>::Iter::operator++() { - fCurr = fCurr->fNext; - return *this; -} - ////////////////////////////////////////////////////////////////////////////// GrOpFlushState::GrOpFlushState(GrGpu* gpu, GrResourceProvider* resourceProvider) diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h index b20098aac7..a9cf9a070f 100644 --- a/src/gpu/GrOpFlushState.h +++ b/src/gpu/GrOpFlushState.h @@ -13,6 +13,7 @@ #include "GrBufferAllocPool.h" #include "GrDeferredUpload.h" #include "SkArenaAlloc.h" +#include "SkArenaAllocList.h" #include "ops/GrMeshDrawOp.h" class GrGpu; @@ -110,53 +111,6 @@ private: uint32_t fOpID; }; - /** - * A singly linked list of Ts stored in a SkArenaAlloc. The arena rather than the list owns - * the elements. This supports forward iteration and range based for loops. - */ - template <typename T> - class List { - private: - struct Node; - - public: - List() = default; - - void reset() { fHead = fTail = nullptr; } - - template <typename... Args> - T& append(SkArenaAlloc* arena, Args... args); - - class Iter { - public: - Iter() = default; - Iter& operator++(); - T& operator*() const { return fCurr->fT; } - T* operator->() const { return &fCurr->fT; } - bool operator==(const Iter& that) const { return fCurr == that.fCurr; } - bool operator!=(const Iter& that) const { return !(*this == that); } - - private: - friend class List; - explicit Iter(Node* node) : fCurr(node) {} - Node* fCurr = nullptr; - }; - - Iter begin() { return Iter(fHead); } - Iter end() { return Iter(); } - Iter tail() { return Iter(fTail); } - - private: - struct Node { - template <typename... Args> - Node(Args... args) : fT(std::forward<Args>(args)...) {} - T fT; - Node* fNext = nullptr; - }; - Node* fHead = nullptr; - Node* fTail = nullptr; - }; - // Storage for ops' pipelines, draws, and inline uploads. SkArenaAlloc fArena{sizeof(GrPipeline) * 100}; @@ -165,9 +119,9 @@ private: GrIndexBufferAllocPool fIndexPool; // Data stored on behalf of the ops being flushed. - List<GrDeferredTextureUploadFn> fAsapUploads; - List<InlineUpload> fInlineUploads; - List<Draw> fDraws; + SkArenaAllocList<GrDeferredTextureUploadFn> fAsapUploads; + SkArenaAllocList<InlineUpload> fInlineUploads; + SkArenaAllocList<Draw> fDraws; // TODO: These should go in the arena. However, GrGpuCommandBuffer and other classes currently // accept contiguous arrays of meshes. SkSTArray<16, GrMesh> fMeshes; @@ -185,9 +139,9 @@ private: GrGpuCommandBuffer* fCommandBuffer = nullptr; // Variables that are used to track where we are in lists as ops are executed - List<Draw>::Iter fCurrDraw; + SkArenaAllocList<Draw>::Iter fCurrDraw; int fCurrMesh; - List<InlineUpload>::Iter fCurrUpload; + SkArenaAllocList<InlineUpload>::Iter fCurrUpload; }; #endif diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h index 240b98b6f0..c8ef643fd2 100644 --- a/src/gpu/ops/GrAtlasTextOp.h +++ b/src/gpu/ops/GrAtlasTextOp.h @@ -12,6 +12,8 @@ #include "text/GrAtlasTextContext.h" #include "text/GrDistanceFieldAdjustTable.h" +class SkAtlasTextTarget; + class GrAtlasTextOp final : public GrMeshDrawOp { public: DEFINE_OP_CLASS_ID @@ -116,6 +118,20 @@ public: RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, GrPixelConfigIsClamped dstIsClamped) override; + enum MaskType { + kGrayscaleCoverageMask_MaskType, + kLCDCoverageMask_MaskType, + kColorBitmapMask_MaskType, + kAliasedDistanceField_MaskType, + kGrayscaleDistanceField_MaskType, + kLCDDistanceField_MaskType, + kLCDBGRDistanceField_MaskType, + }; + + MaskType maskType() const { return fMaskType; } + + void executeForTextTarget(SkAtlasTextTarget*, uint32_t color); + private: // The minimum number of Geometry we will try to allocate. static constexpr auto kMinGeometryAllocated = 12; @@ -180,16 +196,6 @@ private: sk_sp<GrGeometryProcessor> setupDfProcessor() const; - enum MaskType { - kGrayscaleCoverageMask_MaskType, - kLCDCoverageMask_MaskType, - kColorBitmapMask_MaskType, - kAliasedDistanceField_MaskType, - kGrayscaleDistanceField_MaskType, - kLCDDistanceField_MaskType, - kLCDBGRDistanceField_MaskType, - }; - SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData; int fGeoDataAllocSize; GrColor fColor; diff --git a/tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp b/tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp new file mode 100644 index 0000000000..543f46012b --- /dev/null +++ b/tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp @@ -0,0 +1,439 @@ +/* + * 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" + +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* target) override; + + SkBitmap readTargetHandle(void* target) 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(); + 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 vec2 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, 1.0); + } + )"; + 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, 2, GR_GL_FLOAT, GR_GL_FALSE, sizeof(SDFVertex), vertices); + size_t colorOffset = 2 * 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.5, 0.5, 0.5, 1.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* target) { + auto restore = fContext->makeCurrentAndAutoRestore(); + + Target* t = reinterpret_cast<Target*>(target); + callgl(DeleteFramebuffers, 1, &t->fFBOID); + callgl(DeleteRenderbuffers, 1, &t->fRBID); + delete t; +} + +SkBitmap GLTestAtlasTextRenderer::readTargetHandle(void* target) { + auto restore = fContext->makeCurrentAndAutoRestore(); + + Target* t = reinterpret_cast<Target*>(target); + + auto info = + SkImageInfo::Make(t->fWidth, t->fHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + SkBitmap bmp; + bmp.setInfo(info, sizeof(uint32_t) * t->fWidth); + bmp.allocPixels(); + + callgl(BindFramebuffer, GR_GL_FRAMEBUFFER, t->fFBOID); + callgl(ReadPixels, 0, 0, t->fWidth, t->fHeight, GR_GL_RGBA, GR_GL_UNSIGNED_BYTE, + bmp.getPixels()); + checkgl(); + return bmp; +} + +} // 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 diff --git a/tools/gpu/atlastext/GLTestAtlasTextRenderer.h b/tools/gpu/atlastext/GLTestAtlasTextRenderer.h new file mode 100644 index 0000000000..df01b345de --- /dev/null +++ b/tools/gpu/atlastext/GLTestAtlasTextRenderer.h @@ -0,0 +1,25 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GLTestAtlasTextRenderer_DEFINED +#define GLTestAtlasTextRenderer_DEFINED + +#include "SkRefCnt.h" + +namespace sk_gpu_test { + +class TestAtlasTextRenderer; + +/** + * Creates a TestAtlasTextRenderer that uses its own OpenGL context to implement + * SkAtlasTextRenderer. + */ +sk_sp<TestAtlasTextRenderer> MakeGLTestAtlasTextRenderer(); + +} // namespace sk_gpu_test + +#endif diff --git a/tools/gpu/atlastext/TestAtlasTextRenderer.h b/tools/gpu/atlastext/TestAtlasTextRenderer.h new file mode 100644 index 0000000000..068a60d2bb --- /dev/null +++ b/tools/gpu/atlastext/TestAtlasTextRenderer.h @@ -0,0 +1,34 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef TestAtlasTextRenderer_DEFINED +#define TestAtlasTextRenderer_DEFINED + +#include "SkAtlasTextRenderer.h" +#include "SkBitmap.h" + +namespace sk_gpu_test { + +class TestContext; + +/** + * Base class for implementations of SkAtlasTextRenderer in order to test the SkAtlasText APIs. + * Adds a helper for creating SkAtlasTextTargets and to read back the contents of a target as a + * bitmap. + */ +class TestAtlasTextRenderer : public SkAtlasTextRenderer { +public: + /** Returns a handle that can be used to construct a SkAtlasTextTarget instance. */ + virtual void* makeTargetHandle(int width, int height) = 0; + + /** Makes a SkBitmap of the target handle's contents. */ + virtual SkBitmap readTargetHandle(void* targetHandle) = 0; +}; + +} // namespace sk_gpu_test + +#endif diff --git a/tools/gpu/gl/glx/CreatePlatformGLTestContext_glx.cpp b/tools/gpu/gl/glx/CreatePlatformGLTestContext_glx.cpp index a6b2637c4b..066784df69 100644 --- a/tools/gpu/gl/glx/CreatePlatformGLTestContext_glx.cpp +++ b/tools/gpu/gl/glx/CreatePlatformGLTestContext_glx.cpp @@ -107,6 +107,11 @@ GLXGLTestContext::GLXGLTestContext(GrGLStandard forcedGpuAPI, GLXGLTestContext* , fDisplay(nullptr) , fPixmap(0) , fGlxPixmap(0) { + // We cross our fingers that this is the first X call in the program and that if the application + // is actually threaded that this succeeds. + static SkOnce gOnce; + gOnce([] { XInitThreads(); }); + fDisplay = get_display(); GLXContext glxShareContext = shareContext ? shareContext->fContext : nullptr; |