diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-01-29 16:35:13 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-02-01 15:33:35 +0000 |
commit | c8c2d1901aec01e934adf561a9fdf0cc776cdef8 (patch) | |
tree | 9157c3d9815e5870799e070b113813bec53e0535 /chromium/third_party/skia/tools | |
parent | abefd5095b41dac94ca451d784ab6e27372e981a (diff) | |
download | qtwebengine-chromium-c8c2d1901aec01e934adf561a9fdf0cc776cdef8.tar.gz |
BASELINE: Update Chromium to 64.0.3282.139
Change-Id: I1cae68fe9c94ff7608b26b8382fc19862cdb293a
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/third_party/skia/tools')
140 files changed, 5905 insertions, 2236 deletions
diff --git a/chromium/third_party/skia/tools/SkRandomScalerContext.cpp b/chromium/third_party/skia/tools/SkRandomScalerContext.cpp new file mode 100644 index 00000000000..49d9ab43ffd --- /dev/null +++ b/chromium/third_party/skia/tools/SkRandomScalerContext.cpp @@ -0,0 +1,258 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkAdvancedTypefaceMetrics.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkGlyph.h" +#include "SkMakeUnique.h" +#include "SkPath.h" +#include "SkRandomScalerContext.h" +#include "SkRasterizer.h" + +class SkDescriptor; + +class SkRandomScalerContext : public SkScalerContext { +public: + SkRandomScalerContext(sk_sp<SkRandomTypeface>, const SkScalerContextEffects&, + const SkDescriptor*, bool fFakeIt); + +protected: + unsigned generateGlyphCount() override; + uint16_t generateCharToGlyph(SkUnichar) override; + void generateAdvance(SkGlyph*) override; + void generateMetrics(SkGlyph*) override; + void generateImage(const SkGlyph&) override; + void generatePath(SkGlyphID, SkPath*) override; + void generateFontMetrics(SkPaint::FontMetrics*) override; + +private: + SkRandomTypeface* getRandomTypeface() const { + return static_cast<SkRandomTypeface*>(this->getTypeface()); + } + std::unique_ptr<SkScalerContext> fProxy; + bool fFakeIt; +}; + +SkRandomScalerContext::SkRandomScalerContext(sk_sp<SkRandomTypeface> face, + const SkScalerContextEffects& effects, + const SkDescriptor* desc, + bool fakeIt) + : SkScalerContext(std::move(face), effects, desc) + , fFakeIt(fakeIt) { + fProxy = this->getRandomTypeface()->proxy()->createScalerContext(effects, desc); +} + +unsigned SkRandomScalerContext::generateGlyphCount() { + return fProxy->getGlyphCount(); +} + +uint16_t SkRandomScalerContext::generateCharToGlyph(SkUnichar uni) { + return fProxy->charToGlyphID(uni); +} + +void SkRandomScalerContext::generateAdvance(SkGlyph* glyph) { + fProxy->getAdvance(glyph); +} + +void SkRandomScalerContext::generateMetrics(SkGlyph* glyph) { + // Here we will change the mask format of the glyph + // NOTE this is being overridden by the base class + SkMask::Format format = SkMask::kARGB32_Format; // init to handle defective compilers + switch (glyph->getGlyphID() % 4) { + case 0: + format = SkMask::kLCD16_Format; + break; + case 1: + format = SkMask::kA8_Format; + break; + case 2: + format = SkMask::kARGB32_Format; + break; + case 3: + format = SkMask::kBW_Format; + break; + } + + fProxy->getMetrics(glyph); + + glyph->fMaskFormat = format; + if (fFakeIt) { + return; + } + if (SkMask::kARGB32_Format == format) { + SkPath path; + fProxy->getPath(glyph->getPackedID(), &path); + + SkRect storage; + const SkPaint& paint = this->getRandomTypeface()->paint(); + const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(), + &storage, + SkPaint::kFill_Style); + SkIRect ibounds; + newBounds.roundOut(&ibounds); + glyph->fLeft = ibounds.fLeft; + glyph->fTop = ibounds.fTop; + glyph->fWidth = ibounds.width(); + glyph->fHeight = ibounds.height(); + } else { + SkPath devPath, fillPath; + SkMatrix fillToDevMatrix; + + this->internalGetPath(glyph->getPackedID(), &fillPath, &devPath, &fillToDevMatrix); + + // just use devPath + const SkIRect ir = devPath.getBounds().roundOut(); + + if (ir.isEmpty() || !ir.is16Bit()) { + glyph->fLeft = 0; + glyph->fTop = 0; + glyph->fWidth = 0; + glyph->fHeight = 0; + return; + } + glyph->fLeft = ir.fLeft; + glyph->fTop = ir.fTop; + glyph->fWidth = SkToU16(ir.width()); + glyph->fHeight = SkToU16(ir.height()); + + if (glyph->fWidth > 0) { + switch (glyph->fMaskFormat) { + case SkMask::kLCD16_Format: + glyph->fWidth += 2; + glyph->fLeft -= 1; + break; + default: + break; + } + } + } +} + +void SkRandomScalerContext::generateImage(const SkGlyph& glyph) { + SkMask::Format format = (SkMask::Format)glyph.fMaskFormat; + switch (glyph.getGlyphID() % 4) { + case 0: + format = SkMask::kLCD16_Format; + break; + case 1: + format = SkMask::kA8_Format; + break; + case 2: + format = SkMask::kARGB32_Format; + break; + case 3: + format = SkMask::kBW_Format; + break; + } + const_cast<SkGlyph&>(glyph).fMaskFormat = format; + + // if the format is ARGB, we just draw the glyph from path ourselves. Otherwise, we force + // our proxy context to generate the image from paths. + if (!fFakeIt) { + if (SkMask::kARGB32_Format == glyph.fMaskFormat) { + SkPath path; + fProxy->getPath(glyph.getPackedID(), &path); + + SkBitmap bm; + bm.installPixels(SkImageInfo::MakeN32Premul(glyph.fWidth, glyph.fHeight), + glyph.fImage, glyph.rowBytes()); + bm.eraseColor(0); + + SkCanvas canvas(bm); + canvas.translate(-SkIntToScalar(glyph.fLeft), + -SkIntToScalar(glyph.fTop)); + canvas.drawPath(path, this->getRandomTypeface()->paint()); + } else { + fProxy->forceGenerateImageFromPath(); + fProxy->getImage(glyph); + fProxy->forceOffGenerateImageFromPath(); + } + } else { + sk_bzero(glyph.fImage, glyph.computeImageSize()); + } +} + +void SkRandomScalerContext::generatePath(SkGlyphID glyph, SkPath* path) { + fProxy->generatePath(glyph, path); +} + +void SkRandomScalerContext::generateFontMetrics(SkPaint::FontMetrics* metrics) { + fProxy->getFontMetrics(metrics); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkTypefaceCache.h" + +SkRandomTypeface::SkRandomTypeface(sk_sp<SkTypeface> proxy, const SkPaint& paint, bool fakeIt) + : SkTypeface(proxy->fontStyle(), false) + , fProxy(std::move(proxy)) + , fPaint(paint) + , fFakeIt(fakeIt) {} + +SkScalerContext* SkRandomTypeface::onCreateScalerContext(const SkScalerContextEffects& effects, + const SkDescriptor* desc) const { + return new SkRandomScalerContext(sk_ref_sp(const_cast<SkRandomTypeface*>(this)), + effects, desc, fFakeIt); +} + +void SkRandomTypeface::onFilterRec(SkScalerContextRec* rec) const { + fProxy->filterRec(rec); + rec->setHinting(SkPaint::kNo_Hinting); + rec->fMaskFormat = SkMask::kARGB32_Format; +} + +std::unique_ptr<SkAdvancedTypefaceMetrics> SkRandomTypeface::onGetAdvancedMetrics() const { + return fProxy->getAdvancedMetrics(); +} + +SkStreamAsset* SkRandomTypeface::onOpenStream(int* ttcIndex) const { + return fProxy->openStream(ttcIndex); +} + +void SkRandomTypeface::onGetFontDescriptor(SkFontDescriptor* desc, + bool* isLocal) const { + fProxy->getFontDescriptor(desc, isLocal); +} + +int SkRandomTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const { + return fProxy->charsToGlyphs(chars, encoding, glyphs, glyphCount); +} + +int SkRandomTypeface::onCountGlyphs() const { + return fProxy->countGlyphs(); +} + +int SkRandomTypeface::onGetUPEM() const { + return fProxy->getUnitsPerEm(); +} + +void SkRandomTypeface::onGetFamilyName(SkString* familyName) const { + fProxy->getFamilyName(familyName); +} + +SkTypeface::LocalizedStrings* SkRandomTypeface::onCreateFamilyNameIterator() const { + return fProxy->createFamilyNameIterator(); +} + +int SkRandomTypeface::onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const +{ + return fProxy->onGetVariationDesignPosition(coordinates, coordinateCount); +} + +int SkRandomTypeface::onGetTableTags(SkFontTableTag tags[]) const { + return fProxy->getTableTags(tags); +} + +size_t SkRandomTypeface::onGetTableData(SkFontTableTag tag, size_t offset, + size_t length, void* data) const { + return fProxy->getTableData(tag, offset, length, data); +} + diff --git a/chromium/third_party/skia/tools/SkRandomScalerContext.h b/chromium/third_party/skia/tools/SkRandomScalerContext.h new file mode 100644 index 00000000000..b71689d9e2d --- /dev/null +++ b/chromium/third_party/skia/tools/SkRandomScalerContext.h @@ -0,0 +1,54 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRandomScalerContext_DEFINED +#define SkRandomScalerContext_DEFINED + +#include "SkScalerContext.h" +#include "SkTypeface.h" + +/* + * This scaler context is for debug only purposes. It will 'randomly' but deterministically return + * LCD / A8 / BW / RBGA masks based off of the Glyph ID + */ + +class SkRandomTypeface : public SkTypeface { +public: + SkRandomTypeface(sk_sp<SkTypeface> proxy, const SkPaint&, bool fakeit); + + SkTypeface* proxy() const { return fProxy.get(); } + const SkPaint& paint() const { return fPaint; } + +protected: + SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&, + const SkDescriptor*) const override; + void onFilterRec(SkScalerContextRec*) const override; + std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; + SkStreamAsset* onOpenStream(int* ttcIndex) const override; + void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const override; + + int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const override; + int onCountGlyphs() const override; + int onGetUPEM() const override; + + void onGetFamilyName(SkString* familyName) const override; + SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override; + int onGetTableTags(SkFontTableTag tags[]) const override; + size_t onGetTableData(SkFontTableTag, size_t offset, + size_t length, void* data) const override; + +private: + sk_sp<SkTypeface> fProxy; + SkPaint fPaint; + bool fFakeIt; +}; + +#endif diff --git a/chromium/third_party/skia/tools/SkTestScalerContext.cpp b/chromium/third_party/skia/tools/SkTestScalerContext.cpp new file mode 100644 index 00000000000..f74b1bd57ac --- /dev/null +++ b/chromium/third_party/skia/tools/SkTestScalerContext.cpp @@ -0,0 +1,267 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkAdvancedTypefaceMetrics.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkDescriptor.h" +#include "SkFontDescriptor.h" +#include "SkGlyph.h" +#include "SkMakeUnique.h" +#include "SkMask.h" +#include "SkOTUtils.h" +#include "SkPaintPriv.h" +#include "SkScalerContext.h" +#include "SkTestScalerContext.h" +#include "SkTypefaceCache.h" +#include "SkUtils.h" + +SkTestFont::SkTestFont(const SkTestFontData& fontData) + : INHERITED() + , fCharCodes(fontData.fCharCodes) + , fCharCodesCount(fontData.fCharCodes ? fontData.fCharCodesCount : 0) + , fWidths(fontData.fWidths) + , fMetrics(fontData.fMetrics) + , fName(fontData.fName) + , fPaths(nullptr) +{ + init(fontData.fPoints, fontData.fVerbs); +} + +SkTestFont::~SkTestFont() { + for (unsigned index = 0; index < fCharCodesCount; ++index) { + delete fPaths[index]; + } + delete[] fPaths; +} + +int SkTestFont::codeToIndex(SkUnichar charCode) const { + for (unsigned index = 0; index < fCharCodesCount; ++index) { + if (fCharCodes[index] == (unsigned) charCode) { + return (int) index; + } + } + return 0; +} + +void SkTestFont::init(const SkScalar* pts, const unsigned char* verbs) { + fPaths = new SkPath* [fCharCodesCount]; + for (unsigned index = 0; index < fCharCodesCount; ++index) { + SkPath* path = new SkPath; + SkPath::Verb verb; + while ((verb = (SkPath::Verb) *verbs++) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kMove_Verb: + path->moveTo(pts[0], pts[1]); + pts += 2; + break; + case SkPath::kLine_Verb: + path->lineTo(pts[0], pts[1]); + pts += 2; + break; + case SkPath::kQuad_Verb: + path->quadTo(pts[0], pts[1], pts[2], pts[3]); + pts += 4; + break; + case SkPath::kCubic_Verb: + path->cubicTo(pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]); + pts += 6; + break; + case SkPath::kClose_Verb: + path->close(); + break; + default: + SkDEBUGFAIL("bad verb"); + return; + } + } + // This should make SkPath::getBounds() queries threadsafe. + path->updateBoundsCache(); + fPaths[index] = path; + } +} + +SkTestTypeface::SkTestTypeface(sk_sp<SkTestFont> testFont, const SkFontStyle& style) + : SkTypeface(style, false) + , fTestFont(std::move(testFont)) { +} + +void SkTestTypeface::getAdvance(SkGlyph* glyph) { + // TODO(benjaminwagner): Update users to use floats. + glyph->fAdvanceX = SkFixedToFloat(fTestFont->fWidths[glyph->getGlyphID()]); + glyph->fAdvanceY = 0; +} + +void SkTestTypeface::getFontMetrics(SkPaint::FontMetrics* metrics) { + *metrics = fTestFont->fMetrics; +} + +void SkTestTypeface::getMetrics(SkGlyph* glyph) { + SkGlyphID glyphID = glyph->getGlyphID(); + glyphID = glyphID < fTestFont->fCharCodesCount ? glyphID : 0; + + // TODO(benjaminwagner): Update users to use floats. + glyph->fAdvanceX = SkFixedToFloat(fTestFont->fWidths[glyphID]); + glyph->fAdvanceY = 0; +} + +void SkTestTypeface::getPath(SkGlyphID glyphID, SkPath* path) { + glyphID = glyphID < fTestFont->fCharCodesCount ? glyphID : 0; + *path = *fTestFont->fPaths[glyphID]; +} + +void SkTestTypeface::onFilterRec(SkScalerContextRec* rec) const { + rec->setHinting(SkPaint::kNo_Hinting); +} + +std::unique_ptr<SkAdvancedTypefaceMetrics> SkTestTypeface::onGetAdvancedMetrics() const { // pdf only + std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics); + info->fFontName.set(fTestFont->fName); + int glyphCount = this->onCountGlyphs(); + + SkTDArray<SkUnichar>& toUnicode = info->fGlyphToUnicode; + toUnicode.setCount(glyphCount); + SkASSERT(glyphCount == SkToInt(fTestFont->fCharCodesCount)); + for (int gid = 0; gid < glyphCount; ++gid) { + toUnicode[gid] = SkToS32(fTestFont->fCharCodes[gid]); + } + return info; +} + +void SkTestTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const { + desc->setFamilyName(fTestFont->fName); + desc->setStyle(this->fontStyle()); + *isLocal = false; +} + +int SkTestTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const { + auto utf8 = (const char*)chars; + auto utf16 = (const uint16_t*)chars; + auto utf32 = (const SkUnichar*)chars; + + for (int i = 0; i < glyphCount; i++) { + SkUnichar ch; + switch (encoding) { + case kUTF8_Encoding: ch = SkUTF8_NextUnichar(&utf8 ); break; + case kUTF16_Encoding: ch = SkUTF16_NextUnichar(&utf16); break; + case kUTF32_Encoding: ch = *utf32++; break; + } + if (glyphs) { + glyphs[i] = fTestFont->codeToIndex(ch); + } + } + return glyphCount; +} + +void SkTestTypeface::onGetFamilyName(SkString* familyName) const { + *familyName = fTestFont->fName; +} + +SkTypeface::LocalizedStrings* SkTestTypeface::onCreateFamilyNameIterator() const { + SkString familyName(fTestFont->fName); + SkString language("und"); //undetermined + return new SkOTUtils::LocalizedStrings_SingleName(familyName, language); +} + +class SkTestScalerContext : public SkScalerContext { +public: + SkTestScalerContext(sk_sp<SkTestTypeface> face, const SkScalerContextEffects& effects, + const SkDescriptor* desc) + : SkScalerContext(std::move(face), effects, desc) + { + fRec.getSingleMatrix(&fMatrix); + this->forceGenerateImageFromPath(); + } + +protected: + SkTestTypeface* getTestTypeface() const { + return static_cast<SkTestTypeface*>(this->getTypeface()); + } + + unsigned generateGlyphCount() override { + return this->getTestTypeface()->onCountGlyphs(); + } + + uint16_t generateCharToGlyph(SkUnichar uni) override { + uint16_t glyph; + (void) this->getTestTypeface()->onCharsToGlyphs((const void *) &uni, + SkTypeface::kUTF32_Encoding, &glyph, 1); + return glyph; + } + + void generateAdvance(SkGlyph* glyph) override { + this->getTestTypeface()->getAdvance(glyph); + + const SkVector advance = fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), + SkFloatToScalar(glyph->fAdvanceY)); + glyph->fAdvanceX = SkScalarToFloat(advance.fX); + glyph->fAdvanceY = SkScalarToFloat(advance.fY); + } + + void generateMetrics(SkGlyph* glyph) override { + this->getTestTypeface()->getMetrics(glyph); + + const SkVector advance = fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), + SkFloatToScalar(glyph->fAdvanceY)); + glyph->fAdvanceX = SkScalarToFloat(advance.fX); + glyph->fAdvanceY = SkScalarToFloat(advance.fY); + + SkPath path; + this->getTestTypeface()->getPath(glyph->getGlyphID(), &path); + path.transform(fMatrix); + + SkRect storage; + const SkPaint paint; + const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(), + &storage, + SkPaint::kFill_Style); + SkIRect ibounds; + newBounds.roundOut(&ibounds); + glyph->fLeft = ibounds.fLeft; + glyph->fTop = ibounds.fTop; + glyph->fWidth = ibounds.width(); + glyph->fHeight = ibounds.height(); + } + + void generateImage(const SkGlyph& glyph) override { + SkPath path; + this->getTestTypeface()->getPath(glyph.getGlyphID(), &path); + + SkBitmap bm; + bm.installPixels(SkImageInfo::MakeN32Premul(glyph.fWidth, glyph.fHeight), + glyph.fImage, glyph.rowBytes()); + bm.eraseColor(0); + + SkCanvas canvas(bm); + canvas.translate(-SkIntToScalar(glyph.fLeft), + -SkIntToScalar(glyph.fTop)); + canvas.concat(fMatrix); + SkPaint paint; + paint.setAntiAlias(true); + canvas.drawPath(path, paint); + } + + void generatePath(SkGlyphID glyph, SkPath* path) override { + this->getTestTypeface()->getPath(glyph, path); + path->transform(fMatrix); + } + + void generateFontMetrics(SkPaint::FontMetrics* metrics) override { + this->getTestTypeface()->getFontMetrics(metrics); + SkPaintPriv::ScaleFontMetrics(metrics, fMatrix.getScaleY()); + } + +private: + SkMatrix fMatrix; +}; + +SkScalerContext* SkTestTypeface::onCreateScalerContext( + const SkScalerContextEffects& effects, const SkDescriptor* desc) const +{ + return new SkTestScalerContext(sk_ref_sp(const_cast<SkTestTypeface*>(this)), effects, desc); +} diff --git a/chromium/third_party/skia/tools/SkTestScalerContext.h b/chromium/third_party/skia/tools/SkTestScalerContext.h new file mode 100644 index 00000000000..e62210b2e30 --- /dev/null +++ b/chromium/third_party/skia/tools/SkTestScalerContext.h @@ -0,0 +1,102 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTestScalerContext_DEFINED +#define SkTestScalerContext_DEFINED + +#include "SkFixed.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkRefCnt.h" +#include "SkTDArray.h" +#include "SkTypeface.h" + +class SkTestFont; + +struct SkTestFontData { + const SkScalar* fPoints; + const unsigned char* fVerbs; + const unsigned* fCharCodes; + const size_t fCharCodesCount; + const SkFixed* fWidths; + const SkPaint::FontMetrics& fMetrics; + const char* fName; + SkFontStyle fStyle; + sk_sp<SkTestFont> fCachedFont; +}; + +class SkTestFont : public SkRefCnt { +public: + SkTestFont(const SkTestFontData& ); + virtual ~SkTestFont(); + int codeToIndex(SkUnichar charCode) const; + void init(const SkScalar* pts, const unsigned char* verbs); +private: + const unsigned* fCharCodes; + const size_t fCharCodesCount; + const SkFixed* fWidths; + const SkPaint::FontMetrics& fMetrics; + const char* fName; + SkPath** fPaths; + friend class SkTestTypeface; + typedef SkRefCnt INHERITED; +}; + + +class SkTestTypeface : public SkTypeface { +public: + SkTestTypeface(sk_sp<SkTestFont>, const SkFontStyle& style); + void getAdvance(SkGlyph* glyph); + void getFontMetrics(SkPaint::FontMetrics* metrics); + void getMetrics(SkGlyph* glyph); + void getPath(SkGlyphID glyph, SkPath* path); +protected: + SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&, + const SkDescriptor* desc) const override; + void onFilterRec(SkScalerContextRec* rec) const override; + std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; + + SkStreamAsset* onOpenStream(int* ttcIndex) const override { + return nullptr; + } + + void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override; + + int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const override; + + int onCountGlyphs() const override { + return (int) fTestFont->fCharCodesCount; + } + + int onGetUPEM() const override { + return 2048; + } + + void onGetFamilyName(SkString* familyName) const override; + SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return 0; + } + + int onGetTableTags(SkFontTableTag tags[]) const override { + return 0; + } + + size_t onGetTableData(SkFontTableTag tag, size_t offset, + size_t length, void* data) const override { + return 0; + } +private: + sk_sp<SkTestFont> fTestFont; + friend class SkTestScalerContext; +}; + +#endif diff --git a/chromium/third_party/skia/tools/android/upload_to_android.py b/chromium/third_party/skia/tools/android/upload_to_android.py index 9afbf298d44..dba0c1692cc 100644 --- a/chromium/third_party/skia/tools/android/upload_to_android.py +++ b/chromium/third_party/skia/tools/android/upload_to_android.py @@ -52,7 +52,7 @@ def get_change_details(change_num): return json.loads(content[5:]) -def upload_to_android(work_dir, change_num, debug): +def init_work_dir(work_dir): if not os.path.isdir(work_dir): print 'Creating %s' % work_dir os.makedirs(work_dir) @@ -106,23 +106,34 @@ About to run repo init. If it hangs asking you to run glogin then please: subprocess.check_call( 'git config user.email %s@google.com' % getpass.getuser(), shell=True) - # Create repo branch. - subprocess.check_call('%s start %s .' % (repo_binary, REPO_BRANCH_NAME), - shell=True) - try: - change_details = get_change_details(change_num) + return repo_binary + +class Modifier: + def modify(self): + raise NotImplementedError + def get_user_msg(self): + raise NotImplementedError + + +class FetchModifier(Modifier): + def __init__(self, change_num, debug): + self.change_num = change_num + self.debug = debug + + def modify(self): # Download and cherry-pick the patch. + change_details = get_change_details(self.change_num) latest_patchset = len(change_details['revisions']) - mod = int(change_num) % 100 + mod = int(self.change_num) % 100 download_ref = 'refs/changes/%s/%s/%s' % ( - str(mod).zfill(2), change_num, latest_patchset) + str(mod).zfill(2), self.change_num, latest_patchset) subprocess.check_call( 'git fetch https://skia.googlesource.com/skia %s' % download_ref, shell=True) subprocess.check_call('git cherry-pick FETCH_HEAD', shell=True) - if debug: + if self.debug: # Add SK_DEBUG to SkUserConfig.h. with open(SK_USER_CONFIG_PATH, 'a') as f: f.write('#ifndef SK_DEBUG\n') @@ -140,17 +151,70 @@ About to run repo init. If it hangs asking you to run glogin then please: '[DO ' + 'NOT ' + 'SUBMIT] %s\n\n' 'Test: Presubmit checks will test this change.' % ( original_commit_message)) + subprocess.check_call('git commit --amend -m "%s"' % new_commit_message, shell=True) - # Upload to Android Gerrit. - subprocess.check_call('%s upload --verify' % repo_binary, shell=True) - - print """ + def get_user_msg(self): + return """ Open the above URL and trigger TH by checking 'Presubmit-Ready'. You can download binaries (if required) from the TH link after it completes. """ + + +# Add a legacy flag if it doesn't exist, or remove it if it exists. +class AndroidLegacyFlagModifier(Modifier): + def __init__(self, flag): + self.flag = flag + self.verb = "Unknown" + + def modify(self): + flag_line = " #define %s\n" % self.flag + + config_file = os.path.join('include', 'config', 'SkUserConfigManual.h') + + with open(config_file) as f: + lines = f.readlines() + + if flag_line not in lines: + lines.insert( + lines.index("#endif // SkUserConfigManual_DEFINED\n"), flag_line) + verb = "Add" + else: + lines.remove(flag_line) + verb = "Remove" + + with open(config_file, 'w') as f: + for line in lines: + f.write(line) + + subprocess.check_call('git add %s' % config_file, shell=True) + message = '%s %s\n\nTest: Presubmit checks will test this change.' % ( + verb, self.flag) + + subprocess.check_call('git commit -m "%s"' % message, shell=True) + + def get_user_msg(self): + return """ + + Please open the above URL to review and land the change. +""" + + +def upload_to_android(work_dir, modifier): + repo_binary = init_work_dir(work_dir) + + # Create repo branch. + subprocess.check_call('%s start %s .' % (repo_binary, REPO_BRANCH_NAME), + shell=True) + try: + modifier.modify() + + # Upload to Android Gerrit. + subprocess.check_call('%s upload --verify' % repo_binary, shell=True) + + print modifier.get_user_msg() finally: # Abandon repo branch. subprocess.call('%s abandon %s' % (repo_binary, REPO_BRANCH_NAME), @@ -171,7 +235,7 @@ def main(): '--debug', '-d', action='store_true', default=False, help='Adds SK_DEBUG to SkUserConfig.h.') args = parser.parse_args() - upload_to_android(args.work_dir, args.change_num, args.debug) + upload_to_android(args.work_dir, FetchModifier(args.change_num, args.debug)) if __name__ == '__main__': diff --git a/chromium/third_party/skia/tools/bookmaker/bookmaker.cpp b/chromium/third_party/skia/tools/bookmaker/bookmaker.cpp index cc454982fd7..e0483758605 100644 --- a/chromium/third_party/skia/tools/bookmaker/bookmaker.cpp +++ b/chromium/third_party/skia/tools/bookmaker/bookmaker.cpp @@ -7,19 +7,17 @@ #include "bookmaker.h" -#include "SkOSFile.h" -#include "SkOSPath.h" - DEFINE_string2(bmh, b, "", "Path to a *.bmh file or a directory."); +DEFINE_bool2(catalog, c, false, "Write example catalog.htm. (Requires -b -f -r)"); DEFINE_string2(examples, e, "", "File of fiddlecli input, usually fiddle.json (For now, disables -r -f -s)"); DEFINE_string2(fiddle, f, "", "File of fiddlecli output, usually fiddleout.json."); DEFINE_string2(include, i, "", "Path to a *.h file or a directory."); DEFINE_bool2(hack, k, false, "Do a find/replace hack to update all *.bmh files. (Requires -b)"); DEFINE_bool2(stdout, o, false, "Write file out to standard out."); DEFINE_bool2(populate, p, false, "Populate include from bmh. (Requires -b -i)"); -DEFINE_string2(ref, r, "", "Resolve refs and write bmh_*.md files to path. (Requires -b)"); +DEFINE_string2(ref, r, "", "Resolve refs and write bmh_*.md files to path. (Requires -b -f)"); DEFINE_string2(spellcheck, s, "", "Spell-check [once, all, mispelling]. (Requires -b)"); -DEFINE_string2(tokens, t, "", "Directory to write bmh from include. (Requires -i)"); +DEFINE_bool2(tokens, t, false, "Write bmh from include. (Requires -b -i)"); DEFINE_bool2(crosscheck, x, false, "Check bmh against includes. (Requires -b -i)"); DEFINE_bool2(skip, z, false, "Skip degenerate missed in legacy preprocessor."); @@ -42,828 +40,6 @@ trouble with aliases, plurals check for summary containing all methods */ -static string normalized_name(string name) { - string normalizedName = name; - std::replace(normalizedName.begin(), normalizedName.end(), '-', '_'); - do { - size_t doubleColon = normalizedName.find("::", 0); - if (string::npos == doubleColon) { - break; - } - normalizedName = normalizedName.substr(0, doubleColon) - + '_' + normalizedName.substr(doubleColon + 2); - } while (true); - return normalizedName; -} - -static size_t count_indent(const string& text, size_t test, size_t end) { - size_t result = test; - while (test < end) { - if (' ' != text[test]) { - break; - } - ++test; - } - return test - result; -} - -static void add_code(const string& text, int pos, int end, - size_t outIndent, size_t textIndent, string& example) { - do { - // fix this to move whole paragraph in, out, but preserve doc indent - int nextIndent = count_indent(text, pos, end); - size_t len = text.find('\n', pos); - if (string::npos == len) { - len = end; - } - if ((size_t) (pos + nextIndent) < len) { - size_t indent = outIndent + nextIndent; - SkASSERT(indent >= textIndent); - indent -= textIndent; - for (size_t index = 0; index < indent; ++index) { - example += ' '; - } - pos += nextIndent; - while ((size_t) pos < len) { - example += '"' == text[pos] ? "\\\"" : - '\\' == text[pos] ? "\\\\" : - text.substr(pos, 1); - ++pos; - } - example += "\\n"; - } else { - pos += nextIndent; - } - if ('\n' == text[pos]) { - ++pos; - } - } while (pos < end); -} - -// fixme: this will need to be more complicated to handle all of Skia -// for now, just handle paint -- maybe fiddle will loosen naming restrictions -void Definition::setCanonicalFiddle() { - fMethodType = Definition::MethodType::kNone; - size_t doubleColons = fName.find("::", 0); - SkASSERT(string::npos != doubleColons); - string base = fName.substr(0, doubleColons); - string result = base + "_"; - doubleColons += 2; - if (string::npos != fName.find('~', doubleColons)) { - fMethodType = Definition::MethodType::kDestructor; - result += "destructor"; - } else { - bool isMove = string::npos != fName.find("&&", doubleColons); - const char operatorStr[] = "operator"; - size_t opPos = fName.find(operatorStr, doubleColons); - if (string::npos != opPos) { - fMethodType = Definition::MethodType::kOperator; - opPos += sizeof(operatorStr) - 1; - if ('!' == fName[opPos]) { - SkASSERT('=' == fName[opPos + 1]); - result += "not_equal_operator"; - } else if ('=' == fName[opPos]) { - if ('(' == fName[opPos + 1]) { - result += isMove ? "move_" : "copy_"; - result += "assignment_operator"; - } else { - SkASSERT('=' == fName[opPos + 1]); - result += "equal_operator"; - } - } else if ('[' == fName[opPos]) { - result += "subscript_operator"; - const char* end = fContentStart; - while (end > fStart && ' ' >= end[-1]) { - --end; - } - string constCheck(fStart, end - fStart); - size_t constPos = constCheck.rfind("const"); - if (constCheck.length() == constPos + 5) { - result += "_const"; - } - } else { - SkASSERT(0); // todo: incomplete - } - } else { - size_t parens = fName.find("()", doubleColons); - if (string::npos != parens) { - string methodName = fName.substr(doubleColons, parens - doubleColons); - do { - size_t nextDouble = methodName.find("::"); - if (string::npos == nextDouble) { - break; - } - base = methodName.substr(0, nextDouble); - result += base + '_'; - methodName = methodName.substr(nextDouble + 2); - doubleColons += nextDouble + 2; - } while (true); - if (base == methodName) { - fMethodType = Definition::MethodType::kConstructor; - result += "empty_constructor"; - } else { - result += fName.substr(doubleColons, fName.length() - doubleColons - 2); - } - } else { - size_t openParen = fName.find('(', doubleColons); - if (string::npos == openParen) { - result += fName.substr(doubleColons); - } else { - size_t comma = fName.find(',', doubleColons); - if (string::npos == comma) { - result += isMove ? "move_" : "copy_"; - } - fMethodType = Definition::MethodType::kConstructor; - // name them by their param types, - // e.g. SkCanvas__int_int_const_SkSurfaceProps_star - // TODO: move forward until parens are balanced and terminator =,) - TextParser params("", &fName[openParen] + 1, &*fName.end(), 0); - bool underline = false; - while (!params.eof()) { - // SkDEBUGCODE(const char* end = params.anyOf("(),=")); // unused for now - // SkASSERT(end[0] != '('); // fixme: put off handling nested parentheseses - if (params.startsWith("const") || params.startsWith("int") - || params.startsWith("Sk")) { - const char* wordStart = params.fChar; - params.skipToNonAlphaNum(); - if (underline) { - result += '_'; - } else { - underline = true; - } - result += string(wordStart, params.fChar - wordStart); - } else { - params.skipToNonAlphaNum(); - } - if (!params.eof() && '*' == params.peek()) { - if (underline) { - result += '_'; - } else { - underline = true; - } - result += "star"; - params.next(); - params.skipSpace(); - } - params.skipToAlpha(); - } - } - } - } - } - fFiddle = normalized_name(result); -} - -bool Definition::exampleToScript(string* result) const { - bool hasFiddle = true; - const Definition* platform = this->hasChild(MarkType::kPlatform); - if (platform) { - TextParser platParse(platform); - hasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd); - } - if (!hasFiddle) { - *result = ""; - return true; - } - string text = this->extractText(Definition::TrimExtract::kNo); - const char drawWrapper[] = "void draw(SkCanvas* canvas) {"; - const char drawNoCanvas[] = "void draw(SkCanvas* ) {"; - size_t nonSpace = 0; - while (nonSpace < text.length() && ' ' >= text[nonSpace]) { - ++nonSpace; - } - bool hasFunc = !text.compare(nonSpace, sizeof(drawWrapper) - 1, drawWrapper); - bool noCanvas = !text.compare(nonSpace, sizeof(drawNoCanvas) - 1, drawNoCanvas); - bool hasCanvas = string::npos != text.find("SkCanvas canvas"); - SkASSERT(!hasFunc || !noCanvas); - bool textOut = string::npos != text.find("SkDebugf(") - || string::npos != text.find("dump(") - || string::npos != text.find("dumpHex("); - string heightStr = "256"; - string widthStr = "256"; - bool preprocessor = text[0] == '#'; - string normalizedName(fFiddle); - string code; - string imageStr = "0"; - for (auto const& iter : fChildren) { - switch (iter->fMarkType) { - case MarkType::kError: - result->clear(); - return true; - case MarkType::kHeight: - heightStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); - break; - case MarkType::kWidth: - widthStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); - break; - case MarkType::kDescription: - // ignore for now - break; - case MarkType::kFunction: { - // emit this, but don't wrap this in draw() - string funcText(iter->fContentStart, iter->fContentEnd - iter->fContentStart - 1); - size_t pos = 0; - while (pos < funcText.length() && ' ' > funcText[pos]) { - ++pos; - } - size_t indent = count_indent(funcText, pos, funcText.length()); - add_code(funcText, pos, funcText.length(), 0, indent, code); - code += "\\n"; - } break; - case MarkType::kComment: - break; - case MarkType::kImage: - imageStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); - break; - case MarkType::kToDo: - break; - case MarkType::kMarkChar: - case MarkType::kPlatform: - // ignore for now - break; - case MarkType::kStdOut: - textOut = true; - break; - default: - SkASSERT(0); // more coding to do - } - } - string textOutStr = textOut ? "true" : "false"; - size_t pos = 0; - while (pos < text.length() && ' ' > text[pos]) { - ++pos; - } - size_t end = text.length(); - size_t outIndent = 0; - size_t textIndent = count_indent(text, pos, end); - bool wrapCode = !hasFunc && !noCanvas && !preprocessor; - if (wrapCode) { - code += hasCanvas ? drawNoCanvas : drawWrapper; - code += "\\n"; - outIndent = 4; - } - add_code(text, pos, end, outIndent, textIndent, code); - if (wrapCode) { - code += "}"; - } - string example = "\"" + normalizedName + "\": {\n"; - example += " \"code\": \"" + code + "\",\n"; - example += " \"options\": {\n"; - example += " \"width\": " + widthStr + ",\n"; - example += " \"height\": " + heightStr + ",\n"; - example += " \"source\": " + imageStr + ",\n"; - example += " \"srgb\": false,\n"; - example += " \"f16\": false,\n"; - example += " \"textOnly\": " + textOutStr + ",\n"; - example += " \"animated\": false,\n"; - example += " \"duration\": 0\n"; - example += " },\n"; - example += " \"fast\": true\n"; - example += "}"; - *result = example; - return true; -} - -static void space_pad(string* str) { - size_t len = str->length(); - if (len == 0) { - return; - } - char last = (*str)[len - 1]; - if ('~' == last || ' ' >= last) { - return; - } - *str += ' '; -} - -//start here; -// see if it possible to abstract this a little bit so it can -// additionally be used to find params and return in method prototype that -// does not have corresponding doxygen comments -bool Definition::checkMethod() const { - SkASSERT(MarkType::kMethod == fMarkType); - // if method returns a value, look for a return child - // for each parameter, look for a corresponding child - const char* end = fContentStart; - while (end > fStart && ' ' >= end[-1]) { - --end; - } - TextParser methodParser(fFileName, fStart, end, fLineCount); - methodParser.skipWhiteSpace(); - SkASSERT(methodParser.startsWith("#Method")); - methodParser.skipName("#Method"); - methodParser.skipSpace(); - string name = this->methodName(); - if (MethodType::kNone == fMethodType && name.length() > 2 && - "()" == name.substr(name.length() - 2)) { - name = name.substr(0, name.length() - 2); - } - bool expectReturn = this->methodHasReturn(name, &methodParser); - bool foundReturn = false; - bool foundException = false; - for (auto& child : fChildren) { - foundException |= MarkType::kDeprecated == child->fMarkType - || MarkType::kExperimental == child->fMarkType; - if (MarkType::kReturn != child->fMarkType) { - if (MarkType::kParam == child->fMarkType) { - child->fVisited = false; - } - continue; - } - if (!expectReturn) { - return methodParser.reportError<bool>("no #Return expected"); - } - if (foundReturn) { - return methodParser.reportError<bool>("multiple #Return markers"); - } - foundReturn = true; - } - if (expectReturn && !foundReturn && !foundException) { - return methodParser.reportError<bool>("missing #Return marker"); - } - const char* paren = methodParser.strnchr('(', methodParser.fEnd); - if (!paren) { - return methodParser.reportError<bool>("missing #Method function definition"); - } - const char* nextEnd = paren; - do { - string paramName; - methodParser.fChar = nextEnd + 1; - methodParser.skipSpace(); - if (!this->nextMethodParam(&methodParser, &nextEnd, ¶mName)) { - continue; - } - bool foundParam = false; - for (auto& child : fChildren) { - if (MarkType::kParam != child->fMarkType) { - continue; - } - if (paramName != child->fName) { - continue; - } - if (child->fVisited) { - return methodParser.reportError<bool>("multiple #Method param with same name"); - } - child->fVisited = true; - if (foundParam) { - TextParser paramError(child); - return methodParser.reportError<bool>("multiple #Param with same name"); - } - foundParam = true; - - } - if (!foundParam && !foundException) { - return methodParser.reportError<bool>("no #Param found"); - } - if (')' == nextEnd[0]) { - break; - } - } while (')' != nextEnd[0]); - for (auto& child : fChildren) { - if (MarkType::kParam != child->fMarkType) { - continue; - } - if (!child->fVisited) { - TextParser paramError(child); - return paramError.reportError<bool>("#Param without param in #Method"); - } - } - return true; -} - -bool Definition::crossCheck2(const Definition& includeToken) const { - TextParser parser(fFileName, fStart, fContentStart, fLineCount); - parser.skipExact("#"); - bool isMethod = parser.skipName("Method"); - const char* contentEnd; - if (isMethod) { - contentEnd = fContentStart; - } else if (parser.skipName("DefinedBy")) { - contentEnd = fContentEnd; - while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) { - --contentEnd; - } - if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) { - contentEnd -= 2; - } - } else { - return parser.reportError<bool>("unexpected crosscheck marktype"); - } - return crossCheckInside(parser.fChar, contentEnd, includeToken); -} - -bool Definition::crossCheck(const Definition& includeToken) const { - return crossCheckInside(fContentStart, fContentEnd, includeToken); -} - -bool Definition::crossCheckInside(const char* start, const char* end, - const Definition& includeToken) const { - TextParser def(fFileName, start, end, fLineCount); - TextParser inc("", includeToken.fContentStart, includeToken.fContentEnd, 0); - if (inc.startsWith("SK_API")) { - inc.skipWord("SK_API"); - } - if (inc.startsWith("friend")) { - inc.skipWord("friend"); - } - if (inc.startsWith("SK_API")) { - inc.skipWord("SK_API"); - } - do { - bool defEof; - bool incEof; - do { - defEof = def.eof() || !def.skipWhiteSpace(); - incEof = inc.eof() || !inc.skipWhiteSpace(); - if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) { - inc.next(); - if ('*' == inc.peek()) { - inc.skipToEndBracket("*/"); - inc.next(); - } else if ('/' == inc.peek()) { - inc.skipToEndBracket('\n'); - } - } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) { - inc.next(); - if (inc.startsWith("if")) { - inc.skipToEndBracket("\n"); - } else if (inc.startsWith("endif")) { - inc.skipToEndBracket("\n"); - } else { - SkASSERT(0); // incomplete - return false; - } - } else { - break; - } - inc.next(); - } while (true); - if (defEof || incEof) { - if (defEof == incEof || (!defEof && ';' == def.peek())) { - return true; - } - return false; // allow setting breakpoint on failure - } - char defCh; - do { - defCh = def.next(); - char incCh = inc.next(); - if (' ' >= defCh && ' ' >= incCh) { - break; - } - if (defCh != incCh) { - return false; - } - if (';' == defCh) { - return true; - } - } while (!def.eof() && !inc.eof()); - } while (true); - return false; -} - -string Definition::formatFunction() const { - const char* end = fContentStart; - while (end > fStart && ' ' >= end[-1]) { - --end; - } - TextParser methodParser(fFileName, fStart, end, fLineCount); - methodParser.skipWhiteSpace(); - SkASSERT(methodParser.startsWith("#Method")); - methodParser.skipName("#Method"); - methodParser.skipSpace(); - const char* lastStart = methodParser.fChar; - const int limit = 80; // todo: allow this to be set by caller or in global or something - string methodStr; - string name = this->methodName(); - const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd); - methodParser.skipTo(nameInParser); - const char* lastEnd = methodParser.fChar; - const char* paren = methodParser.strnchr('(', methodParser.fEnd); - size_t indent; - if (paren) { - indent = (size_t) (paren - lastStart) + 1; - } else { - indent = (size_t) (lastEnd - lastStart); - } - int written = 0; - do { - const char* nextStart = lastEnd; - SkASSERT(written < limit); - const char* delimiter = methodParser.anyOf(",)"); - const char* nextEnd = delimiter ? delimiter : methodParser.fEnd; - if (delimiter) { - while (nextStart < nextEnd && ' ' >= nextStart[0]) { - ++nextStart; - } - } - while (nextEnd > nextStart && ' ' >= nextEnd[-1]) { - --nextEnd; - } - if (delimiter) { - nextEnd += 1; - delimiter += 1; - } - if (lastEnd > lastStart) { - if (lastStart[0] != ' ') { - space_pad(&methodStr); - } - methodStr += string(lastStart, (size_t) (lastEnd - lastStart)); - written += (size_t) (lastEnd - lastStart); - } - if (delimiter) { - if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) { - written = indent; - methodStr += '\n'; - methodStr += string(indent, ' '); - } - methodParser.skipTo(delimiter); - } - lastStart = nextStart; - lastEnd = nextEnd; - } while (lastStart < lastEnd); - return methodStr; -} - -string Definition::fiddleName() const { - string result; - size_t start = 0; - string parent; - const Definition* parentDef = this; - while ((parentDef = parentDef->fParent)) { - if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) { - parent = parentDef->fFiddle; - break; - } - } - if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) { - start = parent.length(); - while (start < fFiddle.length() && '_' == fFiddle[start]) { - ++start; - } - } - size_t end = fFiddle.find_first_of('(', start); - return fFiddle.substr(start, end - start); -} - -const Definition* Definition::hasChild(MarkType markType) const { - for (auto iter : fChildren) { - if (markType == iter->fMarkType) { - return iter; - } - } - return nullptr; -} - -const Definition* Definition::hasParam(const string& ref) const { - SkASSERT(MarkType::kMethod == fMarkType); - for (auto iter : fChildren) { - if (MarkType::kParam != iter->fMarkType) { - continue; - } - if (iter->fName == ref) { - return &*iter; - } - - } - return nullptr; -} - -bool Definition::methodHasReturn(const string& name, TextParser* methodParser) const { - const char* lastStart = methodParser->fChar; - const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd); - methodParser->skipTo(nameInParser); - const char* lastEnd = methodParser->fChar; - const char* returnEnd = lastEnd; - while (returnEnd > lastStart && ' ' == returnEnd[-1]) { - --returnEnd; - } - bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4); - if (MethodType::kNone != fMethodType && !expectReturn) { - return methodParser->reportError<bool>("unexpected void"); - } - switch (fMethodType) { - case MethodType::kNone: - case MethodType::kOperator: - // either is fine - break; - case MethodType::kConstructor: - expectReturn = true; - break; - case MethodType::kDestructor: - expectReturn = false; - break; - } - return expectReturn; -} - -string Definition::methodName() const { - string result; - size_t start = 0; - string parent; - const Definition* parentDef = this; - while ((parentDef = parentDef->fParent)) { - if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) { - parent = parentDef->fName; - break; - } - } - if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) { - start = parent.length(); - while (start < fName.length() && ':' == fName[start]) { - ++start; - } - } - if (fClone) { - int lastUnder = fName.rfind('_'); - return fName.substr(start, (size_t) (lastUnder - start)); - } - size_t end = fName.find_first_of('(', start); - if (string::npos == end) { - return fName.substr(start); - } - return fName.substr(start, end - start); -} - -bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr, - string* paramName) const { - int parenCount = 0; - TextParser::Save saveState(methodParser); - while (true) { - if (methodParser->eof()) { - return methodParser->reportError<bool>("#Method function missing close paren"); - } - char ch = methodParser->peek(); - if ('(' == ch) { - ++parenCount; - } - if (parenCount == 0 && (')' == ch || ',' == ch)) { - *nextEndPtr = methodParser->fChar; - break; - } - if (')' == ch) { - if (0 > --parenCount) { - return this->reportError<bool>("mismatched parentheses"); - } - } - methodParser->next(); - } - saveState.restore(); - const char* nextEnd = *nextEndPtr; - const char* paramEnd = nextEnd; - const char* assign = methodParser->strnstr(" = ", paramEnd); - if (assign) { - paramEnd = assign; - } - const char* closeBracket = methodParser->strnstr("]", paramEnd); - if (closeBracket) { - const char* openBracket = methodParser->strnstr("[", paramEnd); - if (openBracket && openBracket < closeBracket) { - while (openBracket < --closeBracket && isdigit(closeBracket[0])) - ; - if (openBracket == closeBracket) { - paramEnd = openBracket; - } - } - } - const char* function = methodParser->strnstr(")(", paramEnd); - if (function) { - paramEnd = function; - } - while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) { - --paramEnd; - } - const char* paramStart = paramEnd; - while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) { - --paramStart; - } - if (paramStart > methodParser->fChar && paramStart >= paramEnd) { - return methodParser->reportError<bool>("#Method missing param name"); - } - *paramName = string(paramStart, paramEnd - paramStart); - if (!paramName->length()) { - if (')' != nextEnd[0]) { - return methodParser->reportError<bool>("#Method malformed param"); - } - return false; - } - return true; -} - - bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix) { - if (!sk_isdir(fileOrPath)) { - if (!this->parseFromFile(fileOrPath)) { - SkDebugf("failed to parse %s\n", fileOrPath); - return false; - } - } else { - SkOSFile::Iter it(fileOrPath, suffix); - for (SkString file; it.next(&file); ) { - SkString p = SkOSPath::Join(fileOrPath, file.c_str()); - const char* hunk = p.c_str(); - if (!SkStrEndsWith(hunk, suffix)) { - continue; - } - if (!this->parseFromFile(hunk)) { - SkDebugf("failed to parse %s\n", hunk); - return false; - } - } - } - return true; -} - -bool Definition::paramsMatch(const string& match, const string& name) const { - TextParser def(fFileName, fStart, fContentStart, fLineCount); - const char* dName = def.strnstr(name.c_str(), fContentStart); - if (!dName) { - return false; - } - def.skipTo(dName); - TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount); - const char* mName = m.strnstr(name.c_str(), m.fEnd); - if (!mName) { - return false; - } - m.skipTo(mName); - while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) { - const char* ds = def.fChar; - const char* ms = m.fChar; - const char* de = def.anyOf(") \n"); - const char* me = m.anyOf(") \n"); - def.skipTo(de); - m.skipTo(me); - if (def.fChar - ds != m.fChar - ms) { - return false; - } - if (strncmp(ds, ms, (int) (def.fChar - ds))) { - return false; - } - def.skipWhiteSpace(); - m.skipWhiteSpace(); - } - return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek(); -} - -void RootDefinition::clearVisited() { - fVisited = false; - for (auto& leaf : fLeaves) { - leaf.second.fVisited = false; - } - for (auto& branch : fBranches) { - branch.second->clearVisited(); - } -} - -bool RootDefinition::dumpUnVisited() { - bool allStructElementsFound = true; - for (auto& leaf : fLeaves) { - if (!leaf.second.fVisited) { - // TODO: parse embedded struct in includeParser phase, then remove this condition - if (FLAGS_skip) { - const Definition& def = leaf.second; - if (def.fChildren.size() > 0 && - MarkType::kDeprecated == def.fChildren[0]->fMarkType) { - continue; - } - } - SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str()); - } - } - for (auto& branch : fBranches) { - allStructElementsFound &= branch.second->dumpUnVisited(); - } - return allStructElementsFound; -} - -const Definition* RootDefinition::find(const string& ref, AllowParens allowParens) const { - const auto leafIter = fLeaves.find(ref); - if (leafIter != fLeaves.end()) { - return &leafIter->second; - } - if (AllowParens::kYes == allowParens && string::npos == ref.find("()")) { - string withParens = ref + "()"; - const auto parensIter = fLeaves.find(withParens); - if (parensIter != fLeaves.end()) { - return &parensIter->second; - } - } - const auto branchIter = fBranches.find(ref); - if (branchIter != fBranches.end()) { - const RootDefinition* rootDef = branchIter->second; - return rootDef; - } - const Definition* result = nullptr; - for (const auto& branch : fBranches) { - const RootDefinition* rootDef = branch.second; - result = rootDef->find(ref, allowParens); - if (result) { - break; - } - } - return result; -} - /* class contains named struct, enum, enum-member, method, topic, subtopic everything contained by class is uniquely named @@ -982,7 +158,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy definition->fFiddle = name; } } else { - definition->fFiddle = normalized_name(name); + definition->fFiddle = Definition::NormalizedName(name); } definition->fMarkType = markType; definition->fAnonymous = fAnonymous; @@ -1008,7 +184,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy parent = parent->fParent; } definition->fFiddle = parent ? parent->fFiddle + '_' : ""; - definition->fFiddle += normalized_name(typeNameBuilder[0]); + definition->fFiddle += Definition::NormalizedName(typeNameBuilder[0]); this->setAsParent(definition); } { @@ -1049,7 +225,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy definition->fStart = defStart; definition->fContentStart = start; definition->fName = name; - definition->fFiddle = normalized_name(name); + definition->fFiddle = Definition::NormalizedName(name); definition->fContentEnd = fChar; this->skipToEndBracket('\n'); definition->fTerminator = fChar; @@ -1121,6 +297,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy return this->reportError<bool>("missing example body"); } } + definition->setWrapper(); } } else { fMarkup.emplace_front(markType, defStart, fLineCount, fParent); @@ -1160,6 +337,8 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy case MarkType::kFile: case MarkType::kHeight: case MarkType::kImage: + case MarkType::kLiteral: + case MarkType::kOutdent: case MarkType::kPlatform: case MarkType::kSeeAlso: case MarkType::kSubstitute: @@ -1174,7 +353,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy fMarkup.emplace_front(markType, defStart, fLineCount, fParent); definition = &fMarkup.front(); definition->fName = typeNameBuilder[0]; - definition->fFiddle = normalized_name(typeNameBuilder[0]); + definition->fFiddle = Definition::NormalizedName(typeNameBuilder[0]); definition->fContentStart = fChar; definition->fContentEnd = this->trimmedBracketEnd('\n'); definition->fTerminator = this->lineEnd() - 1; @@ -1384,7 +563,7 @@ bool BmhParser::collectExternals() { RootDefinition* definition = &fExternals.front(); definition->fFileName = fFileName; definition->fName = string(wordStart ,fChar - wordStart); - definition->fFiddle = normalized_name(definition->fName); + definition->fFiddle = Definition::NormalizedName(definition->fName); } } while (!this->eof()); return true; @@ -1393,10 +572,12 @@ bool BmhParser::collectExternals() { static bool dump_examples(FILE* fiddleOut, const Definition& def, bool* continuation) { if (MarkType::kExample == def.fMarkType) { string result; - if (!def.exampleToScript(&result)) { + if (!def.exampleToScript(&result, Definition::ExampleOptions::kAll)) { return false; } if (result.length() > 0) { + result += "\n"; + result += "}"; if (*continuation) { fprintf(fiddleOut, ",\n"); } else { @@ -1601,7 +782,7 @@ bool BmhParser::findDefinitions() { } } if (fParent) { - return this->reportError<bool>("mismatched end"); + return fParent->reportError<bool>("mismatched end"); } return true; } @@ -1769,10 +950,20 @@ string BmhParser::methodName() { } builder = parent->fName + "::"; } + bool addConst = false; if (isConstructor || expectOperator) { paren = this->strnchr(')', end) + 1; + TextParser::Save saveState(this); + this->skipTo(paren); + if (this->skipExact("_const")) { + addConst = true; + } + saveState.restore(); } builder.append(nameStart, paren - nameStart); + if (addConst) { + builder.append("_const"); + } if (!expectOperator && allLower) { builder.append("()"); } @@ -1968,7 +1159,6 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) { case MarkType::kEnumClass: case MarkType::kClass: case MarkType::kStruct: - case MarkType::kTypedef: // expect name builder = this->className(markType); break; @@ -2001,6 +1191,8 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) { case MarkType::kFile: case MarkType::kHeight: case MarkType::kImage: + case MarkType::kLiteral: + case MarkType::kOutdent: case MarkType::kPlatform: case MarkType::kReturn: case MarkType::kSeeAlso: @@ -2020,6 +1212,9 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) { case MarkType::kMethod: builder = this->methodName(); break; + case MarkType::kTypedef: + builder = this->typedefName(); + break; case MarkType::kParam: // fixme: expect camelCase builder = this->word("", ""); @@ -2046,6 +1241,82 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) { return result; } +string BmhParser::typedefName() { + if (this->hasEndToken()) { + if (!fParent || !fParent->fName.length()) { + return this->reportError<string>("missing parent typedef name"); + } + SkASSERT(fMC == this->peek()); + this->next(); + SkASSERT(fMC == this->peek()); + this->next(); + SkASSERT(fMC != this->peek()); + return fParent->fName; + } + // look for typedef as one of three forms: + // typedef return-type (*NAME)(params); + // typedef alias NAME; + // typedef std::function<alias> NAME; + string builder; + const char* end = this->doubleLF(); + if (!end) { + end = fEnd; + } + const char* altEnd = this->strnstr("#Typedef ##", end); + if (altEnd) { + end = this->strnchr('\n', end); + } + if (!end) { + return this->reportError<string>("missing typedef std::function end bracket >"); + } + + if (this->startsWith("std::function")) { + if (!this->skipToEndBracket('>')) { + return this->reportError<string>("missing typedef std::function end bracket >"); + } + this->next(); + this->skipWhiteSpace(); + builder = string(fChar, end - fChar); + } else { + const char* paren = this->strnchr('(', end); + if (!paren) { + const char* lastWord = nullptr; + do { + this->skipToWhiteSpace(); + if (fChar < end && isspace(fChar[0])) { + this->skipWhiteSpace(); + lastWord = fChar; + } else { + break; + } + } while (true); + if (!lastWord) { + return this->reportError<string>("missing typedef name"); + } + builder = string(lastWord, end - lastWord); + } else { + this->skipTo(paren); + this->next(); + if ('*' != this->next()) { + return this->reportError<string>("missing typedef function asterisk"); + } + const char* nameStart = fChar; + if (!this->skipToEndBracket(')')) { + return this->reportError<string>("missing typedef function )"); + } + builder = string(nameStart, fChar - nameStart); + if (!this->skipToEndBracket('(')) { + return this->reportError<string>("missing typedef params ("); + } + if (! this->skipToEndBracket(')')) { + return this->reportError<string>("missing typedef params )"); + } + this->skipTo(end); + } + } + return uniqueRootName(builder, MarkType::kTypedef); +} + string BmhParser::uniqueName(const string& base, MarkType markType) { string builder(base); if (!builder.length()) { @@ -2077,6 +1348,9 @@ string BmhParser::uniqueRootName(const string& base, MarkType markType) { return markType == def.fMarkType && def.fName == numBuilder; }; + if (string::npos != base.find("SkMatrix::operator")) { + SkDebugf(""); + } string builder(base); if (!builder.length()) { builder = fParent->fName; @@ -2115,6 +1389,9 @@ tryNext: ; cloned->fCloned = true; } fCloned = true; + if (string::npos != builder.find("operator")) { + SkDebugf(""); + } numBuilder = builder + '_' + to_string(number); } while (++number); return numBuilder; @@ -2200,11 +1477,11 @@ static int count_children(const Definition& def, MarkType markType) { } int main(int argc, char** const argv) { - BmhParser bmhParser; + BmhParser bmhParser(FLAGS_skip); bmhParser.validate(); SkCommandLineFlags::SetUsage( - "Common Usage: bookmaker -i path/to/include.h -t\n" + "Common Usage: bookmaker -b path/to/bmh_files -i path/to/include.h -t\n" " bookmaker -b path/to/bmh_files -e fiddle.json\n" " ~/go/bin/fiddlecli --input fiddle.json --output fiddleout.json\n" " bookmaker -b path/to/bmh_files -f fiddleout.json -r path/to/md_files\n" @@ -2238,6 +1515,11 @@ int main(int argc, char** const argv) { SkCommandLineFlags::PrintUsage(); return 1; } + if ((FLAGS_bmh.isEmpty() || FLAGS_fiddle.isEmpty() || FLAGS_ref.isEmpty()) && FLAGS_catalog) { + SkDebugf("-c requires -b -f -r\n"); + SkCommandLineFlags::PrintUsage(); + return 1; + } if (FLAGS_bmh.isEmpty() && !FLAGS_examples.isEmpty()) { SkDebugf("-e requires -b\n"); SkCommandLineFlags::PrintUsage(); @@ -2271,8 +1553,8 @@ int main(int argc, char** const argv) { SkCommandLineFlags::PrintUsage(); return 1; } - if (FLAGS_include.isEmpty() && !FLAGS_tokens.isEmpty()) { - SkDebugf("-t requires -i\n"); + if ((FLAGS_include.isEmpty() || FLAGS_bmh.isEmpty()) && FLAGS_tokens) { + SkDebugf("-t requires -b -i\n"); SkCommandLineFlags::PrintUsage(); return 1; } @@ -2282,21 +1564,22 @@ int main(int argc, char** const argv) { return 1; } if (!FLAGS_bmh.isEmpty()) { + bmhParser.reset(); if (!bmhParser.parseFile(FLAGS_bmh[0], ".bmh")) { return -1; } } bool done = false; if (!FLAGS_include.isEmpty()) { - if (!FLAGS_tokens.isEmpty() || FLAGS_crosscheck) { + if (FLAGS_tokens || FLAGS_crosscheck) { IncludeParser includeParser; includeParser.validate(); if (!includeParser.parseFile(FLAGS_include[0], ".h")) { return -1; } - if (!FLAGS_tokens.isEmpty()) { + if (FLAGS_tokens) { includeParser.fDebugOut = FLAGS_stdout; - if (includeParser.dumpTokens(FLAGS_tokens[0])) { + if (includeParser.dumpTokens(FLAGS_bmh[0])) { bmhParser.fWroteOut = true; } done = true; @@ -2320,12 +1603,27 @@ int main(int argc, char** const argv) { done = true; } } - FiddleParser fparser(&bmhParser); - if (!done && !FLAGS_fiddle.isEmpty() && FLAGS_examples.isEmpty()) { + if (!done && !FLAGS_catalog && !FLAGS_fiddle.isEmpty() && FLAGS_examples.isEmpty()) { + FiddleParser fparser(&bmhParser); if (!fparser.parseFile(FLAGS_fiddle[0], ".txt")) { return -1; } } + if (!done && FLAGS_catalog && FLAGS_examples.isEmpty()) { + Catalog fparser(&bmhParser); + fparser.fDebugOut = FLAGS_stdout; + if (!fparser.openCatalog(FLAGS_bmh[0], FLAGS_ref[0])) { + return -1; + } + if (!fparser.parseFile(FLAGS_fiddle[0], ".txt")) { + return -1; + } + if (!fparser.closeCatalog()) { + return -1; + } + bmhParser.fWroteOut = true; + done = true; + } if (!done && !FLAGS_ref.isEmpty() && FLAGS_examples.isEmpty()) { MdOut mdOut(bmhParser); mdOut.fDebugOut = FLAGS_stdout; @@ -2335,6 +1633,7 @@ int main(int argc, char** const argv) { } if (!done && !FLAGS_spellcheck.isEmpty() && FLAGS_examples.isEmpty()) { bmhParser.spellCheck(FLAGS_bmh[0], FLAGS_spellcheck); + bmhParser.fWroteOut = true; done = true; } int examples = 0; diff --git a/chromium/third_party/skia/tools/bookmaker/bookmaker.h b/chromium/third_party/skia/tools/bookmaker/bookmaker.h index 857caf4c105..8cd128804c5 100644 --- a/chromium/third_party/skia/tools/bookmaker/bookmaker.h +++ b/chromium/third_party/skia/tools/bookmaker/bookmaker.h @@ -40,6 +40,7 @@ using std::vector; enum class KeyWord { kNone, kSK_API, + kSK_BEGIN_REQUIRE_DENSE, kBool, kChar, kClass, @@ -107,10 +108,12 @@ enum class MarkType { kLegend, kLink, kList, + kLiteral, // don't lookup hyperlinks, do substitution, etc kMarkChar, kMember, kMethod, kNoExample, + kOutdent, kParam, kPlatform, kPrivate, @@ -300,6 +303,33 @@ public: return *loc; } + const char* doubleLF() const { + int count = 0; + const char* ptr = fChar; + const char* doubleStart = nullptr; + while (ptr < fEnd) { + if ('\n' == ptr[0]) { + if (++count == 1) { + doubleStart = ptr; + } else { + return doubleStart; + } + } else if (' ' < ptr[0]) { + count = 0; + } + ++ptr; + } + return nullptr; + } + + bool endsWith(const char* match) { + int matchLen = strlen(match); + if (matchLen > fChar - fLine) { + return false; + } + return !strncmp(fChar - matchLen, match, matchLen); + } + bool eof() const { return fChar >= fEnd; } const char* lineEnd() const { @@ -428,7 +458,7 @@ public: // since a.b can't be found as a named definition void skipFullName() { while (fChar < fEnd && (isalnum(fChar[0]) - || '_' == fChar[0] || '-' == fChar[0] + || '_' == fChar[0] /* || '-' == fChar[0] */ || (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]))) { if (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]) { fChar++; @@ -465,6 +495,12 @@ public: } } + void skipToWhiteSpace() { + while (fChar < fEnd && ' ' < fChar[0]) { + fChar++; + } + } + bool skipName(const char* word) { size_t len = strlen(word); if (len <= (size_t) (fEnd - fChar) && !strncmp(word, fChar, len)) { @@ -634,10 +670,11 @@ public: for (int i = 0; i < 4; ++i) { unicode <<= 4; SkASSERT((reader[0] >= '0' && reader[0] <= '9') || - (reader[0] >= 'A' && reader[0] <= 'F')); + (reader[0] >= 'A' && reader[0] <= 'F') || + (reader[0] >= 'a' && reader[0] <= 'f')); int nibble = *reader++ - '0'; if (nibble > 9) { - nibble = 'A'- '9' + 1; + nibble = (nibble & ~('a' - 'A')) - 'A' + '9' + 1; } unicode |= nibble; } @@ -679,6 +716,12 @@ public: kYes }; + enum class ExampleOptions { + kText, + kPng, + kAll + }; + enum class MethodType { kNone, kConstructor, @@ -686,6 +729,26 @@ public: kOperator, }; + enum class Operator { + kUnknown, + kAdd, + kAddTo, + kArray, + kCast, + kCopy, + kDelete, + kDereference, + kEqual, + kMinus, + kMove, + kMultiply, + kMultiplyBy, + kNew, + kNotEqual, + kSubtract, + kSubtractFrom, + }; + Definition() {} Definition(const char* start, const char* end, int line, Definition* parent) @@ -735,116 +798,19 @@ public: virtual RootDefinition* asRoot() { SkASSERT(0); return nullptr; } virtual const RootDefinition* asRoot() const { SkASSERT(0); return nullptr; } - - bool boilerplateIfDef(Definition* parent) { - const Definition& label = fTokens.front(); - if (Type::kWord != label.fType) { - return false; - } - fName = string(label.fContentStart, label.fContentEnd - label.fContentStart); - return true; - } - - // todo: this is matching #ifndef SkXXX_DEFINED for no particular reason - // it doesn't do anything useful with arbitrary input, e.g. #ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS -// also doesn't know what to do with SK_REQUIRE_LOCAL_VAR() - bool boilerplateDef(Definition* parent) { - if (!this->boilerplateIfDef(parent)) { - return false; - } - const char* s = fName.c_str(); - const char* e = strchr(s, '_'); - return true; // fixme: if this is trying to do something useful with define, do it here - if (!e) { - return false; - } - string prefix(s, e - s); - const char* inName = strstr(parent->fName.c_str(), prefix.c_str()); - if (!inName) { - return false; - } - if ('/' != inName[-1] && '\\' != inName[-1]) { - return false; - } - if (strcmp(inName + prefix.size(), ".h")) { - return false; - } - return true; - } + bool boilerplateIfDef(Definition* parent); + bool boilerplateDef(Definition* parent); bool boilerplateEndIf() { return true; } bool checkMethod() const; - - void setCanonicalFiddle(); bool crossCheck2(const Definition& includeToken) const; bool crossCheck(const Definition& includeToken) const; bool crossCheckInside(const char* start, const char* end, const Definition& includeToken) const; - bool exampleToScript(string* result) const; - - string extractText(TrimExtract trimExtract) const { - string result; - TextParser parser(fFileName, fContentStart, fContentEnd, fLineCount); - int childIndex = 0; - char mc = '#'; - while (parser.fChar < parser.fEnd) { - if (TrimExtract::kYes == trimExtract && !parser.skipWhiteSpace()) { - break; - } - if (parser.next() == mc) { - if (parser.next() == mc) { - if (parser.next() == mc) { - mc = parser.next(); - } - } else { - // fixme : more work to do if # style comment is in text - // if in method definition, could be alternate method name - --parser.fChar; - if (' ' < parser.fChar[0]) { - if (islower(parser.fChar[0])) { - result += '\n'; - parser.skipLine(); - } else { - SkASSERT(isupper(parser.fChar[0])); - parser.skipTo(fChildren[childIndex]->fTerminator); - if (mc == parser.fChar[0] && mc == parser.fChar[1]) { - parser.next(); - parser.next(); - } - childIndex++; - } - } else { - parser.skipLine(); - } - continue; - } - } else { - --parser.fChar; - } - const char* end = parser.fEnd; - const char* mark = parser.strnchr(mc, end); - if (mark) { - end = mark; - } - string fragment(parser.fChar, end - parser.fChar); - trim_end(fragment); - if (TrimExtract::kYes == trimExtract) { - trim_start(fragment); - if (result.length()) { - result += '\n'; - result += '\n'; - } - } - if (TrimExtract::kYes == trimExtract || has_nonwhitespace(fragment)) { - result += fragment; - } - parser.skipTo(end); - } - return result; - } - + bool exampleToScript(string* result, ExampleOptions ) const; + string extractText(TrimExtract trimExtract) const; string fiddleName() const; string formatFunction() const; const Definition* hasChild(MarkType markType) const; @@ -872,7 +838,9 @@ public: string methodName() const; bool nextMethodParam(TextParser* methodParser, const char** nextEndPtr, string* paramName) const; + static string NormalizedName(string name); bool paramsMatch(const string& fullRef, const string& name) const; + bool parseOperator(size_t doubleColons, string& result); string printableName() const { string result(fName); @@ -887,11 +855,14 @@ public: } virtual RootDefinition* rootParent() { SkASSERT(0); return nullptr; } + void setCanonicalFiddle(); void setParentIndex() { fParentIndex = fParent ? (int) fParent->fTokens.size() : -1; } + void setWrapper(); + string fText; // if text is constructed instead of in a file, it's put here const char* fStart = nullptr; // .. in original text file, or the start of fText const char* fContentStart; // start past optional markup name @@ -904,6 +875,7 @@ public: vector<Definition*> fChildren; string fHash; // generated by fiddle string fFileName; + mutable string fWrapper; // used by Example to wrap into proper function size_t fLineCount = 0; int fParentIndex = 0; MarkType fMarkType = MarkType::kNone; @@ -911,9 +883,11 @@ public: Bracket fBracket = Bracket::kNone; Punctuation fPunctuation = Punctuation::kNone; MethodType fMethodType = MethodType::kNone; + Operator fOperator = Operator::kUnknown; Type fType = Type::kNone; bool fClone = false; bool fCloned = false; + bool fOperatorConst = false; bool fPrivate = false; bool fShort = false; bool fMemberStart = false; @@ -948,7 +922,7 @@ public: RootDefinition* asRoot() override { return this; } const RootDefinition* asRoot() const override { return this; } void clearVisited(); - bool dumpUnVisited(); + bool dumpUnVisited(bool skip); const Definition* find(const string& ref, AllowParens ) const; bool isRoot() const override { return true; } RootDefinition* rootParent() override { return fRootParent; } @@ -1063,6 +1037,7 @@ public: fMaxLF = 2; fPendingLF = 0; fPendingSpace = 0; + fOutdentNext = false; nl(); } @@ -1078,46 +1053,14 @@ public: fMaxLF = 1; } - bool writeBlockTrim(int size, const char* data) { - while (size && ' ' >= data[0]) { - ++data; - --size; - } - while (size && ' ' >= data[size - 1]) { - --size; - } - if (size <= 0) { - fLastChar = '\0'; - return false; - } - SkASSERT(size < 16000); - if (size > 3 && !strncmp("#end", data, 4)) { - fMaxLF = 1; - } - if (this->leadingPunctuation(data, (size_t) size)) { - fPendingSpace = 0; - } - writePending(); - if (fDebugOut) { - string check(data, size); - SkDebugf("%s", check.c_str()); - } - fprintf(fOut, "%.*s", size, data); - int added = 0; - fLastChar = data[size - 1]; - while (size > 0 && '\n' != data[--size]) { - ++added; - } - fColumn = size ? added : fColumn + added; - fSpaces = 0; - fLinefeeds = 0; - fMaxLF = added > 2 && !strncmp("#if", &data[size + (size > 0)], 3) ? 1 : 2; - return true; - } void writeBlock(int size, const char* data) { SkAssertResult(writeBlockTrim(size, data)); } + + void writeBlockIndent(int size, const char* data); + bool writeBlockTrim(int size, const char* data); + void writeCommentHeader() { this->lf(2); this->writeString("/**"); @@ -1129,6 +1072,8 @@ public: this->lfcr(); } + void writePending(); + // write a pending space, so that two consecutive calls // don't double write, and trailing spaces on lines aren't written void writeSpace(int count = 1) { @@ -1139,62 +1084,12 @@ public: fPendingSpace = count; } - void writeString(const char* str) { - const size_t len = strlen(str); - SkASSERT(len > 0); - SkASSERT(' ' < str[0]); - fLastChar = str[len - 1]; - SkASSERT(' ' < fLastChar); - SkASSERT(!strchr(str, '\n')); - if (this->leadingPunctuation(str, strlen(str))) { - fPendingSpace = 0; - } - writePending(); - if (fDebugOut) { - SkDebugf("%s", str); - } - fprintf(fOut, "%s", str); - fColumn += len; - fSpaces = 0; - fLinefeeds = 0; - fMaxLF = 2; - } + void writeString(const char* str); void writeString(const string& str) { this->writeString(str.c_str()); } - void writePending() { - fPendingLF = SkTMin(fPendingLF, fMaxLF); - bool wroteLF = false; - while (fLinefeeds < fPendingLF) { - if (fDebugOut) { - SkDebugf("\n"); - } - fprintf(fOut, "\n"); - ++fLinefeeds; - wroteLF = true; - } - fPendingLF = 0; - if (wroteLF) { - SkASSERT(0 == fColumn); - SkASSERT(fIndent >= fSpaces); - if (fDebugOut) { - SkDebugf("%*s", fIndent - fSpaces, ""); - } - fprintf(fOut, "%*s", fIndent - fSpaces, ""); - fColumn = fIndent; - fSpaces = fIndent; - } - for (int index = 0; index < fPendingSpace; ++index) { - if (fDebugOut) { - SkDebugf(" "); - } - fprintf(fOut, " "); - ++fColumn; - } - fPendingSpace = 0; - } unordered_map<string, sk_sp<SkData>> fRawData; unordered_map<string, vector<char>> fLFOnly; @@ -1209,6 +1104,7 @@ public: int fPendingSpace; // one or two spaces should preceed the next string or block char fLastChar; // last written bool fDebugOut; // set true to write to std out + bool fOutdentNext; // set at end of embedded struct to prevent premature outdent private: typedef TextParser INHERITED; }; @@ -1226,6 +1122,7 @@ public: kNo, // neither resolved nor output kYes, // resolved, output kOut, // not resolved, but output + kLiteral, // output untouched (FIXME: is this really different from kOut?) }; enum class Exemplary { @@ -1258,7 +1155,7 @@ public: #define E_N Exemplary::kNo #define E_O Exemplary::kOptional - BmhParser() : ParserCommon() + BmhParser(bool skip) : ParserCommon() , fMaps { // names without formal definitions (e.g. Column) aren't included // fill in other names once they're actually used @@ -1267,7 +1164,7 @@ public: , { "Alias", nullptr, MarkType::kAlias, R_N, E_N, 0 } , { "Bug", nullptr, MarkType::kBug, R_N, E_N, 0 } , { "Class", &fClassMap, MarkType::kClass, R_Y, E_O, M_CSST | M(Root) } -, { "Code", nullptr, MarkType::kCode, R_O, E_N, M_CSST | M_E } +, { "Code", nullptr, MarkType::kCode, R_O, E_N, M_CSST | M_E | M(Method) } , { "", nullptr, MarkType::kColumn, R_Y, E_N, M(Row) } , { "", nullptr, MarkType::kComment, R_N, E_N, 0 } , { "Const", &fConstMap, MarkType::kConst, R_Y, E_N, M_E | M_ST } @@ -1291,10 +1188,12 @@ public: , { "Legend", nullptr, MarkType::kLegend, R_Y, E_N, M(Table) } , { "", nullptr, MarkType::kLink, R_N, E_N, M(Anchor) } , { "List", nullptr, MarkType::kList, R_Y, E_N, M(Method) | M_CSST | M_E | M_D } +, { "Literal", nullptr, MarkType::kLiteral, R_N, E_N, M(Code) } , { "", nullptr, MarkType::kMarkChar, R_N, E_N, 0 } , { "Member", nullptr, MarkType::kMember, R_Y, E_N, M(Class) | M(Struct) } , { "Method", &fMethodMap, MarkType::kMethod, R_Y, E_Y, M_CSST } , { "NoExample", nullptr, MarkType::kNoExample, R_Y, E_N, 0 } +, { "Outdent", nullptr, MarkType::kOutdent, R_N, E_N, M(Code) } , { "Param", nullptr, MarkType::kParam, R_Y, E_N, M(Method) } , { "Platform", nullptr, MarkType::kPlatform, R_N, E_N, M(Example) } , { "Private", nullptr, MarkType::kPrivate, R_N, E_N, 0 } @@ -1313,10 +1212,11 @@ public: , { "ToDo", nullptr, MarkType::kToDo, R_N, E_N, 0 } , { "Topic", nullptr, MarkType::kTopic, R_Y, E_Y, M_CS | M(Root) | M(Topic) } , { "Track", nullptr, MarkType::kTrack, R_Y, E_N, M_E | M_ST } -, { "Typedef", &fTypedefMap, MarkType::kTypedef, R_Y, E_N, M(Subtopic) | M(Topic) } +, { "Typedef", &fTypedefMap, MarkType::kTypedef, R_Y, E_N, M(Class) | M_ST } , { "", nullptr, MarkType::kUnion, R_Y, E_N, 0 } , { "Volatile", nullptr, MarkType::kVolatile, R_N, E_N, M(StdOut) } , { "Width", nullptr, MarkType::kWidth, R_N, E_N, M(Example) } } +, fSkip(skip) { this->reset(); } @@ -1391,6 +1291,7 @@ public: void spellCheck(const char* match, SkCommandLineFlags::StringArray report) const; vector<string> topicName(); vector<string> typeName(MarkType markType, bool* expectEnd); + string typedefName(); string uniqueName(const string& base, MarkType markType); string uniqueRootName(const string& base, MarkType markType); void validate() const; @@ -1432,6 +1333,7 @@ public: bool fInComment; bool fInString; bool fCheckMethods; + bool fSkip = false; bool fWroteOut = false; private: typedef ParserCommon INHERITED; @@ -1474,10 +1376,12 @@ public: , { nullptr, MarkType::kLegend } , { nullptr, MarkType::kLink } , { nullptr, MarkType::kList } + , { nullptr, MarkType::kLiteral } , { nullptr, MarkType::kMarkChar } , { nullptr, MarkType::kMember } , { nullptr, MarkType::kMethod } , { nullptr, MarkType::kNoExample } + , { nullptr, MarkType::kOutdent } , { nullptr, MarkType::kParam } , { nullptr, MarkType::kPlatform } , { nullptr, MarkType::kPrivate } @@ -1685,6 +1589,20 @@ public: this->writeEndTag(tagType, tagID.c_str(), spaces); } + void writeIncompleteTag(const char* tagType, const string& tagID, int spaces = 1) { + this->writeString(string("#") + tagType + " " + tagID); + this->writeSpace(spaces); + this->writeString("incomplete"); + this->writeSpace(); + this->writeString("##"); + this->lf(1); + } + + void writeIncompleteTag(const char* tagType) { + this->writeString(string("#") + tagType + " incomplete ##"); + this->lf(1); + } + void writeTableHeader(const char* col1, size_t pad, const char* col2) { this->lf(1); this->writeString("#Table"); @@ -1757,6 +1675,7 @@ protected: unordered_map<string, IClassDefinition> fIClassMap; unordered_map<string, Definition> fIDefineMap; unordered_map<string, Definition> fIEnumMap; + unordered_map<string, Definition> fIFunctionMap; unordered_map<string, Definition> fIStructMap; unordered_map<string, Definition> fITemplateMap; unordered_map<string, Definition> fITypedefMap; @@ -1824,7 +1743,10 @@ public: const ParentPair* fPrev; }; - IncludeWriter() : IncludeParser() {} + IncludeWriter() : IncludeParser() { + this->reset(); + } + ~IncludeWriter() override {} bool contentFree(int size, const char* data) const { @@ -1858,11 +1780,13 @@ public: fBmhParser = nullptr; fEnumDef = nullptr; fMethodDef = nullptr; - fStructDef = nullptr; + fBmhStructDef = nullptr; fAttrDeprecated = nullptr; fAnonymousEnumCount = 1; fInStruct = false; fWroteMethod = false; + fIndentNext = false; + fPendingMethod = false; } string resolveMethod(const char* start, const char* end, bool first); @@ -1880,7 +1804,7 @@ private: const Definition* fBmhMethod; const Definition* fEnumDef; const Definition* fMethodDef; - const Definition* fStructDef; + const Definition* fBmhStructDef; const Definition* fAttrDeprecated; const char* fContinuation; // used to construct paren-qualified method name int fAnonymousEnumCount; @@ -1891,18 +1815,47 @@ private: int fStructCommentTab; bool fInStruct; bool fWroteMethod; + bool fIndentNext; + bool fPendingMethod; typedef IncludeParser INHERITED; }; -class FiddleParser : public ParserCommon { -public: - FiddleParser(BmhParser* bmh) : ParserCommon() - , fBmhParser(bmh) { +class FiddleBase : public ParserCommon { +protected: + FiddleBase(BmhParser* bmh) : ParserCommon() + , fBmhParser(bmh) + , fContinuation(false) + , fTextOut(false) + , fPngOut(false) + { this->reset(); } + void reset() override { + INHERITED::resetCommon(); + } + Definition* findExample(const string& name) const; + bool parseFiddles(); + virtual bool pngOut(Definition* example) = 0; + virtual bool textOut(Definition* example, const char* stdOutStart, + const char* stdOutEnd) = 0; + + BmhParser* fBmhParser; // must be writable; writes example hash + string fFullName; + bool fContinuation; + bool fTextOut; + bool fPngOut; +private: + typedef ParserCommon INHERITED; +}; + +class FiddleParser : public FiddleBase { +public: + FiddleParser(BmhParser* bmh) : FiddleBase(bmh) { + fTextOut = true; + } bool parseFromFile(const char* path) override { if (!INHERITED::parseSetup(path)) { @@ -1911,16 +1864,34 @@ public: return parseFiddles(); } - void reset() override { - INHERITED::resetCommon(); +private: + bool pngOut(Definition* example) override { + return true; } + bool textOut(Definition* example, const char* stdOutStart, + const char* stdOutEnd) override; + + typedef FiddleBase INHERITED; +}; + +class Catalog : public FiddleBase { +public: + Catalog(BmhParser* bmh) : FiddleBase(bmh) {} + + bool appendFile(const string& path); + bool closeCatalog(); + bool openCatalog(const char* inDir, const char* outDir); + + bool parseFromFile(const char* path) override ; private: - bool parseFiddles(); + bool pngOut(Definition* example) override; + bool textOut(Definition* example, const char* stdOutStart, + const char* stdOutEnd) override; - BmhParser* fBmhParser; // must be writable; writes example hash + string fDocsDir; - typedef ParserCommon INHERITED; + typedef FiddleBase INHERITED; }; class HackParser : public ParserCommon { @@ -1986,9 +1957,18 @@ private: fHasFiddle = false; fInDescription = false; fInList = false; + fRespectLeadingSpace = false; } - BmhParser::Resolvable resolvable(MarkType markType) { + BmhParser::Resolvable resolvable(const Definition* definition) const { + MarkType markType = definition->fMarkType; + if (MarkType::kCode == markType) { + for (auto child : definition->fChildren) { + if (MarkType::kLiteral == child->fMarkType) { + return BmhParser::Resolvable::kLiteral; + } + } + } if ((MarkType::kExample == markType || MarkType::kFunction == markType) && fHasFiddle) { return BmhParser::Resolvable::kNo; @@ -2008,6 +1988,7 @@ private: bool fInDescription; // FIXME: for now, ignore unfound camelCase in description since it may // be defined in example which at present cannot be linked to bool fInList; + bool fRespectLeadingSpace; typedef ParserCommon INHERITED; }; @@ -2044,10 +2025,11 @@ public: } } if (this->startsWith(fClassName.c_str()) || this->startsWith("operator")) { - const char* ptr = this->anyOf(" ("); + const char* ptr = this->anyOf("\n ("); if (ptr && '(' == *ptr) { this->skipToEndBracket(')'); SkAssertResult(')' == this->next()); + this->skipExact("_const"); return; } } @@ -2056,6 +2038,14 @@ public: this->skipToNonAlphaNum(); } else { this->skipFullName(); + if (this->endsWith("operator")) { + const char* ptr = this->anyOf("\n ("); + if (ptr && '(' == *ptr) { + this->skipToEndBracket(')'); + SkAssertResult(')' == this->next()); + this->skipExact("_const"); + } + } } } diff --git a/chromium/third_party/skia/tools/bookmaker/cataloger.cpp b/chromium/third_party/skia/tools/bookmaker/cataloger.cpp new file mode 100644 index 00000000000..48e3b487534 --- /dev/null +++ b/chromium/third_party/skia/tools/bookmaker/cataloger.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 "bookmaker.h" + +#include "SkOSFile.h" +#include "SkOSPath.h" + +bool Catalog::appendFile(const string& path) { + FILE* file = fopen(path.c_str(), "r"); + if (!file) { + SkDebugf("could not append %s\n", path.c_str()); + return false; + } + fseek(file, 0L, SEEK_END); + int sz = (int) ftell(file); + rewind(file); + char* buffer = new char[sz]; + memset(buffer, ' ', sz); + SkAssertResult(sz == (int)fread(buffer, 1, sz, file)); + fclose(file); + this->writeBlock(sz, buffer); + return true; +} + +bool Catalog::openCatalog(const char* inDir, const char* outDir) { + fDocsDir = inDir; + if ('/' != fDocsDir.back()) { + fDocsDir += '/'; + } + string outie = outDir; + if ('/' != outie.back()) { + outie += '/'; + } + fFullName = outie + "catalog.htm"; + fOut = fopen(fFullName.c_str(), "wb"); + if (!fOut) { + SkDebugf("could not open output file %s\n", fFullName.c_str()); + return false; + } + fContinuation = false; + if (!appendFile(fDocsDir + "catalogHeader.txt")) { + return false; + } + this->lf(1); + return true; +} + +bool Catalog::closeCatalog() { + if (fOut) { + this->lf(1); + this->writeString("}"); + this->lf(1); + if (!appendFile(fDocsDir + "catalogTrailer.txt")) { + return false; + } + this->lf(1); + this->writePending(); + fclose(fOut); + SkDebugf("wrote %s\n", fFullName.c_str()); + fOut = nullptr; + } + return true; +} + +bool Catalog::parseFromFile(const char* path) { + if (!INHERITED::parseSetup(path)) { + return false; + } + fIndent = 4; + this->writeString("var text = {"); + this->lf(1); + fTextOut = true; + TextParser::Save save(this); + if (!parseFiddles()) { + return false; + } + this->lf(1); + this->writeString("}"); + this->lf(2); + this->writeString("var pngs = {"); + fTextOut = false; + fPngOut = true; + save.restore(); + fContinuation = false; + return parseFiddles(); +} + +bool Catalog::pngOut(Definition* example) { + string result; + if (!example->exampleToScript(&result, Definition::ExampleOptions::kPng)) { + return false; + } + if (result.length() > 0) { + if (fContinuation) { + this->writeString(","); + this->lf(1); + } else { + fContinuation = true; + } + this->writeBlock(result.size(), result.c_str()); + } + return true; +} + +bool Catalog::textOut(Definition* example, const char* stdOutStart, + const char* stdOutEnd) { + string result; + if (!example->exampleToScript(&result, Definition::ExampleOptions::kText)) { + return false; + } + if (result.length() > 0) { + if (fContinuation) { + this->writeString(","); + this->lf(1); + } else { + fContinuation = true; + } + fIndent = 8; + this->writeBlock(result.size(), result.c_str()); + this->lf(1); + this->writeString("\"stdout\": \""); + size_t pos = 0; + size_t len = stdOutEnd - stdOutStart; + string example; + while ((size_t) pos < len) { + example += '"' == stdOutStart[pos] ? "\\\"" : + '\\' == stdOutStart[pos] ? "\\\\" : + string(&stdOutStart[pos], 1); + ++pos; + } + this->writeBlock(example.length(), example.c_str()); + this->writeString("\""); + this->lf(1); + fIndent = 4; + this->writeString("}"); + } + return true; +} diff --git a/chromium/third_party/skia/tools/bookmaker/definition.cpp b/chromium/third_party/skia/tools/bookmaker/definition.cpp new file mode 100644 index 00000000000..44da2df2681 --- /dev/null +++ b/chromium/third_party/skia/tools/bookmaker/definition.cpp @@ -0,0 +1,1274 @@ +/* + * 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 "bookmaker.h" + +static size_t count_indent(const string& text, size_t test, size_t end) { + size_t result = test; + while (test < end) { + if (' ' != text[test]) { + break; + } + ++test; + } + return test - result; +} + +static void add_code(const string& text, int pos, int end, + size_t outIndent, size_t textIndent, string& example) { + do { + // fix this to move whole paragraph in, out, but preserve doc indent + int nextIndent = count_indent(text, pos, end); + size_t len = text.find('\n', pos); + if (string::npos == len) { + len = end; + } + if ((size_t) (pos + nextIndent) < len) { + size_t indent = outIndent + nextIndent; + SkASSERT(indent >= textIndent); + indent -= textIndent; + for (size_t index = 0; index < indent; ++index) { + example += ' '; + } + pos += nextIndent; + while ((size_t) pos < len) { + example += '"' == text[pos] ? "\\\"" : + '\\' == text[pos] ? "\\\\" : + text.substr(pos, 1); + ++pos; + } + example += "\\n"; + } else { + pos += nextIndent; + } + if ('\n' == text[pos]) { + ++pos; + } + } while (pos < end); +} + +#ifdef CONST +#undef CONST +#endif + +#ifdef FRIEND +#undef FRIEND +#endif + +#ifdef BLANK +#undef BLANK +#endif + +#ifdef ANY +#undef ANY +#endif + +#ifdef DEFOP +#undef DEFOP +#endif + +#define CONST 1 +#define STATIC 2 +#define BLANK 0 +#define ANY -1 +#define DEFOP Definition::Operator + +enum class OpType : int8_t { + kNone, + kVoid, + kBool, + kChar, + kFloat, + kInt, + kScalar, + kSizeT, + kThis, + kAny, +}; + +enum class OpMod : int8_t { + kNone, + kArray, + kMove, + kPointer, + kReference, + kAny, +}; + +const struct OperatorParser { + DEFOP fOperator; + const char* fSymbol; + const char* fName; + int8_t fFriend; + OpType fReturnType; + OpMod fReturnMod; + int8_t fConstMethod; + struct Param { + int8_t fConst; + OpType fType; + OpMod fMod; + } fParams[2]; +} opData[] = { + { DEFOP::kUnknown, "??", "???", BLANK, OpType::kNone, OpMod::kNone, BLANK, + { } }, + { DEFOP::kAdd, "+", "add", BLANK, OpType::kThis, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kAddTo, "+=", "addto", BLANK, OpType::kVoid, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kAddTo, "+=", "addto1", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kAddTo, "+=", "addto2", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ CONST, OpType::kChar, OpMod::kArray, }}}, + { DEFOP::kAddTo, "+=", "addto3", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ CONST, OpType::kChar, OpMod::kNone, }}}, + { DEFOP::kArray, "[]", "array", BLANK, OpType::kScalar, OpMod::kNone, CONST, + {{ BLANK, OpType::kInt, OpMod::kNone, }}}, + { DEFOP::kArray, "[]", "array1", BLANK, OpType::kScalar, OpMod::kReference, BLANK, + {{ BLANK, OpType::kInt, OpMod::kNone, }}}, + { DEFOP::kArray, "[]", "array2", BLANK, OpType::kChar, OpMod::kNone, CONST, + {{ BLANK, OpType::kSizeT, OpMod::kNone, }}}, + { DEFOP::kArray, "[]", "array3", BLANK, OpType::kChar, OpMod::kReference, BLANK, + {{ BLANK, OpType::kSizeT, OpMod::kNone, }}}, + { DEFOP::kCast, "()", "cast", BLANK, OpType::kAny, OpMod::kAny, ANY, + {{ ANY, OpType::kAny, OpMod::kAny, }}}, + { DEFOP::kCopy, "=", "copy", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kCopy, "=", "copy1", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ CONST, OpType::kChar, OpMod::kArray, }}}, + { DEFOP::kDelete, "delete", "delete", BLANK, OpType::kVoid, OpMod::kNone, BLANK, + {{ BLANK, OpType::kVoid, OpMod::kPointer, }}}, + { DEFOP::kDereference, "->", "deref", ANY, OpType::kThis, OpMod::kPointer, CONST, + { } }, + { DEFOP::kDereference, "*", "deref", BLANK, OpType::kThis, OpMod::kReference, CONST, + { } }, + { DEFOP::kEqual, "==", "equal", BLANK, OpType::kBool, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kEqual, "==", "equal1", BLANK, OpType::kBool, OpMod::kNone, CONST, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kEqual, "==", "equal2", ANY, OpType::kBool, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kMinus, "-", "minus", BLANK, OpType::kThis, OpMod::kNone, CONST, + { } }, + { DEFOP::kMove, "=", "move", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ BLANK, OpType::kThis, OpMod::kMove, }}}, + { DEFOP::kMultiply, "*", "multiply", BLANK, OpType::kThis, OpMod::kNone, CONST, + {{ BLANK, OpType::kScalar, OpMod::kNone, }}}, + { DEFOP::kMultiply, "*", "multiply1", BLANK, OpType::kThis, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kMultiplyBy, "*=", "multiplyby", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ BLANK, OpType::kScalar, OpMod::kNone, }}}, + { DEFOP::kNew, "new", "new", BLANK, OpType::kVoid, OpMod::kPointer, BLANK, + {{ BLANK, OpType::kSizeT, OpMod::kNone, }}}, + { DEFOP::kNotEqual, "!=", "notequal", BLANK, OpType::kBool, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kNotEqual, "!=", "notequal1", BLANK, OpType::kBool, OpMod::kNone, CONST, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kNotEqual, "!=", "notequal2", ANY, OpType::kBool, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kSubtract, "-", "subtract", BLANK, OpType::kThis, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kSubtractFrom, "-=", "subtractfrom", BLANK, OpType::kVoid, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, +}; + +OpType lookup_type(const string& typeWord, const string& name) { + if (typeWord == name || (typeWord == "SkIVector" && name == "SkIPoint") + || (typeWord == "SkVector" && name == "SkPoint")) { + return OpType::kThis; + } + const char* keyWords[] = { "void", "bool", "char", "float", "int", "SkScalar", "size_t" }; + for (unsigned i = 0; i < SK_ARRAY_COUNT(keyWords); ++i) { + if (typeWord == keyWords[i]) { + return (OpType) (i + 1); + } + } + return OpType::kNone; +} + +OpMod lookup_mod(TextParser& iParser) { + OpMod mod = OpMod::kNone; + if ('&' == iParser.peek()) { + mod = OpMod::kReference; + iParser.next(); + if ('&' == iParser.peek()) { + mod = OpMod::kMove; + iParser.next(); + } + } else if ('*' == iParser.peek()) { + mod = OpMod::kPointer; + iParser.next(); + } + iParser.skipWhiteSpace(); + return mod; +} + +bool Definition::parseOperator(size_t doubleColons, string& result) { + const char operatorStr[] = "operator"; + size_t opPos = fName.find(operatorStr, doubleColons); + if (string::npos == opPos) { + return false; + } + string className(fName, 0, doubleColons - 2); + TextParser iParser(fFileName, fStart, fContentStart, fLineCount); + SkAssertResult(iParser.skipWord("#Method")); + iParser.skipExact("SK_API"); + iParser.skipWhiteSpace(); + bool isStatic = iParser.skipExact("static"); + iParser.skipWhiteSpace(); + iParser.skipExact("SK_API"); + iParser.skipWhiteSpace(); + bool returnsConst = iParser.skipExact("const"); + if (returnsConst) { + SkASSERT(0); // incomplete + } + SkASSERT(isStatic == false || returnsConst == false); + iParser.skipWhiteSpace(); + const char* returnTypeStart = iParser.fChar; + iParser.skipToNonAlphaNum(); + SkASSERT(iParser.fChar > returnTypeStart); + string returnType(returnTypeStart, iParser.fChar - returnTypeStart); + OpType returnOpType = lookup_type(returnType, className); + iParser.skipWhiteSpace(); + OpMod returnMod = lookup_mod(iParser); + SkAssertResult(iParser.skipExact("operator")); + iParser.skipWhiteSpace(); + fMethodType = Definition::MethodType::kOperator; + TextParser::Save save(&iParser); + for (auto parser : opData) { + save.restore(); + if (!iParser.skipExact(parser.fSymbol)) { + continue; + } + iParser.skipWhiteSpace(); + if ('(' != iParser.peek()) { + continue; + } + if (parser.fFriend != ANY && (parser.fFriend == STATIC) != isStatic) { + continue; + } + if (parser.fReturnType != OpType::kAny && parser.fReturnType != returnOpType) { + continue; + } + if (parser.fReturnMod != OpMod::kAny && parser.fReturnMod != returnMod) { + continue; + } + iParser.next(); // skip '(' + iParser.skipWhiteSpace(); + int parserCount = (parser.fParams[0].fType != OpType::kNone) + + (parser.fParams[1].fType != OpType::kNone); + bool countsMatch = true; + for (int pIndex = 0; pIndex < 2; ++pIndex) { + if (')' == iParser.peek()) { + countsMatch = pIndex == parserCount; + break; + } + if (',' == iParser.peek()) { + iParser.next(); + iParser.skipWhiteSpace(); + } + bool paramConst = iParser.skipExact("const"); + if (parser.fParams[pIndex].fConst != ANY && + paramConst != (parser.fParams[pIndex].fConst == CONST)) { + countsMatch = false; + break; + } + iParser.skipWhiteSpace(); + const char* paramStart = iParser.fChar; + iParser.skipToNonAlphaNum(); + SkASSERT(iParser.fChar > paramStart); + string paramType(paramStart, iParser.fChar - paramStart); + OpType paramOpType = lookup_type(paramType, className); + if (parser.fParams[pIndex].fType != OpType::kAny && + parser.fParams[pIndex].fType != paramOpType) { + countsMatch = false; + break; + } + iParser.skipWhiteSpace(); + OpMod paramMod = lookup_mod(iParser); + if (parser.fParams[pIndex].fMod != OpMod::kAny && + parser.fParams[pIndex].fMod != paramMod) { + countsMatch = false; + break; + } + iParser.skipToNonAlphaNum(); + if ('[' == iParser.peek()) { + paramMod = OpMod::kArray; + SkAssertResult(iParser.skipExact("[]")); + } + iParser.skipWhiteSpace(); + } + if (!countsMatch) { + continue; + } + if (')' != iParser.peek()) { + continue; + } + iParser.next(); + bool constMethod = iParser.skipExact("_const"); + if (parser.fConstMethod != ANY && (parser.fConstMethod == CONST) != constMethod) { + continue; + } + result += parser.fName; + result += "_operator"; + fOperator = parser.fOperator; + fOperatorConst = constMethod; + return true; + } + SkASSERT(0); // incomplete + return false; +#if 0 + if ('!' == fName[opPos]) { + SkASSERT('=' == fName[opPos + 1]); + result += "not_equal_operator"; + } else if ('=' == fName[opPos]) { + if ('(' == fName[opPos + 1]) { + result += isMove ? "move_" : "copy_"; + result += "assignment_operator"; + } else { + SkASSERT('=' == fName[opPos + 1]); + result += "equal_operator"; + } + } else if ('[' == fName[opPos]) { + result += "subscript_operator"; + const char* end = fContentStart; + while (end > fStart && ' ' >= end[-1]) { + --end; + } + string constCheck(fStart, end - fStart); + size_t constPos = constCheck.rfind("const"); + if (constCheck.length() == constPos + 5) { + result += "_const"; + } + } else if ('*' == fName[opPos]) { + result += "multiply_operator"; + } else if ('-' == fName[opPos]) { + result += "subtract_operator"; + } else if ('+' == fName[opPos]) { + result += "add_operator"; + } else { + SkASSERT(0); // todo: incomplete + } +#endif + return true; +} + +#undef CONST +#undef FRIEND +#undef BLANK +#undef DEFOP + +bool Definition::boilerplateIfDef(Definition* parent) { + const Definition& label = fTokens.front(); + if (Type::kWord != label.fType) { + return false; + } + fName = string(label.fContentStart, label.fContentEnd - label.fContentStart); + return true; +} + +// todo: this is matching #ifndef SkXXX_DEFINED for no particular reason +// it doesn't do anything useful with arbitrary input, e.g. #ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS +// also doesn't know what to do with SK_REQUIRE_LOCAL_VAR() +bool Definition::boilerplateDef(Definition* parent) { + if (!this->boilerplateIfDef(parent)) { + return false; + } + const char* s = fName.c_str(); + const char* e = strchr(s, '_'); + return true; // fixme: if this is trying to do something useful with define, do it here + if (!e) { + return false; + } + string prefix(s, e - s); + const char* inName = strstr(parent->fName.c_str(), prefix.c_str()); + if (!inName) { + return false; + } + if ('/' != inName[-1] && '\\' != inName[-1]) { + return false; + } + if (strcmp(inName + prefix.size(), ".h")) { + return false; + } + return true; +} + +// fixme: this will need to be more complicated to handle all of Skia +// for now, just handle paint -- maybe fiddle will loosen naming restrictions +void Definition::setCanonicalFiddle() { + fMethodType = Definition::MethodType::kNone; + size_t doubleColons = fName.find("::", 0); + SkASSERT(string::npos != doubleColons); + string base = fName.substr(0, doubleColons); + string result = base + "_"; + doubleColons += 2; + if (string::npos != fName.find('~', doubleColons)) { + fMethodType = Definition::MethodType::kDestructor; + result += "destructor"; + } else if (!this->parseOperator(doubleColons, result)) { + bool isMove = string::npos != fName.find("&&", doubleColons); + size_t parens = fName.find("()", doubleColons); + if (string::npos != parens) { + string methodName = fName.substr(doubleColons, parens - doubleColons); + do { + size_t nextDouble = methodName.find("::"); + if (string::npos == nextDouble) { + break; + } + base = methodName.substr(0, nextDouble); + result += base + '_'; + methodName = methodName.substr(nextDouble + 2); + doubleColons += nextDouble + 2; + } while (true); + if (base == methodName) { + fMethodType = Definition::MethodType::kConstructor; + result += "empty_constructor"; + } else { + result += fName.substr(doubleColons, fName.length() - doubleColons - 2); + } + } else { + size_t openParen = fName.find('(', doubleColons); + if (string::npos == openParen) { + result += fName.substr(doubleColons); + } else { + size_t comma = fName.find(',', doubleColons); + if (string::npos == comma) { + result += isMove ? "move_" : "copy_"; + } + fMethodType = Definition::MethodType::kConstructor; + // name them by their param types, + // e.g. SkCanvas__int_int_const_SkSurfaceProps_star + // TODO: move forward until parens are balanced and terminator =,) + TextParser params("", &fName[openParen] + 1, &*fName.end(), 0); + bool underline = false; + while (!params.eof()) { +// SkDEBUGCODE(const char* end = params.anyOf("(),=")); // unused for now +// SkASSERT(end[0] != '('); // fixme: put off handling nested parentheseses + if (params.startsWith("const") || params.startsWith("int") + || params.startsWith("Sk")) { + const char* wordStart = params.fChar; + params.skipToNonAlphaNum(); + if (underline) { + result += '_'; + } else { + underline = true; + } + result += string(wordStart, params.fChar - wordStart); + } else { + params.skipToNonAlphaNum(); + } + if (!params.eof() && '*' == params.peek()) { + if (underline) { + result += '_'; + } else { + underline = true; + } + result += "star"; + params.next(); + params.skipSpace(); + } + params.skipToAlpha(); + } + } + } + } + fFiddle = Definition::NormalizedName(result); +} + +void Definition::setWrapper() { + const char drawWrapper[] = "void draw(SkCanvas* canvas) {"; + const char drawNoCanvas[] = "void draw(SkCanvas* ) {"; + string text = this->extractText(Definition::TrimExtract::kNo); + size_t nonSpace = 0; + while (nonSpace < text.length() && ' ' >= text[nonSpace]) { + ++nonSpace; + } + bool hasFunc = !text.compare(nonSpace, sizeof(drawWrapper) - 1, drawWrapper); + bool noCanvas = !text.compare(nonSpace, sizeof(drawNoCanvas) - 1, drawNoCanvas); + bool hasCanvas = string::npos != text.find("SkCanvas canvas"); + SkASSERT(!hasFunc || !noCanvas); + bool preprocessor = text[0] == '#'; + bool wrapCode = !hasFunc && !noCanvas && !preprocessor; + if (wrapCode) { + fWrapper = hasCanvas ? string(drawNoCanvas) : string(drawWrapper); + } +} + +bool Definition::exampleToScript(string* result, ExampleOptions exampleOptions) const { + bool hasFiddle = true; + const Definition* platform = this->hasChild(MarkType::kPlatform); + if (platform) { + TextParser platParse(platform); + hasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd); + } + if (!hasFiddle) { + *result = ""; + return true; + } + string text = this->extractText(Definition::TrimExtract::kNo); + bool textOut = string::npos != text.find("SkDebugf(") + || string::npos != text.find("dump(") + || string::npos != text.find("dumpHex("); + string heightStr = "256"; + string widthStr = "256"; + string normalizedName(fFiddle); + string code; + string imageStr = "0"; + for (auto const& iter : fChildren) { + switch (iter->fMarkType) { + case MarkType::kError: + result->clear(); + return true; + case MarkType::kHeight: + heightStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); + break; + case MarkType::kWidth: + widthStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); + break; + case MarkType::kDescription: + // ignore for now + break; + case MarkType::kFunction: { + // emit this, but don't wrap this in draw() + string funcText(iter->fContentStart, iter->fContentEnd - iter->fContentStart - 1); + size_t pos = 0; + while (pos < funcText.length() && ' ' > funcText[pos]) { + ++pos; + } + size_t indent = count_indent(funcText, pos, funcText.length()); + add_code(funcText, pos, funcText.length(), 0, indent, code); + code += "\\n"; + } break; + case MarkType::kComment: + break; + case MarkType::kImage: + imageStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); + break; + case MarkType::kToDo: + break; + case MarkType::kMarkChar: + case MarkType::kPlatform: + // ignore for now + break; + case MarkType::kStdOut: + textOut = true; + break; + default: + SkASSERT(0); // more coding to do + } + } + string textOutStr = textOut ? "true" : "false"; + size_t pos = 0; + while (pos < text.length() && ' ' > text[pos]) { + ++pos; + } + size_t end = text.length(); + size_t outIndent = 0; + size_t textIndent = count_indent(text, pos, end); + if ("MakeFromBackendTexture" == fName) { + SkDebugf(""); + } + if (fWrapper.length() > 0) { + code += fWrapper; + code += "\\n"; + outIndent = 4; + } + add_code(text, pos, end, outIndent, textIndent, code); + if (fWrapper.length() > 0) { + code += "}"; + } + string example = "\"" + normalizedName + "\": {\n"; + size_t nameStart = fFileName.find("\\", 0); + SkASSERT(string::npos != nameStart); + string baseFile = fFileName.substr(nameStart + 1, fFileName.length() - nameStart - 5); + if (ExampleOptions::kText == exampleOptions) { + example += " \"code\": \"" + code + "\",\n"; + example += " \"hash\": \"" + fHash + "\",\n"; + example += " \"file\": \"" + baseFile + "\",\n"; + example += " \"name\": \"" + fName + "\","; + } else { + example += " \"code\": \"" + code + "\",\n"; + if (ExampleOptions::kPng == exampleOptions) { + example += " \"width\": " + widthStr + ",\n"; + example += " \"height\": " + heightStr + ",\n"; + example += " \"hash\": \"" + fHash + "\",\n"; + example += " \"file\": \"" + baseFile + "\",\n"; + example += " \"name\": \"" + fName + "\"\n"; + example += "}"; + } else { + example += " \"options\": {\n"; + example += " \"width\": " + widthStr + ",\n"; + example += " \"height\": " + heightStr + ",\n"; + example += " \"source\": " + imageStr + ",\n"; + example += " \"srgb\": false,\n"; + example += " \"f16\": false,\n"; + example += " \"textOnly\": " + textOutStr + ",\n"; + example += " \"animated\": false,\n"; + example += " \"duration\": 0\n"; + example += " },\n"; + example += " \"fast\": true"; + } + } + *result = example; + return true; +} + +string Definition::extractText(TrimExtract trimExtract) const { + string result; + TextParser parser(fFileName, fContentStart, fContentEnd, fLineCount); + int childIndex = 0; + char mc = '#'; + while (parser.fChar < parser.fEnd) { + if (TrimExtract::kYes == trimExtract && !parser.skipWhiteSpace()) { + break; + } + if (parser.next() == mc) { + if (parser.next() == mc) { + if (parser.next() == mc) { + mc = parser.next(); + } + } else { + // fixme : more work to do if # style comment is in text + // if in method definition, could be alternate method name + --parser.fChar; + if (' ' < parser.fChar[0]) { + if (islower(parser.fChar[0])) { + result += '\n'; + parser.skipLine(); + } else { + SkASSERT(isupper(parser.fChar[0])); + parser.skipTo(fChildren[childIndex]->fTerminator); + if (mc == parser.fChar[0] && mc == parser.fChar[1]) { + parser.next(); + parser.next(); + } + childIndex++; + } + } else { + parser.skipLine(); + } + continue; + } + } else { + --parser.fChar; + } + const char* end = parser.fEnd; + const char* mark = parser.strnchr(mc, end); + if (mark) { + end = mark; + } + string fragment(parser.fChar, end - parser.fChar); + trim_end(fragment); + if (TrimExtract::kYes == trimExtract) { + trim_start(fragment); + if (result.length()) { + result += '\n'; + result += '\n'; + } + } + if (TrimExtract::kYes == trimExtract || has_nonwhitespace(fragment)) { + result += fragment; + } + parser.skipTo(end); + } + return result; +} + +static void space_pad(string* str) { + size_t len = str->length(); + if (len == 0) { + return; + } + char last = (*str)[len - 1]; + if ('~' == last || ' ' >= last) { + return; + } + *str += ' '; +} + +//start here; +// see if it possible to abstract this a little bit so it can +// additionally be used to find params and return in method prototype that +// does not have corresponding doxygen comments +bool Definition::checkMethod() const { + SkASSERT(MarkType::kMethod == fMarkType); + // if method returns a value, look for a return child + // for each parameter, look for a corresponding child + const char* end = fContentStart; + while (end > fStart && ' ' >= end[-1]) { + --end; + } + TextParser methodParser(fFileName, fStart, end, fLineCount); + methodParser.skipWhiteSpace(); + SkASSERT(methodParser.startsWith("#Method")); + methodParser.skipName("#Method"); + methodParser.skipSpace(); + string name = this->methodName(); + if (MethodType::kNone == fMethodType && name.length() > 2 && + "()" == name.substr(name.length() - 2)) { + name = name.substr(0, name.length() - 2); + } + bool expectReturn = this->methodHasReturn(name, &methodParser); + bool foundReturn = false; + bool foundException = false; + for (auto& child : fChildren) { + foundException |= MarkType::kDeprecated == child->fMarkType + || MarkType::kExperimental == child->fMarkType; + if (MarkType::kReturn != child->fMarkType) { + if (MarkType::kParam == child->fMarkType) { + child->fVisited = false; + } + continue; + } + if (!expectReturn) { + return methodParser.reportError<bool>("no #Return expected"); + } + if (foundReturn) { + return methodParser.reportError<bool>("multiple #Return markers"); + } + foundReturn = true; + } + if (expectReturn && !foundReturn && !foundException) { + return methodParser.reportError<bool>("missing #Return marker"); + } + const char* paren = methodParser.strnchr('(', methodParser.fEnd); + if (!paren) { + return methodParser.reportError<bool>("missing #Method function definition"); + } + const char* nextEnd = paren; + do { + string paramName; + methodParser.fChar = nextEnd + 1; + methodParser.skipSpace(); + if (!this->nextMethodParam(&methodParser, &nextEnd, ¶mName)) { + continue; + } + bool foundParam = false; + for (auto& child : fChildren) { + if (MarkType::kParam != child->fMarkType) { + continue; + } + if (paramName != child->fName) { + continue; + } + if (child->fVisited) { + return methodParser.reportError<bool>("multiple #Method param with same name"); + } + child->fVisited = true; + if (foundParam) { + TextParser paramError(child); + return methodParser.reportError<bool>("multiple #Param with same name"); + } + foundParam = true; + + } + if (!foundParam && !foundException) { + return methodParser.reportError<bool>("no #Param found"); + } + if (')' == nextEnd[0]) { + break; + } + } while (')' != nextEnd[0]); + for (auto& child : fChildren) { + if (MarkType::kParam != child->fMarkType) { + continue; + } + if (!child->fVisited) { + TextParser paramError(child); + return paramError.reportError<bool>("#Param without param in #Method"); + } + } + return true; +} + +bool Definition::crossCheck2(const Definition& includeToken) const { + TextParser parser(fFileName, fStart, fContentStart, fLineCount); + parser.skipExact("#"); + bool isMethod = parser.skipName("Method"); + const char* contentEnd; + if (isMethod) { + contentEnd = fContentStart; + } else if (parser.skipName("DefinedBy")) { + contentEnd = fContentEnd; + while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) { + --contentEnd; + } + if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) { + contentEnd -= 2; + } + } else { + return parser.reportError<bool>("unexpected crosscheck marktype"); + } + return crossCheckInside(parser.fChar, contentEnd, includeToken); +} + +bool Definition::crossCheck(const Definition& includeToken) const { + return crossCheckInside(fContentStart, fContentEnd, includeToken); +} + +bool Definition::crossCheckInside(const char* start, const char* end, + const Definition& includeToken) const { + TextParser def(fFileName, start, end, fLineCount); + TextParser inc("", includeToken.fContentStart, includeToken.fContentEnd, 0); + if (inc.startsWith("SK_API")) { + inc.skipWord("SK_API"); + } + if (inc.startsWith("friend")) { + inc.skipWord("friend"); + } + if (inc.startsWith("SK_API")) { + inc.skipWord("SK_API"); + } + inc.skipExact("SkDEBUGCODE("); + do { + bool defEof; + bool incEof; + do { + defEof = def.eof() || !def.skipWhiteSpace(); + incEof = inc.eof() || !inc.skipWhiteSpace(); + if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) { + inc.next(); + if ('*' == inc.peek()) { + inc.skipToEndBracket("*/"); + inc.next(); + } else if ('/' == inc.peek()) { + inc.skipToEndBracket('\n'); + } + } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) { + inc.next(); + if (inc.startsWith("if")) { + inc.skipToEndBracket("\n"); + } else if (inc.startsWith("endif")) { + inc.skipToEndBracket("\n"); + } else { + SkASSERT(0); // incomplete + return false; + } + } else { + break; + } + inc.next(); + } while (true); + if (defEof || incEof) { + if (defEof == incEof || (!defEof && ';' == def.peek())) { + return true; + } + return false; // allow setting breakpoint on failure + } + char defCh; + do { + defCh = def.next(); + char incCh = inc.next(); + if (' ' >= defCh && ' ' >= incCh) { + break; + } + if (defCh != incCh) { + if ('_' != defCh || ' ' != incCh || !fOperatorConst || !def.startsWith("const")) { + return false; + } + } + if (';' == defCh) { + return true; + } + } while (!def.eof() && !inc.eof()); + } while (true); + return false; +} + +string Definition::formatFunction() const { + const char* end = fContentStart; + while (end > fStart && ' ' >= end[-1]) { + --end; + } + TextParser methodParser(fFileName, fStart, end, fLineCount); + methodParser.skipWhiteSpace(); + SkASSERT(methodParser.startsWith("#Method")); + methodParser.skipName("#Method"); + methodParser.skipSpace(); + const char* lastStart = methodParser.fChar; + const int limit = 100; // todo: allow this to be set by caller or in global or something + string name = this->methodName(); + if ("MakeFromBackendTextureAsRenderTarget" == name) { + SkDebugf(""); + } + const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd); + methodParser.skipTo(nameInParser); + const char* lastEnd = methodParser.fChar; + const char* paren = methodParser.strnchr('(', methodParser.fEnd); + size_t indent; + if (paren) { + indent = (size_t) (paren - lastStart) + 1; + } else { + indent = (size_t) (lastEnd - lastStart); + } + // trim indent so longest line doesn't exceed box width + TextParser::Save savePlace(&methodParser); + const char* saveStart = lastStart; + ptrdiff_t maxLine = 0; + do { + const char* nextStart = lastEnd; + const char* delimiter = methodParser.anyOf(",)"); + const char* nextEnd = delimiter ? delimiter : methodParser.fEnd; + if (delimiter) { + while (nextStart < nextEnd && ' ' >= nextStart[0]) { + ++nextStart; + } + } + while (nextEnd > nextStart && ' ' >= nextEnd[-1]) { + --nextEnd; + } + if (delimiter) { + nextEnd += 1; + delimiter += 1; + } + if (lastEnd > lastStart) { + maxLine = SkTMax(maxLine, lastEnd - lastStart); + } + if (delimiter) { + methodParser.skipTo(delimiter); + } + lastStart = nextStart; + lastEnd = nextEnd; + } while (lastStart < lastEnd); + savePlace.restore(); + lastStart = saveStart; + lastEnd = methodParser.fChar; + indent = SkTMin(indent, (size_t) (limit - maxLine)); + // write string wtih trimmmed indent + string methodStr; + int written = 0; + do { + const char* nextStart = lastEnd; + SkASSERT(written < limit); + const char* delimiter = methodParser.anyOf(",)"); + const char* nextEnd = delimiter ? delimiter : methodParser.fEnd; + if (delimiter) { + while (nextStart < nextEnd && ' ' >= nextStart[0]) { + ++nextStart; + } + } + while (nextEnd > nextStart && ' ' >= nextEnd[-1]) { + --nextEnd; + } + if (delimiter) { + nextEnd += 1; + delimiter += 1; + } + if (lastEnd > lastStart) { + if (lastStart[0] != ' ') { + space_pad(&methodStr); + } + methodStr += string(lastStart, (size_t) (lastEnd - lastStart)); + written += (size_t) (lastEnd - lastStart); + } + if (delimiter) { + if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) { + written = indent; + methodStr += '\n'; + methodStr += string(indent, ' '); + } + methodParser.skipTo(delimiter); + } + lastStart = nextStart; + lastEnd = nextEnd; + } while (lastStart < lastEnd); + return methodStr; +} + +string Definition::fiddleName() const { + string result; + size_t start = 0; + string parent; + const Definition* parentDef = this; + while ((parentDef = parentDef->fParent)) { + if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) { + parent = parentDef->fFiddle; + break; + } + } + if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) { + start = parent.length(); + while (start < fFiddle.length() && '_' == fFiddle[start]) { + ++start; + } + } + size_t end = fFiddle.find_first_of('(', start); + return fFiddle.substr(start, end - start); +} + +const Definition* Definition::hasChild(MarkType markType) const { + for (auto iter : fChildren) { + if (markType == iter->fMarkType) { + return iter; + } + } + return nullptr; +} + +const Definition* Definition::hasParam(const string& ref) const { + SkASSERT(MarkType::kMethod == fMarkType); + for (auto iter : fChildren) { + if (MarkType::kParam != iter->fMarkType) { + continue; + } + if (iter->fName == ref) { + return &*iter; + } + + } + return nullptr; +} + +bool Definition::methodHasReturn(const string& name, TextParser* methodParser) const { + if (methodParser->skipExact("static")) { + methodParser->skipWhiteSpace(); + } + const char* lastStart = methodParser->fChar; + const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd); + methodParser->skipTo(nameInParser); + const char* lastEnd = methodParser->fChar; + const char* returnEnd = lastEnd; + while (returnEnd > lastStart && ' ' == returnEnd[-1]) { + --returnEnd; + } + bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4); + if (MethodType::kNone != fMethodType && MethodType::kOperator != fMethodType && !expectReturn) { + return methodParser->reportError<bool>("unexpected void"); + } + switch (fMethodType) { + case MethodType::kNone: + case MethodType::kOperator: + // either is fine + break; + case MethodType::kConstructor: + expectReturn = true; + break; + case MethodType::kDestructor: + expectReturn = false; + break; + } + return expectReturn; +} + +string Definition::methodName() const { + string result; + size_t start = 0; + string parent; + const Definition* parentDef = this; + while ((parentDef = parentDef->fParent)) { + if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) { + parent = parentDef->fName; + break; + } + } + if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) { + start = parent.length(); + while (start < fName.length() && ':' == fName[start]) { + ++start; + } + } + if (fClone) { + int lastUnder = fName.rfind('_'); + return fName.substr(start, (size_t) (lastUnder - start)); + } + size_t end = fName.find_first_of('(', start); + if (string::npos == end) { + return fName.substr(start); + } + return fName.substr(start, end - start); +} + +bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr, + string* paramName) const { + int parenCount = 0; + TextParser::Save saveState(methodParser); + while (true) { + if (methodParser->eof()) { + return methodParser->reportError<bool>("#Method function missing close paren"); + } + char ch = methodParser->peek(); + if ('(' == ch) { + ++parenCount; + } + if (parenCount == 0 && (')' == ch || ',' == ch)) { + *nextEndPtr = methodParser->fChar; + break; + } + if (')' == ch) { + if (0 > --parenCount) { + return this->reportError<bool>("mismatched parentheses"); + } + } + methodParser->next(); + } + saveState.restore(); + const char* nextEnd = *nextEndPtr; + const char* paramEnd = nextEnd; + const char* assign = methodParser->strnstr(" = ", paramEnd); + if (assign) { + paramEnd = assign; + } + const char* closeBracket = methodParser->strnstr("]", paramEnd); + if (closeBracket) { + const char* openBracket = methodParser->strnstr("[", paramEnd); + if (openBracket && openBracket < closeBracket) { + while (openBracket < --closeBracket && isdigit(closeBracket[0])) + ; + if (openBracket == closeBracket) { + paramEnd = openBracket; + } + } + } + const char* function = methodParser->strnstr(")(", paramEnd); + if (function) { + paramEnd = function; + } + while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) { + --paramEnd; + } + const char* paramStart = paramEnd; + while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) { + --paramStart; + } + if (paramStart > methodParser->fChar && paramStart >= paramEnd) { + return methodParser->reportError<bool>("#Method missing param name"); + } + *paramName = string(paramStart, paramEnd - paramStart); + if (!paramName->length()) { + if (')' != nextEnd[0]) { + return methodParser->reportError<bool>("#Method malformed param"); + } + return false; + } + return true; +} + +string Definition::NormalizedName(string name) { + string normalizedName = name; + std::replace(normalizedName.begin(), normalizedName.end(), '-', '_'); + do { + size_t doubleColon = normalizedName.find("::", 0); + if (string::npos == doubleColon) { + break; + } + normalizedName = normalizedName.substr(0, doubleColon) + + '_' + normalizedName.substr(doubleColon + 2); + } while (true); + return normalizedName; +} + +bool Definition::paramsMatch(const string& match, const string& name) const { + TextParser def(fFileName, fStart, fContentStart, fLineCount); + const char* dName = def.strnstr(name.c_str(), fContentStart); + if (!dName) { + return false; + } + def.skipTo(dName); + TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount); + const char* mName = m.strnstr(name.c_str(), m.fEnd); + if (!mName) { + return false; + } + m.skipTo(mName); + while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) { + const char* ds = def.fChar; + const char* ms = m.fChar; + const char* de = def.anyOf(") \n"); + const char* me = m.anyOf(") \n"); + def.skipTo(de); + m.skipTo(me); + if (def.fChar - ds != m.fChar - ms) { + return false; + } + if (strncmp(ds, ms, (int) (def.fChar - ds))) { + return false; + } + def.skipWhiteSpace(); + m.skipWhiteSpace(); + } + return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek(); +} + +void RootDefinition::clearVisited() { + fVisited = false; + for (auto& leaf : fLeaves) { + leaf.second.fVisited = false; + } + for (auto& branch : fBranches) { + branch.second->clearVisited(); + } +} + +bool RootDefinition::dumpUnVisited(bool skip) { + bool allStructElementsFound = true; + for (auto& leaf : fLeaves) { + if (!leaf.second.fVisited) { + // TODO: parse embedded struct in includeParser phase, then remove this condition + if (skip) { + const Definition& def = leaf.second; + if (def.fChildren.size() > 0 && + MarkType::kDeprecated == def.fChildren[0]->fMarkType) { + continue; + } + } + // FIXME: bugs requiring long tail fixes, suppressed here: + // SkBitmap::validate() is wrapped in SkDEBUGCODE in .h and not parsed + if ("SkBitmap::validate()" == leaf.first) { + continue; + } + // typedef uint32_t SaveLayerFlags not seen in SkCanvas.h, don't know why + if ("SaveLayerFlags" == leaf.first) { + continue; + } + // SkPath::pathRefIsValid in #ifdef ; prefer to remove chrome dependency to fix + if ("SkPath::pathRefIsValid" == leaf.first) { + continue; + } + // FIXME: end of long tail bugs + SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str()); + } + } + for (auto& branch : fBranches) { + allStructElementsFound &= branch.second->dumpUnVisited(skip); + } + return allStructElementsFound; +} + +const Definition* RootDefinition::find(const string& ref, AllowParens allowParens) const { + const auto leafIter = fLeaves.find(ref); + if (leafIter != fLeaves.end()) { + return &leafIter->second; + } + if (AllowParens::kYes == allowParens && string::npos == ref.find("()")) { + string withParens = ref + "()"; + const auto parensIter = fLeaves.find(withParens); + if (parensIter != fLeaves.end()) { + return &parensIter->second; + } + } + const auto branchIter = fBranches.find(ref); + if (branchIter != fBranches.end()) { + const RootDefinition* rootDef = branchIter->second; + return rootDef; + } + const Definition* result = nullptr; + for (const auto& branch : fBranches) { + const RootDefinition* rootDef = branch.second; + result = rootDef->find(ref, allowParens); + if (result) { + break; + } + } + return result; +} diff --git a/chromium/third_party/skia/tools/bookmaker/fiddleParser.cpp b/chromium/third_party/skia/tools/bookmaker/fiddleParser.cpp index 33615433016..faf551006f7 100644 --- a/chromium/third_party/skia/tools/bookmaker/fiddleParser.cpp +++ b/chromium/third_party/skia/tools/bookmaker/fiddleParser.cpp @@ -20,7 +20,7 @@ static Definition* find_fiddle(Definition* def, const string& name) { return nullptr; } -Definition* FiddleParser::findExample(const string& name) const { +Definition* FiddleBase::findExample(const string& name) const { for (const auto& topic : fBmhParser->fTopicMap) { if (topic.second->fParent) { continue; @@ -33,7 +33,7 @@ Definition* FiddleParser::findExample(const string& name) const { return nullptr; } -bool FiddleParser::parseFiddles() { +bool FiddleBase::parseFiddles() { if (!this->skipExact("{\n")) { return false; } @@ -110,50 +110,15 @@ bool FiddleParser::parseFiddles() { } } while (!this->eof() && this->next()); const char* stdOutEnd = fChar; - if (example) { - bool foundStdOut = false; - for (auto& textOut : example->fChildren) { - if (MarkType::kStdOut != textOut->fMarkType) { - continue; - } - foundStdOut = true; - bool foundVolatile = false; - for (auto& stdOutChild : textOut->fChildren) { - if (MarkType::kVolatile == stdOutChild->fMarkType) { - foundVolatile = true; - break; - } - } - TextParser bmh(textOut); - EscapeParser fiddle(stdOutStart, stdOutEnd); - do { - bmh.skipWhiteSpace(); - fiddle.skipWhiteSpace(); - const char* bmhEnd = bmh.trimmedLineEnd(); - const char* fiddleEnd = fiddle.trimmedLineEnd(); - ptrdiff_t bmhLen = bmhEnd - bmh.fChar; - SkASSERT(bmhLen > 0); - ptrdiff_t fiddleLen = fiddleEnd - fiddle.fChar; - SkASSERT(fiddleLen > 0); - if (bmhLen != fiddleLen) { - if (!foundVolatile) { - bmh.reportError("mismatched stdout len\n"); - } - } else if (strncmp(bmh.fChar, fiddle.fChar, fiddleLen)) { - if (!foundVolatile) { - bmh.reportError("mismatched stdout text\n"); - } - } - bmh.skipToLineStart(); - fiddle.skipToLineStart(); - } while (!bmh.eof() && !fiddle.eof()); - if (!foundStdOut) { - bmh.reportError("bmh %s missing stdout\n"); - } else if (!bmh.eof() || !fiddle.eof()) { - if (!foundVolatile) { - bmh.reportError("%s mismatched stdout eof\n"); - } - } + if (example && fTextOut) { + if (!this->textOut(example, stdOutStart, stdOutEnd)) { + return false; + } + } + } else { + if (example && fPngOut) { + if (!this->pngOut(example)) { + return false; } } } @@ -170,58 +135,54 @@ bool FiddleParser::parseFiddles() { return false; } } -#if 0 - // compare the text output with the expected output in the markup tree - this->skipToSpace(); - SkASSERT(' ' == fChar[0]); - this->next(); - const char* nameLoc = fChar; - this->skipToNonAlphaNum(); - const char* nameEnd = fChar; - string name(nameLoc, nameEnd - nameLoc); - const Definition* example = this->findExample(name); - if (!example) { - return this->reportError<bool>("missing stdout name"); - } - SkASSERT(':' == fChar[0]); - this->next(); - this->skipSpace(); - const char* stdOutLoc = fChar; - do { - this->skipToLineStart(); - } while (!this->eof() && !this->startsWith("fiddles.htm:")); - const char* stdOutEnd = fChar; - for (auto& textOut : example->fChildren) { - if (MarkType::kStdOut != textOut->fMarkType) { - continue; + return true; +} + +bool FiddleParser::textOut(Definition* example, const char* stdOutStart, + const char* stdOutEnd) { + bool foundStdOut = false; + for (auto& textOut : example->fChildren) { + if (MarkType::kStdOut != textOut->fMarkType) { + continue; + } + foundStdOut = true; + bool foundVolatile = false; + for (auto& stdOutChild : textOut->fChildren) { + if (MarkType::kVolatile == stdOutChild->fMarkType) { + foundVolatile = true; + break; } - TextParser bmh(textOut); - TextParser fiddle(fFileName, stdOutLoc, stdOutEnd, fLineCount); - do { - bmh.skipWhiteSpace(); - fiddle.skipWhiteSpace(); - const char* bmhEnd = bmh.trimmedLineEnd(); - const char* fiddleEnd = fiddle.trimmedLineEnd(); - ptrdiff_t bmhLen = bmhEnd - bmh.fChar; - SkASSERT(bmhLen > 0); - ptrdiff_t fiddleLen = fiddleEnd - fiddle.fChar; - SkASSERT(fiddleLen > 0); - if (bmhLen != fiddleLen) { - return this->reportError<bool>("mismatched stdout len"); - } - if (strncmp(bmh.fChar, fiddle.fChar, fiddleLen)) { - return this->reportError<bool>("mismatched stdout text"); - } - bmh.skipToLineStart(); - fiddle.skipToLineStart(); - } while (!bmh.eof() && !fiddle.eof()); - if (!bmh.eof() || (!fiddle.eof() && !fiddle.startsWith("</pre>"))) { - return this->reportError<bool>("mismatched stdout eof"); + } + TextParser bmh(textOut); + EscapeParser fiddle(stdOutStart, stdOutEnd); + do { + bmh.skipWhiteSpace(); + fiddle.skipWhiteSpace(); + const char* bmhEnd = bmh.trimmedLineEnd(); + const char* fiddleEnd = fiddle.trimmedLineEnd(); + ptrdiff_t bmhLen = bmhEnd - bmh.fChar; + SkASSERT(bmhLen > 0); + ptrdiff_t fiddleLen = fiddleEnd - fiddle.fChar; + SkASSERT(fiddleLen > 0); + if (bmhLen != fiddleLen) { + if (!foundVolatile) { + bmh.reportError("mismatched stdout len\n"); + } + } else if (strncmp(bmh.fChar, fiddle.fChar, fiddleLen)) { + if (!foundVolatile) { + bmh.reportError("mismatched stdout text\n"); } - break; + } + bmh.skipToLineStart(); + fiddle.skipToLineStart(); + } while (!bmh.eof() && !fiddle.eof()); + if (!foundStdOut) { + bmh.reportError("bmh %s missing stdout\n"); + } else if (!bmh.eof() || !fiddle.eof()) { + if (!foundVolatile) { + bmh.reportError("%s mismatched stdout eof\n"); } } } -#endif return true; } diff --git a/chromium/third_party/skia/tools/bookmaker/includeParser.cpp b/chromium/third_party/skia/tools/bookmaker/includeParser.cpp index 4ad83d8aa7e..955e6ef724e 100644 --- a/chromium/third_party/skia/tools/bookmaker/includeParser.cpp +++ b/chromium/third_party/skia/tools/bookmaker/includeParser.cpp @@ -10,6 +10,7 @@ const IncludeKey kKeyWords[] = { { "", KeyWord::kNone, KeyProperty::kNone }, { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier }, + { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier }, { "bool", KeyWord::kBool, KeyProperty::kNumber }, { "char", KeyWord::kChar, KeyProperty::kNumber }, { "class", KeyWord::kClass, KeyProperty::kObject }, @@ -105,7 +106,7 @@ void IncludeParser::checkForMissingParams(const vector<string>& methodParams, } } if (!found) { - this->writeEndTag("Param", methodParam, 2); + this->writeIncompleteTag("Param", methodParam, 2); } } for (auto& foundParam : foundParams) { @@ -438,7 +439,7 @@ bool IncludeParser::crossCheck(BmhParser& bmhParser) { continue; } RootDefinition* root = &finder->second; - if (!root->dumpUnVisited()) { + if (!root->dumpUnVisited(bmhParser.fSkip)) { SkDebugf("some struct elements not found; struct finding in includeParser is missing\n"); } SkDebugf("cross-checked %s\n", className.c_str()); @@ -494,6 +495,7 @@ void IncludeParser::dumpClassTokens(IClassDefinition& classDef) { } switch (token.fMarkType) { case MarkType::kEnum: + case MarkType::kEnumClass: this->dumpEnum(token); break; case MarkType::kMethod: @@ -508,11 +510,30 @@ void IncludeParser::dumpClassTokens(IClassDefinition& classDef) { } this->lf(2); this->writeTag("Example"); + this->lf(1); + this->writeString("// incomplete"); + this->lf(1); this->writeEndTag(); this->lf(2); - this->writeEndTag("ToDo", "incomplete"); + this->writeTag("SeeAlso"); + this->writeSpace(); + this->writeString("incomplete"); this->lf(2); - this->writeEndTag(); + switch (token.fMarkType) { + case MarkType::kEnum: + case MarkType::kEnumClass: + this->writeEndTag("Enum"); + break; + case MarkType::kMethod: + this->writeEndTag("Method"); + break; + case MarkType::kMember: + this->writeEndTag("Member"); + continue; + break; + default: + SkASSERT(0); + } this->lf(2); } } @@ -534,6 +555,7 @@ void IncludeParser::dumpComment(const Definition& token) { methodName.fName = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart)); methodHasReturn = !methodParser.startsWith("void ") + && !methodParser.startsWith("static void ") && !methodParser.strnchr('~', methodParser.fEnd); const char* paren = methodParser.strnchr('(', methodParser.fEnd); const char* nextEnd = paren; @@ -673,7 +695,7 @@ void IncludeParser::dumpComment(const Definition& token) { this->nl(); } this->lf(2); - this->writeEndTag("Return"); + this->writeIncompleteTag("Return"); } } } @@ -829,7 +851,8 @@ bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) { this->writeTag("Alias", topicName + "_Reference"); this->lf(2); auto& classMap = fIClassMap[skClassName]; - const char* containerType = kKeyWords[(int) classMap.fKeyWord].fName; + SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord); + const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct"; this->writeTag(containerType, skClassName); this->lf(2); auto& tokens = classMap.fTokens; @@ -951,7 +974,9 @@ bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) { this->writeString( "# ------------------------------------------------------------------------------"); this->lf(2); - const char* containerType = kKeyWords[(int) oneClass.second.fKeyWord].fName; + KeyWord keyword = oneClass.second.fKeyWord; + SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword); + const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct"; this->writeTag(containerType, innerName); this->lf(2); this->writeTag("Code"); @@ -1389,11 +1414,39 @@ bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) { auto tokenIter = child->fParent->fTokens.begin(); std::advance(tokenIter, child->fParentIndex); tokenIter = std::prev(tokenIter); - string nameStr(tokenIter->fStart, tokenIter->fContentEnd - tokenIter->fStart); + const char* nameEnd = tokenIter->fContentEnd; + bool addConst = false; + auto operatorCheck = tokenIter; + if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) { + operatorCheck = std::prev(tokenIter); + } + if (KeyWord::kOperator == operatorCheck->fKeyWord) { + auto closeParen = std::next(tokenIter); + SkASSERT(Definition::Type::kBracket == closeParen->fType && + '(' == closeParen->fContentStart[0]); + nameEnd = closeParen->fContentEnd + 1; + closeParen = std::next(closeParen); + if (Definition::Type::kKeyWord == closeParen->fType && + KeyWord::kConst == closeParen->fKeyWord) { + addConst = true; + } + tokenIter = operatorCheck; + } + string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart); + if (addConst) { + nameStr += "_const"; + } while (tokenIter != child->fParent->fTokens.begin()) { auto testIter = std::prev(tokenIter); switch (testIter->fType) { case Definition::Type::kWord: + if (testIter == child->fParent->fTokens.begin() && + (KeyWord::kIfdef == child->fParent->fKeyWord || + KeyWord::kIfndef == child->fParent->fKeyWord || + KeyWord::kIf == child->fParent->fKeyWord)) { + std::next(tokenIter); + break; + } goto keepGoing; case Definition::Type::kKeyWord: { KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty; @@ -1421,6 +1474,9 @@ bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) { break; } tokenIter->fName = nameStr; + if ("operator" == nameStr) { + SkDebugf(""); + } tokenIter->fMarkType = MarkType::kMethod; tokenIter->fPrivate = string::npos != nameStr.find("::"); auto testIter = child->fParent->fTokens.begin(); @@ -1469,11 +1525,27 @@ bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) { SkASSERT(child->fParentIndex > 0); std::advance(parentIter, child->fParentIndex - 1); Definition* methodName = &*parentIter; - TextParser name(methodName); - if (name.skipToEndBracket(':') && name.startsWith("::")) { + TextParser nameParser(methodName); + if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) { return true; // expect this is inline class definition outside of class } - SkASSERT(0); // code incomplete + string name(nameParser.fLine, nameParser.lineLength()); + auto finder = fIFunctionMap.find(name); + if (fIFunctionMap.end() != finder) { + // create unique name + SkASSERT(0); // incomplete + } + auto globalFunction = &fIFunctionMap[name]; + globalFunction->fContentStart = start; + globalFunction->fName = name; + if ("operator+" == name) { + SkDebugf(""); + } + globalFunction->fFiddle = name; + globalFunction->fContentEnd = end; + globalFunction->fMarkType = MarkType::kMethod; + globalFunction->fLineCount = tokenIter->fLineCount; + return true; } markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount, markupDef); @@ -1483,6 +1555,9 @@ bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) { SkASSERT(classDef.fStart); string uniqueName = this->uniqueName(classDef.fMethods, nameStr); markupChild->fName = uniqueName; + if ("operator+" == uniqueName) { + SkDebugf(""); + } if (!this->findComments(*child, markupChild)) { return false; } @@ -1574,6 +1649,7 @@ bool IncludeParser::parseObject(Definition* child, Definition* markupDef) { case Bracket::kPound: // special-case the #xxx xxx_DEFINED entries switch (child->fKeyWord) { + case KeyWord::kIf: case KeyWord::kIfndef: case KeyWord::kIfdef: if (child->boilerplateIfDef(fParent)) { @@ -1608,7 +1684,9 @@ bool IncludeParser::parseObject(Definition* child, Definition* markupDef) { // pick up templated function pieces when method is found break; case Bracket::kDebugCode: - // todo: handle this + if (!this->parseObjects(child, markupDef)) { + return false; + } break; case Bracket::kSquare: { // check to see if parent is operator, the only case we handle so far @@ -1703,8 +1781,10 @@ bool IncludeParser::parseChar() { return reportError<bool>("malformed closing comment"); } if (Bracket::kSlashStar == this->topBracket()) { - this->next(); // include close in bracket -- FIXME? will this skip stuff? + TextParser::Save save(this); + this->next(); // include close in bracket this->popBracket(); + save.restore(); // put things back so nothing is skipped } break; } @@ -1865,6 +1945,9 @@ bool IncludeParser::parseChar() { if (fInEnum) { break; } + if (Bracket::kPound == this->topBracket()) { + break; + } if (Bracket::kAngle == this->topBracket()) { this->popBracket(); } else { @@ -1882,6 +1965,10 @@ bool IncludeParser::parseChar() { case '&': case ',': case ' ': + case '+': + case '=': + case '-': + case '!': if (fInCharCommentString || fInBrace) { break; } diff --git a/chromium/third_party/skia/tools/bookmaker/includeWriter.cpp b/chromium/third_party/skia/tools/bookmaker/includeWriter.cpp index ff7c0e3c56f..c462826f48a 100644 --- a/chromium/third_party/skia/tools/bookmaker/includeWriter.cpp +++ b/chromium/third_party/skia/tools/bookmaker/includeWriter.cpp @@ -8,11 +8,50 @@ #include "bookmaker.h" void IncludeWriter::descriptionOut(const Definition* def) { + if ("SkPoint_length" == def->fFiddle) { + SkDebugf(""); + } const char* commentStart = def->fContentStart; int commentLen = (int) (def->fContentEnd - commentStart); bool breakOut = false; + SkDEBUGCODE(bool wroteCode = false); for (auto prop : def->fChildren) { switch (prop->fMarkType) { + case MarkType::kCode: { + bool literal = false; + bool literalOutdent = false; + commentLen = (int) (prop->fStart - commentStart); + if (commentLen > 0) { + SkASSERT(commentLen < 1000); + if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { + this->lf(2); + } + } + size_t childSize = prop->fChildren.size(); + if (childSize) { + SkASSERT(1 == childSize || 2 == childSize); // incomplete + SkASSERT(MarkType::kLiteral == prop->fChildren[0]->fMarkType); + SkASSERT(1 == childSize || MarkType::kOutdent == prop->fChildren[1]->fMarkType); + commentStart = prop->fChildren[childSize - 1]->fContentStart; + literal = true; + literalOutdent = 2 == childSize && + MarkType::kOutdent == prop->fChildren[1]->fMarkType; + } + commentLen = (int) (prop->fContentEnd - commentStart); + SkASSERT(commentLen > 0); + if (literal) { + if (!literalOutdent) { + fIndent += 4; + } + this->writeBlockIndent(commentLen, commentStart); + this->lf(2); + if (!literalOutdent) { + fIndent -= 4; + } + commentStart = prop->fTerminator; + SkDEBUGCODE(wroteCode = true); + } + } break; case MarkType::kDefinedBy: commentStart = prop->fTerminator; break; @@ -48,20 +87,35 @@ void IncludeWriter::descriptionOut(const Definition* def) { commentStart = prop->fTerminator; commentLen = (int) (def->fContentEnd - commentStart); break; - case MarkType::kFormula: + case MarkType::kFormula: { commentLen = prop->fStart - commentStart; if (commentLen > 0) { if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { - this->lfcr(); + if (commentLen > 1 && '\n' == prop->fStart[-1] && + '\n' == prop->fStart[-2]) { + this->lf(1); + } else { + this->writeSpace(); + } } } - this->writeBlock(prop->length(), prop->fContentStart); + int saveIndent = fIndent; + if (fIndent < fColumn + 1) { + fIndent = fColumn + 1; + } + this->writeBlockIndent(prop->length(), prop->fContentStart); + fIndent = saveIndent; commentStart = prop->fTerminator; commentLen = (int) (def->fContentEnd - commentStart); - if ('\n' == commentStart[0] && '\n' == commentStart[1]) { + if (commentLen > 1 && '\n' == commentStart[0] && '\n' == commentStart[1]) { this->lf(2); + } else { + SkASSERT('\n' == prop->fTerminator[0]); + if ('.' != prop->fTerminator[1] && !fLinefeeds) { + this->writeSpace(); + } } - break; + } break; case MarkType::kToDo: commentLen = (int) (prop->fStart - commentStart); if (commentLen > 0) { @@ -105,8 +159,10 @@ void IncludeWriter::descriptionOut(const Definition* def) { break; } } - SkASSERT(commentLen > 0 && commentLen < 1500); - this->rewriteBlock(commentLen, commentStart, Phrase::kNo); + SkASSERT(wroteCode || (commentLen > 0 && commentLen < 1500)); + if (commentLen > 0) { + this->rewriteBlock(commentLen, commentStart, Phrase::kNo); + } } void IncludeWriter::enumHeaderOut(const RootDefinition* root, @@ -116,6 +172,10 @@ void IncludeWriter::enumHeaderOut(const RootDefinition* root, child.fContentStart; this->writeBlockTrim((int) (bodyEnd - fStart), fStart); // may write nothing this->lf(2); + if (fIndentNext) { + fIndent += 4; + fIndentNext = false; + } fDeferComment = nullptr; fStart = child.fContentStart; const auto& nameDef = child.fTokens.front(); @@ -172,6 +232,9 @@ void IncludeWriter::enumHeaderOut(const RootDefinition* root, const char* commentEnd = test->fStart; if (!wroteHeader && !this->contentFree((int) (commentEnd - commentStart), commentStart)) { + if (fIndentNext) { + fIndent += 4; + } this->writeCommentHeader(); this->writeString("\\enum"); if (fullName.length() > 0) { @@ -193,9 +256,15 @@ void IncludeWriter::enumHeaderOut(const RootDefinition* root, } this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo); if (MarkType::kAnchor == test->fMarkType) { + bool newLine = commentEnd - commentStart > 1 && + '\n' == commentEnd[-1] && '\n' == commentEnd[-2]; commentStart = test->fContentStart; commentEnd = test->fChildren[0]->fStart; - this->writeSpace(); + if (newLine) { + this->lf(2); + } else { + this->writeSpace(); + } this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo); lastAnchor = true; // this->writeSpace(); } @@ -518,12 +587,17 @@ void IncludeWriter::enumSizeItems(const Definition& child) { // walk children and output complete method doxygen description void IncludeWriter::methodOut(const Definition* method, const Definition& child) { + if (fPendingMethod) { + fIndent -= 4; + fPendingMethod = false; + } fBmhMethod = method; fMethodDef = &child; fContinuation = nullptr; fDeferComment = nullptr; - if (0 == fIndent) { - fIndent = 4; + if (0 == fIndent || fIndentNext) { + fIndent += 4; + fIndentNext = false; } this->writeCommentHeader(); fIndent += 4; @@ -602,13 +676,17 @@ Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const const char* blockEnd = fWroteMethod && fDeferComment ? fDeferComment->fStart - 1 : memberStart->fStart; this->writeBlockTrim((int) (blockEnd - blockStart), blockStart); + if (fIndentNext) { + fIndent += 4; + fIndentNext = false; + } fWroteMethod = false; const char* commentStart = nullptr; ptrdiff_t commentLen = 0; string name(child.fContentStart, (int) (child.fContentEnd - child.fContentStart)); bool isShort; Definition* commentBlock = nullptr; - for (auto memberDef : fStructDef->fChildren) { + for (auto memberDef : fBmhStructDef->fChildren) { if (memberDef->fName.length() - name.length() == memberDef->fName.find(name)) { commentStart = memberDef->fContentStart; commentLen = memberDef->fContentEnd - commentStart; @@ -668,8 +746,8 @@ Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const this->writeString("//!<"); this->writeSpace(); this->rewriteBlock(commentLen, commentStart, Phrase::kNo); - this->lfcr(); } + this->lf(2); return valueEnd; } @@ -784,7 +862,7 @@ void IncludeWriter::structSizeMembers(const Definition& child) { fStructValueTab -= 1 /* ; */ ; } // iterate through bmh children and see which comments fit on include lines - for (auto& member : fStructDef->fChildren) { + for (auto& member : fBmhStructDef->fChildren) { if (MarkType::kMember != member->fMarkType) { continue; } @@ -800,6 +878,21 @@ void IncludeWriter::structSizeMembers(const Definition& child) { } } +static bool find_start(const Definition* startDef, const char* start) { + for (const auto& child : startDef->fTokens) { + if (child.fContentStart == start) { + return MarkType::kMethod == child.fMarkType; + } + if (child.fContentStart >= start) { + break; + } + if (find_start(&child, start)) { + return true; + } + } + return false; +} + bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefinition* root) { ParentPair pair = { def, prevPair }; // write bulk of original include up to class, method, enum, etc., excepting preceding comment @@ -816,11 +909,30 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti bool inStruct = false; bool inConstructor = false; bool inInline = false; + bool eatOperator = false; + const Definition* requireDense = nullptr; + const Definition* startDef = nullptr; for (auto& child : def->fTokens) { + if (KeyWord::kOperator == child.fKeyWord && method && + Definition::MethodType::kOperator == method->fMethodType) { + eatOperator = true; + continue; + } + if (eatOperator) { + if (Bracket::kSquare == child.fBracket || Bracket::kParen == child.fBracket) { + continue; + } + eatOperator = false; + fContinuation = nullptr; + if (KeyWord::kConst == child.fKeyWord) { + continue; + } + } if (memberEnd) { if (memberEnd != &child) { continue; } + startDef = &child; fStart = child.fContentStart + 1; memberEnd = nullptr; } @@ -915,6 +1027,9 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti --continueEnd; } methodName += string(fContinuation, continueEnd - fContinuation); + if ("SkIPoint::operator+" == methodName) { + SkDebugf(""); + } method = root->find(methodName, RootDefinition::AllowParens::kNo); if (!method) { fLineCount = child.fLineCount; @@ -958,6 +1073,7 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti continue; } const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 : + fAttrDeprecated ? fAttrDeprecated->fContentStart - 1 : child.fContentStart; // FIXME: roll end-trimming into writeBlockTrim call while (fStart < bodyEnd && ' ' >= bodyEnd[-1]) { @@ -967,11 +1083,18 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti if (blockSize) { this->writeBlock(blockSize, fStart); } + startDef = &child; fStart = child.fContentStart; methodName = root->fName + "::" + child.fName; + if ("SkIPoint::operator+" == methodName) { + SkDebugf(""); + } inConstructor = root->fName == child.fName; fContinuation = child.fContentEnd; method = root->find(methodName, RootDefinition::AllowParens::kNo); +// if (!method) { +// method = root->find(methodName + "()", RootDefinition::AllowParens::kNo); +// } if (!method) { continue; } @@ -981,13 +1104,25 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti } this->methodOut(method, child); if (fAttrDeprecated) { + startDef = fAttrDeprecated; fStart = fAttrDeprecated->fContentStart; fAttrDeprecated = nullptr; } continue; } if (Definition::Type::kKeyWord == child.fType) { - const Definition* structDef = nullptr; + if (fIndentNext) { + // too soon +#if 0 // makes struct Lattice indent when it oughtn't + if (KeyWord::kEnum == child.fKeyWord) { + fIndent += 4; + } + if (KeyWord::kPublic != child.fKeyWord) { + fIndentNext = false; + } +#endif + } + const Definition* cIncludeStructDef = nullptr; switch (child.fKeyWord) { case KeyWord::kStruct: case KeyWord::kClass: @@ -1002,20 +1137,51 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti } } if (fInStruct) { + // try child; root+child; root->parent+child; etc. + int trial = 0; + const RootDefinition* search = root; + const Definition* parent = search->fParent; + do { + string name; + if (0 == trial) { + name = child.fName; + } else if (1 == trial) { + name = root->fName + "::" + child.fName; + } else { + SkASSERT(parent); + name = parent->fName + "::" + child.fName; + search = parent->asRoot(); + parent = search->fParent; + } + fBmhStructDef = search->find(name, RootDefinition::AllowParens::kNo); + } while (!fBmhStructDef && ++trial); + root = const_cast<RootDefinition*>(fBmhStructDef->asRoot()); + SkASSERT(root); fIndent += 4; - fStructDef = root->find(child.fName, RootDefinition::AllowParens::kNo); - if (nullptr == structDef) { - fStructDef = root->find(root->fName + "::" + child.fName, - RootDefinition::AllowParens::kNo); - } this->structSizeMembers(child); fIndent -= 4; + SkASSERT(!fIndentNext); + fIndentNext = true; } if (child.fChildren.size() > 0) { const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 : child.fContentStart; this->writeBlockTrim((int) (bodyEnd - fStart), fStart); - fStart = child.fContentStart; + if (fPendingMethod) { + fIndent -= 4; + fPendingMethod = false; + } + startDef = requireDense ? requireDense : &child; + fStart = requireDense ? requireDense->fContentStart : child.fContentStart; + requireDense = nullptr; + if (!fInStruct && child.fName != root->fName) { + root = &fBmhParser->fClassMap[child.fName]; + fRootTopic = root->fParent; + SkASSERT(!root->fVisited); + root->clearVisited(); + fIndent = 0; + fBmhStructDef = root; + } if (child.fName == root->fName) { if (Definition* parent = root->fParent) { if (MarkType::kTopic == parent->fMarkType || @@ -1030,36 +1196,43 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti SkASSERT(0); // incomplete } } else { - structDef = root->find(child.fName, RootDefinition::AllowParens::kNo); - if (nullptr == structDef) { - structDef = root->find(root->fName + "::" + child.fName, + SkASSERT(fInStruct); + #if 0 + fBmhStructDef = root->find(child.fName, RootDefinition::AllowParens::kNo); + if (nullptr == fBmhStructDef) { + fBmhStructDef = root->find(root->fName + "::" + child.fName, RootDefinition::AllowParens::kNo); } - if (!structDef) { + if (!fBmhStructDef) { this->lf(2); fIndent = 0; this->writeBlock((int) (fStart - bodyEnd), bodyEnd); this->lfcr(); continue; } + #endif Definition* codeBlock = nullptr; - SkDEBUGCODE(Definition* nextBlock = nullptr); - for (auto test : structDef->fChildren) { + Definition* nextBlock = nullptr; + for (auto test : fBmhStructDef->fChildren) { if (MarkType::kCode == test->fMarkType) { SkASSERT(!codeBlock); // FIXME: check enum for correct order earlier codeBlock = test; continue; } if (codeBlock) { - SkDEBUGCODE(nextBlock = test); + nextBlock = test; break; } } // FIXME: trigger error earlier if inner #Struct or #Class is missing #Code SkASSERT(nextBlock); // FIXME: check enum for correct order earlier - const char* commentStart = structDef->fContentStart; - const char* commentEnd = codeBlock->fStart; - this->structOut(root, *structDef, commentStart, commentEnd); + const char* commentStart = codeBlock->fTerminator; + const char* commentEnd = nextBlock->fStart; + if (fIndentNext) { +// fIndent += 4; + } + fIndentNext = true; + this->structOut(root, *fBmhStructDef, commentStart, commentEnd); } fDeferComment = nullptr; } else { @@ -1096,17 +1269,21 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti case KeyWord::kSK_API: case KeyWord::kTypedef: break; + case KeyWord::kSK_BEGIN_REQUIRE_DENSE: + requireDense = &child; + break; default: SkASSERT(0); } - if (structDef) { + if (cIncludeStructDef) { TextParser structName(&child); SkAssertResult(structName.skipToEndBracket('{')); + startDef = &child; fStart = structName.fChar + 1; this->writeBlock((int) (fStart - child.fStart), child.fStart); this->lf(2); fIndent += 4; - if (!this->populate(&child, &pair, const_cast<Definition*>(structDef)->asRoot())) { + if (!this->populate(&child, &pair, const_cast<Definition*>(cIncludeStructDef)->asRoot())) { return false; } // output any remaining definitions at current indent level @@ -1126,8 +1303,28 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti if (!this->populate(child.fChildren[0], &pair, root)) { return false; } - } else if (!this->populate(&child, &pair, root)) { - return false; + } else { + if (!this->populate(&child, &pair, root)) { + return false; + } + if (KeyWord::kClass == child.fKeyWord || KeyWord::kStruct == child.fKeyWord) { + fStructMemberTab = 0; + if (fInStruct) { + fInStruct = false; + do { + SkASSERT(root); + root = const_cast<RootDefinition*>(root->fParent->asRoot()); + } while (MarkType::kTopic == root->fMarkType || + MarkType::kSubtopic == root->fMarkType); + SkASSERT(MarkType::kStruct == root->fMarkType || + MarkType::kClass == root->fMarkType); + fPendingMethod = false; + if (startDef) { + fPendingMethod = find_start(startDef, fStart); + } + fOutdentNext = !fPendingMethod; + } + } } } continue; @@ -1140,17 +1337,25 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti this->enumMembersOut(root, child); this->writeString("};"); this->lf(2); + startDef = child.fParent; fStart = child.fParent->fContentEnd; SkASSERT(';' == fStart[0]); ++fStart; fDeferComment = nullptr; fInEnum = false; + if (fIndentNext) { +// fIndent -= 4; + fIndentNext = false; + } continue; } if (fAttrDeprecated) { continue; } fDeferComment = nullptr; + if (KeyWord::kClass == def->fKeyWord || KeyWord::kStruct == def->fKeyWord) { + fIndentNext = true; + } if (!this->populate(&child, &pair, root)) { return false; } @@ -1162,13 +1367,17 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti auto iter = def->fTokens.begin(); std::advance(iter, child.fParentIndex - 1); memberStart = &*iter; - if (!fStructDef) { + if (!fStructMemberTab) { SkASSERT(KeyWord::kStruct == def->fParent->fKeyWord); - fStructDef = def->fParent; - this->structSizeMembers(*fStructDef); + fIndent += 4; + this->structSizeMembers(*def->fParent); + fIndent -= 4; +// SkASSERT(!fIndentNext); + fIndentNext = true; } } memberEnd = this->structMemberOut(memberStart, child); + startDef = &child; fStart = child.fContentEnd + 1; fDeferComment = nullptr; } @@ -1322,8 +1531,8 @@ string IncludeWriter::resolveRef(const char* start, const char* end, bool first, rootDefIter = fBmhParser->fTopicMap.find(prefixedName); if (fBmhParser->fTopicMap.end() != rootDefIter) { rootDef = rootDefIter->second; - } else if (fStructDef) { - string localPrefix = fStructDef->fFiddle + '_' + undername; + } else if (fBmhStructDef) { + string localPrefix = fBmhStructDef->fFiddle + '_' + undername; rootDefIter = fBmhParser->fTopicMap.find(localPrefix); if (fBmhParser->fTopicMap.end() != rootDefIter) { rootDef = rootDefIter->second; @@ -1500,6 +1709,7 @@ IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phr if (lastPrintable >= lastWrite) { if (' ' == data[lastWrite]) { this->writeSpace(); + lastWrite++; } this->writeBlock(lastPrintable - lastWrite + 1, &data[lastWrite]); } @@ -1648,7 +1858,7 @@ IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phr break; case Word::kCap: case Word::kFirst: - if (!isupper(last)) { + if (!isupper(last) && '~' != last) { word = Word::kMixed; } break; @@ -1697,6 +1907,14 @@ IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phr hasIndirection |= embeddedIndirection; hasSymbol |= embeddedSymbol; break; + case '~': + SkASSERT(Word::kStart == word); + word = PunctuationState::kStart == punctuation ? Word::kFirst : Word::kCap; + start = run; + hasUpper = true; + hasIndirection |= embeddedIndirection; + hasSymbol |= embeddedSymbol; + break; default: SkASSERT(0); } diff --git a/chromium/third_party/skia/tools/bookmaker/mdOut.cpp b/chromium/third_party/skia/tools/bookmaker/mdOut.cpp index 242d667736d..6b082ad1de4 100644 --- a/chromium/third_party/skia/tools/bookmaker/mdOut.cpp +++ b/chromium/third_party/skia/tools/bookmaker/mdOut.cpp @@ -62,7 +62,10 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, lineStart = false; } else { leadingSpaces = string(base, wordStart - base); - } + } + if (!strncmp("SkPoint::operator-()", start, 20)) { + SkDebugf(""); + } t.skipToMethodEnd(); if (base == t.fChar) { break; @@ -74,6 +77,9 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, continue; } ref = string(start, t.fChar - start); + if (412 == t.fLineCount) { + SkDebugf(""); + } if (const Definition* def = this->isDefined(t, ref, BmhParser::Resolvable::kOut != resolvable)) { SkASSERT(def->fFiddle.length()); @@ -105,7 +111,8 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, return result; } if (!foundMatch) { - if (!(def = this->isDefined(t, fullRef, true))) { + if (!(def = this->isDefined(t, fullRef, + BmhParser::Resolvable::kOut != resolvable))) { if (!result.size()) { t.reportError("missing method"); } @@ -324,7 +331,7 @@ bool MdOut::buildRefFromFile(const char* name, const char* outDir) { bool MdOut::checkParamReturnBody(const Definition* def) const { TextParser paramBody(def); const char* descriptionStart = paramBody.fChar; - if (!islower(descriptionStart[0])) { + if (!islower(descriptionStart[0]) && !isdigit(descriptionStart[0])) { paramBody.skipToNonAlphaNum(); string ref = string(descriptionStart, paramBody.fChar - descriptionStart); if (!this->isDefined(paramBody, ref, true)) { @@ -349,7 +356,7 @@ void MdOut::childrenOut(const Definition* def, const char* start) { } else if (MarkType::kEnumClass == def->fMarkType) { fEnumClass = def; } - BmhParser::Resolvable resolvable = this->resolvable(def->fMarkType); + BmhParser::Resolvable resolvable = this->resolvable(def); for (auto& child : def->fChildren) { end = child->fStart; if (BmhParser::Resolvable::kNo != resolvable) { @@ -640,7 +647,7 @@ void MdOut::markTypeOut(Definition* def) { case MarkType::kCode: this->lfAlways(2); fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em;" - "width: 44em; background-color: #f0f0f0\">"); + "width: 62.5em; background-color: #f0f0f0\">"); this->lf(1); break; case MarkType::kColumn: @@ -705,17 +712,34 @@ void MdOut::markTypeOut(Definition* def) { fprintf(fOut, "Example\n" "\n"); fHasFiddle = true; + bool showGpu = false; + bool gpuAndCpu = false; const Definition* platform = def->hasChild(MarkType::kPlatform); if (platform) { TextParser platParse(platform); fHasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd); + showGpu = platParse.strnstr("gpu", platParse.fEnd); + if (showGpu) { + gpuAndCpu = platParse.strnstr("cpu", platParse.fEnd); + } } if (fHasFiddle) { - fprintf(fOut, "<div><fiddle-embed name=\"%s\">", def->fHash.c_str()); + fprintf(fOut, "<div><fiddle-embed name=\"%s\"", def->fHash.c_str()); + if (showGpu) { + fprintf(fOut, "gpu=\"true\""); + if (gpuAndCpu) { + fprintf(fOut, "cpu=\"true\""); + } + } + fprintf(fOut, ">"); } else { - fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em;" - "width: 44em; background-color: #f0f0f0\">"); - this->lf(1); + fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em; font-size: 13px" + " width: 62.5em; background-color: #f0f0f0\">"); + this->lfAlways(1); + if (def->fWrapper.length() > 0) { + fprintf(fOut, "%s", def->fWrapper.c_str()); + } + fRespectLeadingSpace = true; } } break; case MarkType::kExperimental: @@ -742,6 +766,8 @@ void MdOut::markTypeOut(Definition* def) { fprintf(fOut, "<table>"); this->lf(1); break; + case MarkType::kLiteral: + break; case MarkType::kMarkChar: fBmhParser.fMC = def->fContentStart[0]; break; @@ -768,11 +794,11 @@ void MdOut::markTypeOut(Definition* def) { } // TODO: put in css spec that we can define somewhere else (if markup supports that) - // TODO: 50em below should match limt = 80 in formatFunction() + // TODO: 50em below should match limit = 80 in formatFunction() this->writePending(); string preformattedStr = preformat(formattedStr); fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em;" - "width: 50em; background-color: #f0f0f0\">\n" + "width: 62.5em; background-color: #f0f0f0\">\n" "%s\n" "</pre>", preformattedStr.c_str()); this->lf(2); @@ -781,6 +807,8 @@ void MdOut::markTypeOut(Definition* def) { } break; case MarkType::kNoExample: break; + case MarkType::kOutdent: + break; case MarkType::kParam: { if (TableState::kNone == fTableState) { this->mdHeaderOut(3); @@ -932,9 +960,15 @@ void MdOut::markTypeOut(Definition* def) { if (fHasFiddle) { fprintf(fOut, "</fiddle-embed></div>"); } else { + this->lfAlways(1); + if (def->fWrapper.length() > 0) { + fprintf(fOut, "}"); + this->lfAlways(1); + } fprintf(fOut, "</pre>"); } this->lf(2); + fRespectLeadingSpace = false; break; case MarkType::kList: fInList = false; @@ -1004,6 +1038,24 @@ void MdOut::mdHeaderOutLF(int depth, int lf) { } void MdOut::resolveOut(const char* start, const char* end, BmhParser::Resolvable resolvable) { + if ((BmhParser::Resolvable::kLiteral == resolvable || fRespectLeadingSpace) && end > start) { + while ('\n' == *start) { + ++start; + } + const char* spaceStart = start; + while (' ' == *start) { + ++start; + } + if (start > spaceStart) { + fIndent = start - spaceStart; + } + this->writeBlockTrim(end - start, start); + if ('\n' == end[-1]) { + this->lf(1); + } + fIndent = 0; + return; + } // FIXME: this needs the markdown character present when the def was defined, // not the last markdown character the parser would have seen... while (fBmhParser.fMC == end[-1]) { diff --git a/chromium/third_party/skia/tools/bookmaker/parserCommon.cpp b/chromium/third_party/skia/tools/bookmaker/parserCommon.cpp index cb55bcb640b..79503a66b31 100644 --- a/chromium/third_party/skia/tools/bookmaker/parserCommon.cpp +++ b/chromium/third_party/skia/tools/bookmaker/parserCommon.cpp @@ -7,8 +7,41 @@ #include "bookmaker.h" +#include "SkOSFile.h" +#include "SkOSPath.h" + +static void debug_out(int len, const char* data) { + // convenient place to intercept arbitrary output + SkDebugf("%.*s", len, data); +} + +bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix) { +// this->reset(); + if (!sk_isdir(fileOrPath)) { + if (!this->parseFromFile(fileOrPath)) { + SkDebugf("failed to parse %s\n", fileOrPath); + return false; + } + } else { + SkOSFile::Iter it(fileOrPath, suffix); + for (SkString file; it.next(&file); ) { + SkString p = SkOSPath::Join(fileOrPath, file.c_str()); + const char* hunk = p.c_str(); + if (!SkStrEndsWith(hunk, suffix)) { + continue; + } + if (!this->parseFromFile(hunk)) { + SkDebugf("failed to parse %s\n", hunk); + return false; + } + } + } + return true; +} + + bool ParserCommon::parseSetup(const char* path) { - this->reset(); +// this->reset(); sk_sp<SkData> data = SkData::MakeFromFileName(path); if (nullptr == data.get()) { SkDebugf("%s missing\n", path); @@ -49,3 +82,131 @@ bool ParserCommon::parseSetup(const char* path) { fLineCount = 1; return true; } + +void ParserCommon::writeBlockIndent(int size, const char* data) { + while (size && ' ' >= data[size - 1]) { + --size; + } + bool newLine = false; + while (size) { + while (size && ' ' > data[0]) { + ++data; + --size; + } + if (!size) { + return; + } + if (newLine) { + this->lf(1); + } + TextParser parser(fFileName, data, data + size, fLineCount); + const char* lineEnd = parser.strnchr('\n', data + size); + int len = lineEnd ? (int) (lineEnd - data) : size; + this->writePending(); + this->indentToColumn(fIndent); + if (fDebugOut) { + debug_out(len, data); + } + fprintf(fOut, "%.*s", len, data); + size -= len; + data += len; + newLine = true; + } +} + +bool ParserCommon::writeBlockTrim(int size, const char* data) { + if (fOutdentNext) { + fIndent -= 4; + fOutdentNext = false; + } + while (size && ' ' >= data[0]) { + ++data; + --size; + } + while (size && ' ' >= data[size - 1]) { + --size; + } + if (size <= 0) { + fLastChar = '\0'; + return false; + } + SkASSERT(size < 16000); + if (size > 3 && !strncmp("#end", data, 4)) { + fMaxLF = 1; + } + if (this->leadingPunctuation(data, (size_t) size)) { + fPendingSpace = 0; + } + this->writePending(); + if (fDebugOut) { + debug_out(size, data); + } + fprintf(fOut, "%.*s", size, data); + int added = 0; + fLastChar = data[size - 1]; + while (size > 0 && '\n' != data[--size]) { + ++added; + } + fColumn = size ? added : fColumn + added; + fSpaces = 0; + fLinefeeds = 0; + fMaxLF = added > 2 && !strncmp("#if", &data[size + (size > 0)], 3) ? 1 : 2; + if (fOutdentNext) { + fIndent -= 4; + fOutdentNext = false; + } + return true; +} + +void ParserCommon::writePending() { + fPendingLF = SkTMin(fPendingLF, fMaxLF); + bool wroteLF = false; + while (fLinefeeds < fPendingLF) { + if (fDebugOut) { + SkDebugf("\n"); + } + fprintf(fOut, "\n"); + ++fLinefeeds; + wroteLF = true; + } + fPendingLF = 0; + if (wroteLF) { + SkASSERT(0 == fColumn); + SkASSERT(fIndent >= fSpaces); + if (fDebugOut) { + SkDebugf("%*s", fIndent - fSpaces, ""); + } + fprintf(fOut, "%*s", fIndent - fSpaces, ""); + fColumn = fIndent; + fSpaces = fIndent; + } + for (int index = 0; index < fPendingSpace; ++index) { + if (fDebugOut) { + SkDebugf(" "); + } + fprintf(fOut, " "); + ++fColumn; + } + fPendingSpace = 0; +} + +void ParserCommon::writeString(const char* str) { + const size_t len = strlen(str); + SkASSERT(len > 0); + SkASSERT(' ' < str[0]); + fLastChar = str[len - 1]; + SkASSERT(' ' < fLastChar); + SkASSERT(!strchr(str, '\n')); + if (this->leadingPunctuation(str, strlen(str))) { + fPendingSpace = 0; + } + this->writePending(); + if (fDebugOut) { + debug_out((int) strlen(str), str); + } + fprintf(fOut, "%s", str); + fColumn += len; + fSpaces = 0; + fLinefeeds = 0; + fMaxLF = 2; +} diff --git a/chromium/third_party/skia/tools/bookmaker/spellCheck.cpp b/chromium/third_party/skia/tools/bookmaker/spellCheck.cpp index 3a32f37bc48..6f50cb3bb52 100644 --- a/chromium/third_party/skia/tools/bookmaker/spellCheck.cpp +++ b/chromium/third_party/skia/tools/bookmaker/spellCheck.cpp @@ -202,6 +202,8 @@ bool SpellCheck::check(Definition* def) { break; case MarkType::kList: break; + case MarkType::kLiteral: + break; case MarkType::kMarkChar: break; case MarkType::kMember: @@ -212,7 +214,7 @@ bool SpellCheck::check(Definition* def) { method_name += "()"; } string formattedStr = def->formatFunction(); - if (!def->isClone()) { + if (!def->isClone() && Definition::MethodType::kOperator != def->fMethodType) { this->wordCheck(method_name); } fTableState = TableState::kNone; @@ -220,6 +222,8 @@ bool SpellCheck::check(Definition* def) { } break; case MarkType::kNoExample: break; + case MarkType::kOutdent: + break; case MarkType::kParam: { if (TableState::kNone == fTableState) { fTableState = TableState::kRow; @@ -490,6 +494,7 @@ void SpellCheck::report(SkCommandLineFlags::StringArray report) { if (report.contains("all")) { int column = 0; char lastInitial = 'a'; + int count = 0; for (auto iter : elems) { if (string::npos != iter.second.fFile.find("undocumented.bmh")) { continue; @@ -521,8 +526,9 @@ void SpellCheck::report(SkCommandLineFlags::StringArray report) { } SkDebugf("%s ", check.c_str()); column += check.length(); + ++count; } - SkDebugf("\n\n"); + SkDebugf("\n\ncount = %d\n", count); return; } int index = 0; @@ -607,7 +613,9 @@ void SpellCheck::wordCheck(const string& str) { std::istringstream ss(str); string token; while (std::getline(ss, token, '_')) { - this->wordCheck(token); + if (token.length()) { + this->wordCheck(token); + } } return; } diff --git a/chromium/third_party/skia/tools/calmbench/ab.py b/chromium/third_party/skia/tools/calmbench/ab.py index 2ffb9e3ec36..b5b5c8d5e10 100644 --- a/chromium/third_party/skia/tools/calmbench/ab.py +++ b/chromium/third_party/skia/tools/calmbench/ab.py @@ -74,6 +74,15 @@ def parse_args(): parser.add_argument('noinit', type=str, help=("whether to skip running B" " ('true' or 'false')")) + parser.add_argument('--concise', dest='concise', action="store_true", + help="If set, no verbose thread info will be printed.") + parser.set_defaults(concise=False) + + # Additional args for bots + BHELP = "bot specific options" + parser.add_argument('--githash', type=str, default="", help=BHELP) + parser.add_argument('--keys', type=str, default=[], nargs='+', help=BHELP) + args = parser.parse_args() args.skip_b = args.skip_b == "true" args.noinit = args.noinit == "true" @@ -112,7 +121,8 @@ def append_times_from_file(args, name, filename): class ThreadRunner: """Simplest and stupidiest threaded executer.""" - def __init__(self): + def __init__(self, args): + self.concise = args.concise self.threads = [] def add(self, args, fn): @@ -138,12 +148,16 @@ class ThreadRunner: time.sleep(0.5) i += 1 - ts = Thread(target = spin); - ts.start() + if not self.concise: + ts = Thread(target = spin); + ts.start() + for t in self.threads: t.join() self.threads = [] - ts.join() + + if not self.concise: + ts.join() def split_arg(arg): @@ -183,7 +197,7 @@ def run(args, threadRunner, name, nano, arg, i): def init_run(args): - threadRunner = ThreadRunner() + threadRunner = ThreadRunner(args) for i in range(1, max(args.repeat, args.threads / 2) + 1): run(args, threadRunner, args.a, args.nano_a, args.arg_a, i) run(args, threadRunner, args.b, args.nano_b, args.arg_b, i) @@ -243,6 +257,13 @@ def format_r(r): return ('%6.2f' % percentage(r)) + "%" +def normalize_r(r): + if r > 1.0: + return r - 1.0 + else: + return 1.0 - 1/r + + def test(): args = parse_args() @@ -261,7 +282,7 @@ def test(): break print "Number of suspects at iteration %d: %d" % (it, len(suspects)) - threadRunner = ThreadRunner() + threadRunner = ThreadRunner(args) for j in range(1, max(1, args.threads / 2) + 1): run(args, threadRunner, args.a, args.nano_a, args.arg_a + suspects_arg(suspects), -j) @@ -287,10 +308,26 @@ def test(): (format_r(r), suspect) with open("%s/bench_%s_%s.json" % (args.outdir, args.a, args.b), 'w') as f: - f.write(json.dumps(map( - lambda bench: {bench: regression(bench)}, - suspects - ))) + results = {} + for bench in timesA: + r = regression(bench) if bench in suspects else 1.0 + results[bench] = { + args.config: { + "signed_regression": normalize_r(r), + "lower_quantile_ms": get_lower_upper(timesA[bench])[0] * 1e-6, + "upper_quantile_ms": get_lower_upper(timesA[bench])[1] * 1e-6 + } + } + + output = {"results": results} + if args.githash: + output["gitHash"] = args.githash + if args.keys: + keys = {} + for i in range(len(args.keys) / 2): + keys[args.keys[i * 2]] = args.keys[i * 2 + 1] + output["key"] = keys + f.write(json.dumps(output, indent=4)) print ("\033[36mJSON results available in %s\033[0m" % f.name) with open("%s/bench_%s_%s.csv" % (args.outdir, args.a, args.b), 'w') as out: diff --git a/chromium/third_party/skia/tools/calmbench/calmbench.py b/chromium/third_party/skia/tools/calmbench/calmbench.py index 4d1319de1a0..b786b5f346d 100644 --- a/chromium/third_party/skia/tools/calmbench/calmbench.py +++ b/chromium/third_party/skia/tools/calmbench/calmbench.py @@ -74,6 +74,9 @@ def parse_args(): '(i.e., reuse previous baseline measurements)') noinit_help = ( 'whether to skip initial nanobench runs (default: %(default)s)') + branch_help = ( + "the test branch to benchmark; if it's 'modified', we'll benchmark the " + "current modified code against 'git stash'.") definitions = [ # argname, type, default value, help @@ -85,22 +88,30 @@ def parse_args(): ['--baseline', str, 'master', baseline_help], ['--basearg', str, '', basearg_help], ['--reps', int, 2, reps_help], - ['--threads', int, default_threads, threads_help] + ['--threads', int, default_threads, threads_help], ] for d in definitions: parser.add_argument(d[0], type=d[1], default=d[2], help=d[3]) - parser.add_argument('branch', type=str, help="the test branch to benchmark") + parser.add_argument('branch', type=str, help=branch_help) parser.add_argument('--no-compile', dest='no_compile', action="store_true", help=no_compile_help) parser.add_argument('--skip-base', dest='skipbase', action="store_true", help=skip_base_help) parser.add_argument('--noinit', dest='noinit', action="store_true", help=noinit_help) + parser.add_argument('--concise', dest='concise', action="store_true", + help="If set, no verbose thread info will be printed.") parser.set_defaults(no_compile=False); parser.set_defaults(skipbase=False); parser.set_defaults(noinit=False); + parser.set_defaults(concise=False); + + # Additional args for bots + BHELP = "bot specific options" + parser.add_argument('--githash', type=str, help=BHELP) + parser.add_argument('--keys', type=str, default=[], nargs='+', help=BHELP) args = parser.parse_args() if not args.basearg: @@ -116,7 +127,6 @@ def nano_path(args, branch): def compile_branch(args, branch): print "Compiling branch %s" % args.branch - os.chdir(args.skiadir) commands = [ ['git', 'checkout', branch], ['ninja', '-C', args.ninjadir, 'nanobench'], @@ -126,9 +136,33 @@ def compile_branch(args, branch): subprocess.check_call(command, cwd=args.skiadir) +def compile_modified(args): + print "Compiling modified code" + subprocess.check_call( + ['ninja', '-C', args.ninjadir, 'nanobench'], cwd=args.skiadir) + subprocess.check_call( + ['cp', args.ninjadir + '/nanobench', nano_path(args, args.branch)], + cwd=args.skiadir) + + print "Compiling stashed code" + stash_output = subprocess.check_output(['git', 'stash'], cwd=args.skiadir) + if 'No local changes to save' in stash_output: + subprocess.check_call(['git', 'reset', 'HEAD^', '--soft']) + subprocess.check_call(['git', 'stash']) + + subprocess.check_call( + ['ninja', '-C', args.ninjadir, 'nanobench'], cwd=args.skiadir) + subprocess.check_call( + ['cp', args.ninjadir + '/nanobench', nano_path(args, args.baseline)], + cwd=args.skiadir) + subprocess.check_call(['git', 'stash', 'pop'], cwd=args.skiadir) + def compile_nanobench(args): - compile_branch(args, args.branch) - compile_branch(args, args.baseline) + if args.branch == 'modified': + compile_modified(args) + else: + compile_branch(args, args.branch) + compile_branch(args, args.baseline) def main(): @@ -160,6 +194,14 @@ def main(): "true" if args.noinit else "false" ] + if args.githash: + command += ['--githash', args.githash] + if args.keys: + command += (['--keys'] + args.keys) + + if args.concise: + command.append("--concise") + p = subprocess.Popen(command, cwd=args.skiadir) try: p.wait() diff --git a/chromium/third_party/skia/tools/colorspaceinfo.cpp b/chromium/third_party/skia/tools/colorspaceinfo.cpp index 3e09a9aeebe..0002e961cf7 100644 --- a/chromium/third_party/skia/tools/colorspaceinfo.cpp +++ b/chromium/third_party/skia/tools/colorspaceinfo.cpp @@ -565,13 +565,13 @@ int main(int argc, char** argv) { SkColorSpace_A2B* a2b = static_cast<SkColorSpace_A2B*>(colorSpace.get()); SkDebugf("Conversion type: "); switch (a2b->iccType()) { - case SkColorSpace_Base::kRGB_ICCTypeFlag: + case SkColorSpace::kRGB_Type: SkDebugf("RGB"); break; - case SkColorSpace_Base::kCMYK_ICCTypeFlag: + case SkColorSpace::kCMYK_Type: SkDebugf("CMYK"); break; - case SkColorSpace_Base::kGray_ICCTypeFlag: + case SkColorSpace::kGray_Type: SkDebugf("Gray"); break; default: diff --git a/chromium/third_party/skia/tools/create_flutter_test_images.cpp b/chromium/third_party/skia/tools/create_flutter_test_images.cpp index 69a0d112a32..2fa39f6c759 100644 --- a/chromium/third_party/skia/tools/create_flutter_test_images.cpp +++ b/chromium/third_party/skia/tools/create_flutter_test_images.cpp @@ -15,19 +15,7 @@ * Create a color space that swaps the red, green, and blue channels. */ static sk_sp<SkColorSpace> gbr_color_space() { - float gbr[9]; - gbr[0] = gSRGB_toXYZD50[1]; - gbr[1] = gSRGB_toXYZD50[2]; - gbr[2] = gSRGB_toXYZD50[0]; - gbr[3] = gSRGB_toXYZD50[4]; - gbr[4] = gSRGB_toXYZD50[5]; - gbr[5] = gSRGB_toXYZD50[3]; - gbr[6] = gSRGB_toXYZD50[7]; - gbr[7] = gSRGB_toXYZD50[8]; - gbr[8] = gSRGB_toXYZD50[6]; - SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); - toXYZD50.set3x3RowMajorf(gbr); - return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50); + return as_CSB(SkColorSpace::MakeSRGB())->makeColorSpin(); } /** diff --git a/chromium/third_party/skia/tools/debugger/SkDebugCanvas.h b/chromium/third_party/skia/tools/debugger/SkDebugCanvas.h index 72d65d7da7e..634d5b416df 100644 --- a/chromium/third_party/skia/tools/debugger/SkDebugCanvas.h +++ b/chromium/third_party/skia/tools/debugger/SkDebugCanvas.h @@ -165,25 +165,6 @@ public: Json::Value toJSONOpList(int n, SkCanvas*); - //////////////////////////////////////////////////////////////////////////////// - // Inherited from SkCanvas - //////////////////////////////////////////////////////////////////////////////// - - static const int kVizImageHeight = 256; - static const int kVizImageWidth = 256; - - bool isClipEmpty() const override { return false; } - - bool isClipRect() const override { return true; } - - SkRect onGetLocalClipBounds() const override { - return SkRect::MakeIWH(this->imageInfo().width(), this->imageInfo().height()); - } - - SkIRect onGetDeviceClipBounds() const override { - return SkIRect::MakeWH(this->imageInfo().width(), this->imageInfo().height()); - } - void detachCommands(SkTDArray<SkDrawCommand*>* dst) { fCommandVector.swap(*dst); } diff --git a/chromium/third_party/skia/tools/debugger/SkJsonWriteBuffer.cpp b/chromium/third_party/skia/tools/debugger/SkJsonWriteBuffer.cpp index 4b075dd2f24..bdabc8aea56 100644 --- a/chromium/third_party/skia/tools/debugger/SkJsonWriteBuffer.cpp +++ b/chromium/third_party/skia/tools/debugger/SkJsonWriteBuffer.cpp @@ -134,12 +134,6 @@ size_t SkJsonWriteBuffer::writeStream(SkStream* stream, size_t length) { return 0; } -void SkJsonWriteBuffer::writeBitmap(const SkBitmap& bitmap) { - Json::Value jsonBitmap; - SkDrawCommand::flatten(bitmap, &jsonBitmap, *fUrlDataManager); - this->append("bitmap", jsonBitmap); -} - void SkJsonWriteBuffer::writeImage(const SkImage* image) { Json::Value jsonImage; SkDrawCommand::flatten(*image, &jsonImage, *fUrlDataManager); diff --git a/chromium/third_party/skia/tools/debugger/SkJsonWriteBuffer.h b/chromium/third_party/skia/tools/debugger/SkJsonWriteBuffer.h index a0cf4e720bf..c1a72313e0c 100644 --- a/chromium/third_party/skia/tools/debugger/SkJsonWriteBuffer.h +++ b/chromium/third_party/skia/tools/debugger/SkJsonWriteBuffer.h @@ -45,7 +45,6 @@ public: void writeRegion(const SkRegion& region) override; void writePath(const SkPath& path) override; size_t writeStream(SkStream* stream, size_t length) override; - void writeBitmap(const SkBitmap& bitmap) override; void writeImage(const SkImage*) override; void writeTypeface(SkTypeface* typeface) override; void writePaint(const SkPaint& paint) override; diff --git a/chromium/third_party/skia/tools/dump_record.cpp b/chromium/third_party/skia/tools/dump_record.cpp index 24017bd6c9e..783c378f447 100644 --- a/chromium/third_party/skia/tools/dump_record.cpp +++ b/chromium/third_party/skia/tools/dump_record.cpp @@ -8,7 +8,6 @@ #include "DumpRecord.h" #include "SkBitmap.h" #include "SkCommandLineFlags.h" -#include "SkDeferredCanvas.h" #include "SkPicture.h" #include "SkPictureRecorder.h" #include "SkRecordDraw.h" @@ -24,7 +23,6 @@ DEFINE_bool(optimize2, false, "Run SkRecordOptimize2 before dumping."); DEFINE_int32(tile, 1000000000, "Simulated tile size."); DEFINE_bool(timeWithCommand, false, "If true, print time next to command, else in first column."); DEFINE_string2(write, w, "", "Write the (optimized) picture to the named file."); -DEFINE_bool(defer, false, "Defer clips and translates"); static void dump(const char* name, int w, int h, const SkRecord& record) { SkBitmap bitmap; @@ -56,13 +54,6 @@ int main(int argc, char** argv) { SkDebugf("Could not read %s as an SkPicture.\n", FLAGS_skps[i]); return 1; } - if (FLAGS_defer) { - SkPictureRecorder recorder; - SkDeferredCanvas deferred(recorder.beginRecording(src->cullRect()), - SkDeferredCanvas::kEager); - src->playback(&deferred); - src = recorder.finishRecordingAsPicture(); - } const int w = SkScalarCeilToInt(src->cullRect().width()); const int h = SkScalarCeilToInt(src->cullRect().height()); diff --git a/chromium/third_party/skia/tools/fiddle/draw.cpp b/chromium/third_party/skia/tools/fiddle/draw.cpp index 8e94883313b..346d1caf592 100644 --- a/chromium/third_party/skia/tools/fiddle/draw.cpp +++ b/chromium/third_party/skia/tools/fiddle/draw.cpp @@ -13,7 +13,8 @@ DrawOptions GetDrawOptions() { // path *should* be absolute. static const char path[] = "resources/color_wheel.png"; - return DrawOptions(256, 256, true, true, true, true, true, false, false, path); + return DrawOptions(256, 256, true, true, true, true, true, false, false, path, + GrMipMapped::kYes, 64, 64, 0, GrMipMapped::kYes); } void draw(SkCanvas* canvas) { canvas->clear(SK_ColorWHITE); @@ -26,4 +27,26 @@ void draw(SkCanvas* canvas) { &matrix)); canvas->drawPaint(paint); SkDebugf("This is text output: %d", 2); + + GrContext* context = canvas->getGrContext(); + if (context) { + sk_sp<SkImage> tmp = SkImage::MakeFromTexture(context, + backEndTexture, + kTopLeft_GrSurfaceOrigin, + kOpaque_SkAlphaType, + nullptr); + + // TODO: this sampleCnt parameter here should match that set in the options! + sk_sp<SkSurface> tmp2 = SkSurface::MakeFromBackendTexture(context, + backEndTextureRenderTarget, + kTopLeft_GrSurfaceOrigin, + 0, nullptr, nullptr); + + // Note: this surface should only be renderable (i.e., not textureable) + sk_sp<SkSurface> tmp3 = SkSurface::MakeFromBackendRenderTarget(context, + backEndRenderTarget, + kTopLeft_GrSurfaceOrigin, + nullptr, nullptr); + } + } diff --git a/chromium/third_party/skia/tools/fiddle/fiddle_main.cpp b/chromium/third_party/skia/tools/fiddle/fiddle_main.cpp index 6ceffc9b9c7..92767d1b401 100644 --- a/chromium/third_party/skia/tools/fiddle/fiddle_main.cpp +++ b/chromium/third_party/skia/tools/fiddle/fiddle_main.cpp @@ -11,13 +11,31 @@ #include <string> #include "SkCommandLineFlags.h" +#include "SkMipMap.h" +#include "SkUtils.h" #include "fiddle_main.h" DEFINE_double(duration, 1.0, "The total duration, in seconds, of the animation we are drawing."); DEFINE_double(frame, 1.0, "A double value in [0, 1] that specifies the point in animation to draw."); +#include "GrBackendSurface.h" +#include "GrContextPriv.h" +#include "GrGpu.h" + +#include "GrTest.h" + + // Globals externed in fiddle_main.h +sk_sp<GrTexture> backingTexture; // not externed +GrBackendTexture backEndTexture; + +sk_sp<GrRenderTarget> backingRenderTarget; // not externed +GrBackendRenderTarget backEndRenderTarget; + +sk_sp<GrTexture> backingTextureRenderTarget; // not externed +GrBackendTexture backEndTextureRenderTarget; + SkBitmap source; sk_sp<SkImage> image; double duration; // The total duration of the animation in seconds. @@ -98,6 +116,132 @@ static SkCanvas* prepare_canvas(SkCanvas * canvas) { return canvas; } +static bool setup_backend_objects(GrContext* context, + const SkBitmap& bm, + const DrawOptions& options) { + if (!context) { + return false; + } + + GrBackend backend = context->contextPriv().getBackend(); + const GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig; + + GrSurfaceDesc backingDesc; + backingDesc.fFlags = kNone_GrSurfaceFlags; + backingDesc.fOrigin = kTopLeft_GrSurfaceOrigin; + backingDesc.fWidth = bm.width(); + backingDesc.fHeight = bm.height(); + backingDesc.fConfig = kConfig; + backingDesc.fSampleCnt = 0; + + if (!bm.empty()) { + int mipLevelCount = GrMipMapped::kYes == options.fMipMapping + ? SkMipMap::ComputeLevelCount(bm.width(), bm.height()) + : 1; + std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]); + + texels[0].fPixels = bm.getPixels(); + texels[0].fRowBytes = bm.rowBytes(); + + for (int i = 1; i < mipLevelCount; i++) { + texels[i].fPixels = nullptr; + texels[i].fRowBytes = 0; + } + + backingTexture = context->resourceProvider()->createTexture( + backingDesc, SkBudgeted::kNo, + texels.get(), mipLevelCount, + SkDestinationSurfaceColorMode::kLegacy); + if (!backingTexture) { + return false; + } + + backEndTexture = GrTest::CreateBackendTexture(backend, + backingDesc.fWidth, + backingDesc.fHeight, + kConfig, + options.fMipMapping, + backingTexture->getTextureHandle()); + if (!backEndTexture.isValid()) { + return false; + } + } + + backingDesc.fFlags = kRenderTarget_GrSurfaceFlag; + backingDesc.fWidth = options.fOffScreenWidth; + backingDesc.fHeight = options.fOffScreenHeight; + backingDesc.fSampleCnt = options.fOffScreenSampleCount; + + SkAutoTMalloc<uint32_t> data(backingDesc.fWidth * backingDesc.fHeight); + sk_memset32(data.get(), 0, backingDesc.fWidth * backingDesc.fHeight); + + + { + // This backend object should be renderable but not textureable. Given the limitations + // of how we're creating it though it will wind up being secretly textureable. + // We use this fact to initialize it with data but don't allow mipmaps + GrMipLevel level0 = { data.get(), backingDesc.fWidth*sizeof(uint32_t) }; + + sk_sp<GrTexture> tmp = context->resourceProvider()->createTexture( + backingDesc, SkBudgeted::kNo, + &level0, 1, + SkDestinationSurfaceColorMode::kLegacy); + if (!tmp || !tmp->asRenderTarget()) { + return false; + } + + backingRenderTarget = sk_ref_sp(tmp->asRenderTarget()); + + backEndRenderTarget = GrTest::CreateBackendRenderTarget( + backend, + backingDesc.fWidth, + backingDesc.fHeight, + backingDesc.fSampleCnt, 0, + kConfig, + backingRenderTarget->getRenderTargetHandle()); + if (!backEndRenderTarget.isValid()) { + return false; + } + } + + { + int mipLevelCount = GrMipMapped::kYes == options.fOffScreenMipMapping + ? SkMipMap::ComputeLevelCount(backingDesc.fWidth, backingDesc.fHeight) + : 1; + std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]); + + texels[0].fPixels = data.get(); + texels[0].fRowBytes = backingDesc.fWidth*sizeof(uint32_t); + + for (int i = 1; i < mipLevelCount; i++) { + texels[i].fPixels = nullptr; + texels[i].fRowBytes = 0; + } + + backingTextureRenderTarget = context->resourceProvider()->createTexture( + backingDesc, SkBudgeted::kNo, + texels.get(), mipLevelCount, + SkDestinationSurfaceColorMode::kLegacy); + if (!backingTextureRenderTarget || !backingTextureRenderTarget->asRenderTarget()) { + return false; + } + + backEndTextureRenderTarget = GrTest::CreateBackendTexture( + backend, + backingDesc.fWidth, + backingDesc.fHeight, + kConfig, + options.fOffScreenMipMapping, + backingTextureRenderTarget->getTextureHandle()); + if (!backEndTextureRenderTarget.isValid()) { + return false; + } + } + + + return true; +} + int main(int argc, char** argv) { SkCommandLineFlags::Parse(argc, argv); duration = FLAGS_duration; @@ -122,8 +266,7 @@ int main(int argc, char** argv) { perror("Unable to decode the source image."); return 1; } - SkAssertResult(image->asLegacyBitmap( - &source, SkImage::kRO_LegacyBitmapMode)); + SkAssertResult(image->asLegacyBitmap(&source, SkImage::kRO_LegacyBitmapMode)); } } sk_sp<SkData> rasterData, gpuData, pdfData, skpData; @@ -145,10 +288,15 @@ int main(int argc, char** argv) { rasterData = encode_snapshot(rasterSurface); } if (options.gpu) { - auto grContext = create_grcontext(gGLDriverInfo); + sk_sp<GrContext> grContext = create_grcontext(gGLDriverInfo); if (!grContext) { fputs("Unable to get GrContext.\n", stderr); } else { + if (!setup_backend_objects(grContext.get(), source, options)) { + fputs("Unable to create backend objects.\n", stderr); + exit(1); + } + auto surface = SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kNo, info); if (!surface) { fputs("Unable to get render surface.\n", stderr); diff --git a/chromium/third_party/skia/tools/fiddle/fiddle_main.h b/chromium/third_party/skia/tools/fiddle/fiddle_main.h index fb9d4094828..5492a424ea7 100644 --- a/chromium/third_party/skia/tools/fiddle/fiddle_main.h +++ b/chromium/third_party/skia/tools/fiddle/fiddle_main.h @@ -22,13 +22,22 @@ #include <sstream> +extern GrBackendTexture backEndTexture; +extern GrBackendRenderTarget backEndRenderTarget; +extern GrBackendTexture backEndTextureRenderTarget; extern SkBitmap source; extern sk_sp<SkImage> image; extern double duration; // The total duration of the animation in seconds. extern double frame; // A value in [0, 1] of where we are in the animation. struct DrawOptions { - DrawOptions(int w, int h, bool r, bool g, bool p, bool k, bool srgb, bool f16, bool textOnly, const char* s) + DrawOptions(int w, int h, bool r, bool g, bool p, bool k, bool srgb, bool f16, + bool textOnly, const char* s, + GrMipMapped mipMapping, + int offScreenWidth, + int offScreenHeight, + int offScreenSampleCount, + GrMipMapped offScreenMipMapping) : size(SkISize::Make(w, h)) , raster(r) , gpu(g) @@ -38,7 +47,11 @@ struct DrawOptions { , f16(f16) , textOnly(textOnly) , source(s) - { + , fMipMapping(mipMapping) + , fOffScreenWidth(offScreenWidth) + , fOffScreenHeight(offScreenHeight) + , fOffScreenSampleCount(offScreenSampleCount) + , fOffScreenMipMapping(offScreenMipMapping) { // F16 mode is only valid for color correct backends. SkASSERT(srgb || !f16); } @@ -51,6 +64,20 @@ struct DrawOptions { bool f16; bool textOnly; const char* source; + + // This flag is used when a GPU texture resource is created and exposed as a GrBackendTexture. + // In this case the resource is created with extra room to accomodate mipmaps. + // TODO: The SkImage::makeTextureImage API would need to be widened to allow this to be true + // for the non-backend gpu SkImages. + GrMipMapped fMipMapping; + + // Parameters for an GPU offscreen resource exposed as a GrBackendRenderTarget + int fOffScreenWidth; + int fOffScreenHeight; + int fOffScreenSampleCount; + // TODO: should we also expose stencilBits here? How about the config? + + GrMipMapped fOffScreenMipMapping; // only applicable if the offscreen is also textureable }; extern DrawOptions GetDrawOptions(); diff --git a/chromium/third_party/skia/tools/fiddle/mesa_context.cpp b/chromium/third_party/skia/tools/fiddle/mesa_context.cpp deleted file mode 100644 index 89902ec711e..00000000000 --- a/chromium/third_party/skia/tools/fiddle/mesa_context.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 "fiddle_main.h" - -#include <GL/osmesa.h> - -// create_grcontext implementation for Mesa. -sk_sp<GrContext> create_grcontext(std::ostringstream &driverinfo) { - // We just leak the OSMesaContext... the process will die soon anyway. - if (OSMesaContext osMesaContext = OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, nullptr)) { - static uint32_t buffer[16 * 16]; - OSMesaMakeCurrent(osMesaContext, &buffer, GL_UNSIGNED_BYTE, 16, 16); - } - driverinfo << "Mesa"; - - auto osmesa_get = [](void* ctx, const char name[]) { - SkASSERT(nullptr == ctx); - SkASSERT(OSMesaGetCurrentContext()); - return OSMesaGetProcAddress(name); - }; - sk_sp<const GrGLInterface> mesa(GrGLAssembleInterface(nullptr, osmesa_get)); - if (!mesa) { - return nullptr; - } - return GrContext::MakeGL(mesa.get()); -} diff --git a/chromium/third_party/skia/tools/flags/SkCommonFlags.cpp b/chromium/third_party/skia/tools/flags/SkCommonFlags.cpp index 0fc1e1d1868..bebb88b088a 100644 --- a/chromium/third_party/skia/tools/flags/SkCommonFlags.cpp +++ b/chromium/third_party/skia/tools/flags/SkCommonFlags.cpp @@ -87,6 +87,10 @@ DEFINE_bool(deltaAA, kDefaultDeltaAA, DEFINE_bool(forceDeltaAA, false, "Force delta anti-aliasing for all paths."); +#if SK_SUPPORT_GPU +DEFINE_bool(cachePathMasks, true, "Allows path mask textures to be cached in GPU configs."); +#endif + bool CollectImages(SkCommandLineFlags::StringArray images, SkTArray<SkString>* output) { SkASSERT(output); diff --git a/chromium/third_party/skia/tools/flags/SkCommonFlags.h b/chromium/third_party/skia/tools/flags/SkCommonFlags.h index 3d4b33e640d..4f93d1974f7 100644 --- a/chromium/third_party/skia/tools/flags/SkCommonFlags.h +++ b/chromium/third_party/skia/tools/flags/SkCommonFlags.h @@ -36,6 +36,9 @@ DECLARE_bool(analyticAA); DECLARE_bool(forceAnalyticAA); DECLARE_bool(deltaAA); DECLARE_bool(forceDeltaAA); +#if SK_SUPPORT_GPU +DECLARE_bool(cachePathMasks); +#endif DECLARE_string(key); DECLARE_string(properties); diff --git a/chromium/third_party/skia/tools/flags/SkCommonFlagsConfig.cpp b/chromium/third_party/skia/tools/flags/SkCommonFlagsConfig.cpp index 16fe914f770..fe9faaef1d7 100644 --- a/chromium/third_party/skia/tools/flags/SkCommonFlagsConfig.cpp +++ b/chromium/third_party/skia/tools/flags/SkCommonFlagsConfig.cpp @@ -86,9 +86,6 @@ static const struct { { "angle_gl_es3", "gpu", "api=angle_gl_es3" }, { "commandbuffer", "gpu", "api=commandbuffer" }, { "mock", "gpu", "api=mock" } -#if SK_MESA - ,{ "mesa", "gpu", "api=mesa" } -#endif #ifdef SK_VULKAN ,{ "vk", "gpu", "api=vulkan" } ,{ "vksrgb", "gpu", "api=vulkan,color=srgb" } @@ -141,9 +138,6 @@ static const char configExtendedHelp[] = "\t\tangle_gl_es3\t\t\tUse OpenGL ES3 on the ANGLE OpenGL backend.\n" "\t\tcommandbuffer\t\tUse command buffer.\n" "\t\tmock\t\tUse mock context.\n" -#if SK_MESA - "\t\tmesa\t\t\tUse MESA.\n" -#endif #ifdef SK_VULKAN "\t\tvulkan\t\t\tUse Vulkan.\n" #endif @@ -307,12 +301,6 @@ static bool parse_option_gpu_api(const SkString& value, *outContextType = GrContextFactory::kMock_ContextType; return true; } -#if SK_MESA - if (value.equals("mesa")) { - *outContextType = GrContextFactory::kMESA_ContextType; - return true; - } -#endif #ifdef SK_VULKAN if (value.equals("vulkan")) { *outContextType = GrContextFactory::kVulkan_ContextType; diff --git a/chromium/third_party/skia/tools/gdb/bitmap.py b/chromium/third_party/skia/tools/gdb/bitmap.py new file mode 100644 index 00000000000..e9e18f973ee --- /dev/null +++ b/chromium/third_party/skia/tools/gdb/bitmap.py @@ -0,0 +1,83 @@ +# Copyright 2017 Google Inc. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Visualize bitmaps in gdb. + +(gdb) source <path to this file> +(gdb) sk_bitmap <symbol> + +This should pop up a window with the bitmap displayed. +Right clicking should bring up a menu, allowing the +bitmap to be saved to a file. +""" + +import gdb +from enum import Enum +try: + from PIL import Image +except ImportError: + import Image + +class ColorType(Enum): + unknown = 0 + alpha_8 = 1 + rgb_565 = 2 + argb_4444 = 3 + rgba_8888 = 4 + bgra_8888 = 5 + gray_8 = 6 + rgba_F16 = 7 + +class AlphaType(Enum): + unknown = 0 + opaque = 1 + premul = 2 + unpremul = 3 + +class sk_bitmap(gdb.Command): + """Displays the content of an SkBitmap image.""" + + def __init__(self): + super(sk_bitmap, self).__init__('sk_bitmap', + gdb.COMMAND_SUPPORT, + gdb.COMPLETE_FILENAME) + + def invoke(self, arg, from_tty): + frame = gdb.selected_frame() + val = frame.read_var(arg) + if str(val.type.strip_typedefs()) == 'SkBitmap': + pixels = val['fPixels'] + row_bytes = val['fRowBytes'] + info = val['fInfo'] + width = info['fWidth'] + height = info['fHeight'] + color_type = info['fColorType'] + alpha_type = info['fAlphaType'] + + process = gdb.selected_inferior() + memory_data = process.read_memory(pixels, row_bytes * height) + size = (width, height) + image = None + # See Unpack.c for the values understood after the "raw" parameter. + if color_type == ColorType.bgra_8888.value: + if alpha_type == AlphaType.unpremul.value: + image = Image.frombytes("RGBA", size, memory_data.tobytes(), + "raw", "BGRA", row_bytes, 1) + elif alpha_type == AlphaType.premul.value: + # RGBA instead of RGBa, because Image.show() doesn't work with RGBa. + image = Image.frombytes("RGBA", size, memory_data.tobytes(), + "raw", "BGRa", row_bytes, 1) + + if image: + # Fails on premultiplied alpha, it cannot convert to RGB. + image.show() + else: + print ("Need to add support for %s %s." % ( + str(ColorType(int(color_type))), + str(AlphaType(int(alpha_type))) + )) + +sk_bitmap() diff --git a/chromium/third_party/skia/tools/gpu/GrContextFactory.cpp b/chromium/third_party/skia/tools/gpu/GrContextFactory.cpp index b3ca1d929eb..00c4b65edc1 100644 --- a/chromium/third_party/skia/tools/gpu/GrContextFactory.cpp +++ b/chromium/third_party/skia/tools/gpu/GrContextFactory.cpp @@ -14,9 +14,6 @@ #endif #include "gl/command_buffer/GLTestContext_command_buffer.h" #include "gl/debug/DebugGLTestContext.h" -#if SK_MESA - #include "gl/mesa/GLTestContext_mesa.h" -#endif #ifdef SK_VULKAN #include "vk/VkTestContext.h" #endif @@ -55,8 +52,9 @@ GrContextFactory::~GrContextFactory() { void GrContextFactory::destroyContexts() { for (Context& context : fContexts) { + SkScopeExit restore(nullptr); if (context.fTestContext) { - context.fTestContext->makeCurrent(); + restore = context.fTestContext->makeCurrentAndAutoRestore(); } if (!context.fGrContext->unique()) { context.fGrContext->releaseResourcesAndAbandonContext(); @@ -72,7 +70,7 @@ void GrContextFactory::abandonContexts() { for (Context& context : fContexts) { if (!context.fAbandoned) { if (context.fTestContext) { - context.fTestContext->makeCurrent(); + auto restore = context.fTestContext->makeCurrentAndAutoRestore(); context.fTestContext->testAbandon(); delete(context.fTestContext); context.fTestContext = nullptr; @@ -85,9 +83,10 @@ void GrContextFactory::abandonContexts() { void GrContextFactory::releaseResourcesAndAbandonContexts() { for (Context& context : fContexts) { + SkScopeExit restore(nullptr); if (!context.fAbandoned) { if (context.fTestContext) { - context.fTestContext->makeCurrent(); + restore = context.fTestContext->makeCurrentAndAutoRestore(); } context.fGrContext->releaseResourcesAndAbandonContext(); context.fAbandoned = true; @@ -116,7 +115,8 @@ ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOv context.fShareIndex == shareIndex && !context.fAbandoned) { context.fTestContext->makeCurrent(); - return ContextInfo(context.fType, context.fTestContext, context.fGrContext); + return ContextInfo(context.fType, context.fTestContext, context.fGrContext, + context.fOptions); } } @@ -173,11 +173,6 @@ ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOv glCtx = CommandBufferGLTestContext::Create(glShareContext); break; #endif -#if SK_MESA - case kMESA_ContextType: - glCtx = CreateMesaGLTestContext(glShareContext); - break; -#endif case kNullGL_ContextType: glCtx = CreateNullGLTestContext( ContextOverrides::kRequireNVPRSupport & overrides, glShareContext); @@ -244,7 +239,7 @@ ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOv default: return ContextInfo(); } - testCtx->makeCurrent(); + SkASSERT(testCtx && testCtx->backend() == backend); GrContextOptions grOptions = fGlobalOptions; if (ContextOverrides::kDisableNVPR & overrides) { @@ -259,7 +254,11 @@ ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOv if (ContextOverrides::kAvoidStencilBuffers & overrides) { grOptions.fAvoidStencilBuffers = true; } - sk_sp<GrContext> grCtx = testCtx->makeGrContext(grOptions); + sk_sp<GrContext> grCtx; + { + auto restore = testCtx->makeCurrentAndAutoRestore(); + grCtx = testCtx->makeGrContext(grOptions); + } if (!grCtx.get()) { return ContextInfo(); } @@ -288,7 +287,9 @@ ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOv context.fAbandoned = false; context.fShareContext = shareContext; context.fShareIndex = shareIndex; - return ContextInfo(context.fType, context.fTestContext, context.fGrContext); + context.fOptions = grOptions; + context.fTestContext->makeCurrent(); + return ContextInfo(context.fType, context.fTestContext, context.fGrContext, context.fOptions); } ContextInfo GrContextFactory::getContextInfo(ContextType type, ContextOverrides overrides) { diff --git a/chromium/third_party/skia/tools/gpu/GrContextFactory.h b/chromium/third_party/skia/tools/gpu/GrContextFactory.h index 8d3e69c765e..6997ca1337a 100644 --- a/chromium/third_party/skia/tools/gpu/GrContextFactory.h +++ b/chromium/third_party/skia/tools/gpu/GrContextFactory.h @@ -39,7 +39,6 @@ public: kANGLE_GL_ES2_ContextType, //! ANGLE on OpenGL OpenGL ES 2 context. kANGLE_GL_ES3_ContextType, //! ANGLE on OpenGL OpenGL ES 3 context. kCommandBuffer_ContextType, //! Chromium command buffer OpenGL ES context. - kMESA_ContextType, //! MESA OpenGL context kNullGL_ContextType, //! Non-rendering OpenGL mock context. kDebugGL_ContextType, //! Non-rendering, state verifying OpenGL context. kVulkan_ContextType, //! Vulkan @@ -107,8 +106,6 @@ public: return "ANGLE GL ES3"; case kCommandBuffer_ContextType: return "Command Buffer"; - case kMESA_ContextType: - return "Mesa"; case kNullGL_ContextType: return "Null GL"; case kDebugGL_ContextType: @@ -159,6 +156,7 @@ private: struct Context { ContextType fType; ContextOverrides fOverrides; + GrContextOptions fOptions; GrBackend fBackend; TestContext* fTestContext; GrContext* fGrContext; @@ -189,19 +187,18 @@ public: return static_cast<GLTestContext*>(fTestContext); } + const GrContextOptions& options() const { return fOptions; } + private: - ContextInfo(GrContextFactory::ContextType type, - TestContext* testContext, - GrContext* grContext) - : fType(type) - , fTestContext(testContext) - , fGrContext(grContext) { - } + ContextInfo(GrContextFactory::ContextType type, TestContext* testContext, GrContext* grContext, + const GrContextOptions& options) + : fType(type), fTestContext(testContext), fGrContext(grContext), fOptions(options) {} GrContextFactory::ContextType fType = GrContextFactory::kGL_ContextType; // Valid until the factory destroys it via abandonContexts() or destroyContexts(). - TestContext* fTestContext = nullptr; - GrContext* fGrContext = nullptr; + TestContext* fTestContext = nullptr; + GrContext* fGrContext = nullptr; + GrContextOptions fOptions; friend class GrContextFactory; }; diff --git a/chromium/third_party/skia/tools/gpu/GrTest.cpp b/chromium/third_party/skia/tools/gpu/GrTest.cpp index 3a74b6fe540..47f98a6c209 100644 --- a/chromium/third_party/skia/tools/gpu/GrTest.cpp +++ b/chromium/third_party/skia/tools/gpu/GrTest.cpp @@ -55,27 +55,51 @@ void SetupAlwaysEvictAtlas(GrContext* context) { } GrBackendTexture CreateBackendTexture(GrBackend backend, int width, int height, - GrPixelConfig config, GrBackendObject handle) { + GrPixelConfig config, GrMipMapped mipMapped, + GrBackendObject handle) { switch (backend) { #ifdef SK_VULKAN case kVulkan_GrBackend: { GrVkImageInfo* vkInfo = (GrVkImageInfo*)(handle); + SkASSERT((GrMipMapped::kYes == mipMapped) == (vkInfo->fLevelCount > 1)); return GrBackendTexture(width, height, *vkInfo); } #endif case kOpenGL_GrBackend: { GrGLTextureInfo* glInfo = (GrGLTextureInfo*)(handle); - return GrBackendTexture(width, height, config, *glInfo); + return GrBackendTexture(width, height, config, mipMapped, *glInfo); } case kMock_GrBackend: { GrMockTextureInfo* mockInfo = (GrMockTextureInfo*)(handle); - return GrBackendTexture(width, height, config, *mockInfo); + return GrBackendTexture(width, height, config, mipMapped, *mockInfo); } default: return GrBackendTexture(); } } +GrBackendRenderTarget CreateBackendRenderTarget(GrBackend backend, int width, int height, + int sampleCnt, int stencilBits, + GrPixelConfig config, + GrBackendObject handle) { + switch (backend) { +#ifdef SK_VULKAN + case kVulkan_GrBackend: { + GrVkImageInfo* vkInfo = (GrVkImageInfo*)(handle); + return GrBackendRenderTarget(width, height, sampleCnt, stencilBits, *vkInfo); + } +#endif + case kOpenGL_GrBackend: { + GrGLFramebufferInfo glInfo; + glInfo.fFBOID = handle; + return GrBackendRenderTarget(width, height, sampleCnt, stencilBits, config, glInfo); + } + case kMock_GrBackend: // fall through + default: + return GrBackendRenderTarget(); + } +} + } // namespace GrTest bool GrSurfaceProxy::isWrapped_ForTesting() const { diff --git a/chromium/third_party/skia/tools/gpu/GrTest.h b/chromium/third_party/skia/tools/gpu/GrTest.h index d4a4c6dfac3..1134932ad9f 100644 --- a/chromium/third_party/skia/tools/gpu/GrTest.h +++ b/chromium/third_party/skia/tools/gpu/GrTest.h @@ -19,7 +19,11 @@ namespace GrTest { void SetupAlwaysEvictAtlas(GrContext*); GrBackendTexture CreateBackendTexture(GrBackend, int width, int height, - GrPixelConfig, GrBackendObject); + GrPixelConfig, GrMipMapped, GrBackendObject); + + GrBackendRenderTarget CreateBackendRenderTarget(GrBackend, int width, int height, + int sampleCnt, int stencilBits, + GrPixelConfig, GrBackendObject); }; #endif diff --git a/chromium/third_party/skia/tools/gpu/TestContext.cpp b/chromium/third_party/skia/tools/gpu/TestContext.cpp index c80c4ea4dfb..f760cc7738a 100644 --- a/chromium/third_party/skia/tools/gpu/TestContext.cpp +++ b/chromium/third_party/skia/tools/gpu/TestContext.cpp @@ -37,8 +37,15 @@ sk_sp<GrContext> TestContext::makeGrContext(const GrContextOptions&) { void TestContext::makeCurrent() const { this->onPlatformMakeCurrent(); } +SkScopeExit TestContext::makeCurrentAndAutoRestore() const { + auto asr = SkScopeExit(this->onPlatformGetAutoContextRestore()); + this->makeCurrent(); + return asr; +} + void TestContext::swapBuffers() { this->onPlatformSwapBuffers(); } + void TestContext::waitOnSyncOrSwap() { if (!fFenceSync) { // Fallback on the platform SwapBuffers method for synchronization. This may have no effect. diff --git a/chromium/third_party/skia/tools/gpu/TestContext.h b/chromium/third_party/skia/tools/gpu/TestContext.h index 84794f3c34e..5b512db7ba4 100644 --- a/chromium/third_party/skia/tools/gpu/TestContext.h +++ b/chromium/third_party/skia/tools/gpu/TestContext.h @@ -9,10 +9,11 @@ #ifndef TestContext_DEFINED #define TestContext_DEFINED +#include "../private/SkTemplates.h" #include "FenceSync.h" #include "GrTypes.h" #include "SkRefCnt.h" -#include "../private/SkTemplates.h" +#include "SkScopeExit.h" class GrContext; struct GrContextOptions; @@ -45,6 +46,18 @@ public: void makeCurrent() const; + /** + * Like makeCurrent() but this returns an object that will restore the previous current + * context in its destructor. Useful to undo the effect making this current before returning to + * a caller that doesn't expect the current context to be changed underneath it. + * + * The returned object restores the current context of the same type (e.g. egl, glx, ...) in its + * destructor. It is undefined behavior if that context is destroyed before the destructor + * executes. If the concept of a current context doesn't make sense for this context type then + * the returned object's destructor is a no-op. + */ + SkScopeExit SK_WARN_UNUSED_RESULT makeCurrentAndAutoRestore() const; + virtual GrBackend backend() = 0; virtual GrBackendContext backendContext() = 0; @@ -94,6 +107,14 @@ protected: virtual void teardown(); virtual void onPlatformMakeCurrent() const = 0; + /** + * Subclasses should implement such that the returned function will cause the current context + * of this type to be made current again when it is called. It should additionally be the + * case that if "this" is already current when this is called, then "this" is destroyed (thereby + * setting the null context as current), and then the std::function is called the null context + * should remain current. + */ + virtual std::function<void()> onPlatformGetAutoContextRestore() const = 0; virtual void onPlatformSwapBuffers() const = 0; private: diff --git a/chromium/third_party/skia/tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp b/chromium/third_party/skia/tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp new file mode 100644 index 00000000000..e1130966928 --- /dev/null +++ b/chromium/third_party/skia/tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp @@ -0,0 +1,455 @@ +/* + * 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* 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(); + 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, 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 diff --git a/chromium/third_party/skia/tools/gpu/atlastext/GLTestAtlasTextRenderer.h b/chromium/third_party/skia/tools/gpu/atlastext/GLTestAtlasTextRenderer.h new file mode 100644 index 00000000000..df01b345de2 --- /dev/null +++ b/chromium/third_party/skia/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/chromium/third_party/skia/tools/gpu/atlastext/TestAtlasTextRenderer.h b/chromium/third_party/skia/tools/gpu/atlastext/TestAtlasTextRenderer.h new file mode 100644 index 00000000000..6ba43260648 --- /dev/null +++ b/chromium/third_party/skia/tools/gpu/atlastext/TestAtlasTextRenderer.h @@ -0,0 +1,37 @@ +/* + * 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; + + /** Clears the target to the specified color, encoded as RGBA (low to high byte order) */ + virtual void clearTarget(void* targetHandle, uint32_t color) = 0; +}; + +} // namespace sk_gpu_test + +#endif diff --git a/chromium/third_party/skia/tools/gpu/gl/angle/GLTestContext_angle.cpp b/chromium/third_party/skia/tools/gpu/gl/angle/GLTestContext_angle.cpp index 52cc5128dad..3b55c40bac9 100644 --- a/chromium/third_party/skia/tools/gpu/gl/angle/GLTestContext_angle.cpp +++ b/chromium/third_party/skia/tools/gpu/gl/angle/GLTestContext_angle.cpp @@ -34,6 +34,16 @@ struct Libs { void* fEGLLib; }; +std::function<void()> context_restorer() { + auto display = eglGetCurrentDisplay(); + auto dsurface = eglGetCurrentSurface(EGL_DRAW); + auto rsurface = eglGetCurrentSurface(EGL_READ); + auto context = eglGetCurrentContext(); + return [display, dsurface, rsurface, context] { + eglMakeCurrent(display, dsurface, rsurface, context); + }; +} + static GrGLFuncPtr angle_get_gl_proc(void* ctx, const char name[]) { const Libs* libs = reinterpret_cast<const Libs*>(ctx); GrGLFuncPtr proc = (GrGLFuncPtr) GetProcedureAddress(libs->fGLLib, name); @@ -87,6 +97,7 @@ private: void destroyGLContext(); void onPlatformMakeCurrent() const override; + std::function<void()> onPlatformGetAutoContextRestore() const override; void onPlatformSwapBuffers() const override; GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override; @@ -214,6 +225,7 @@ ANGLEGLContext::ANGLEGLContext(ANGLEBackend type, ANGLEContextVersion version, fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs); + SkScopeExit restorer(context_restorer()); if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { SkDebugf("Could not set the context."); this->destroyGLContext(); @@ -320,7 +332,10 @@ std::unique_ptr<sk_gpu_test::GLTestContext> ANGLEGLContext::makeNew() const { void ANGLEGLContext::destroyGLContext() { if (EGL_NO_DISPLAY != fDisplay) { - eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (eglGetCurrentContext() == fContext) { + // This will ensure that the context is immediately deleted. + eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } if (EGL_NO_CONTEXT != fContext) { eglDestroyContext(fDisplay, fContext); @@ -355,6 +370,13 @@ void ANGLEGLContext::onPlatformMakeCurrent() const { } } +std::function<void()> ANGLEGLContext::onPlatformGetAutoContextRestore() const { + if (eglGetCurrentContext() == fContext) { + return nullptr; + } + return context_restorer(); +} + void ANGLEGLContext::onPlatformSwapBuffers() const { if (!eglSwapBuffers(fDisplay, fSurface)) { SkDebugf("Could not complete eglSwapBuffers.\n"); diff --git a/chromium/third_party/skia/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp b/chromium/third_party/skia/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp index 54845fc28b4..be2b6ad5e69 100644 --- a/chromium/third_party/skia/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp +++ b/chromium/third_party/skia/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp @@ -10,11 +10,14 @@ #include "SkMutex.h" #include "SkOnce.h" +#include "SkTLS.h" #include "gl/GrGLInterface.h" #include "gl/GrGLAssembleInterface.h" #include "gl/command_buffer/GLTestContext_command_buffer.h" #include "../ports/SkOSLibrary.h" +namespace { + typedef void *EGLDisplay; typedef unsigned int EGLBoolean; typedef void *EGLConfig; @@ -25,6 +28,7 @@ typedef void* EGLNativeDisplayType; typedef void* EGLNativeWindowType; typedef void (*__eglMustCastToProperFunctionPointerType)(void); #define EGL_FALSE 0 +#define EGL_TRUE 1 #define EGL_OPENGL_ES2_BIT 0x0004 #define EGL_CONTEXT_CLIENT_VERSION 0x3098 #define EGL_NO_SURFACE ((EGLSurface)0) @@ -45,6 +49,8 @@ typedef void (*__eglMustCastToProperFunctionPointerType)(void); #define EGL_NONE 0x3038 #define EGL_WIDTH 0x3057 #define EGL_HEIGHT 0x3056 +#define EGL_DRAW 0x3059 +#define EGL_READ 0x305A typedef EGLDisplay (*GetDisplayProc)(EGLNativeDisplayType display_id); typedef EGLBoolean (*InitializeProc)(EGLDisplay dpy, EGLint *major, EGLint *minor); @@ -77,7 +83,6 @@ static GetProcAddressProc gfGetProcAddress = nullptr; static void* gLibrary = nullptr; static bool gfFunctionsLoadedSuccessfully = false; -namespace { static void load_command_buffer_functions() { if (!gLibrary) { static constexpr const char* libName = @@ -104,12 +109,11 @@ static void load_command_buffer_functions() { gfSwapBuffers = (SwapBuffersProc)GetProcedureAddress(gLibrary, "eglSwapBuffers"); gfGetProcAddress = (GetProcAddressProc)GetProcedureAddress(gLibrary, "eglGetProcAddress"); - gfFunctionsLoadedSuccessfully = gfGetDisplay && gfInitialize && gfTerminate && - gfChooseConfig && gfCreateWindowSurface && - gfCreatePbufferSurface && gfDestroySurface && - gfCreateContext && gfDestroyContext && gfMakeCurrent && - gfSwapBuffers && gfGetProcAddress; - + gfFunctionsLoadedSuccessfully = + gfGetDisplay && gfInitialize && gfTerminate && gfChooseConfig && + gfCreateWindowSurface && gfCreatePbufferSurface && gfDestroySurface && + gfCreateContext && gfDestroyContext && gfMakeCurrent && gfSwapBuffers && + gfGetProcAddress; } } } @@ -134,6 +138,80 @@ static const GrGLInterface* create_command_buffer_interface() { return GrGLAssembleGLESInterface(gLibrary, command_buffer_get_gl_proc); } + +// The command buffer does not correctly implement eglGetCurrent. It always returns EGL_NO_<foo>. +// So we implement them ourselves and hook eglMakeCurrent to store the current values in TLS. +class TLSCurrentObjects { +public: + static EGLDisplay CurrentDisplay() { + if (auto objects = Get()) { + return objects->fDisplay; + } + return EGL_NO_DISPLAY; + } + + static EGLSurface CurrentSurface(EGLint readdraw) { + if (auto objects = Get()) { + switch (readdraw) { + case EGL_DRAW: + return objects->fDrawSurface; + case EGL_READ: + return objects->fReadSurface; + default: + return EGL_NO_SURFACE; + } + } + return EGL_NO_SURFACE; + } + + static EGLContext CurrentContext() { + if (auto objects = Get()) { + return objects->fContext; + } + return EGL_NO_CONTEXT; + } + + static EGLBoolean MakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, + EGLContext ctx) { + if (gfFunctionsLoadedSuccessfully && EGL_TRUE == gfMakeCurrent(display, draw, read, ctx)) { + if (auto objects = Get()) { + objects->fDisplay = display; + objects->fDrawSurface = draw; + objects->fReadSurface = read; + objects->fContext = ctx; + } + return EGL_TRUE; + } + return EGL_FALSE; + + } + +private: + EGLDisplay fDisplay = EGL_NO_DISPLAY; + EGLSurface fReadSurface = EGL_NO_SURFACE; + EGLSurface fDrawSurface = EGL_NO_SURFACE; + EGLContext fContext = EGL_NO_CONTEXT; + + static TLSCurrentObjects* Get() { + return (TLSCurrentObjects*) SkTLS::Get(TLSCreate, TLSDelete); + } + static void* TLSCreate() { return new TLSCurrentObjects(); } + static void TLSDelete(void* objs) { delete (TLSCurrentObjects*)objs; } +}; + +std::function<void()> context_restorer() { + if (!gfFunctionsLoadedSuccessfully) { + return nullptr; + } + auto display = TLSCurrentObjects::CurrentDisplay(); + auto dsurface = TLSCurrentObjects::CurrentSurface(EGL_DRAW); + auto rsurface = TLSCurrentObjects::CurrentSurface(EGL_READ); + auto context = TLSCurrentObjects::CurrentContext(); + return [display, dsurface, rsurface, context] { + TLSCurrentObjects::MakeCurrent(display, dsurface, rsurface, context); + }; +} + } // anonymous namespace namespace sk_gpu_test { @@ -204,7 +282,8 @@ CommandBufferGLTestContext::CommandBufferGLTestContext(CommandBufferGLTestContex return; } - if (!gfMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { + SkScopeExit restorer(context_restorer()); + if (!TLSCurrentObjects::MakeCurrent(fDisplay, fSurface, fSurface, fContext)) { SkDebugf("Command Buffer: Could not make EGL context current.\n"); this->destroyGLContext(); return; @@ -237,15 +316,19 @@ void CommandBufferGLTestContext::destroyGLContext() { if (EGL_NO_DISPLAY == fDisplay) { return; } + bool wasCurrent = false; if (EGL_NO_CONTEXT != fContext) { + wasCurrent = (TLSCurrentObjects::CurrentContext() == fContext); gfDestroyContext(fDisplay, fContext); fContext = EGL_NO_CONTEXT; } - // Call MakeCurrent after destroying the context, so that the EGL implementation knows that - // the context is not used anymore after it is released from being current. This way - // command buffer does not need to abandon the context before destruction, and no - // client-side errors are printed. - gfMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (wasCurrent) { + // Call MakeCurrent after destroying the context, so that the EGL implementation knows that + // the context is not used anymore after it is released from being current.This way the + // command buffer does not need to abandon the context before destruction, and no + // client-side errors are printed. + TLSCurrentObjects::MakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } if (EGL_NO_SURFACE != fSurface) { gfDestroySurface(fDisplay, fSurface); @@ -258,11 +341,18 @@ void CommandBufferGLTestContext::onPlatformMakeCurrent() const { if (!gfFunctionsLoadedSuccessfully) { return; } - if (!gfMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { + if (!TLSCurrentObjects::MakeCurrent(fDisplay, fSurface, fSurface, fContext)) { SkDebugf("Command Buffer: Could not make EGL context current.\n"); } } +std::function<void()> CommandBufferGLTestContext::onPlatformGetAutoContextRestore() const { + if (!gfFunctionsLoadedSuccessfully || TLSCurrentObjects::CurrentContext() == fContext) { + return nullptr; + } + return context_restorer(); +} + void CommandBufferGLTestContext::onPlatformSwapBuffers() const { if (!gfFunctionsLoadedSuccessfully) { return; @@ -288,7 +378,7 @@ void CommandBufferGLTestContext::presentCommandBuffer() { } bool CommandBufferGLTestContext::makeCurrent() { - return gfMakeCurrent(fDisplay, fSurface, fSurface, fContext) != EGL_FALSE; + return TLSCurrentObjects::MakeCurrent(fDisplay, fSurface, fSurface, fContext) != EGL_FALSE; } int CommandBufferGLTestContext::getStencilBits() { diff --git a/chromium/third_party/skia/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.h b/chromium/third_party/skia/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.h index 7582f163516..6a631be8fbf 100644 --- a/chromium/third_party/skia/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.h +++ b/chromium/third_party/skia/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.h @@ -42,6 +42,8 @@ private: void onPlatformMakeCurrent() const override; + std::function<void()> onPlatformGetAutoContextRestore() const override; + void onPlatformSwapBuffers() const override; GrGLFuncPtr onPlatformGetProcAddress(const char *name) const override; diff --git a/chromium/third_party/skia/tools/gpu/gl/debug/DebugGLTestContext.cpp b/chromium/third_party/skia/tools/gpu/gl/debug/DebugGLTestContext.cpp index 3cecd07a0ad..e28a3a7f13d 100644 --- a/chromium/third_party/skia/tools/gpu/gl/debug/DebugGLTestContext.cpp +++ b/chromium/third_party/skia/tools/gpu/gl/debug/DebugGLTestContext.cpp @@ -982,7 +982,8 @@ private: case GR_GL_COMPILE_STATUS: *params = GR_GL_TRUE; break; - case GR_GL_INFO_LOG_LENGTH: + case GR_GL_INFO_LOG_LENGTH: // fallthru + case GL_PROGRAM_BINARY_LENGTH: *params = 0; break; // we don't expect any other pnames @@ -1202,6 +1203,7 @@ public: private: void onPlatformMakeCurrent() const override {} + std::function<void()> onPlatformGetAutoContextRestore() const override { return nullptr; } void onPlatformSwapBuffers() const override {} GrGLFuncPtr onPlatformGetProcAddress(const char*) const override { return nullptr; } }; diff --git a/chromium/third_party/skia/tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp b/chromium/third_party/skia/tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp index c006098d2fb..4a09d2202a2 100644 --- a/chromium/third_party/skia/tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp +++ b/chromium/third_party/skia/tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp @@ -39,6 +39,16 @@ private: typedef sk_gpu_test::FenceSync INHERITED; }; +std::function<void()> context_restorer() { + auto display = eglGetCurrentDisplay(); + auto dsurface = eglGetCurrentSurface(EGL_DRAW); + auto rsurface = eglGetCurrentSurface(EGL_READ); + auto context = eglGetCurrentContext(); + return [display, dsurface, rsurface, context] { + eglMakeCurrent(display, dsurface, rsurface, context); + }; +} + class EGLGLTestContext : public sk_gpu_test::GLTestContext { public: EGLGLTestContext(GrGLStandard forcedGpuAPI, EGLGLTestContext* shareContext); @@ -53,6 +63,7 @@ private: void destroyGLContext(); void onPlatformMakeCurrent() const override; + std::function<void()> onPlatformGetAutoContextRestore() const override; void onPlatformSwapBuffers() const override; GrGLFuncPtr onPlatformGetProcAddress(const char*) const override; @@ -168,6 +179,7 @@ EGLGLTestContext::EGLGLTestContext(GrGLStandard forcedGpuAPI, EGLGLTestContext* continue; } + SkScopeExit restorer(context_restorer()); if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { SkDebugf("eglMakeCurrent failed. EGL Error: 0x%08x\n", eglGetError()); this->destroyGLContext(); @@ -199,9 +211,11 @@ EGLGLTestContext::~EGLGLTestContext() { void EGLGLTestContext::destroyGLContext() { if (fDisplay) { - eglMakeCurrent(fDisplay, 0, 0, 0); - if (fContext) { + if (eglGetCurrentContext() == fContext) { + // This will ensure that the context is immediately deleted. + eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } eglDestroyContext(fDisplay, fContext); fContext = EGL_NO_CONTEXT; } @@ -237,7 +251,6 @@ GrGLuint EGLGLTestContext::eglImageToExternalTexture(GrEGLImage image) const { if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) { return 0; } -#ifndef EGL_NO_IMAGE_EXTERNAL typedef GrGLvoid (*EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage); EGLImageTargetTexture2DProc glEGLImageTargetTexture2D = @@ -246,24 +259,21 @@ GrGLuint EGLGLTestContext::eglImageToExternalTexture(GrEGLImage image) const { return 0; } GrGLuint texID; - glGenTextures(1, &texID); + GR_GL_CALL(this->gl(), GenTextures(1, &texID)); if (!texID) { return 0; } - glBindTexture(GR_GL_TEXTURE_EXTERNAL, texID); - if (glGetError() != GR_GL_NO_ERROR) { - glDeleteTextures(1, &texID); + GR_GL_CALL_NOERRCHECK(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID)); + if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) { + GR_GL_CALL(this->gl(), DeleteTextures(1, &texID)); return 0; } glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image); - if (glGetError() != GR_GL_NO_ERROR) { - glDeleteTextures(1, &texID); + if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) { + GR_GL_CALL(this->gl(), DeleteTextures(1, &texID)); return 0; } return texID; -#else - return 0; -#endif //EGL_NO_IMAGE_EXTERNAL } std::unique_ptr<sk_gpu_test::GLTestContext> EGLGLTestContext::makeNew() const { @@ -281,6 +291,13 @@ void EGLGLTestContext::onPlatformMakeCurrent() const { } } +std::function<void()> EGLGLTestContext::onPlatformGetAutoContextRestore() const { + if (eglGetCurrentContext() == fContext) { + return nullptr; + } + return context_restorer(); +} + void EGLGLTestContext::onPlatformSwapBuffers() const { if (!eglSwapBuffers(fDisplay, fSurface)) { SkDebugf("Could not complete eglSwapBuffers.\n"); diff --git a/chromium/third_party/skia/tools/gpu/gl/glx/CreatePlatformGLTestContext_glx.cpp b/chromium/third_party/skia/tools/gpu/gl/glx/CreatePlatformGLTestContext_glx.cpp index 76b6d216617..066784df691 100644 --- a/chromium/third_party/skia/tools/gpu/gl/glx/CreatePlatformGLTestContext_glx.cpp +++ b/chromium/third_party/skia/tools/gpu/gl/glx/CreatePlatformGLTestContext_glx.cpp @@ -62,6 +62,7 @@ private: GLXContext glxSharedContext); void onPlatformMakeCurrent() const override; + std::function<void()> onPlatformGetAutoContextRestore() const override; void onPlatformSwapBuffers() const override; GrGLFuncPtr onPlatformGetProcAddress(const char*) const override; @@ -90,11 +91,27 @@ static Display* get_display() { return ad->display(); } +std::function<void()> context_restorer() { + auto display = glXGetCurrentDisplay(); + auto drawable = glXGetCurrentDrawable(); + auto context = glXGetCurrentContext(); + // On some systems calling glXMakeCurrent with a null display crashes. + if (!display) { + display = get_display(); + } + return [display, drawable, context] { glXMakeCurrent(display, drawable, context); }; +} + GLXGLTestContext::GLXGLTestContext(GrGLStandard forcedGpuAPI, GLXGLTestContext* shareContext) : fContext(nullptr) , 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; @@ -214,6 +231,7 @@ GLXGLTestContext::GLXGLTestContext(GrGLStandard forcedGpuAPI, GLXGLTestContext* //SkDebugf("Direct GLX rendering context obtained.\n"); } + SkScopeExit restorer(context_restorer()); //SkDebugf("Making context current.\n"); if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) { SkDebugf("Could not set the context.\n"); @@ -245,9 +263,11 @@ GLXGLTestContext::~GLXGLTestContext() { void GLXGLTestContext::destroyGLContext() { if (fDisplay) { - glXMakeCurrent(fDisplay, 0, 0); - if (fContext) { + if (glXGetCurrentContext() == fContext) { + // This will ensure that the context is immediately deleted. + glXMakeContextCurrent(fDisplay, None, None, nullptr); + } glXDestroyContext(fDisplay, fContext); fContext = nullptr; } @@ -334,6 +354,13 @@ void GLXGLTestContext::onPlatformMakeCurrent() const { } } +std::function<void()> GLXGLTestContext::onPlatformGetAutoContextRestore() const { + if (glXGetCurrentContext() == fContext) { + return nullptr; + } + return context_restorer(); +} + void GLXGLTestContext::onPlatformSwapBuffers() const { glXSwapBuffers(fDisplay, fGlxPixmap); } diff --git a/chromium/third_party/skia/tools/gpu/gl/iOS/CreatePlatformGLTestContext_iOS.mm b/chromium/third_party/skia/tools/gpu/gl/iOS/CreatePlatformGLTestContext_iOS.mm index e897e8c7c40..65d28614832 100644 --- a/chromium/third_party/skia/tools/gpu/gl/iOS/CreatePlatformGLTestContext_iOS.mm +++ b/chromium/third_party/skia/tools/gpu/gl/iOS/CreatePlatformGLTestContext_iOS.mm @@ -14,6 +14,11 @@ namespace { +std::function<void()> context_restorer() { + EAGLContext* context = [EAGLContext currentContext]; + return [context] { [EAGLContext setCurrentContext:context]; }; +} + class IOSGLTestContext : public sk_gpu_test::GLTestContext { public: IOSGLTestContext(IOSGLTestContext* shareContext); @@ -23,6 +28,7 @@ private: void destroyGLContext(); void onPlatformMakeCurrent() const override; + std::function<void()> onPlatformGetAutoContextRestore() const override; void onPlatformSwapBuffers() const override; GrGLFuncPtr onPlatformGetProcAddress(const char*) const override; @@ -41,6 +47,7 @@ IOSGLTestContext::IOSGLTestContext(IOSGLTestContext* shareContext) } else { fEAGLContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; } + SkScopeExit restorer(context_restorer()); [EAGLContext setCurrentContext:fEAGLContext]; sk_sp<const GrGLInterface> gl(GrGLCreateNativeInterface()); @@ -70,6 +77,7 @@ IOSGLTestContext::~IOSGLTestContext() { void IOSGLTestContext::destroyGLContext() { if (fEAGLContext) { if ([EAGLContext currentContext] == fEAGLContext) { + // This will ensure that the context is immediately deleted. [EAGLContext setCurrentContext:nil]; } fEAGLContext = nil; @@ -86,6 +94,13 @@ void IOSGLTestContext::onPlatformMakeCurrent() const { } } +std::function<void()> IOSGLTestContext::onPlatformGetAutoContextRestore() const { + if ([EAGLContext currentContext] == fEAGLContext) { + return nullptr; + } + return context_restorer(); +} + void IOSGLTestContext::onPlatformSwapBuffers() const { } GrGLFuncPtr IOSGLTestContext::onPlatformGetProcAddress(const char* procName) const { diff --git a/chromium/third_party/skia/tools/gpu/gl/mac/CreatePlatformGLTestContext_mac.cpp b/chromium/third_party/skia/tools/gpu/gl/mac/CreatePlatformGLTestContext_mac.cpp index a94f503d48a..9f1c61e5648 100644 --- a/chromium/third_party/skia/tools/gpu/gl/mac/CreatePlatformGLTestContext_mac.cpp +++ b/chromium/third_party/skia/tools/gpu/gl/mac/CreatePlatformGLTestContext_mac.cpp @@ -14,6 +14,12 @@ #include <dlfcn.h> namespace { + +std::function<void()> context_restorer() { + auto context = CGLGetCurrentContext(); + return [context] { CGLSetCurrentContext(context); }; +} + class MacGLTestContext : public sk_gpu_test::GLTestContext { public: MacGLTestContext(MacGLTestContext* shareContext); @@ -23,6 +29,7 @@ private: void destroyGLContext(); void onPlatformMakeCurrent() const override; + std::function<void()> onPlatformGetAutoContextRestore() const override; void onPlatformSwapBuffers() const override; GrGLFuncPtr onPlatformGetProcAddress(const char*) const override; @@ -58,6 +65,7 @@ MacGLTestContext::MacGLTestContext(MacGLTestContext* shareContext) return; } + SkScopeExit restorer(context_restorer()); CGLSetCurrentContext(fContext); sk_sp<const GrGLInterface> gl(GrGLCreateNativeInterface()); @@ -86,6 +94,10 @@ MacGLTestContext::~MacGLTestContext() { void MacGLTestContext::destroyGLContext() { if (fContext) { + if (CGLGetCurrentContext() == fContext) { + // This will ensure that the context is immediately deleted. + CGLSetCurrentContext(nullptr); + } CGLReleaseContext(fContext); fContext = nullptr; } @@ -98,6 +110,13 @@ void MacGLTestContext::onPlatformMakeCurrent() const { CGLSetCurrentContext(fContext); } +std::function<void()> MacGLTestContext::onPlatformGetAutoContextRestore() const { + if (CGLGetCurrentContext() == fContext) { + return nullptr; + } + return context_restorer(); +} + void MacGLTestContext::onPlatformSwapBuffers() const { CGLFlushDrawable(fContext); } diff --git a/chromium/third_party/skia/tools/gpu/gl/mesa/GLTestContext_mesa.cpp b/chromium/third_party/skia/tools/gpu/gl/mesa/GLTestContext_mesa.cpp deleted file mode 100644 index 5d0014b82c4..00000000000 --- a/chromium/third_party/skia/tools/gpu/gl/mesa/GLTestContext_mesa.cpp +++ /dev/null @@ -1,156 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include <GL/osmesa.h> - -#include "gl/mesa/GLTestContext_mesa.h" -#include "gl/GrGLDefines.h" - -#include "gl/GrGLAssembleInterface.h" -#include "gl/GrGLUtil.h" -#include "osmesa_wrapper.h" - -namespace { - -static GrGLFuncPtr osmesa_get(void* ctx, const char name[]) { - SkASSERT(nullptr == ctx); - SkASSERT(OSMesaGetCurrentContext()); - return OSMesaGetProcAddress(name); -} - -static const GrGLInterface* create_mesa_interface() { - if (nullptr == OSMesaGetCurrentContext()) { - return nullptr; - } - return GrGLAssembleInterface(nullptr, osmesa_get); -} - -static const GrGLint gBOGUS_SIZE = 16; - -class MesaGLContext : public sk_gpu_test::GLTestContext { -private: - typedef intptr_t Context; - -public: - MesaGLContext(MesaGLContext* shareContext); - ~MesaGLContext() override; - -private: - void destroyGLContext(); - - void onPlatformMakeCurrent() const override; - - void onPlatformSwapBuffers() const override; - - GrGLFuncPtr onPlatformGetProcAddress(const char *) const override; - - Context fContext; - GrGLubyte *fImage; -}; - -MesaGLContext::MesaGLContext(MesaGLContext* shareContext) - : fContext(static_cast<Context>(0)) - , fImage(nullptr) { - GR_STATIC_ASSERT(sizeof(Context) == sizeof(OSMesaContext)); - OSMesaContext mesaShareContext = shareContext ? (OSMesaContext)(shareContext->fContext) - : nullptr; - - /* Create an RGBA-mode context */ -#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305 - /* specify Z, stencil, accum sizes */ - fContext = (Context)OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, mesaShareContext); -#else - fContext = (Context) OSMesaCreateContext(OSMESA_BGRA, mesaShareContext); -#endif - if (!fContext) { - SkDebugf("OSMesaCreateContext failed!\n"); - this->destroyGLContext(); - return; - } - // Allocate the image buffer - fImage = (GrGLubyte *) sk_malloc_throw(gBOGUS_SIZE * gBOGUS_SIZE * - 4 * sizeof(GrGLubyte)); - if (!fImage) { - SkDebugf("Alloc image buffer failed!\n"); - this->destroyGLContext(); - return; - } - - // Bind the buffer to the context and make it current - if (!OSMesaMakeCurrent((OSMesaContext) fContext, - fImage, - GR_GL_UNSIGNED_BYTE, - gBOGUS_SIZE, - gBOGUS_SIZE)) { - SkDebugf("OSMesaMakeCurrent failed!\n"); - this->destroyGLContext(); - return; - } - - sk_sp<const GrGLInterface> gl(create_mesa_interface()); - if (nullptr == gl.get()) { - SkDebugf("Could not create GL interface!\n"); - this->destroyGLContext(); - return; - } - - if (!gl->validate()) { - SkDebugf("Could not validate GL interface!\n"); - this->destroyGLContext(); - return; - } - - this->init(gl.release()); -} - -MesaGLContext::~MesaGLContext() { - this->teardown(); - this->destroyGLContext(); -} - -void MesaGLContext::destroyGLContext() { - if (fImage) { - sk_free(fImage); - fImage = nullptr; - } - - if (fContext) { - OSMesaDestroyContext((OSMesaContext) fContext); - fContext = static_cast<Context>(0); - } -} - - -void MesaGLContext::onPlatformMakeCurrent() const { - if (fContext) { - if (!OSMesaMakeCurrent((OSMesaContext) fContext, fImage, - GR_GL_UNSIGNED_BYTE, gBOGUS_SIZE, gBOGUS_SIZE)) { - SkDebugf("Could not make MESA context current."); - } - } -} - -void MesaGLContext::onPlatformSwapBuffers() const { } - -GrGLFuncPtr MesaGLContext::onPlatformGetProcAddress(const char *procName) const { - return OSMesaGetProcAddress(procName); -} -} // anonymous namespace - - -namespace sk_gpu_test { -GLTestContext *CreateMesaGLTestContext(GLTestContext* shareContext) { - MesaGLContext* mesaShareContext = reinterpret_cast<MesaGLContext*>(shareContext); - MesaGLContext *ctx = new MesaGLContext(mesaShareContext); - if (!ctx->isValid()) { - delete ctx; - return nullptr; - } - return ctx; -} -} // sk_gpu_test diff --git a/chromium/third_party/skia/tools/gpu/gl/mesa/GLTestContext_mesa.h b/chromium/third_party/skia/tools/gpu/gl/mesa/GLTestContext_mesa.h deleted file mode 100644 index 40184aa7ea2..00000000000 --- a/chromium/third_party/skia/tools/gpu/gl/mesa/GLTestContext_mesa.h +++ /dev/null @@ -1,17 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef GLTestContext_mesa_DEFINED -#define GLTestContext_mesa_DEFINED - -#include "gl/GLTestContext.h" - -namespace sk_gpu_test { -GLTestContext* CreateMesaGLTestContext(GLTestContext* shareContext); -} // namespace sk_gpu_test - -#endif diff --git a/chromium/third_party/skia/tools/gpu/gl/mesa/osmesa_wrapper.h b/chromium/third_party/skia/tools/gpu/gl/mesa/osmesa_wrapper.h deleted file mode 100644 index 70de99376d7..00000000000 --- a/chromium/third_party/skia/tools/gpu/gl/mesa/osmesa_wrapper.h +++ /dev/null @@ -1,16 +0,0 @@ - -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -// Older versions of XQuartz have a bug where a header included by osmesa.h -// defines GL_GLEXT_PROTOTYPES. This will cause a redefinition warning if -// the file that includes osmesa.h already defined it. XCode 3 uses a version -// of gcc (4.2.1) that does not support the diagnostic pragma to disable a -// warning (added in 4.2.4). So we use the system_header pragma to shut GCC -// up about warnings in osmesa.h -#pragma GCC system_header -#include <GL/osmesa.h> diff --git a/chromium/third_party/skia/tools/gpu/gl/null/NullGLTestContext.cpp b/chromium/third_party/skia/tools/gpu/gl/null/NullGLTestContext.cpp index 894de0709a6..9e7279b501a 100644 --- a/chromium/third_party/skia/tools/gpu/gl/null/NullGLTestContext.cpp +++ b/chromium/third_party/skia/tools/gpu/gl/null/NullGLTestContext.cpp @@ -22,6 +22,7 @@ public: private: void onPlatformMakeCurrent() const override {} + std::function<void()> onPlatformGetAutoContextRestore() const override { return nullptr; } void onPlatformSwapBuffers() const override {} GrGLFuncPtr onPlatformGetProcAddress(const char*) const override { return nullptr; } }; diff --git a/chromium/third_party/skia/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp b/chromium/third_party/skia/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp index 0e97153794c..5fc355a22a1 100644 --- a/chromium/third_party/skia/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp +++ b/chromium/third_party/skia/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp @@ -16,6 +16,12 @@ namespace { +std::function<void()> context_restorer() { + auto glrc = wglGetCurrentContext(); + auto dc = wglGetCurrentDC(); + return [glrc, dc] { wglMakeCurrent(dc, glrc); }; +} + class WinGLTestContext : public sk_gpu_test::GLTestContext { public: WinGLTestContext(GrGLStandard forcedGpuAPI, WinGLTestContext* shareContext); @@ -25,6 +31,7 @@ private: void destroyGLContext(); void onPlatformMakeCurrent() const override; + std::function<void()> onPlatformGetAutoContextRestore() const override; void onPlatformSwapBuffers() const override; GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override; @@ -113,6 +120,7 @@ WinGLTestContext::WinGLTestContext(GrGLStandard forcedGpuAPI, WinGLTestContext* glrc = fPbufferContext->getGLRC(); } + SkScopeExit restorer(context_restorer()); if (!(wglMakeCurrent(dc, glrc))) { SkDebugf("Could not set the context.\n"); this->destroyGLContext(); @@ -142,6 +150,7 @@ WinGLTestContext::~WinGLTestContext() { void WinGLTestContext::destroyGLContext() { SkSafeSetNull(fPbufferContext); if (fGlRenderContext) { + // This deletes the context immediately even if it is current. wglDeleteContext(fGlRenderContext); fGlRenderContext = 0; } @@ -172,6 +181,13 @@ void WinGLTestContext::onPlatformMakeCurrent() const { } } +std::function<void()> WinGLTestContext::onPlatformGetAutoContextRestore() const { + if (wglGetCurrentContext() == fGlRenderContext) { + return nullptr; + } + return context_restorer(); +} + void WinGLTestContext::onPlatformSwapBuffers() const { HDC dc; diff --git a/chromium/third_party/skia/tools/gpu/mock/MockTestContext.cpp b/chromium/third_party/skia/tools/gpu/mock/MockTestContext.cpp index 68941ad4ef0..5cc5529dfe6 100644 --- a/chromium/third_party/skia/tools/gpu/mock/MockTestContext.cpp +++ b/chromium/third_party/skia/tools/gpu/mock/MockTestContext.cpp @@ -34,6 +34,7 @@ public: protected: void teardown() override {} void onPlatformMakeCurrent() const override {} + std::function<void()> onPlatformGetAutoContextRestore() const override { return nullptr; } void onPlatformSwapBuffers() const override {} private: diff --git a/chromium/third_party/skia/tools/gpu/mtl/MtlTestContext.mm b/chromium/third_party/skia/tools/gpu/mtl/MtlTestContext.mm index 4014e2ba706..38755b2ef0f 100644 --- a/chromium/third_party/skia/tools/gpu/mtl/MtlTestContext.mm +++ b/chromium/third_party/skia/tools/gpu/mtl/MtlTestContext.mm @@ -144,6 +144,7 @@ private: } void onPlatformMakeCurrent() const override {} + std::function<void()> onPlatformGetAutoContextRestore() const override { return nullptr; } void onPlatformSwapBuffers() const override {} id<MTLDevice> fDevice; diff --git a/chromium/third_party/skia/tools/gpu/vk/VkTestContext.cpp b/chromium/third_party/skia/tools/gpu/vk/VkTestContext.cpp index e329583a037..fa40c7374a7 100644 --- a/chromium/third_party/skia/tools/gpu/vk/VkTestContext.cpp +++ b/chromium/third_party/skia/tools/gpu/vk/VkTestContext.cpp @@ -10,9 +10,9 @@ #ifdef SK_VULKAN #include "GrContext.h" +#include "VkTestUtils.h" #include "vk/GrVkInterface.h" #include "vk/GrVkUtil.h" -#include <vulkan/vulkan.h> namespace { /** @@ -114,8 +114,12 @@ public: if (sharedContext) { backendContext = sharedContext->getVkBackendContext(); } else { - backendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, - vkGetDeviceProcAddr)); + PFN_vkGetInstanceProcAddr instProc; + PFN_vkGetDeviceProcAddr devProc; + if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) { + return nullptr; + } + backendContext.reset(GrVkBackendContext::Create(instProc, devProc)); } if (!backendContext) { return nullptr; @@ -150,6 +154,7 @@ private: } void onPlatformMakeCurrent() const override {} + std::function<void()> onPlatformGetAutoContextRestore() const override { return nullptr; } void onPlatformSwapBuffers() const override {} typedef sk_gpu_test::VkTestContext INHERITED; diff --git a/chromium/third_party/skia/tools/gpu/vk/VkTestUtils.cpp b/chromium/third_party/skia/tools/gpu/vk/VkTestUtils.cpp new file mode 100644 index 00000000000..3033076a4c6 --- /dev/null +++ b/chromium/third_party/skia/tools/gpu/vk/VkTestUtils.cpp @@ -0,0 +1,44 @@ +/* + * 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 "VkTestUtils.h" + +#ifdef SK_VULKAN + +#include "../ports/SkOSLibrary.h" + +namespace sk_gpu_test { + +bool LoadVkLibraryAndGetProcAddrFuncs(PFN_vkGetInstanceProcAddr* instProc, + PFN_vkGetDeviceProcAddr* devProc) { + static void* vkLib = nullptr; + static PFN_vkGetInstanceProcAddr localInstProc = nullptr; + static PFN_vkGetDeviceProcAddr localDevProc = nullptr; + if (!vkLib) { +#if defined _WIN32 + vkLib = DynamicLoadLibrary("vulkan-1.dll"); +#else + vkLib = DynamicLoadLibrary("libvulkan.so"); +#endif + if (!vkLib) { + return false; + } + localInstProc = (PFN_vkGetInstanceProcAddr) GetProcedureAddress(vkLib, + "vkGetInstanceProcAddr"); + localDevProc = (PFN_vkGetDeviceProcAddr) GetProcedureAddress(vkLib, + "vkGetDeviceProcAddr"); + } + if (!localInstProc || !localDevProc) { + return false; + } + *instProc = localInstProc; + *devProc = localDevProc; + return true; +} +} + +#endif diff --git a/chromium/third_party/skia/tools/gpu/vk/VkTestUtils.h b/chromium/third_party/skia/tools/gpu/vk/VkTestUtils.h new file mode 100644 index 00000000000..9f34ef09c58 --- /dev/null +++ b/chromium/third_party/skia/tools/gpu/vk/VkTestUtils.h @@ -0,0 +1,23 @@ +/* + * 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 VkTestUtils_DEFINED +#define VkTestUtils_DEFINED + +#include "SkTypes.h" + +#ifdef SK_VULKAN + +#include "vk/GrVkDefines.h" + +namespace sk_gpu_test { + bool LoadVkLibraryAndGetProcAddrFuncs(PFN_vkGetInstanceProcAddr*, PFN_vkGetDeviceProcAddr*); +} + +#endif +#endif + diff --git a/chromium/third_party/skia/tools/gpucts/gm_knowledge.c b/chromium/third_party/skia/tools/gpucts/gm_knowledge.c new file mode 100644 index 00000000000..8dbdd00f09c --- /dev/null +++ b/chromium/third_party/skia/tools/gpucts/gm_knowledge.c @@ -0,0 +1,12 @@ +/* + * 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_knowledge.h" + +// placeholder function definitions: +float GMK_Check(GMK_ImageData d, const char* n) { return 0; } +bool GMK_IsGoodGM(const char* n) { return true; } diff --git a/chromium/third_party/skia/tools/gpucts/gm_knowledge.h b/chromium/third_party/skia/tools/gpucts/gm_knowledge.h new file mode 100644 index 00000000000..d9d71e05c53 --- /dev/null +++ b/chromium/third_party/skia/tools/gpucts/gm_knowledge.h @@ -0,0 +1,57 @@ +/* + * 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 gm_knowledge_DEFINED +#define gm_knowledge_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdbool.h> + +/** +A structure representing an image. pix should either be nullptr (representing +a missing image) or point to a block of memory width*height in size. + +Each pixel is an un-pre-multiplied RGBA color: + void set_color(GMK_ImageData* data, int x, int y, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + data->pix[x + data->width * y] = (r << 0) | (g << 8) | (b << 16) | (a << 24); + } + */ +typedef struct { + const uint32_t* pix; + int width; + int height; +} GMK_ImageData; + +/** +Check if the given test image matches the expected results. + +@param data the image +@param gm_name the name of the rendering test that produced the image + +@return 0 if the test passes, otherwise a positive number representing how + badly it failed. + */ +float GMK_Check(GMK_ImageData data, const char* gm_name); + +/** +Check to see if the given test has expected results. + +@param gm_name the name of a rendering test. + +@return true of expected results are known for the given test. +*/ +bool GMK_IsGoodGM(const char* gm_name); + +#ifdef __cplusplus +} +#endif + +#endif // gm_knowledge_DEFINED diff --git a/chromium/third_party/skia/tools/gpucts/gm_runner.cpp b/chromium/third_party/skia/tools/gpucts/gm_runner.cpp new file mode 100644 index 00000000000..a18c457d1bb --- /dev/null +++ b/chromium/third_party/skia/tools/gpucts/gm_runner.cpp @@ -0,0 +1,109 @@ +/* + * 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_runner.h" + +#include <algorithm> + +#include "SkSurface.h" +#include "gm.h" + +#if SK_SUPPORT_GPU + +#include "GrContextFactory.h" + +using sk_gpu_test::GrContextFactory; + +namespace gm_runner { + +static GrContextFactory::ContextType to_context_type(SkiaBackend backend) { + switch (backend) { + case SkiaBackend::kGL: return GrContextFactory::kGL_ContextType; + case SkiaBackend::kGLES: return GrContextFactory::kGLES_ContextType; + case SkiaBackend::kVulkan: return GrContextFactory::kVulkan_ContextType; + } + SkDEBUGFAIL(""); return (GrContextFactory::ContextType)0; +} + +const char* GetBackendName(SkiaBackend backend) { + return GrContextFactory::ContextTypeName(to_context_type(backend)); +} + +bool BackendSupported(SkiaBackend backend) { + GrContextFactory factory; + return factory.get(to_context_type(backend)) != nullptr; +} + + +GMK_ImageData Evaluate(SkiaBackend backend, + GMFactory gmFact, + std::vector<uint32_t>* storage) { + SkASSERT(gmFact); + SkASSERT(storage); + std::unique_ptr<skiagm::GM> gm(gmFact(nullptr)); + SkASSERT(gm.get()); + int w = SkScalarRoundToInt(gm->width()); + int h = SkScalarRoundToInt(gm->height()); + GrContextFactory contextFactory; + GrContext* context = contextFactory.get(to_context_type(backend)); + if (!context) { + return GMK_ImageData{nullptr, w, h}; + } + SkASSERT(context); + constexpr SkColorType ct = kRGBA_8888_SkColorType; + + sk_sp<SkSurface> s = SkSurface::MakeRenderTarget( + context, SkBudgeted::kNo, SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType)); + if (!s) { + return GMK_ImageData{nullptr, w, h}; + } + gm->draw(s->getCanvas()); + + storage->resize(w * h); + uint32_t* pix = storage->data(); + SkASSERT(SkColorTypeBytesPerPixel(ct) == sizeof(uint32_t)); + SkAssertResult(s->readPixels(SkImageInfo::Make(w, h, ct, kUnpremul_SkAlphaType), + pix, w * sizeof(uint32_t), 0, 0)); + return GMK_ImageData{pix, w, h}; +} + +} // namespace gm_runner + +#else +namespace sk_gpu_test { + class GrContextFactory {}; +} +namespace gm_runner { +bool BackendSupported(SkiaBackend) { return false; } +GMK_ImageData Evaluate(SkiaBackend, GMFactory, std::vector<uint32_t>*) { + return GMK_ImageData{nullptr, 0, 0}; +} +const char* GetBackendName(SkiaBackend backend) { return "Unknown"; } +} // namespace gm_runner +#endif + +namespace gm_runner { + +std::vector<GMFactory> GetGMFactories() { + std::vector<GMFactory> result; + for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) { + result.push_back(r->factory()); + } + struct { + bool operator()(GMFactory u, GMFactory v) const { return GetGMName(u) < GetGMName(v); } + } less; + std::sort(result.begin(), result.end(), less); + return result; +} + +std::string GetGMName(GMFactory gmFactory) { + SkASSERT(gmFactory); + std::unique_ptr<skiagm::GM> gm(gmFactory(nullptr)); + SkASSERT(gm); + return std::string(gm->getName()); +} +} // namespace gm_runner diff --git a/chromium/third_party/skia/tools/gpucts/gm_runner.h b/chromium/third_party/skia/tools/gpucts/gm_runner.h new file mode 100644 index 00000000000..cd0d7d32bb0 --- /dev/null +++ b/chromium/third_party/skia/tools/gpucts/gm_runner.h @@ -0,0 +1,65 @@ +/* + * 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 gm_runner_DEFINED +#define gm_runner_DEFINED + +#include <memory> +#include <string> +#include <vector> + +#include "gm_knowledge.h" + +/** +A Skia GM is a single rendering test that can be executed on any Skia backend Canvas. +*/ +namespace skiagm { + class GM; +} + +namespace sk_gpu_test { + class GrContextFactory; +} + +namespace gm_runner { + +using GMFactory = skiagm::GM* (*)(void*); + +enum class SkiaBackend { + kGL, + kGLES, + kVulkan, +}; + +bool BackendSupported(SkiaBackend); + +/** +@return a list of all Skia GMs in lexicographic order. +*/ +std::vector<GMFactory> GetGMFactories(); + +/** +@return a descriptive name for the GM. +*/ +std::string GetGMName(GMFactory); +/** +@return a descriptive name for the backend. +*/ +const char* GetBackendName(SkiaBackend); + +/** +Execute the given GM on the given Skia backend. Then copy the pixels into the +storage (overwriting existing contents of storage). + +@return the rendered image. Return a null ImageData on error. +*/ +GMK_ImageData Evaluate(SkiaBackend, + GMFactory, + std::vector<uint32_t>* storage); + +} // namespace gm_runner + +#endif // gm_runner_DEFINED diff --git a/chromium/third_party/skia/tools/gpucts/gpucts.cpp b/chromium/third_party/skia/tools/gpucts/gpucts.cpp new file mode 100644 index 00000000000..c50519cc5be --- /dev/null +++ b/chromium/third_party/skia/tools/gpucts/gpucts.cpp @@ -0,0 +1,159 @@ +/* + * 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 "SkGraphics.h" +#include "gm_runner.h" + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#endif + +#include "gtest/gtest.h" + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#include "Test.h" + +//////////////////////////////////////////////////////////////////////////////// + +struct GMTestCase { + gm_runner::GMFactory fGMFactory; + gm_runner::SkiaBackend fBackend; +}; + +struct GMTest : public testing::Test { + GMTestCase fTest; + GMTest(GMTestCase t) : fTest(t) {} + void TestBody() override { + if (!fTest.fGMFactory) { + EXPECT_TRUE(gm_runner::BackendSupported(fTest.fBackend)); + return; + } + std::vector<uint32_t> pixels; + GMK_ImageData imgData = gm_runner::Evaluate(fTest.fBackend, fTest.fGMFactory, &pixels); + EXPECT_TRUE(imgData.pix); + if (!imgData.pix) { + return; + } + std::string gmName = gm_runner::GetGMName(fTest.fGMFactory); + float result = GMK_Check(imgData, gmName.c_str()); + EXPECT_EQ(result, 0); + } +}; + +struct GMTestFactory : public testing::internal::TestFactoryBase { + GMTestCase fTest; + GMTestFactory(GMTestCase t) : fTest(t) {} + testing::Test* CreateTest() override { return new GMTest(fTest); } +}; + +//////////////////////////////////////////////////////////////////////////////// + +#if !SK_SUPPORT_GPU +struct GrContextOptions {}; +#endif + +struct UnitTest : public testing::Test { + skiatest::TestProc fProc; + UnitTest(skiatest::TestProc proc) : fProc(proc) {} + void TestBody() override { + struct : skiatest::Reporter { + void reportFailed(const skiatest::Failure& failure) override { + SkString desc = failure.toString(); + SK_ABORT(""); + GTEST_NONFATAL_FAILURE_(desc.c_str()); + } + } r; + fProc(&r, GrContextOptions()); + } +}; + +struct UnitTestFactory : testing::internal::TestFactoryBase { + skiatest::TestProc fProc; + UnitTestFactory(skiatest::TestProc proc) : fProc(proc) {} + testing::Test* CreateTest() override { return new UnitTest(fProc); } +}; + +std::vector<const skiatest::Test*> GetUnitTests() { + // Unit Tests + std::vector<const skiatest::Test*> tests; + for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) { + tests.push_back(&r->factory()); + } + struct { + bool operator()(const skiatest::Test* u, const skiatest::Test* v) const { + return strcmp(u->name, v->name) < 0; + } + } less; + std::sort(tests.begin(), tests.end(), less); + return tests; +} + +//////////////////////////////////////////////////////////////////////////////// + +static void reg_test(const char* test, const char* testCase, + testing::internal::TestFactoryBase* fact) { + testing::internal::MakeAndRegisterTestInfo( + test, + testCase, + nullptr, + nullptr, + testing::internal::CodeLocation(__FILE__, __LINE__), + testing::internal::GetTestTypeId(), + testing::Test::SetUpTestCase, + testing::Test::TearDownTestCase, + fact); +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + SkGraphics::Init(); + + // Rendering Tests + gm_runner::SkiaBackend backends[] = { + #ifndef SK_BUILD_FOR_ANDROID + gm_runner::SkiaBackend::kGL, // Used for testing on desktop machines. + #endif + gm_runner::SkiaBackend::kGLES, + gm_runner::SkiaBackend::kVulkan, + }; + std::vector<gm_runner::GMFactory> gms = gm_runner::GetGMFactories(); + for (auto backend : backends) { + const char* backendName = GetBackendName(backend); + std::string test = std::string("SkiaGM_") + backendName; + reg_test(test.c_str(), "BackendSupported", new GMTestFactory(GMTestCase{nullptr, backend})); + + if (!gm_runner::BackendSupported(backend)) { + continue; + } + for (auto gmFactory : gms) { + std::string gmName = gm_runner::GetGMName(gmFactory); + if (!GMK_IsGoodGM(gmName.c_str())) { + continue; + } + #ifdef SK_DEBUG + // The following test asserts on my phone. + // TODO(halcanary): fix this. + if(gmName == std::string("complexclip3_simple") && + backend == gm_runner::SkiaBackend::kGLES) { + continue; + } + #endif + reg_test(test.c_str(), gmName.c_str(), + new GMTestFactory(GMTestCase{gmFactory, backend})); + } + } + + for (const skiatest::Test* test : GetUnitTests()) { + reg_test("Skia_Unit_Tests", test->name, new UnitTestFactory(test->proc)); + } + return RUN_ALL_TESTS(); +} + diff --git a/chromium/third_party/skia/tools/iOSShell.cpp b/chromium/third_party/skia/tools/iOSShell.cpp deleted file mode 100644 index 2e4f0fdb5e3..00000000000 --- a/chromium/third_party/skia/tools/iOSShell.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "iOSShell.h" - -#include "Resources.h" -#include "SkApplication.h" -#include "SkCanvas.h" -#include "SkCommonFlags.h" -#include "SkGraphics.h" -#include "SkWindow.h" -#include "sk_tool_utils.h" - -////////////////////////////////////////////////////////////////////////////// - -static SkView* curr_view(SkWindow* wind) { - SkView::F2BIter iter(wind); - return iter.next(); -} - -ShellWindow::ShellWindow(void* hwnd, int argc, char** argv) - : INHERITED(hwnd) { - SkCommandLineFlags::Parse(argc, argv); -} - -ShellWindow::~ShellWindow() { -} - -/////////////////////////////////////////////////////////////////////////////// - -bool ShellWindow::onDispatchClick(int x, int y, Click::State state, - void* owner, unsigned modi) { - int w = SkScalarRoundToInt(this->width()); - int h = SkScalarRoundToInt(this->height()); - - // check for the resize-box - if (w - x < 16 && h - y < 16) { - return false; // let the OS handle the click - } else { - return this->INHERITED::onDispatchClick(x, y, state, owner, modi); - } -} - -void ShellWindow::onSizeChange() { - this->INHERITED::onSizeChange(); - - SkView::F2BIter iter(this); - SkView* view = iter.next(); - view->setSize(this->width(), this->height()); -} - -DEFINE_bool(dm, false, "run dm"); -DEFINE_bool(nanobench, false, "run nanobench"); - -int nanobench_main(); -int dm_main(); - -IOS_launch_type set_cmd_line_args(int argc, char *argv[], const char* resourceDir) { - SkCommandLineFlags::Parse(argc, argv); - if (FLAGS_nanobench) { - return nanobench_main() ? kError_iOSLaunchType : kTool_iOSLaunchType; - } - if (FLAGS_dm) { - return dm_main() ? kError_iOSLaunchType : kTool_iOSLaunchType; - } - return kError_iOSLaunchType; -} - -// FIXME: this should be in a header -SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv); -SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) { - return new ShellWindow(hwnd, argc, argv); -} - -// FIXME: this should be in a header -void application_init(); -void application_init() { - SkGraphics::Init(); - SkEvent::Init(); -} - -// FIXME: this should be in a header -void application_term(); -void application_term() { - SkEvent::Term(); -} diff --git a/chromium/third_party/skia/tools/iOSShell.h b/chromium/third_party/skia/tools/iOSShell.h deleted file mode 100644 index eaecba5e0b4..00000000000 --- a/chromium/third_party/skia/tools/iOSShell.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2014 Skia - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef iOSShell_DEFINED -#define iOSShell_DEFINED - -#include "SkWindow.h" - -class SkCanvas; -class SkEvent; -class SkViewFactory; - -class ShellWindow : public SkOSWindow { -public: - ShellWindow(void* hwnd, int argc, char** argv); - virtual ~ShellWindow(); - -protected: - void onSizeChange() override; - - virtual bool onDispatchClick(int x, int y, Click::State, void* owner, - unsigned modi) override; - -private: - typedef SkOSWindow INHERITED; -}; - -#endif diff --git a/chromium/third_party/skia/tools/install_dependencies.sh b/chromium/third_party/skia/tools/install_dependencies.sh index fd5c3669d0c..9d1a45fa157 100755 --- a/chromium/third_party/skia/tools/install_dependencies.sh +++ b/chromium/third_party/skia/tools/install_dependencies.sh @@ -31,7 +31,6 @@ if command -v lsb_release > /dev/null ; then libfreetype6-dev libgif-dev libglu1-mesa-dev - libosmesa6-dev libpng12-dev libqt4-dev EOF diff --git a/chromium/third_party/skia/tools/mirror-dev.sh b/chromium/third_party/skia/tools/mirror-dev.sh index eb948f44174..47786de2bcc 100755 --- a/chromium/third_party/skia/tools/mirror-dev.sh +++ b/chromium/third_party/skia/tools/mirror-dev.sh @@ -3,7 +3,7 @@ # found in the LICENSE file. # For each installed *-dev package DEV - for DEV in $(dpkg --list | grep '^ii [^ ]' | cut -d ' ' -f 3 | grep '\-dev$') + for DEV in $(dpkg --list | grep '^ii [^ ]' | cut -d ' ' -f 3 | grep -P '\-dev($|\:amd64$)') do # For each multi-arch *.so SO installed by DEV for DEV_64_SO in $(dpkg -L $DEV | grep '/lib/x86_64-linux-gnu/.*\.so$') diff --git a/chromium/third_party/skia/tools/ok_test.cpp b/chromium/third_party/skia/tools/ok_test.cpp index 7c020d88e1d..232904209d4 100644 --- a/chromium/third_party/skia/tools/ok_test.cpp +++ b/chromium/third_party/skia/tools/ok_test.cpp @@ -10,6 +10,8 @@ #if SK_SUPPORT_GPU #include "GrContextFactory.h" +#else +struct GrContextOptions {}; #endif struct TestStream : Stream { @@ -46,14 +48,8 @@ struct TestStream : Stream { reporter.extended = extended; reporter.verbose_ = verbose; - sk_gpu_test::GrContextFactory* factory = nullptr; - #if SK_SUPPORT_GPU GrContextOptions options; - sk_gpu_test::GrContextFactory a_real_factory(options); - factory = &a_real_factory; - #endif - - test.run(&reporter, factory); + test.run(&reporter, options); return reporter.status; } }; @@ -95,7 +91,7 @@ namespace skiatest { #endif void RunWithGPUTestContexts(GrContextTestFn* test, GrContextTypeFilterFn* contextTypeFilter, - Reporter* reporter, sk_gpu_test::GrContextFactory* factory) { + Reporter* reporter, const GrContextOptions& options) { // TODO(bsalomon) } } diff --git a/chromium/third_party/skia/tools/ok_vias.cpp b/chromium/third_party/skia/tools/ok_vias.cpp index 815b159a01e..7c246644eef 100644 --- a/chromium/third_party/skia/tools/ok_vias.cpp +++ b/chromium/third_party/skia/tools/ok_vias.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "../dm/DMFontMgr.h" #include "ProcStats.h" #include "SkColorFilter.h" #include "SkEventTracingPriv.h" @@ -266,7 +267,6 @@ struct Memory : Dst { }; static Register memory{"memory", "print process maximum memory usage", Memory::Create}; -static SkOnce init_tracing_once; struct Trace : Dst { std::unique_ptr<Dst> target; std::string trace_mode; @@ -279,7 +279,8 @@ struct Trace : Dst { } Status draw(Src* src) override { - init_tracing_once([&] { initializeEventTracingForTools(trace_mode.c_str()); }); + static SkOnce once; + once([&] { initializeEventTracingForTools(trace_mode.c_str()); }); return target->draw(src); } @@ -287,6 +288,31 @@ struct Trace : Dst { return target->image(); } }; -static Register trace {"trace", - "enable tracing in mode=atrace, mode=debugf, or mode=trace.json", - Trace::Create}; +static Register trace{"trace", + "enable tracing in mode=atrace, mode=debugf, or mode=trace.json", + Trace::Create}; + +extern sk_sp<SkFontMgr> (*gSkFontMgr_DefaultFactory)(); + +struct PortableFonts : Dst { + std::unique_ptr<Dst> target; + + static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) { + PortableFonts via; + via.target = std::move(dst); + return move_unique(via); + } + + Status draw(Src* src) override { + static SkOnce once; + once([]{ gSkFontMgr_DefaultFactory = &DM::MakeFontMgr; }); + return target->draw(src); + } + + sk_sp<SkImage> image() override { + return target->image(); + } +}; +static Register portable_fonts{"portable_fonts", + "use DM::FontMgr to make fonts more portable", + PortableFonts::Create}; diff --git a/chromium/third_party/skia/tools/rebaseline/toggle_legacy_flag.py b/chromium/third_party/skia/tools/rebaseline/toggle_legacy_flag.py new file mode 100644 index 00000000000..80edeb2bc46 --- /dev/null +++ b/chromium/third_party/skia/tools/rebaseline/toggle_legacy_flag.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# Copyright (c) 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +README = """ +Automatically add or remove a specific legacy flag to multiple Skia client repos. + +This would only work on Google desktop. + +Example usage: + $ python toggle_legacy_flag.py SK_SUPPORT_LEGACY_SOMETHING \\ + -a /data/android -c ~/chromium/src -g legacyflag + +If you only need to add the flag to one repo, for example, Android, please give +only -a (--android-dir) argument: + $ python toggle_legacy_flag.py SK_SUPPORT_LEGACY_SOMETHING -a /data/android + +""" + +import os, sys +import argparse +import subprocess +import getpass +from random import randint + + +ANDROID_TOOLS_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + 'android') + + +def toggle_android(args): + sys.path.append(ANDROID_TOOLS_DIR) + import upload_to_android + + modifier = upload_to_android.AndroidLegacyFlagModifier(args.flag) + upload_to_android.upload_to_android(args.android_dir, modifier) + + +def toggle_chromium(args): + os.chdir(args.chromium_dir) + + branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) + branch = branch.strip() + + EXPECTED_STASH_OUT = "No local changes to save" + stash_output = subprocess.check_output(['git', 'stash']).strip() + + if branch != "master" or stash_output != EXPECTED_STASH_OUT: + print ("Please checkout a clean master branch at your chromium repo (%s) " + "before running this script") % args.chromium_dir + if stash_output != EXPECTED_STASH_OUT: + subprocess.check_call(['git', 'stash', 'pop']) + exit(1) + + # Update the repository to avoid conflicts + subprocess.check_call(['git', 'pull']) + subprocess.check_call(['gclient', 'sync']); + + # Use random number to avoid branch name collision. + # We'll delete the branch in the end. + random = randint(1, 10000) + subprocess.check_call(['git', 'checkout', '-b', 'legacyflag_%d' % random]) + + try: + config_file = os.path.join('skia', 'config', 'SkUserConfig.h') + with open(config_file) as f: + lines = f.readlines() + + flag_line = "#define %s\n" % args.flag + if flag_line in lines: + index = lines.index(flag_line) + del lines[index-1 : index +2] + verb = "Remove" + else: + separator = ( + "/////////////////////////" + " Imported from BUILD.gn and skia_common.gypi\n") + content = ("#ifndef {0}\n" + "#define {0}\n" + "#endif\n\n").format(args.flag) + lines.insert(lines.index(separator), content) + verb = "Add" + + with open(config_file, 'w') as f: + for line in lines: + f.write(line) + + message = "%s %s" % (verb, args.flag) + + subprocess.check_call('git commit -a -m "%s"' % message, shell=True) + subprocess.check_call('git cl upload -m "%s" -f' % message, + shell=True) + finally: + subprocess.check_call(['git', 'checkout', 'master']) + subprocess.check_call(['git', 'branch', '-D', 'legacyflag_%d' % random]) + + +def toggle_google3(args): + G3_SCRIPT_DIR = os.path.expanduser("~/skia-g3/scripts") + if not os.path.isdir(G3_SCRIPT_DIR): + print ("Google3 directory unavailable.\n" + "Please see " + "https://sites.google.com/a/google.com/skia/rebaseline#g3_flag " + "for Google3 setup.") + exit(1) + sys.path.append(G3_SCRIPT_DIR) + import citc_flag + + citc_flag.toggle_google3(args.google3, args.flag) + + +def main(): + if len(sys.argv) <= 1 or sys.argv[1] == '-h' or sys.argv[1] == '--help': + print README + + parser = argparse.ArgumentParser() + parser.add_argument( + '--android-dir', '-a', required=False, + help='Directory where an Android checkout will be created (if it does ' + 'not already exist). Note: ~1GB space will be used.') + parser.add_argument( + '--chromium-dir', '-c', required=False, + help='Directory of an EXISTING Chromium checkout (e.g., ~/chromium/src)') + parser.add_argument( + '--google3', '-g', required=False, + help='Google3 workspace to be created (if it does not already exist).') + parser.add_argument('flag', type=str, help='legacy flag name') + + args = parser.parse_args() + + if not args.android_dir and not args.chromium_dir and not args.google3: + print """ +Nothing to do. Please give me at least one of these three arguments: + -a (--android-dir) + -c (--chromium-dir) + -g (--google3) +""" + exit(1) + + end_message = "CLs generated. Now go review and land them:\n" + if args.chromium_dir: + args.chromium_dir = os.path.expanduser(args.chromium_dir) + toggle_chromium(args) + end_message += " * https://chromium-review.googlesource.com\n" + if args.google3: + toggle_google3(args) + end_message += " * http://goto.google.com/cl\n" + if args.android_dir: + args.android_dir = os.path.expanduser(args.android_dir) + toggle_android(args) + end_message += " * http://goto.google.com/androidcl\n" + + print end_message + + +if __name__ == '__main__': + main() diff --git a/chromium/third_party/skia/tools/viewer/sk_app/Application.h b/chromium/third_party/skia/tools/sk_app/Application.h index df9a20d3583..df9a20d3583 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/Application.h +++ b/chromium/third_party/skia/tools/sk_app/Application.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/CommandSet.cpp b/chromium/third_party/skia/tools/sk_app/CommandSet.cpp index d0154d6e617..d0154d6e617 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/CommandSet.cpp +++ b/chromium/third_party/skia/tools/sk_app/CommandSet.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/CommandSet.h b/chromium/third_party/skia/tools/sk_app/CommandSet.h index 0784a3875e8..0784a3875e8 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/CommandSet.h +++ b/chromium/third_party/skia/tools/sk_app/CommandSet.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/DisplayParams.h b/chromium/third_party/skia/tools/sk_app/DisplayParams.h index 959735e8ff2..959735e8ff2 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/DisplayParams.h +++ b/chromium/third_party/skia/tools/sk_app/DisplayParams.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/GLWindowContext.cpp b/chromium/third_party/skia/tools/sk_app/GLWindowContext.cpp index bdfa12a8eca..bdfa12a8eca 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/GLWindowContext.cpp +++ b/chromium/third_party/skia/tools/sk_app/GLWindowContext.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/GLWindowContext.h b/chromium/third_party/skia/tools/sk_app/GLWindowContext.h index 44810c93d23..44810c93d23 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/GLWindowContext.h +++ b/chromium/third_party/skia/tools/sk_app/GLWindowContext.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/RasterWindowContext.h b/chromium/third_party/skia/tools/sk_app/RasterWindowContext.h index 75bde03ad71..75bde03ad71 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/RasterWindowContext.h +++ b/chromium/third_party/skia/tools/sk_app/RasterWindowContext.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/VulkanWindowContext.cpp b/chromium/third_party/skia/tools/sk_app/VulkanWindowContext.cpp index a9124d5e413..5e0f12412e3 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/VulkanWindowContext.cpp +++ b/chromium/third_party/skia/tools/sk_app/VulkanWindowContext.cpp @@ -22,14 +22,16 @@ #undef CreateSemaphore #endif -#define GET_PROC(F) f ## F = (PFN_vk ## F) vkGetInstanceProcAddr(instance, "vk" #F) -#define GET_DEV_PROC(F) f ## F = (PFN_vk ## F) vkGetDeviceProcAddr(device, "vk" #F) +#define GET_PROC(F) f ## F = (PFN_vk ## F) fGetInstanceProcAddr(instance, "vk" #F) +#define GET_DEV_PROC(F) f ## F = (PFN_vk ## F) fGetDeviceProcAddr(device, "vk" #F) namespace sk_app { VulkanWindowContext::VulkanWindowContext(const DisplayParams& params, CreateVkSurfaceFn createVkSurface, - CanPresentFn canPresent) + CanPresentFn canPresent, + PFN_vkGetInstanceProcAddr instProc, + PFN_vkGetDeviceProcAddr devProc) : WindowContext(params) , fCreateVkSurfaceFn(createVkSurface) , fCanPresentFn(canPresent) @@ -40,12 +42,14 @@ VulkanWindowContext::VulkanWindowContext(const DisplayParams& params, , fSurfaces(nullptr) , fCommandPool(VK_NULL_HANDLE) , fBackbuffers(nullptr) { + fGetInstanceProcAddr = instProc; + fGetDeviceProcAddr = devProc; this->initializeContext(); } void VulkanWindowContext::initializeContext() { // any config code here (particularly for msaa)? - fBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr, + fBackendContext.reset(GrVkBackendContext::Create(fGetInstanceProcAddr, fGetDeviceProcAddr, &fPresentQueueIndex, fCanPresentFn)); if (!(fBackendContext->fExtensions & kKHR_surface_GrVkExtensionFlag) || @@ -66,6 +70,7 @@ void VulkanWindowContext::initializeContext() { GET_DEV_PROC(GetSwapchainImagesKHR); GET_DEV_PROC(AcquireNextImageKHR); GET_DEV_PROC(QueuePresentKHR); + GET_DEV_PROC(GetDeviceQueue); fContext = GrContext::MakeVulkan(fBackendContext.get(), fDisplayParams.fGrContextOptions); @@ -90,7 +95,7 @@ void VulkanWindowContext::initializeContext() { } // create presentQueue - vkGetDeviceQueue(fBackendContext->fDevice, fPresentQueueIndex, 0, &fPresentQueue); + fGetDeviceQueue(fBackendContext->fDevice, fPresentQueueIndex, 0, &fPresentQueue); } bool VulkanWindowContext::createSwapchain(int width, int height, diff --git a/chromium/third_party/skia/tools/viewer/sk_app/VulkanWindowContext.h b/chromium/third_party/skia/tools/sk_app/VulkanWindowContext.h index 81e5f3d2d5b..d02b11428e1 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/VulkanWindowContext.h +++ b/chromium/third_party/skia/tools/sk_app/VulkanWindowContext.h @@ -47,7 +47,8 @@ public: /** Platform specific function that determines whether presentation will succeed. */ using CanPresentFn = GrVkBackendContext::CanPresentFn; - VulkanWindowContext(const DisplayParams&, CreateVkSurfaceFn, CanPresentFn); + VulkanWindowContext(const DisplayParams&, CreateVkSurfaceFn, CanPresentFn, + PFN_vkGetInstanceProcAddr, PFN_vkGetDeviceProcAddr); private: void initializeContext(); @@ -82,6 +83,10 @@ private: CreateVkSurfaceFn fCreateVkSurfaceFn; CanPresentFn fCanPresentFn; + // Vulkan GetProcAddr functions + VkPtr<PFN_vkGetInstanceProcAddr> fGetInstanceProcAddr; + VkPtr<PFN_vkGetDeviceProcAddr> fGetDeviceProcAddr; + // WSI interface functions VkPtr<PFN_vkDestroySurfaceKHR> fDestroySurfaceKHR; VkPtr<PFN_vkGetPhysicalDeviceSurfaceSupportKHR> fGetPhysicalDeviceSurfaceSupportKHR; @@ -94,7 +99,7 @@ private: VkPtr<PFN_vkGetSwapchainImagesKHR> fGetSwapchainImagesKHR; VkPtr<PFN_vkAcquireNextImageKHR> fAcquireNextImageKHR; VkPtr<PFN_vkQueuePresentKHR> fQueuePresentKHR; - VkPtr<PFN_vkCreateSharedSwapchainsKHR> fCreateSharedSwapchainsKHR; + VkPtr<PFN_vkGetDeviceQueue> fGetDeviceQueue; VkSurfaceKHR fSurface; VkSwapchainKHR fSwapchain; diff --git a/chromium/third_party/skia/tools/viewer/sk_app/Window.cpp b/chromium/third_party/skia/tools/sk_app/Window.cpp index d7904fd8a77..d7904fd8a77 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/Window.cpp +++ b/chromium/third_party/skia/tools/sk_app/Window.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/Window.h b/chromium/third_party/skia/tools/sk_app/Window.h index 73578605126..4d407809645 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/Window.h +++ b/chromium/third_party/skia/tools/sk_app/Window.h @@ -12,7 +12,6 @@ #include "SkRect.h" #include "SkTouchGesture.h" #include "SkTypes.h" -#include "SkJSONCPP.h" class GrContext; class SkCanvas; @@ -30,7 +29,9 @@ public: virtual void setTitle(const char*) = 0; virtual void show() = 0; - virtual void setUIState(const Json::Value& state) {} // do nothing in default + + // JSON-formatted UI state for Android. Do nothing by default + virtual void setUIState(const char*) {} // Shedules an invalidation event for window if one is not currently pending. // Make sure that either onPaint or markInvalReceived is called when the client window consumes diff --git a/chromium/third_party/skia/tools/viewer/sk_app/WindowContext.h b/chromium/third_party/skia/tools/sk_app/WindowContext.h index cd4c357e207..cd4c357e207 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/WindowContext.h +++ b/chromium/third_party/skia/tools/sk_app/WindowContext.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/android/GLWindowContext_android.cpp b/chromium/third_party/skia/tools/sk_app/android/GLWindowContext_android.cpp index 944865909b5..944865909b5 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/android/GLWindowContext_android.cpp +++ b/chromium/third_party/skia/tools/sk_app/android/GLWindowContext_android.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/android/RasterWindowContext_android.cpp b/chromium/third_party/skia/tools/sk_app/android/RasterWindowContext_android.cpp index 101e51ef425..101e51ef425 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/android/RasterWindowContext_android.cpp +++ b/chromium/third_party/skia/tools/sk_app/android/RasterWindowContext_android.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/android/VulkanWindowContext_android.cpp b/chromium/third_party/skia/tools/sk_app/android/VulkanWindowContext_android.cpp index b50e152291b..a7d8aa7ea1a 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/android/VulkanWindowContext_android.cpp +++ b/chromium/third_party/skia/tools/sk_app/android/VulkanWindowContext_android.cpp @@ -9,15 +9,22 @@ #include "WindowContextFactory_android.h" #include "../VulkanWindowContext.h" +#include "vk/VkTestUtils.h" + namespace sk_app { namespace window_context_factory { WindowContext* NewVulkanForAndroid(ANativeWindow* window, const DisplayParams& params) { - auto createVkSurface = [window] (VkInstance instance) -> VkSurfaceKHR { + PFN_vkGetInstanceProcAddr instProc; + PFN_vkGetDeviceProcAddr devProc; + if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) { + return nullptr; + } + + auto createVkSurface = [window, instProc] (VkInstance instance) -> VkSurfaceKHR { PFN_vkCreateAndroidSurfaceKHR createAndroidSurfaceKHR = - (PFN_vkCreateAndroidSurfaceKHR)vkGetInstanceProcAddr(instance, - "vkCreateAndroidSurfaceKHR"); + (PFN_vkCreateAndroidSurfaceKHR) instProc(instance, "vkCreateAndroidSurfaceKHR"); if (!window) { return VK_NULL_HANDLE; @@ -38,7 +45,8 @@ WindowContext* NewVulkanForAndroid(ANativeWindow* window, const DisplayParams& p auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; }; - WindowContext* ctx = new VulkanWindowContext(params, createVkSurface, canPresent); + WindowContext* ctx = new VulkanWindowContext(params, createVkSurface, canPresent, + instProc, devProc); if (!ctx->isValid()) { delete ctx; return nullptr; diff --git a/chromium/third_party/skia/tools/viewer/sk_app/android/WindowContextFactory_android.h b/chromium/third_party/skia/tools/sk_app/android/WindowContextFactory_android.h index 00198da8d3b..00198da8d3b 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/android/WindowContextFactory_android.h +++ b/chromium/third_party/skia/tools/sk_app/android/WindowContextFactory_android.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/android/Window_android.cpp b/chromium/third_party/skia/tools/sk_app/android/Window_android.cpp index 16ad430ac28..96acfc6564c 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/android/Window_android.cpp +++ b/chromium/third_party/skia/tools/sk_app/android/Window_android.cpp @@ -31,7 +31,7 @@ void Window_android::setTitle(const char* title) { fSkiaAndroidApp->setTitle(title); } -void Window_android::setUIState(const Json::Value& state) { +void Window_android::setUIState(const char* state) { fSkiaAndroidApp->setUIState(state); } diff --git a/chromium/third_party/skia/tools/viewer/sk_app/android/Window_android.h b/chromium/third_party/skia/tools/sk_app/android/Window_android.h index 791801140e7..9e28a8075b6 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/android/Window_android.h +++ b/chromium/third_party/skia/tools/sk_app/android/Window_android.h @@ -27,7 +27,7 @@ public: bool attach(BackendType) override; void onInval() override; - void setUIState(const Json::Value& state) override; + void setUIState(const char* state) override; void paintIfNeeded(); diff --git a/chromium/third_party/skia/tools/viewer/sk_app/android/main_android.cpp b/chromium/third_party/skia/tools/sk_app/android/main_android.cpp index cb8db6c3b49..cb8db6c3b49 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/android/main_android.cpp +++ b/chromium/third_party/skia/tools/sk_app/android/main_android.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/android/surface_glue_android.cpp b/chromium/third_party/skia/tools/sk_app/android/surface_glue_android.cpp index 4fb6c3d9acc..9c734247dbb 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/android/surface_glue_android.cpp +++ b/chromium/third_party/skia/tools/sk_app/android/surface_glue_android.cpp @@ -71,8 +71,8 @@ void SkiaAndroidApp::setTitle(const char* title) const { fPThreadEnv->DeleteLocalRef(titleString); } -void SkiaAndroidApp::setUIState(const Json::Value& state) const { - jstring jstr = fPThreadEnv->NewStringUTF(state.toStyledString().c_str()); +void SkiaAndroidApp::setUIState(const char* state) const { + jstring jstr = fPThreadEnv->NewStringUTF(state); fPThreadEnv->CallVoidMethod(fAndroidApp, fSetStateMethodID, jstr); fPThreadEnv->DeleteLocalRef(jstr); } diff --git a/chromium/third_party/skia/tools/viewer/sk_app/android/surface_glue_android.h b/chromium/third_party/skia/tools/sk_app/android/surface_glue_android.h index 3bbf3af9c3c..1dd1f2854ab 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/android/surface_glue_android.h +++ b/chromium/third_party/skia/tools/sk_app/android/surface_glue_android.h @@ -57,7 +57,7 @@ struct SkiaAndroidApp { // These must be called in SkiaAndroidApp's own pthread because the JNIEnv is thread sensitive void setTitle(const char* title) const; - void setUIState(const Json::Value& state) const; + void setUIState(const char* state) const; private: pthread_t fThread; diff --git a/chromium/third_party/skia/tools/viewer/sk_app/ios/GLWindowContext_ios.cpp b/chromium/third_party/skia/tools/sk_app/ios/GLWindowContext_ios.cpp index 30bacf5cea3..30bacf5cea3 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/ios/GLWindowContext_ios.cpp +++ b/chromium/third_party/skia/tools/sk_app/ios/GLWindowContext_ios.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/ios/RasterWindowContext_ios.cpp b/chromium/third_party/skia/tools/sk_app/ios/RasterWindowContext_ios.cpp index 08b6560510e..08b6560510e 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/ios/RasterWindowContext_ios.cpp +++ b/chromium/third_party/skia/tools/sk_app/ios/RasterWindowContext_ios.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/ios/WindowContextFactory_ios.h b/chromium/third_party/skia/tools/sk_app/ios/WindowContextFactory_ios.h index 09999c4c83d..09999c4c83d 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/ios/WindowContextFactory_ios.h +++ b/chromium/third_party/skia/tools/sk_app/ios/WindowContextFactory_ios.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/ios/Window_ios.cpp b/chromium/third_party/skia/tools/sk_app/ios/Window_ios.cpp index c1bdeae5fc7..c1bdeae5fc7 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/ios/Window_ios.cpp +++ b/chromium/third_party/skia/tools/sk_app/ios/Window_ios.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/ios/Window_ios.h b/chromium/third_party/skia/tools/sk_app/ios/Window_ios.h index 667fa74e825..667fa74e825 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/ios/Window_ios.h +++ b/chromium/third_party/skia/tools/sk_app/ios/Window_ios.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/ios/main_ios.cpp b/chromium/third_party/skia/tools/sk_app/ios/main_ios.cpp index fe82c46485e..fe82c46485e 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/ios/main_ios.cpp +++ b/chromium/third_party/skia/tools/sk_app/ios/main_ios.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/mac/GLWindowContext_mac.cpp b/chromium/third_party/skia/tools/sk_app/mac/GLWindowContext_mac.cpp index 7f09d545220..7f09d545220 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/mac/GLWindowContext_mac.cpp +++ b/chromium/third_party/skia/tools/sk_app/mac/GLWindowContext_mac.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/mac/RasterWindowContext_mac.cpp b/chromium/third_party/skia/tools/sk_app/mac/RasterWindowContext_mac.cpp index 409c49f218b..409c49f218b 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/mac/RasterWindowContext_mac.cpp +++ b/chromium/third_party/skia/tools/sk_app/mac/RasterWindowContext_mac.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/mac/WindowContextFactory_mac.h b/chromium/third_party/skia/tools/sk_app/mac/WindowContextFactory_mac.h index 3adc68bbc23..3adc68bbc23 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/mac/WindowContextFactory_mac.h +++ b/chromium/third_party/skia/tools/sk_app/mac/WindowContextFactory_mac.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/mac/Window_mac.cpp b/chromium/third_party/skia/tools/sk_app/mac/Window_mac.cpp index 8de5b104500..8de5b104500 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/mac/Window_mac.cpp +++ b/chromium/third_party/skia/tools/sk_app/mac/Window_mac.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/mac/Window_mac.h b/chromium/third_party/skia/tools/sk_app/mac/Window_mac.h index aa5c8df6962..aa5c8df6962 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/mac/Window_mac.h +++ b/chromium/third_party/skia/tools/sk_app/mac/Window_mac.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/mac/main_mac.cpp b/chromium/third_party/skia/tools/sk_app/mac/main_mac.cpp index 6dcf5b93f76..6dcf5b93f76 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/mac/main_mac.cpp +++ b/chromium/third_party/skia/tools/sk_app/mac/main_mac.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/unix/GLWindowContext_unix.cpp b/chromium/third_party/skia/tools/sk_app/unix/GLWindowContext_unix.cpp index d7a43878808..d7a43878808 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/unix/GLWindowContext_unix.cpp +++ b/chromium/third_party/skia/tools/sk_app/unix/GLWindowContext_unix.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/unix/RasterWindowContext_unix.cpp b/chromium/third_party/skia/tools/sk_app/unix/RasterWindowContext_unix.cpp index 6bfa6fd0be2..6bfa6fd0be2 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/unix/RasterWindowContext_unix.cpp +++ b/chromium/third_party/skia/tools/sk_app/unix/RasterWindowContext_unix.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/unix/VulkanWindowContext_unix.cpp b/chromium/third_party/skia/tools/sk_app/unix/VulkanWindowContext_unix.cpp index b94e8edc209..b2f1ffc7630 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/unix/VulkanWindowContext_unix.cpp +++ b/chromium/third_party/skia/tools/sk_app/unix/VulkanWindowContext_unix.cpp @@ -10,6 +10,8 @@ #include "vk/GrVkInterface.h" #include "vk/GrVkUtil.h" +#include "vk/VkTestUtils.h" + #include <X11/Xlib-xcb.h> #include "WindowContextFactory_unix.h" @@ -20,12 +22,17 @@ namespace sk_app { namespace window_context_factory { WindowContext* NewVulkanForXlib(const XlibWindowInfo& info, const DisplayParams& displayParams) { - auto createVkSurface = [&info](VkInstance instance) -> VkSurfaceKHR { + PFN_vkGetInstanceProcAddr instProc; + PFN_vkGetDeviceProcAddr devProc; + if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) { + return nullptr; + } + + auto createVkSurface = [&info, instProc](VkInstance instance) -> VkSurfaceKHR { static PFN_vkCreateXcbSurfaceKHR createXcbSurfaceKHR = nullptr; if (!createXcbSurfaceKHR) { createXcbSurfaceKHR = - (PFN_vkCreateXcbSurfaceKHR) vkGetInstanceProcAddr(instance, - "vkCreateXcbSurfaceKHR"); + (PFN_vkCreateXcbSurfaceKHR) instProc(instance, "vkCreateXcbSurfaceKHR"); } VkSurfaceKHR surface; @@ -46,15 +53,14 @@ WindowContext* NewVulkanForXlib(const XlibWindowInfo& info, const DisplayParams& return surface; }; - auto canPresent = [&info](VkInstance instance, VkPhysicalDevice physDev, + auto canPresent = [&info, instProc](VkInstance instance, VkPhysicalDevice physDev, uint32_t queueFamilyIndex) { static PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR getPhysicalDeviceXcbPresentationSupportKHR = nullptr; if (!getPhysicalDeviceXcbPresentationSupportKHR) { getPhysicalDeviceXcbPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) - vkGetInstanceProcAddr(instance, - "vkGetPhysicalDeviceXcbPresentationSupportKHR"); + instProc(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); } @@ -66,7 +72,8 @@ WindowContext* NewVulkanForXlib(const XlibWindowInfo& info, const DisplayParams& visualID); return (VK_FALSE != check); }; - WindowContext* context = new VulkanWindowContext(displayParams, createVkSurface, canPresent); + WindowContext* context = new VulkanWindowContext(displayParams, createVkSurface, canPresent, + instProc, devProc); if (!context->isValid()) { delete context; return nullptr; diff --git a/chromium/third_party/skia/tools/viewer/sk_app/unix/WindowContextFactory_unix.h b/chromium/third_party/skia/tools/sk_app/unix/WindowContextFactory_unix.h index e6d033b4cd6..e6d033b4cd6 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/unix/WindowContextFactory_unix.h +++ b/chromium/third_party/skia/tools/sk_app/unix/WindowContextFactory_unix.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/unix/Window_unix.cpp b/chromium/third_party/skia/tools/sk_app/unix/Window_unix.cpp index f5ca5ee0736..f5ca5ee0736 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/unix/Window_unix.cpp +++ b/chromium/third_party/skia/tools/sk_app/unix/Window_unix.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/unix/Window_unix.h b/chromium/third_party/skia/tools/sk_app/unix/Window_unix.h index b59f502eb95..b59f502eb95 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/unix/Window_unix.h +++ b/chromium/third_party/skia/tools/sk_app/unix/Window_unix.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/unix/main_unix.cpp b/chromium/third_party/skia/tools/sk_app/unix/main_unix.cpp index 4d9a64d6b62..4d9a64d6b62 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/unix/main_unix.cpp +++ b/chromium/third_party/skia/tools/sk_app/unix/main_unix.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/win/ANGLEWindowContext_win.cpp b/chromium/third_party/skia/tools/sk_app/win/ANGLEWindowContext_win.cpp index bfdff5c6f4f..bfdff5c6f4f 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/win/ANGLEWindowContext_win.cpp +++ b/chromium/third_party/skia/tools/sk_app/win/ANGLEWindowContext_win.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/win/GLWindowContext_win.cpp b/chromium/third_party/skia/tools/sk_app/win/GLWindowContext_win.cpp index 17a6b329623..17a6b329623 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/win/GLWindowContext_win.cpp +++ b/chromium/third_party/skia/tools/sk_app/win/GLWindowContext_win.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/win/RasterWindowContext_win.cpp b/chromium/third_party/skia/tools/sk_app/win/RasterWindowContext_win.cpp index 85bb65e674d..85bb65e674d 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/win/RasterWindowContext_win.cpp +++ b/chromium/third_party/skia/tools/sk_app/win/RasterWindowContext_win.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/win/VulkanWindowContext_win.cpp b/chromium/third_party/skia/tools/sk_app/win/VulkanWindowContext_win.cpp index dbb59c10f2e..16c527cba00 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/win/VulkanWindowContext_win.cpp +++ b/chromium/third_party/skia/tools/sk_app/win/VulkanWindowContext_win.cpp @@ -15,15 +15,23 @@ #include "vk/GrVkInterface.h" #include "vk/GrVkUtil.h" +#include "vk/VkTestUtils.h" + namespace sk_app { namespace window_context_factory { WindowContext* NewVulkanForWin(HWND hwnd, const DisplayParams& params) { - auto createVkSurface = [hwnd] (VkInstance instance) -> VkSurfaceKHR { + PFN_vkGetInstanceProcAddr instProc; + PFN_vkGetDeviceProcAddr devProc; + if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) { + return nullptr; + } + + auto createVkSurface = [hwnd, instProc] (VkInstance instance) -> VkSurfaceKHR { static PFN_vkCreateWin32SurfaceKHR createWin32SurfaceKHR = nullptr; if (!createWin32SurfaceKHR) { createWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) - vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"); + instProc(instance, "vkCreateWin32SurfaceKHR"); } HINSTANCE hinstance = GetModuleHandle(0); VkSurfaceKHR surface; @@ -44,22 +52,22 @@ WindowContext* NewVulkanForWin(HWND hwnd, const DisplayParams& params) { return surface; }; - auto canPresent = [] (VkInstance instance, VkPhysicalDevice physDev, - uint32_t queueFamilyIndex) { + auto canPresent = [instProc] (VkInstance instance, VkPhysicalDevice physDev, + uint32_t queueFamilyIndex) { static PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR getPhysicalDeviceWin32PresentationSupportKHR = nullptr; if (!getPhysicalDeviceWin32PresentationSupportKHR) { getPhysicalDeviceWin32PresentationSupportKHR = (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) - vkGetInstanceProcAddr(instance, - "vkGetPhysicalDeviceWin32PresentationSupportKHR"); + instProc(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); } VkBool32 check = getPhysicalDeviceWin32PresentationSupportKHR(physDev, queueFamilyIndex); return (VK_FALSE != check); }; - WindowContext* ctx = new VulkanWindowContext(params, createVkSurface, canPresent); + WindowContext* ctx = new VulkanWindowContext(params, createVkSurface, canPresent, + instProc, devProc); if (!ctx->isValid()) { delete ctx; return nullptr; diff --git a/chromium/third_party/skia/tools/viewer/sk_app/win/WindowContextFactory_win.h b/chromium/third_party/skia/tools/sk_app/win/WindowContextFactory_win.h index 959b529f494..959b529f494 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/win/WindowContextFactory_win.h +++ b/chromium/third_party/skia/tools/sk_app/win/WindowContextFactory_win.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/win/Window_win.cpp b/chromium/third_party/skia/tools/sk_app/win/Window_win.cpp index 10db0ec675c..10db0ec675c 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/win/Window_win.cpp +++ b/chromium/third_party/skia/tools/sk_app/win/Window_win.cpp diff --git a/chromium/third_party/skia/tools/viewer/sk_app/win/Window_win.h b/chromium/third_party/skia/tools/sk_app/win/Window_win.h index 139ab874c63..139ab874c63 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/win/Window_win.h +++ b/chromium/third_party/skia/tools/sk_app/win/Window_win.h diff --git a/chromium/third_party/skia/tools/viewer/sk_app/win/main_win.cpp b/chromium/third_party/skia/tools/sk_app/win/main_win.cpp index 4800258973f..4800258973f 100644 --- a/chromium/third_party/skia/tools/viewer/sk_app/win/main_win.cpp +++ b/chromium/third_party/skia/tools/sk_app/win/main_win.cpp diff --git a/chromium/third_party/skia/tools/sk_tool_utils.cpp b/chromium/third_party/skia/tools/sk_tool_utils.cpp index f1968742d4e..5321513a171 100644 --- a/chromium/third_party/skia/tools/sk_tool_utils.cpp +++ b/chromium/third_party/skia/tools/sk_tool_utils.cpp @@ -6,7 +6,6 @@ */ #include "sk_tool_utils.h" -#include "sk_tool_utils_flags.h" #include "Resources.h" #include "SkBitmap.h" @@ -21,146 +20,77 @@ #include "SkTestScalerContext.h" #include "SkTextBlob.h" -DEFINE_bool(portableFonts, false, "Use portable fonts"); - namespace sk_tool_utils { -/* these are the default fonts chosen by Chrome for serif, sans-serif, and monospace */ -static const char* gStandardFontNames[][3] = { - { "Times", "Helvetica", "Courier" }, // Mac - { "Times New Roman", "Helvetica", "Courier" }, // iOS - { "Times New Roman", "Arial", "Courier New" }, // Win - { "Times New Roman", "Arial", "Monospace" }, // Ubuntu - { "serif", "sans-serif", "monospace" }, // Android - { "Tinos", "Arimo", "Cousine" } // ChromeOS -}; - -const char* platform_font_name(const char* name) { - SkString platform = major_platform_os_name(); - int index; - if (!strcmp(name, "serif")) { - index = 0; - } else if (!strcmp(name, "san-serif")) { - index = 1; - } else if (!strcmp(name, "monospace")) { - index = 2; - } else { - return name; - } - if (platform.equals("Mac")) { - return gStandardFontNames[0][index]; - } - if (platform.equals("iOS")) { - return gStandardFontNames[1][index]; - } - if (platform.equals("Win")) { - return gStandardFontNames[2][index]; - } - if (platform.equals("Ubuntu") || platform.equals("Debian")) { - return gStandardFontNames[3][index]; - } - if (platform.equals("Android")) { - return gStandardFontNames[4][index]; - } - if (platform.equals("ChromeOS")) { - return gStandardFontNames[5][index]; - } - return name; -} - -const char* platform_os_emoji() { - const char* osName = platform_os_name(); - if (!strcmp(osName, "Android") || !strcmp(osName, "Ubuntu") || !strcmp(osName, "Debian")) { - return "CBDT"; - } - if (!strncmp(osName, "Mac", 3) || !strncmp(osName, "iOS", 3)) { - return "SBIX"; - } - if (!strncmp(osName, "Win", 3)) { - return "COLR"; +static const char* platform_os_name() { + for (int index = 0; index < FLAGS_key.count(); index += 2) { + if (!strcmp("os", FLAGS_key[index])) { + return FLAGS_key[index + 1]; + } } return ""; } sk_sp<SkTypeface> emoji_typeface() { - if (!strcmp(sk_tool_utils::platform_os_emoji(), "CBDT")) { - return MakeResourceAsTypeface("/fonts/Funkster.ttf"); +#if defined(SK_BUILD_FOR_WIN) + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + const char *colorEmojiFontName = "Segoe UI Emoji"; + sk_sp<SkTypeface> typeface(fm->matchFamilyStyle(colorEmojiFontName, SkFontStyle())); + if (typeface) { + return typeface; } - if (!strcmp(sk_tool_utils::platform_os_emoji(), "SBIX")) { - return SkTypeface::MakeFromName("Apple Color Emoji", SkFontStyle()); + sk_sp<SkTypeface> fallback(fm->matchFamilyStyleCharacter( + colorEmojiFontName, SkFontStyle(), nullptr /* bcp47 */, 0 /* bcp47Count */, + 0x1f4b0 /* character: 💰 */)); + if (fallback) { + return fallback; } - if (!strcmp(sk_tool_utils::platform_os_emoji(), "COLR")) { - sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); - const char *colorEmojiFontName = "Segoe UI Emoji"; - sk_sp<SkTypeface> typeface(fm->matchFamilyStyle(colorEmojiFontName, SkFontStyle())); - if (typeface) { - return typeface; - } - sk_sp<SkTypeface> fallback(fm->matchFamilyStyleCharacter( - colorEmojiFontName, SkFontStyle(), nullptr /* bcp47 */, 0 /* bcp47Count */, - 0x1f4b0 /* character: 💰 */)); - if (fallback) { - return fallback; - } - // If we don't have Segoe UI Emoji and can't find a fallback, try Segoe UI Symbol. - // Windows 7 does not have Segoe UI Emoji; Segoe UI Symbol has the (non - color) emoji. - return SkTypeface::MakeFromName("Segoe UI Symbol", SkFontStyle()); - } - return nullptr; + // If we don't have Segoe UI Emoji and can't find a fallback, try Segoe UI Symbol. + // Windows 7 does not have Segoe UI Emoji; Segoe UI Symbol has the (non - color) emoji. + return SkTypeface::MakeFromName("Segoe UI Symbol", SkFontStyle()); + +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + return SkTypeface::MakeFromName("Apple Color Emoji", SkFontStyle()); + +#else + return MakeResourceAsTypeface("/fonts/Funkster.ttf"); + +#endif } const char* emoji_sample_text() { - if (!strcmp(sk_tool_utils::platform_os_emoji(), "CBDT")) { - return "Hamburgefons"; - } - if (!strcmp(sk_tool_utils::platform_os_emoji(), "SBIX") || - !strcmp(sk_tool_utils::platform_os_emoji(), "COLR")) - { - return "\xF0\x9F\x92\xB0" "\xF0\x9F\x8F\xA1" "\xF0\x9F\x8E\x85" // 💰🏡🎅 - "\xF0\x9F\x8D\xAA" "\xF0\x9F\x8D\x95" "\xF0\x9F\x9A\x80" // 🍪🍕🚀 - "\xF0\x9F\x9A\xBB" "\xF0\x9F\x92\xA9" "\xF0\x9F\x93\xB7" // 🚻💩📷 - "\xF0\x9F\x93\xA6" // 📦 - "\xF0\x9F\x87\xBA" "\xF0\x9F\x87\xB8" "\xF0\x9F\x87\xA6"; // 🇺🇸🇦 - } - return ""; +#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + return "\xF0\x9F\x92\xB0" "\xF0\x9F\x8F\xA1" "\xF0\x9F\x8E\x85" // 💰🏡🎅 + "\xF0\x9F\x8D\xAA" "\xF0\x9F\x8D\x95" "\xF0\x9F\x9A\x80" // 🍪🍕🚀 + "\xF0\x9F\x9A\xBB" "\xF0\x9F\x92\xA9" "\xF0\x9F\x93\xB7" // 🚻💩📷 + "\xF0\x9F\x93\xA6" // 📦 + "\xF0\x9F\x87\xBA" "\xF0\x9F\x87\xB8" "\xF0\x9F\x87\xA6"; // 🇺🇸🇦 +#else + return "Hamburgefons"; +#endif } -const char* platform_os_name() { +static bool extra_config_contains(const char* substring) { for (int index = 0; index < FLAGS_key.count(); index += 2) { - if (!strcmp("os", FLAGS_key[index])) { - return FLAGS_key[index + 1]; + if (0 == strcmp("extra_config", FLAGS_key[index]) + && strstr(FLAGS_key[index + 1], substring)) { + return true; } } - // when running SampleApp or dm without a --key pair, omit the platform name - return ""; + return false; } -// omit version number in returned value -SkString major_platform_os_name() { - SkString name; - for (int index = 0; index < FLAGS_key.count(); index += 2) { - if (!strcmp("os", FLAGS_key[index])) { - const char* platform = FLAGS_key[index + 1]; - const char* end = platform; - while (*end && (*end < '0' || *end > '9')) { - ++end; - } - name.append(platform, end - platform); - break; - } +const char* platform_font_manager() { + if (extra_config_contains("GDI")) { + return "GDI"; } - return name; -} - -const char* platform_extra_config(const char* config) { - for (int index = 0; index < FLAGS_key.count(); index += 2) { - if (!strcmp("extra_config", FLAGS_key[index]) && !strcmp(config, FLAGS_key[index + 1])) { - return config; - } + if (extra_config_contains("NativeFonts")){ + return platform_os_name(); } return ""; } + const char* colortype_name(SkColorType ct) { switch (ct) { case kUnknown_SkColorType: return "Unknown"; @@ -266,6 +196,21 @@ void add_to_text_blob(SkTextBlobBuilder* builder, const char* text, add_to_text_blob_w_len(builder, text, strlen(text), origPaint, x, y); } +SkPath make_star(const SkRect& bounds, int numPts, int step) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(0,-1); + for (int i = 1; i < numPts; ++i) { + int idx = i*step; + SkScalar theta = idx * 2*SK_ScalarPI/numPts + SK_ScalarPI/2; + SkScalar x = SkScalarCos(theta); + SkScalar y = -SkScalarSin(theta); + path.lineTo(x, y); + } + path.transform(SkMatrix::MakeRectToRect(path.getBounds(), bounds, SkMatrix::kFill_ScaleToFit)); + return path; +} + #if !defined(__clang__) && defined(_MSC_VER) // MSVC takes ~2 minutes to compile this function with optimization. // We don't really care to wait that long for this function. diff --git a/chromium/third_party/skia/tools/sk_tool_utils.h b/chromium/third_party/skia/tools/sk_tool_utils.h index b1bde770ea8..abf43323c57 100644 --- a/chromium/third_party/skia/tools/sk_tool_utils.h +++ b/chromium/third_party/skia/tools/sk_tool_utils.h @@ -47,29 +47,9 @@ namespace sk_tool_utils { const char* emoji_sample_text(); /** - * If the platform supports color emoji, return the type (i.e. "CBDT", "SBIX", ""). + * Returns a string describing the platform font manager, if we're using one, otherwise "". */ - const char* platform_os_emoji(); - - /** - * Return the platform name with the version number ("Mac10.9", "Win8", etc.) if available. - */ - const char* platform_os_name(); - - /** - * Return the platform name without the version number ("Mac", "Win", etc.) if available. - */ - SkString major_platform_os_name(); - - /** - * Return the platform extra config (e.g. "GDI") if available. - */ - const char* platform_extra_config(const char* config); - - /** - * Map serif, san-serif, and monospace to the platform-specific font name. - */ - const char* platform_font_name(const char* name); + const char* platform_font_manager(); /** * Sets the paint to use a platform-independent text renderer @@ -135,6 +115,18 @@ namespace sk_tool_utils { void add_to_text_blob(SkTextBlobBuilder* builder, const char* text, const SkPaint& origPaint, SkScalar x, SkScalar y); + // Constructs a star by walking a 'numPts'-sided regular polygon with even/odd fill: + // + // moveTo(pts[0]); + // lineTo(pts[step % numPts]); + // ... + // lineTo(pts[(step * (N - 1)) % numPts]); + // + // numPts=5, step=2 will produce a classic five-point star. + // + // numPts and step must be co-prime. + SkPath make_star(const SkRect& bounds, int numPts = 5, int step = 2); + void make_big_path(SkPath& path); // Return a blurred version of 'src'. This doesn't use a separable filter diff --git a/chromium/third_party/skia/tools/sk_tool_utils_flags.h b/chromium/third_party/skia/tools/sk_tool_utils_flags.h deleted file mode 100644 index ee15fbdee18..00000000000 --- a/chromium/third_party/skia/tools/sk_tool_utils_flags.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef sk_tool_utils_flags_DEFINED -#define sk_tool_utils_flags_DEFINED - -#include "SkCommandLineFlags.h" - -DECLARE_bool(portableFonts); -DECLARE_bool(resourceFonts); - -#endif // sk_tool_utils_flags_DEFINED diff --git a/chromium/third_party/skia/tools/sk_tool_utils_font.cpp b/chromium/third_party/skia/tools/sk_tool_utils_font.cpp index 4d82368520e..8b2041ad1e6 100644 --- a/chromium/third_party/skia/tools/sk_tool_utils_font.cpp +++ b/chromium/third_party/skia/tools/sk_tool_utils_font.cpp @@ -61,8 +61,6 @@ sk_sp<SkTypeface> create_font(const char* name, SkFontStyle style) { font = fontData->fCachedFont; } else { font = sk_make_sp<SkTestFont>(*fontData); - SkDEBUGCODE(font->fDebugName = sub->fName); - SkDEBUGCODE(font->fDebugStyle = sub->fStyle); fontData->fCachedFont = font; } } diff --git a/chromium/third_party/skia/tools/skiaserve/Request.cpp b/chromium/third_party/skia/tools/skiaserve/Request.cpp index 12f35968179..6a0f4df0a93 100644 --- a/chromium/third_party/skia/tools/skiaserve/Request.cpp +++ b/chromium/third_party/skia/tools/skiaserve/Request.cpp @@ -79,10 +79,6 @@ SkCanvas* Request::getCanvas() { gl = factory->getContextInfo(GrContextFactory::kGLES_ContextType, GrContextFactory::ContextOverrides::kNone).glContext(); } - if (!gl) { - gl = factory->getContextInfo(GrContextFactory::kMESA_ContextType, - GrContextFactory::ContextOverrides::kNone).glContext(); - } if (gl) { gl->makeCurrent(); } @@ -136,10 +132,6 @@ GrContext* Request::getContext() { result = fContextFactory->get(GrContextFactory::kGLES_ContextType, GrContextFactory::ContextOverrides::kNone); } - if (!result) { - result = fContextFactory->get(GrContextFactory::kMESA_ContextType, - GrContextFactory::ContextOverrides::kNone); - } return result; #else return nullptr; diff --git a/chromium/third_party/skia/tools/skp/webpages_playback.py b/chromium/third_party/skia/tools/skp/webpages_playback.py index 62feaa9381c..6a8562b1582 100644 --- a/chromium/third_party/skia/tools/skp/webpages_playback.py +++ b/chromium/third_party/skia/tools/skp/webpages_playback.py @@ -99,7 +99,7 @@ DEVICE_TO_PLATFORM_PREFIX = { # How many times the record_wpr binary should be retried. RETRY_RECORD_WPR_COUNT = 5 # How many times the run_benchmark binary should be retried. -RETRY_RUN_MEASUREMENT_COUNT = 5 +RETRY_RUN_MEASUREMENT_COUNT = 3 # Location of the credentials.json file in Google Storage. CREDENTIALS_GS_PATH = 'playback/credentials/credentials.json' @@ -115,6 +115,11 @@ CHROMIUM_PAGE_SETS_TO_PREFIX = { 'top_25_smooth.py': 'top25desk', } +PAGE_SETS_TO_EXCLUSIONS = { + # See skbug.com/7348 + 'key_mobile_sites_smooth.py': '"(digg|worldjournal)"', +} + def remove_prefix(s, prefix): if s.startswith(prefix): @@ -278,7 +283,7 @@ class SkPicturePlayback(object): # Get the webpages archive so that it can be replayed. self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name) - run_benchmark_cmd = ( + run_benchmark_cmd = [ 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir), 'DISPLAY=%s' % X11_DISPLAY, 'timeout', '1800', @@ -290,28 +295,28 @@ class SkPicturePlayback(object): '--page-set-name=%s' % page_set_basename, '--page-set-base-dir=%s' % page_set_dir, '--skp-outdir=%s' % TMP_SKP_DIR, - '--also-run-disabled-tests' - ) + '--also-run-disabled-tests', + ] + + exclusions = PAGE_SETS_TO_EXCLUSIONS.get(os.path.basename(page_set)) + if exclusions: + run_benchmark_cmd.append('--story-filter-exclude=' + exclusions) for _ in range(RETRY_RUN_MEASUREMENT_COUNT): try: print '\n\n=======Capturing SKP of %s=======\n\n' % page_set subprocess.check_call(' '.join(run_benchmark_cmd), shell=True) except subprocess.CalledProcessError: - # skpicture_printer sometimes fails with AssertionError but the - # captured SKP is still valid. This is a known issue. - pass - - # Rename generated SKP files into more descriptive names. - try: - self._RenameSkpFiles(page_set) - # Break out of the retry loop since there were no errors. - break - except Exception: # There was a failure continue with the loop. traceback.print_exc() print '\n\n=======Retrying %s=======\n\n' % page_set time.sleep(10) + continue + + # Rename generated SKP files into more descriptive names. + self._RenameSkpFiles(page_set) + # Break out of the retry loop since there were no errors. + break else: # If we get here then run_benchmark did not succeed and thus did not # break out of the loop. diff --git a/chromium/third_party/skia/tools/skp_parser.cpp b/chromium/third_party/skia/tools/skp_parser.cpp index 887c50d1825..92e1369483a 100644 --- a/chromium/third_party/skia/tools/skp_parser.cpp +++ b/chromium/third_party/skia/tools/skp_parser.cpp @@ -16,6 +16,24 @@ #include <io.h> #endif +/* +If you execute skp_parser with one argument, it spits out a json representation +of the skp, but that's incomplete since it's missing many binary blobs (these +could represent images or typefaces or just anything that doesn't currently +have a json representation). Each unique blob is labeled with a string in the +form "data/%d". So for example: + + tools/git-sync-deps + bin/gn gen out/debug + ninja -C out/debug dm skp_parser + out/debug/dm -m grayscale -w /tmp/dm --config skp + out/debug/skp_parser /tmp/dm/skp/gm/grayscalejpg.skp | less + out/debug/skp_parser /tmp/dm/skp/gm/grayscalejpg.skp | grep data + out/debug/skp_parser /tmp/dm/skp/gm/grayscalejpg.skp data/0 | file - + out/debug/skp_parser /tmp/dm/skp/gm/grayscalejpg.skp data/0 > /tmp/data0.png + +"data/0" is an image that the SKP serializer has encoded as PNG. +*/ int main(int argc, char** argv) { if (argc < 2) { SkDebugf("Usage:\n %s SKP_FILE [DATA_URL]\n", argv[0]); diff --git a/chromium/third_party/skia/tools/skpbench/_adb.py b/chromium/third_party/skia/tools/skpbench/_adb.py index 66ef623fa57..3bf61bc86b8 100644 --- a/chromium/third_party/skia/tools/skpbench/_adb.py +++ b/chromium/third_party/skia/tools/skpbench/_adb.py @@ -5,12 +5,13 @@ from __future__ import print_function import re +import time import subprocess import sys class Adb: - def __init__(self, device_serial=None, echo=False): - self.__invocation = ['adb'] + def __init__(self, device_serial=None, adb_binary=None, echo=False): + self.__invocation = [adb_binary] if device_serial: self.__invocation.extend(['-s', device_serial]) self.__echo = echo @@ -44,6 +45,13 @@ class Adb: def remount(self): self.__invoke('remount') + def reboot(self): + self.__is_root = None + self.shell('reboot') + self.__invoke('wait-for-device') + while '1' != self.check('getprop sys.boot_completed').strip(): + time.sleep(1) + def __echo_shell_cmd(self, cmd): escaped = [re.sub(r'([^a-zA-Z0-9])', r'\\\1', x) for x in cmd.strip().splitlines()] diff --git a/chromium/third_party/skia/tools/skpbench/_adb_path.py b/chromium/third_party/skia/tools/skpbench/_adb_path.py index 4d8bce44255..dc23fc5895b 100644 --- a/chromium/third_party/skia/tools/skpbench/_adb_path.py +++ b/chromium/third_party/skia/tools/skpbench/_adb_path.py @@ -9,9 +9,9 @@ import subprocess __ADB = None -def init(device_serial): +def init(device_serial, adb_binary): global __ADB - __ADB = Adb(device_serial) + __ADB = Adb(device_serial, adb_binary) def join(*pathnames): return '/'.join(pathnames) diff --git a/chromium/third_party/skia/tools/skpbench/_hardware.py b/chromium/third_party/skia/tools/skpbench/_hardware.py index de8848df783..9283243c048 100644 --- a/chromium/third_party/skia/tools/skpbench/_hardware.py +++ b/chromium/third_party/skia/tools/skpbench/_hardware.py @@ -41,10 +41,6 @@ class Hardware: """Prints any info that may help improve or debug hardware monitoring.""" pass - def sleep(self, sleeptime): - """Puts the hardware into a resting state for a fixed amount of time.""" - time.sleep(sleeptime) - class HardwareException(Exception): """Gets thrown when certain hardware state is not what we expect. diff --git a/chromium/third_party/skia/tools/skpbench/_hardware_android.py b/chromium/third_party/skia/tools/skpbench/_hardware_android.py index ebaba0ab6d8..62e3ddd4207 100644 --- a/chromium/third_party/skia/tools/skpbench/_hardware_android.py +++ b/chromium/third_party/skia/tools/skpbench/_hardware_android.py @@ -16,10 +16,12 @@ class HardwareAndroid(Hardware): if self._adb.root(): self._adb.remount() - self._initial_ASLR = \ - self._adb.check('cat /proc/sys/kernel/randomize_va_space') def __enter__(self): + Hardware.__enter__(self) + if not self._adb.is_root() and self._adb.root(): + self._adb.remount() + self._adb.shell('\n'.join([ # turn on airplane mode. ''' @@ -53,23 +55,11 @@ class HardwareAndroid(Hardware): print("WARNING: no adb root access; results may be unreliable.", file=sys.stderr) - return Hardware.__enter__(self) + return self def __exit__(self, exception_type, exception_value, traceback): Hardware.__exit__(self, exception_type, exception_value, traceback) - - if self._adb.is_root(): - self._adb.shell('\n'.join([ - # restore ASLR. - ''' - echo %s > /proc/sys/kernel/randomize_va_space''' % self._initial_ASLR, - - # revive the gui. - ''' - setprop ctl.start drm - setprop ctl.start surfaceflinger - setprop ctl.start zygote - setprop ctl.start media'''])) + self._adb.reboot() # some devices struggle waking up; just hard reboot. def sanity_check(self): Hardware.sanity_check(self) @@ -102,6 +92,3 @@ class HardwareAndroid(Hardware): done''') Hardware.print_debug_diagnostics(self) - - def sleep(self, sleeptime): - Hardware.sleep(self, sleeptime) diff --git a/chromium/third_party/skia/tools/skpbench/_hardware_nexus_6p.py b/chromium/third_party/skia/tools/skpbench/_hardware_nexus_6p.py index 077933bb410..5ca63fdedab 100644 --- a/chromium/third_party/skia/tools/skpbench/_hardware_nexus_6p.py +++ b/chromium/third_party/skia/tools/skpbench/_hardware_nexus_6p.py @@ -14,17 +14,9 @@ class HardwareNexus6P(HardwareAndroid): HardwareAndroid.__init__(self, adb) def __enter__(self): - self._lock_clocks() - return HardwareAndroid.__enter__(self) - - def __exit__(self, exception_type, exception_value, exception_traceback): - HardwareAndroid.__exit__(self, exception_type, - exception_value, exception_traceback) - self._unlock_clocks() - - def _lock_clocks(self): + HardwareAndroid.__enter__(self) if not self._adb.is_root(): - return + return self self._adb.shell('''\ stop thermal-engine @@ -72,51 +64,7 @@ class HardwareNexus6P(HardwareAndroid): echo 9887 > /sys/class/devfreq/qcom,gpubw.70/max_freq echo 9887 > /sys/class/devfreq/qcom,gpubw.70/min_freq''') - def _unlock_clocks(self): - if not self._adb.is_root(): - return - - # restore ddr settings to default. - self._adb.shell('''\ - echo 1525 > /sys/class/devfreq/qcom,cpubw.32/min_freq - echo 9887 > /sys/class/devfreq/qcom,cpubw.32/max_freq - echo bw_hwmon > /sys/class/devfreq/qcom,cpubw.32/governor - echo 1525 > /sys/class/devfreq/qcom,gpubw.70/min_freq - echo 9887 > /sys/class/devfreq/qcom,gpubw.70/max_freq - echo bw_hwmon > /sys/class/devfreq/qcom,gpubw.70/governor''') - - # restore gpu settings to default. - self._adb.shell('''\ - echo 180000000 > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq - echo 600000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq - echo 180000000 > /sys/class/kgsl/kgsl-3d0/gpuclk - echo msm-adreno-tz > /sys/class/kgsl/kgsl-3d0/devfreq/governor - echo 0 > /sys/class/kgsl/kgsl-3d0/idle_timer - echo 0 > /sys/class/kgsl/kgsl-3d0/force_clk_on - echo 0 > /sys/class/kgsl/kgsl-3d0/force_rail_on - echo 0 > /sys/class/kgsl/kgsl-3d0/force_bus_on - echo 1 > /sys/class/kgsl/kgsl-3d0/bus_split''') - - # turn the disabled cores back on. - self._adb.shell('''\ - for N in 7 3 2 1 0; do - echo 1 > /sys/devices/system/cpu/cpu$N/online - done''') - - # unlock the 3 enabled big cores. - self._adb.shell('''\ - for N in 6 5 4; do - echo 633600 > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_min_freq - echo 1958400 > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_max_freq - echo 0 > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_setspeed - echo interactive > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_governor - done''') - - self._adb.shell('''\ - start mpdecision - start perfd - start thermald - start thermal-engine''') + return self def sanity_check(self): HardwareAndroid.sanity_check(self) @@ -148,8 +96,3 @@ class HardwareNexus6P(HardwareAndroid): for i in range(4, 7)] Expectation.check_all(expectations, result.splitlines()) - - def sleep(self, sleeptime): - self._unlock_clocks() - HardwareAndroid.sleep(self, sleeptime) - self._lock_clocks() diff --git a/chromium/third_party/skia/tools/skpbench/_hardware_pixel.py b/chromium/third_party/skia/tools/skpbench/_hardware_pixel.py new file mode 100644 index 00000000000..4cdf21ac8c7 --- /dev/null +++ b/chromium/third_party/skia/tools/skpbench/_hardware_pixel.py @@ -0,0 +1,161 @@ +# Copyright 2017 Google Inc. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +from _hardware import HardwareException, Expectation +from _hardware_android import HardwareAndroid +from collections import namedtuple +import itertools + +CPU_CLOCK_RATE = 1670400 +GPU_CLOCK_RATE = 510000000 + +DEVFREQ_DIRNAME = '/sys/class/devfreq' +DEVFREQ_THROTTLE = 0.74 +DEVFREQ_BLACKLIST = ('b00000.qcom,kgsl-3d0', 'soc:qcom,kgsl-busmon', + 'soc:qcom,m4m') +DevfreqKnobs = namedtuple('knobs', + ('available_governors', 'available_frequencies', + 'governor', 'min_freq', 'max_freq', 'cur_freq')) + +class HardwarePixel(HardwareAndroid): + def __init__(self, adb): + HardwareAndroid.__init__(self, adb) + self._discover_devfreqs() + + def __enter__(self): + HardwareAndroid.__enter__(self) + if not self._adb.is_root(): + return self + + self._adb.shell('\n'.join(['''\ + stop thermal-engine + stop thermald + stop perfd + stop mpdecision''', + + # enable and lock the two fast cores. + ''' + for N in 3 2; do + echo 1 > /sys/devices/system/cpu/cpu$N/online + echo userspace > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_governor + echo %i > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_max_freq + echo %i > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_min_freq + echo %i > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_setspeed + done''' % tuple(CPU_CLOCK_RATE for _ in range(3)), + + # turn off the two slow cores + ''' + for N in 1 0; do + echo 0 > /sys/devices/system/cpu/cpu$N/online + done''', + + # gpu perf commands from + # https://developer.qualcomm.com/qfile/28823/lm80-p0436-11_adb_commands.pdf + ''' + echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split + echo 1 > /sys/class/kgsl/kgsl-3d0/force_bus_on + echo 1 > /sys/class/kgsl/kgsl-3d0/force_rail_on + echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on + echo 1000000 > /sys/class/kgsl/kgsl-3d0/idle_timer + echo userspace > /sys/class/kgsl/kgsl-3d0/devfreq/governor + echo 2 > /sys/class/kgsl/kgsl-3d0/max_pwrlevel + echo 2 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel + echo 2 > /sys/class/kgsl/kgsl-3d0/thermal_pwrlevel + echo %i > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq + echo %i > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq + echo %i > /sys/class/kgsl/kgsl-3d0/max_gpuclk + echo %i > /sys/class/kgsl/kgsl-3d0/gpuclk''' % + tuple(GPU_CLOCK_RATE for _ in range(4))] + \ + + self._devfreq_lock_cmds)) + + return self + + def sanity_check(self): + HardwareAndroid.sanity_check(self) + + if not self._adb.is_root(): + return + + result = self._adb.check(' '.join( + ['cat', + '/sys/class/power_supply/battery/capacity', + '/sys/devices/system/cpu/online'] + \ + ['/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq' % i + for i in range(2, 4)] + \ + ['/sys/class/kgsl/kgsl-3d0/thermal_pwrlevel', + '/sys/kernel/debug/clk/gpu_gx_gfx3d_clk/measure', + '/sys/kernel/debug/clk/bimc_clk/measure', + '/sys/class/thermal/thermal_zone22/temp', + '/sys/class/thermal/thermal_zone23/temp'] + \ + self._devfreq_sanity_knobs)) + + expectations = \ + [Expectation(int, min_value=30, name='battery', sleeptime=30*60), + Expectation(str, exact_value='2-3', name='online cpus')] + \ + [Expectation(int, exact_value=CPU_CLOCK_RATE, name='cpu_%i clock rate' %i) + for i in range(2, 4)] + \ + [Expectation(int, exact_value=2, name='gpu thermal power level'), + Expectation(long, min_value=(GPU_CLOCK_RATE - 5000), + max_value=(GPU_CLOCK_RATE + 5000), + name='measured gpu clock'), + Expectation(long, min_value=902390000, max_value=902409999, + name='measured ddr clock', sleeptime=10), + Expectation(int, max_value=41000, name='pm8994_tz temperature'), + Expectation(int, max_value=40, name='msm_therm temperature')] + \ + self._devfreq_sanity_expectations + + Expectation.check_all(expectations, result.splitlines()) + + def _discover_devfreqs(self): + self._devfreq_lock_cmds = list() + self._devfreq_sanity_knobs = list() + self._devfreq_sanity_expectations = list() + + results = iter(self._adb.check('''\ + KNOBS='%s' + for DEVICE in %s/*; do + if cd $DEVICE && ls $KNOBS >/dev/null; then + basename $DEVICE + cat $KNOBS + fi + done 2>/dev/null''' % + (' '.join(DevfreqKnobs._fields), DEVFREQ_DIRNAME)).splitlines()) + + while True: + batch = tuple(itertools.islice(results, 1 + len(DevfreqKnobs._fields))) + if not batch: + break + + devfreq = batch[0] + if devfreq in DEVFREQ_BLACKLIST: + continue + + path = '%s/%s' % (DEVFREQ_DIRNAME, devfreq) + + knobs = DevfreqKnobs(*batch[1:]) + if not 'performance' in knobs.available_governors.split(): + print('WARNING: devfreq %s does not have performance governor' % path) + continue + + self._devfreq_lock_cmds.append('echo performance > %s/governor' % path) + + frequencies = map(int, knobs.available_frequencies.split()) + if frequencies: + # choose the lowest frequency that is >= DEVFREQ_THROTTLE * max. + frequencies.sort() + target = DEVFREQ_THROTTLE * frequencies[-1] + idx = len(frequencies) - 1 + while idx > 0 and frequencies[idx - 1] >= target: + idx -= 1 + bench_frequency = frequencies[idx] + self._devfreq_lock_cmds.append('echo %i > %s/min_freq' % + (bench_frequency, path)) + self._devfreq_lock_cmds.append('echo %i > %s/max_freq' % + (bench_frequency, path)) + self._devfreq_sanity_knobs.append('%s/cur_freq' % path) + self._devfreq_sanity_expectations.append( + Expectation(int, exact_value=bench_frequency, + name='%s/cur_freq' % path)) diff --git a/chromium/third_party/skia/tools/skpbench/_hardware_pixel_c.py b/chromium/third_party/skia/tools/skpbench/_hardware_pixel_c.py index a1cd17a0846..037ae215c22 100644 --- a/chromium/third_party/skia/tools/skpbench/_hardware_pixel_c.py +++ b/chromium/third_party/skia/tools/skpbench/_hardware_pixel_c.py @@ -15,22 +15,9 @@ class HardwarePixelC(HardwareAndroid): HardwareAndroid.__init__(self, adb) def __enter__(self): - self._lock_clocks() - return HardwareAndroid.__enter__(self) - - def __exit__(self, exception_type, exception_value, exception_traceback): - HardwareAndroid.__exit__(self, exception_type, - exception_value, exception_traceback) - self._unlock_clocks() - - def filter_line(self, line): - JUNK = ['NvRmPrivGetChipPlatform: Could not read platform information', - 'Expected on kernels without fuse support, using silicon'] - return False if line in JUNK else HardwareAndroid.filter_line(self, line) - - def _lock_clocks(self): + HardwareAndroid.__enter__(self) if not self._adb.is_root(): - return + return self self._adb.shell('\n'.join([ # turn on and lock the first 3 cores. @@ -52,28 +39,12 @@ class HardwarePixelC(HardwareAndroid): chown root:root /sys/devices/57000000.gpu/pstate echo %s > /sys/devices/57000000.gpu/pstate''' % GPU_EMC_PROFILE_ID])) - def _unlock_clocks(self): - if not self._adb.is_root(): - return + return self - self._adb.shell('\n'.join([ - # unlock gpu/emc clocks. - ''' - echo auto > /sys/devices/57000000.gpu/pstate - chown system:system /sys/devices/57000000.gpu/pstate''', - - # turn the fourth core back on. - ''' - echo 1 > /sys/devices/system/cpu/cpu3/online''', - - # unlock the first 3 cores. - ''' - for N in 2 1 0; do - echo 1912500 > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_max_freq - echo 51000 > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_min_freq - echo 0 > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_setspeed - echo interactive >/sys/devices/system/cpu/cpu$N/cpufreq/scaling_governor - done'''])) + def filter_line(self, line): + JUNK = ['NvRmPrivGetChipPlatform: Could not read platform information', + 'Expected on kernels without fuse support, using silicon'] + return False if line in JUNK else HardwareAndroid.filter_line(self, line) def sanity_check(self): HardwareAndroid.sanity_check(self) @@ -109,8 +80,3 @@ class HardwarePixelC(HardwareAndroid): [Expectation(str, exact_value=GPU_EMC_PROFILE, name='gpu/emc profile')] Expectation.check_all(expectations, result.splitlines()) - - def sleep(self, sleeptime): - self._unlock_clocks() - HardwareAndroid.sleep(self, sleeptime) - self._lock_clocks() diff --git a/chromium/third_party/skia/tools/skpbench/skpbench.cpp b/chromium/third_party/skia/tools/skpbench/skpbench.cpp index c0ead46155e..ef7a5bbabf6 100644 --- a/chromium/third_party/skia/tools/skpbench/skpbench.cpp +++ b/chromium/third_party/skia/tools/skpbench/skpbench.cpp @@ -10,6 +10,7 @@ #include "SkGr.h" #include "SkCanvas.h" +#include "SkCommonFlags.h" #include "SkCommonFlagsPathRenderer.h" #include "SkOSFile.h" #include "SkOSPath.h" @@ -277,6 +278,7 @@ int main(int argc, char** argv) { // Create a context. GrContextOptions ctxOptions; ctxOptions.fGpuPathRenderers = CollectGpuPathRenderersFromFlags(); + ctxOptions.fAllowPathMaskCaching = FLAGS_cachePathMasks; sk_gpu_test::GrContextFactory factory(ctxOptions); sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo(config->getContextType(), config->getContextOverrides()); diff --git a/chromium/third_party/skia/tools/skpbench/skpbench.py b/chromium/third_party/skia/tools/skpbench/skpbench.py index ef44577c532..f2df9b679d8 100755 --- a/chromium/third_party/skia/tools/skpbench/skpbench.py +++ b/chromium/third_party/skia/tools/skpbench/skpbench.py @@ -33,6 +33,8 @@ __argparse.add_argument('skpbench', help="path to the skpbench binary") __argparse.add_argument('--adb', action='store_true', help="execute skpbench over adb") +__argparse.add_argument('--adb_binary', default='adb', + help="The name of the adb binary to use.") __argparse.add_argument('-s', '--device-serial', help="if using adb, ID of the specific device to target " "(only required if more than 1 device is attached)") @@ -54,6 +56,12 @@ __argparse.add_argument('--gpu', help="perform timing on the gpu clock instead of cpu (gpu work only)") __argparse.add_argument('--fps', action='store_true', help="use fps instead of ms") +__argparse.add_argument('--pr', + help="comma- or space-separated list of GPU path renderers, including: " + "[[~]all [~]default [~]dashline [~]nvpr [~]msaa [~]aaconvex " + "[~]aalinearizing [~]small [~]tess]") +__argparse.add_argument('--nocache', + action='store_true', help="disable caching of path mask textures") __argparse.add_argument('-c', '--config', default='gl', help="comma- or space-separated list of GPU configs") __argparse.add_argument('-a', '--resultsfile', @@ -65,7 +73,7 @@ __argparse.add_argument('skps', FLAGS = __argparse.parse_args() if FLAGS.adb: import _adb_path as _path - _path.init(FLAGS.device_serial) + _path.init(FLAGS.device_serial, FLAGS.adb_binary) else: import _os_path as _path @@ -108,11 +116,15 @@ class SKPBench: ARGV.extend(['--gpuClock', 'true']) if FLAGS.fps: ARGV.extend(['--fps', 'true']) + if FLAGS.pr: + ARGV.extend(['--pr'] + re.split(r'[ ,]', FLAGS.pr)) + if FLAGS.nocache: + ARGV.extend(['--cachePathMasks', 'false']) if FLAGS.adb: if FLAGS.device_serial is None: - ARGV[:0] = ['adb', 'shell'] + ARGV[:0] = [FLAGS.adb_binary, 'shell'] else: - ARGV[:0] = ['adb', '-s', FLAGS.device_serial, 'shell'] + ARGV[:0] = [FLAGS.adb_binary, '-s', FLAGS.device_serial, 'shell'] @classmethod def get_header(cls, outfile=sys.stdout): @@ -231,45 +243,53 @@ def emit_result(line, resultsfile=None): resultsfile.flush() def run_benchmarks(configs, skps, hardware, resultsfile=None): - emit_result(SKPBench.get_header(), resultsfile) + hasheader = False benches = collections.deque([(skp, config, FLAGS.max_stddev) for skp in skps for config in configs]) while benches: - benchargs = benches.popleft() - with SKPBench(*benchargs) as skpbench: - try: - skpbench.execute(hardware) - if skpbench.best_result: - emit_result(skpbench.best_result.format(FLAGS.suffix), resultsfile) - else: - print("WARNING: no result for %s with config %s" % - (skpbench.skp, skpbench.config), file=sys.stderr) - - except StddevException: - retry_max_stddev = skpbench.max_stddev * math.sqrt(2) - if FLAGS.verbosity >= 1: - print("stddev is too high for %s/%s (%s%%, max=%.2f%%), " - "re-queuing with max=%.2f%%." % - (skpbench.best_result.config, skpbench.best_result.bench, - skpbench.best_result.stddev, skpbench.max_stddev, - retry_max_stddev), - file=sys.stderr) - benches.append((skpbench.skp, skpbench.config, retry_max_stddev, - skpbench.best_result)) - - except HardwareException as exception: - skpbench.terminate() - if FLAGS.verbosity >= 4: - hardware.print_debug_diagnostics() - if FLAGS.verbosity >= 1: - print("%s; taking a %i second nap..." % - (exception.message, exception.sleeptime), file=sys.stderr) - benches.appendleft(benchargs) # retry the same bench next time. - hardware.sleep(exception.sleeptime) - if FLAGS.verbosity >= 4: - hardware.print_debug_diagnostics() + try: + with hardware: SKPBench.run_warmup(hardware.warmup_time, configs[0]) + if not hasheader: + emit_result(SKPBench.get_header(), resultsfile) + hasheader = True + while benches: + benchargs = benches.popleft() + with SKPBench(*benchargs) as skpbench: + try: + skpbench.execute(hardware) + if skpbench.best_result: + emit_result(skpbench.best_result.format(FLAGS.suffix), + resultsfile) + else: + print("WARNING: no result for %s with config %s" % + (skpbench.skp, skpbench.config), file=sys.stderr) + + except StddevException: + retry_max_stddev = skpbench.max_stddev * math.sqrt(2) + if FLAGS.verbosity >= 1: + print("stddev is too high for %s/%s (%s%%, max=%.2f%%), " + "re-queuing with max=%.2f%%." % + (skpbench.best_result.config, skpbench.best_result.bench, + skpbench.best_result.stddev, skpbench.max_stddev, + retry_max_stddev), + file=sys.stderr) + benches.append((skpbench.skp, skpbench.config, retry_max_stddev, + skpbench.best_result)) + + except HardwareException as exception: + skpbench.terminate() + if FLAGS.verbosity >= 4: + hardware.print_debug_diagnostics() + if FLAGS.verbosity >= 1: + print("%s; rebooting and taking a %i second nap..." % + (exception.message, exception.sleeptime), file=sys.stderr) + benches.appendleft(benchargs) # retry the same bench next time. + raise # wake hw up from benchmarking mode before the nap. + + except HardwareException as exception: + time.sleep(exception.sleeptime) def main(): # Delimiter is ',' or ' ', skip if nested inside parens (e.g. gpu(a=b,c=d)). @@ -278,11 +298,15 @@ def main(): skps = _path.find_skps(FLAGS.skps) if FLAGS.adb: - adb = Adb(FLAGS.device_serial, echo=(FLAGS.verbosity >= 5)) + adb = Adb(FLAGS.device_serial, FLAGS.adb_binary, + echo=(FLAGS.verbosity >= 5)) model = adb.check('getprop ro.product.model').strip() if model == 'Pixel C': from _hardware_pixel_c import HardwarePixelC hardware = HardwarePixelC(adb) + elif model == 'Pixel': + from _hardware_pixel import HardwarePixel + hardware = HardwarePixel(adb) elif model == 'Nexus 6P': from _hardware_nexus_6p import HardwareNexus6P hardware = HardwareNexus6P(adb) @@ -294,13 +318,11 @@ def main(): else: hardware = Hardware() - with hardware: - SKPBench.run_warmup(hardware.warmup_time, configs[0]) - if FLAGS.resultsfile: - with open(FLAGS.resultsfile, mode='a+') as resultsfile: - run_benchmarks(configs, skps, hardware, resultsfile=resultsfile) - else: - run_benchmarks(configs, skps, hardware) + if FLAGS.resultsfile: + with open(FLAGS.resultsfile, mode='a+') as resultsfile: + run_benchmarks(configs, skps, hardware, resultsfile=resultsfile) + else: + run_benchmarks(configs, skps, hardware) if __name__ == '__main__': diff --git a/chromium/third_party/skia/tools/valgrind.supp b/chromium/third_party/skia/tools/valgrind.supp index 064f8efa34b..0108b37ef6f 100644 --- a/chromium/third_party/skia/tools/valgrind.supp +++ b/chromium/third_party/skia/tools/valgrind.supp @@ -6,7 +6,7 @@ Memcheck:Leak match-leak-kinds: possible,definite ... - fun:_ZN8SkThreadC1EPFvPvES0_ + fun:start_keepalive ... fun:main ... diff --git a/chromium/third_party/skia/tools/viewer/SampleSlide.cpp b/chromium/third_party/skia/tools/viewer/SampleSlide.cpp index 93a495eb8c7..7c6fe11feaf 100644 --- a/chromium/third_party/skia/tools/viewer/SampleSlide.cpp +++ b/chromium/third_party/skia/tools/viewer/SampleSlide.cpp @@ -9,16 +9,21 @@ #include "SkCanvas.h" #include "SkCommonFlags.h" +#include "SkKey.h" #include "SkOSFile.h" #include "SkStream.h" -SampleSlide::SampleSlide(const SkViewFactory* factory) : fViewFactory(factory) { +using namespace sk_app; + +SampleSlide::SampleSlide(const SkViewFactory* factory) + : fViewFactory(factory) + , fClick(nullptr) { SkView* view = (*factory)(); SampleCode::RequestTitle(view, &fName); view->unref(); } -SampleSlide::~SampleSlide() {} +SampleSlide::~SampleSlide() { delete fClick; } void SampleSlide::draw(SkCanvas* canvas) { SkASSERT(fView); @@ -45,10 +50,43 @@ bool SampleSlide::onChar(SkUnichar c) { return fView->doQuery(&evt); } -#if defined(SK_BUILD_FOR_ANDROID) -// these are normally defined in SkOSWindow_unix, but we don't -// want to include that -void SkEvent::SignalNonEmptyQueue() {} +bool SampleSlide::onMouse(SkScalar x, SkScalar y, Window::InputState state, + uint32_t modifiers) { + // map to SkView modifiers + unsigned modifierKeys = 0; + modifierKeys |= (state & Window::kShift_ModifierKey) ? kShift_SkModifierKey : 0; + modifierKeys |= (state & Window::kControl_ModifierKey) ? kControl_SkModifierKey : 0; + modifierKeys |= (state & Window::kOption_ModifierKey) ? kOption_SkModifierKey : 0; + modifierKeys |= (state & Window::kCommand_ModifierKey) ? kCommand_SkModifierKey : 0; + + bool handled = false; + switch (state) { + case Window::kDown_InputState: { + delete fClick; + fClick = fView->findClickHandler(SkIntToScalar(x), SkIntToScalar(y), modifierKeys); + if (fClick) { + SkView::DoClickDown(fClick, x, y, modifierKeys); + handled = true; + } + break; + } + case Window::kMove_InputState: { + if (fClick) { + SkView::DoClickMoved(fClick, x, y, modifierKeys); + handled = true; + } + break; + } + case Window::kUp_InputState: { + if (fClick) { + SkView::DoClickUp(fClick, x, y, modifierKeys); + delete fClick; + fClick = nullptr; + handled = true; + } + break; + } + } -void SkEvent::SignalQueueTimer(SkMSec delay) {} -#endif + return handled; +} diff --git a/chromium/third_party/skia/tools/viewer/SampleSlide.h b/chromium/third_party/skia/tools/viewer/SampleSlide.h index 24325c91a02..4b91f39f746 100644 --- a/chromium/third_party/skia/tools/viewer/SampleSlide.h +++ b/chromium/third_party/skia/tools/viewer/SampleSlide.h @@ -27,10 +27,13 @@ public: } bool onChar(SkUnichar c) override; + bool onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState state, + uint32_t modifiers) override; private: const SkViewFactory* fViewFactory; sk_sp<SkView> fView; + SkView::Click* fClick; }; #endif diff --git a/chromium/third_party/skia/tools/viewer/Slide.h b/chromium/third_party/skia/tools/viewer/Slide.h index 9ec7a3dbe19..e23a2eb78d3 100644 --- a/chromium/third_party/skia/tools/viewer/Slide.h +++ b/chromium/third_party/skia/tools/viewer/Slide.h @@ -11,6 +11,7 @@ #include "SkRefCnt.h" #include "SkSize.h" #include "SkString.h" +#include "sk_app/Window.h" class SkCanvas; class SkAnimTimer; @@ -29,9 +30,12 @@ public: virtual void unload() {} virtual bool onChar(SkUnichar c) { return false; } + virtual bool onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState state, + uint32_t modifiers) { return false; } SkString getName() { return fName; } + protected: SkString fName; }; diff --git a/chromium/third_party/skia/tools/viewer/Viewer.cpp b/chromium/third_party/skia/tools/viewer/Viewer.cpp index e1f72d7575e..8c320938fb0 100644 --- a/chromium/third_party/skia/tools/viewer/Viewer.cpp +++ b/chromium/third_party/skia/tools/viewer/Viewer.cpp @@ -17,6 +17,7 @@ #include "SkATrace.h" #include "SkCanvas.h" #include "SkColorSpace_Base.h" +#include "SkColorSpacePriv.h" #include "SkColorSpaceXformCanvas.h" #include "SkCommandLineFlags.h" #include "SkCommonFlagsGpuThreads.h" @@ -35,6 +36,7 @@ #include "SkSurface.h" #include "SkSwizzle.h" #include "SkTaskGroup.h" +#include "SkThreadedBMPDevice.h" #include "SkTime.h" #include "SkVertices.h" @@ -140,8 +142,8 @@ static DEFINE_string2(match, m, nullptr, "If a bench does not match any list entry,\n" "it is skipped unless some list entry starts with ~"); -DEFINE_string(slide, "", "Start on this sample."); -DEFINE_bool(list, false, "List samples?"); +static DEFINE_string(slide, "", "Start on this sample."); +static DEFINE_bool(list, false, "List samples?"); #ifdef SK_VULKAN # define BACKENDS_STR "\"sw\", \"gl\", and \"vk\"" @@ -159,10 +161,11 @@ static DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from."); static DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR "."); -DEFINE_int32(msaa, 0, "Number of subpixel samples. 0 for no HW antialiasing."); +static DEFINE_int32(msaa, 0, "Number of subpixel samples. 0 for no HW antialiasing."); +static DEFINE_bool(cachePathMasks, true, "Allows path mask textures to be cached in GPU configs."); DEFINE_pathrenderer_flag; -DEFINE_bool(instancedRendering, false, "Enable instanced rendering on GPU backends."); +static DEFINE_bool(instancedRendering, false, "Enable instanced rendering on GPU backends."); DECLARE_int32(threads) const char* kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = { @@ -235,6 +238,12 @@ static bool primaries_equal(const SkColorSpacePrimaries& a, const SkColorSpacePr return memcmp(&a, &b, sizeof(SkColorSpacePrimaries)) == 0; } +static Window::BackendType backend_type_for_window(Window::BackendType backendType) { + // In raster mode, we still use GL for the window. + // This lets us render the GUI faster (and correct). + return Window::kRaster_BackendType == backendType ? Window::kNativeGL_BackendType : backendType; +} + const char* kName = "name"; const char* kValue = "value"; const char* kOptions = "options"; @@ -252,17 +261,24 @@ const char* kRefreshStateName = "Refresh"; Viewer::Viewer(int argc, char** argv, void* platformData) : fCurrentMeasurement(0) + , fCumulativeMeasurementTime(0) + , fCumulativeMeasurementCount(0) , fDisplayStats(false) , fRefresh(false) , fShowImGuiDebugWindow(false) + , fShowSlidePicker(false) , fShowImGuiTestWindow(false) , fShowZoomWindow(false) , fLastImage(nullptr) , fBackendType(sk_app::Window::kNativeGL_BackendType) , fColorMode(ColorMode::kLegacy) , fColorSpacePrimaries(gSrgbPrimaries) + // Our UI can only tweak gamma (currently), so start out gamma-only + , fColorSpaceTransferFn(g2Dot2_TransferFn) , fZoomLevel(0.0f) , fGestureDevice(GestureDevice::kNone) + , fTileCnt(0) + , fThreadCnt(0) { SkGraphics::Init(); @@ -304,6 +320,7 @@ Viewer::Viewer(int argc, char** argv, void* platformData) displayParams.fMSAASampleCount = FLAGS_msaa; displayParams.fGrContextOptions.fEnableInstancedRendering = FLAGS_instancedRendering; displayParams.fGrContextOptions.fGpuPathRenderers = CollectGpuPathRenderersFromFlags(); + displayParams.fGrContextOptions.fAllowPathMaskCaching = FLAGS_cachePathMasks; displayParams.fGrContextOptions.fExecutor = GpuExecutorForTools(); fWindow->setRequestedDisplayParams(displayParams); @@ -323,6 +340,18 @@ Viewer::Viewer(int argc, char** argv, void* platformData) this->fShowImGuiDebugWindow = !this->fShowImGuiDebugWindow; fWindow->inval(); }); + // Command to jump directly to the slide picker and give it focus + fCommands.addCommand('/', "GUI", "Jump to slide picker", [this]() { + this->fShowImGuiDebugWindow = true; + this->fShowSlidePicker = true; + fWindow->inval(); + }); + // Alias that to Backspace, to match SampleApp + fCommands.addCommand(Window::Key::kBack, "Backspace", "GUI", "Jump to slide picker", [this]() { + this->fShowImGuiDebugWindow = true; + this->fShowSlidePicker = true; + fWindow->inval(); + }); fCommands.addCommand('g', "GUI", "Toggle GUI Demo", [this]() { this->fShowImGuiTestWindow = !this->fShowImGuiTestWindow; fWindow->inval(); @@ -335,6 +364,11 @@ Viewer::Viewer(int argc, char** argv, void* platformData) this->fDisplayStats = !this->fDisplayStats; fWindow->inval(); }); + fCommands.addCommand('0', "Overlays", "Reset stats", [this]() { + this->resetMeasurements(); + this->updateTitle(); + fWindow->inval(); + }); fCommands.addCommand('c', "Modes", "Cycle color mode", [this]() { switch (fColorMode) { case ColorMode::kLegacy: @@ -390,7 +424,7 @@ Viewer::Viewer(int argc, char** argv, void* platformData) this->setBackend(newBackend); }); - fCommands.addCommand('A', "AAA", "Toggle analytic AA", [this]() { + fCommands.addCommand('A', "AA", "Toggle analytic AA", [this]() { if (!gSkUseAnalyticAA) { gSkUseAnalyticAA = true; } else if (!gSkForceAnalyticAA) { @@ -401,7 +435,7 @@ Viewer::Viewer(int argc, char** argv, void* platformData) this->updateTitle(); fWindow->inval(); }); - fCommands.addCommand('D', "DAA", "Toggle delta AA", [this]() { + fCommands.addCommand('D', "AA", "Toggle delta AA", [this]() { if (!gSkUseDeltaAA) { gSkUseDeltaAA = true; } else if (!gSkForceDeltaAA) { @@ -413,6 +447,41 @@ Viewer::Viewer(int argc, char** argv, void* platformData) fWindow->inval(); }); + fCommands.addCommand('+', "Threaded Backend", "Increase tile count", [this]() { + fTileCnt++; + if (fThreadCnt == 0) { + this->resetExecutor(); + } + this->updateTitle(); + fWindow->inval(); + }); + fCommands.addCommand('-', "Threaded Backend", "Decrease tile count", [this]() { + fTileCnt = SkTMax(0, fTileCnt - 1); + if (fThreadCnt == 0) { + this->resetExecutor(); + } + this->updateTitle(); + fWindow->inval(); + }); + fCommands.addCommand('>', "Threaded Backend", "Increase thread count", [this]() { + if (fTileCnt == 0) { + return; + } + fThreadCnt = (fThreadCnt + 1) % fTileCnt; + this->resetExecutor(); + this->updateTitle(); + fWindow->inval(); + }); + fCommands.addCommand('<', "Threaded Backend", "Decrease thread count", [this]() { + if (fTileCnt == 0) { + return; + } + fThreadCnt = (fThreadCnt + fTileCnt - 1) % fTileCnt; + this->resetExecutor(); + this->updateTitle(); + fWindow->inval(); + }); + // set up slides this->initSlides(); this->setStartupSlide(); @@ -468,7 +537,7 @@ Viewer::Viewer(int argc, char** argv, void* platformData) fImGuiGamutPaint.setColor(SK_ColorWHITE); fImGuiGamutPaint.setFilterQuality(kLow_SkFilterQuality); - fWindow->attach(fBackendType); + fWindow->attach(backend_type_for_window(fBackendType)); } void Viewer::initSlides() { @@ -581,6 +650,13 @@ void Viewer::updateTitle() { } } + if (fTileCnt > 0) { + title.appendf(" T%d", fTileCnt); + if (fThreadCnt > 0) { + title.appendf("/%d", fThreadCnt); + } + } + switch (fColorMode) { case ColorMode::kLegacy: title.append(" Legacy 8888"); @@ -605,6 +681,10 @@ void Viewer::updateTitle() { } } title.appendf(" %s", curPrimaries >= 0 ? gNamedPrimaries[curPrimaries].fName : "Custom"); + + if (ColorMode::kColorManagedSRGB8888_NonLinearBlending == fColorMode) { + title.appendf(" Gamma %f", fColorSpaceTransferFn.fG); + } } title.append(" ["); @@ -648,6 +728,15 @@ void Viewer::listNames() { } } +void Viewer::resetMeasurements() { + memset(fPaintTimes, 0, sizeof(fPaintTimes)); + memset(fFlushTimes, 0, sizeof(fFlushTimes)); + memset(fAnimateTimes, 0, sizeof(fAnimateTimes)); + fCurrentMeasurement = 0; + fCumulativeMeasurementTime = 0; + fCumulativeMeasurementCount = 0; +} + void Viewer::setupCurrentSlide(int previousSlide) { if (fCurrentSlide == previousSlide) { return; // no change; do nothing @@ -677,6 +766,9 @@ void Viewer::setupCurrentSlide(int previousSlide) { if (previousSlide >= 0) { fSlides[previousSlide]->unload(); } + + this->resetMeasurements(); + fWindow->inval(); } @@ -706,40 +798,33 @@ void Viewer::setBackend(sk_app::Window::BackendType backendType) { fWindow->detach(); -#if defined(SK_BUILD_FOR_WIN) && defined(SK_VULKAN) +#if defined(SK_BUILD_FOR_WIN) // Switching between OpenGL, Vulkan, and ANGLE in the same window is problematic at this point // on Windows, so we just delete the window and recreate it. - if (sk_app::Window::kVulkan_BackendType == fBackendType || - sk_app::Window::kNativeGL_BackendType == fBackendType -#if SK_ANGLE - || sk_app::Window::kANGLE_BackendType == fBackendType -#endif - ) { - DisplayParams params = fWindow->getRequestedDisplayParams(); - delete fWindow; - fWindow = Window::CreateNativeWindow(nullptr); - - // re-register callbacks - fCommands.attach(fWindow); - fWindow->registerBackendCreatedFunc(on_backend_created_func, this); - fWindow->registerPaintFunc(on_paint_handler, this); - fWindow->registerTouchFunc(on_touch_handler, this); - fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this); - fWindow->registerMouseFunc(on_mouse_handler, this); - fWindow->registerMouseWheelFunc(on_mouse_wheel_handler, this); - fWindow->registerKeyFunc(on_key_handler, this); - fWindow->registerCharFunc(on_char_handler, this); - // Don't allow the window to re-attach. If we're in MSAA mode, the params we grabbed above - // will still include our correct sample count. But the re-created fWindow will lose that - // information. On Windows, we need to re-create the window when changing sample count, - // so we'll incorrectly detect that situation, then re-initialize the window in GL mode, - // rendering this tear-down step pointless (and causing the Vulkan window context to fail - // as if we had never changed windows at all). - fWindow->setRequestedDisplayParams(params, false); - } + DisplayParams params = fWindow->getRequestedDisplayParams(); + delete fWindow; + fWindow = Window::CreateNativeWindow(nullptr); + + // re-register callbacks + fCommands.attach(fWindow); + fWindow->registerBackendCreatedFunc(on_backend_created_func, this); + fWindow->registerPaintFunc(on_paint_handler, this); + fWindow->registerTouchFunc(on_touch_handler, this); + fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this); + fWindow->registerMouseFunc(on_mouse_handler, this); + fWindow->registerMouseWheelFunc(on_mouse_wheel_handler, this); + fWindow->registerKeyFunc(on_key_handler, this); + fWindow->registerCharFunc(on_char_handler, this); + // Don't allow the window to re-attach. If we're in MSAA mode, the params we grabbed above + // will still include our correct sample count. But the re-created fWindow will lose that + // information. On Windows, we need to re-create the window when changing sample count, + // so we'll incorrectly detect that situation, then re-initialize the window in GL mode, + // rendering this tear-down step pointless (and causing the Vulkan window context to fail + // as if we had never changed windows at all). + fWindow->setRequestedDisplayParams(params, false); #endif - fWindow->attach(fBackendType); + fWindow->attach(backend_type_for_window(fBackendType)); } void Viewer::setColorMode(ColorMode colorMode) { @@ -774,13 +859,21 @@ void Viewer::drawSlide(SkCanvas* canvas) { ? SkColorSpace::kLinear_RenderTargetGamma : SkColorSpace::kSRGB_RenderTargetGamma; SkMatrix44 toXYZ(SkMatrix44::kIdentity_Constructor); SkAssertResult(fColorSpacePrimaries.toXYZD50(&toXYZ)); - cs = SkColorSpace::MakeRGB(transferFn, toXYZ); + if (ColorMode::kColorManagedSRGB8888_NonLinearBlending == fColorMode) { + cs = SkColorSpace::MakeRGB(fColorSpaceTransferFn, toXYZ); + } else { + cs = SkColorSpace::MakeRGB(transferFn, toXYZ); + } } // If we're in F16, or we're zooming, or we're in color correct 8888 and the gamut isn't sRGB, - // we need to render offscreen + // we need to render offscreen. We also need to render offscreen if we're in any raster mode, + // because the window surface is actually GL. sk_sp<SkSurface> offscreenSurface = nullptr; - if (ColorMode::kColorManagedLinearF16 == fColorMode || + std::unique_ptr<SkThreadedBMPDevice> threadedDevice; + std::unique_ptr<SkCanvas> threadedCanvas; + if (Window::kRaster_BackendType == fBackendType || + ColorMode::kColorManagedLinearF16 == fColorMode || fShowZoomWindow || (ColorMode::kColorManagedSRGB8888 == fColorMode && !primaries_equal(fColorSpacePrimaries, gSrgbPrimaries))) { @@ -793,8 +886,19 @@ void Viewer::drawSlide(SkCanvas* canvas) { (ColorMode::kColorManagedSRGB8888_NonLinearBlending == fColorMode) ? nullptr : cs; SkImageInfo info = SkImageInfo::Make(fWindow->width(), fWindow->height(), colorType, kPremul_SkAlphaType, std::move(offscreenColorSpace)); - offscreenSurface = canvas->makeSurface(info); - slideCanvas = offscreenSurface->getCanvas(); + offscreenSurface = Window::kRaster_BackendType == fBackendType ? SkSurface::MakeRaster(info) + : canvas->makeSurface(info); + SkPixmap offscreenPixmap; + if (fTileCnt > 0 && offscreenSurface->peekPixels(&offscreenPixmap)) { + SkBitmap offscreenBitmap; + offscreenBitmap.installPixels(offscreenPixmap); + threadedDevice.reset(new SkThreadedBMPDevice(offscreenBitmap, fTileCnt, + fThreadCnt, fExecutor.get())); + threadedCanvas.reset(new SkCanvas(threadedDevice.get())); + slideCanvas = threadedCanvas.get(); + } else { + slideCanvas = offscreenSurface->getCanvas(); + } } std::unique_ptr<SkCanvas> xformCanvas = nullptr; @@ -835,6 +939,7 @@ void Viewer::onBackendCreated() { this->updateTitle(); this->updateUIState(); this->setupCurrentSlide(-1); + this->resetMeasurements(); fWindow->show(); fWindow->inval(); } @@ -852,9 +957,13 @@ void Viewer::onPaint(SkCanvas* canvas) { ImGui::NewFrame(); - drawSlide(canvas); + this->drawSlide(canvas); // Advance our timing bookkeeping + fCumulativeMeasurementTime += fAnimateTimes[fCurrentMeasurement] + + fPaintTimes[fCurrentMeasurement] + + fFlushTimes[fCurrentMeasurement]; + fCumulativeMeasurementCount++; fCurrentMeasurement = (fCurrentMeasurement + 1) & (kMeasurementCount - 1); SkASSERT(fCurrentMeasurement < kMeasurementCount); @@ -864,10 +973,10 @@ void Viewer::onPaint(SkCanvas* canvas) { } fCommands.drawHelp(canvas); - drawImGui(canvas); + this->drawImGui(canvas); // Update the FPS - updateUIState(); + this->updateUIState(); } bool Viewer::onTouch(intptr_t owner, Window::InputState state, float x, float y) { @@ -895,32 +1004,36 @@ bool Viewer::onTouch(intptr_t owner, Window::InputState state, float x, float y) } bool Viewer::onMouse(float x, float y, Window::InputState state, uint32_t modifiers) { - if (GestureDevice::kTouch == fGestureDevice) { - return false; - } - switch (state) { - case Window::kUp_InputState: { - fGesture.touchEnd(nullptr); - break; - } - case Window::kDown_InputState: { - fGesture.touchBegin(nullptr, x, y); - break; + if (!fSlides[fCurrentSlide]->onMouse(x, y, state, modifiers)) { + if (GestureDevice::kTouch == fGestureDevice) { + return false; } - case Window::kMove_InputState: { - fGesture.touchMoved(nullptr, x, y); - break; + switch (state) { + case Window::kUp_InputState: { + fGesture.touchEnd(nullptr); + break; + } + case Window::kDown_InputState: { + fGesture.touchBegin(nullptr, x, y); + break; + } + case Window::kMove_InputState: { + fGesture.touchMoved(nullptr, x, y); + break; + } } + fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kMouse : GestureDevice::kNone; } - fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kMouse : GestureDevice::kNone; fWindow->inval(); return true; } void Viewer::drawStats(SkCanvas* canvas) { static const float kPixelPerMS = 2.0f; - static const int kDisplayWidth = 130; - static const int kDisplayHeight = 100; + static const int kDisplayWidth = 192; + static const int kGraphHeight = 100; + static const int kTextHeight = 60; + static const int kDisplayHeight = kGraphHeight + kTextHeight; static const int kDisplayPadding = 10; static const int kGraphPadding = 3; static const SkScalar kBaseMS = 1000.f / 60.f; // ms/frame to hit 60 fps @@ -932,7 +1045,6 @@ void Viewer::drawStats(SkCanvas* canvas) { SkPaint paint; canvas->save(); - canvas->clipRect(rect); paint.setColor(SK_ColorBLACK); canvas->drawRect(rect, paint); // draw the 16ms line @@ -942,35 +1054,73 @@ void Viewer::drawStats(SkCanvas* canvas) { paint.setColor(SK_ColorRED); paint.setStyle(SkPaint::kStroke_Style); canvas->drawRect(rect, paint); + paint.setStyle(SkPaint::kFill_Style); int x = SkScalarTruncToInt(rect.fLeft) + kGraphPadding; - const int xStep = 2; + const int xStep = 3; int i = fCurrentMeasurement; + double ms = 0; + double animateMS = 0; + double paintMS = 0; + double flushMS = 0; + int count = 0; do { // Round to nearest values int animateHeight = (int)(fAnimateTimes[i] * kPixelPerMS + 0.5); int paintHeight = (int)(fPaintTimes[i] * kPixelPerMS + 0.5); int flushHeight = (int)(fFlushTimes[i] * kPixelPerMS + 0.5); int startY = SkScalarTruncToInt(rect.fBottom); - int endY = startY - flushHeight; + int endY = SkTMax(startY - flushHeight, kDisplayPadding + kTextHeight); paint.setColor(SK_ColorRED); canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY), SkIntToScalar(x), SkIntToScalar(endY), paint); startY = endY; - endY = startY - paintHeight; + endY = SkTMax(startY - paintHeight, kDisplayPadding + kTextHeight); paint.setColor(SK_ColorGREEN); canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY), SkIntToScalar(x), SkIntToScalar(endY), paint); startY = endY; - endY = startY - animateHeight; + endY = SkTMax(startY - animateHeight, kDisplayPadding + kTextHeight); paint.setColor(SK_ColorMAGENTA); canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY), SkIntToScalar(x), SkIntToScalar(endY), paint); + + double inc = fAnimateTimes[i] + fPaintTimes[i] + fFlushTimes[i]; + if (inc > 0) { + ms += inc; + animateMS += fAnimateTimes[i]; + paintMS += fPaintTimes[i]; + flushMS += fFlushTimes[i]; + ++count; + } + i++; i &= (kMeasurementCount - 1); // fast mod x += xStep; } while (i != fCurrentMeasurement); + paint.setTextSize(16); + SkString mainString; + mainString.appendf("%4.3f ms -> %4.3f ms", ms / SkTMax(1, count), + fCumulativeMeasurementTime / SkTMax(1, fCumulativeMeasurementCount)); + paint.setColor(SK_ColorWHITE); + canvas->drawString(mainString.c_str(), rect.fLeft+3, rect.fTop + 14, paint); + + SkString animateString; + animateString.appendf("Animate: %4.3f ms", animateMS / SkTMax(1, count)); + paint.setColor(0xffff66ff); // pure magenta is hard to read + canvas->drawString(animateString.c_str(), rect.fLeft+3, rect.fTop + 28, paint); + + SkString paintString; + paintString.appendf("Paint: %4.3f ms", paintMS / SkTMax(1, count)); + paint.setColor(SK_ColorGREEN); + canvas->drawString(paintString.c_str(), rect.fLeft+3, rect.fTop + 42, paint); + + SkString flushString; + flushString.appendf("Flush: %4.3f ms", flushMS / SkTMax(1, count)); + paint.setColor(0xffff6666); // pure red is hard to read + canvas->drawString(flushString.c_str(), rect.fLeft+3, rect.fTop + 56, paint); + canvas->restore(); } @@ -1034,6 +1184,15 @@ static void ImGui_Primaries(SkColorSpacePrimaries* primaries, SkPaint* gamutPain ImGui::SetCursorPos(endPos); } +typedef std::function<void(SkCanvas*)> CustomGuiPainter; +static SkTArray<CustomGuiPainter> gCustomGuiPainters; + +static void ImGui_Skia_Callback(const ImVec2& size, CustomGuiPainter painter) { + intptr_t painterIndex = gCustomGuiPainters.count(); + gCustomGuiPainters.push_back(painter); + ImGui::Image((ImTextureID)painterIndex, size); +} + void Viewer::drawImGui(SkCanvas* canvas) { // Support drawing the ImGui demo window. Superfluous, but gives a good idea of what's possible if (fShowImGuiTestWindow) { @@ -1129,23 +1288,40 @@ void Viewer::drawImGui(SkCanvas* canvas) { } } + if (fShowSlidePicker) { + ImGui::SetNextTreeNodeOpen(true); + } + if (ImGui::CollapsingHeader("Slide")) { static ImGuiTextFilter filter; + static ImVector<const char*> filteredSlideNames; + static ImVector<int> filteredSlideIndices; + + if (fShowSlidePicker) { + ImGui::SetKeyboardFocusHere(); + fShowSlidePicker = false; + } + filter.Draw(); - int previousSlide = fCurrentSlide; - fCurrentSlide = 0; - for (auto slide : fSlides) { - if (filter.PassFilter(slide->getName().c_str())) { - ImGui::BulletText("%s", slide->getName().c_str()); - if (ImGui::IsItemClicked()) { - setupCurrentSlide(previousSlide); - break; + filteredSlideNames.clear(); + filteredSlideIndices.clear(); + int filteredIndex = 0; + for (int i = 0; i < fSlides.count(); ++i) { + const char* slideName = fSlides[i]->getName().c_str(); + if (filter.PassFilter(slideName) || i == fCurrentSlide) { + if (i == fCurrentSlide) { + filteredIndex = filteredSlideIndices.size(); } + filteredSlideNames.push_back(slideName); + filteredSlideIndices.push_back(i); } - ++fCurrentSlide; } - if (fCurrentSlide >= fSlides.count()) { - fCurrentSlide = previousSlide; + + int previousSlide = fCurrentSlide; + if (ImGui::ListBox("", &filteredIndex, filteredSlideNames.begin(), + filteredSlideNames.size(), 20)) { + fCurrentSlide = filteredSlideIndices[filteredIndex]; + setupCurrentSlide(previousSlide); } } @@ -1180,6 +1356,11 @@ void Viewer::drawImGui(SkCanvas* canvas) { } } + // When we're in xform canvas mode, we can alter the transfer function, too + if (ColorMode::kColorManagedSRGB8888_NonLinearBlending == fColorMode) { + ImGui::SliderFloat("Gamma", &fColorSpaceTransferFn.fG, 0.5f, 3.5f); + } + if (ImGui::Combo("Primaries", &primariesIdx, "sRGB\0AdobeRGB\0P3\0Rec. 2020\0Custom\0\0")) { if (primariesIdx >= 0 && primariesIdx <= 3) { @@ -1201,24 +1382,42 @@ void Viewer::drawImGui(SkCanvas* canvas) { ImGui::End(); } - SkPaint zoomImagePaint; if (fShowZoomWindow && fLastImage) { if (ImGui::Begin("Zoom", &fShowZoomWindow, ImVec2(200, 200))) { - static int zoomFactor = 4; - ImGui::SliderInt("Scale", &zoomFactor, 1, 16); - - zoomImagePaint.setShader(fLastImage->makeShader()); - zoomImagePaint.setColor(SK_ColorWHITE); + static int zoomFactor = 8; + if (ImGui::Button("<<")) { + zoomFactor = SkTMax(zoomFactor / 2, 4); + } + ImGui::SameLine(); ImGui::Text("%2d", zoomFactor); ImGui::SameLine(); + if (ImGui::Button(">>")) { + zoomFactor = SkTMin(zoomFactor * 2, 32); + } - // Zoom by shrinking the corner UVs towards the mouse cursor ImVec2 mousePos = ImGui::GetMousePos(); ImVec2 avail = ImGui::GetContentRegionAvail(); - ImVec2 zoomHalfExtents = ImVec2((avail.x * 0.5f) / zoomFactor, - (avail.y * 0.5f) / zoomFactor); - ImGui::Image(&zoomImagePaint, avail, - ImVec2(mousePos.x - zoomHalfExtents.x, mousePos.y - zoomHalfExtents.y), - ImVec2(mousePos.x + zoomHalfExtents.x, mousePos.y + zoomHalfExtents.y)); + uint32_t pixel = 0; + SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); + if (fLastImage->readPixels(info, &pixel, info.minRowBytes(), mousePos.x, mousePos.y)) { + ImGui::SameLine(); + ImGui::Text("(X, Y): %d, %d RGBA: %x %x %x %x", + sk_float_round2int(mousePos.x), sk_float_round2int(mousePos.y), + SkGetPackedR32(pixel), SkGetPackedG32(pixel), + SkGetPackedB32(pixel), SkGetPackedA32(pixel)); + } + + ImGui_Skia_Callback(avail, [=](SkCanvas* c) { + // Translate so the region of the image that's under the mouse cursor is centered + // in the zoom canvas: + c->scale(zoomFactor, zoomFactor); + c->translate(avail.x * 0.5f / zoomFactor - mousePos.x - 0.5f, + avail.y * 0.5f / zoomFactor - mousePos.y - 0.5f); + c->drawImage(this->fLastImage, 0, 0); + + SkPaint outline; + outline.setStyle(SkPaint::kStroke_Style); + c->drawRect(SkRect::MakeXYWH(mousePos.x, mousePos.y, 1, 1), outline); + }); } ImGui::End(); @@ -1254,27 +1453,42 @@ void Viewer::drawImGui(SkCanvas* canvas) { for (int j = 0; j < drawList->CmdBuffer.size(); ++j) { const ImDrawCmd* drawCmd = &drawList->CmdBuffer[j]; + SkAutoCanvasRestore acr(canvas, true); + // TODO: Find min/max index for each draw, so we know how many vertices (sigh) if (drawCmd->UserCallback) { drawCmd->UserCallback(drawList, drawCmd); } else { - SkPaint* paint = static_cast<SkPaint*>(drawCmd->TextureId); - SkASSERT(paint); - - canvas->save(); - canvas->clipRect(SkRect::MakeLTRB(drawCmd->ClipRect.x, drawCmd->ClipRect.y, - drawCmd->ClipRect.z, drawCmd->ClipRect.w)); - canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, - drawList->VtxBuffer.size(), pos.begin(), - uv.begin(), color.begin(), - drawCmd->ElemCount, - drawList->IdxBuffer.begin() + indexOffset), - SkBlendMode::kModulate, *paint); - indexOffset += drawCmd->ElemCount; - canvas->restore(); + intptr_t idIndex = (intptr_t)drawCmd->TextureId; + if (idIndex < gCustomGuiPainters.count()) { + // Small image IDs are actually indices into a list of callbacks. We directly + // examing the vertex data to deduce the image rectangle, then reconfigure the + // canvas to be clipped and translated so that the callback code gets to use + // Skia to render a widget in the middle of an ImGui panel. + ImDrawIdx rectIndex = drawList->IdxBuffer[indexOffset]; + SkPoint tl = pos[rectIndex], br = pos[rectIndex + 2]; + canvas->clipRect(SkRect::MakeLTRB(tl.fX, tl.fY, br.fX, br.fY)); + canvas->translate(tl.fX, tl.fY); + gCustomGuiPainters[idIndex](canvas); + } else { + SkPaint* paint = static_cast<SkPaint*>(drawCmd->TextureId); + SkASSERT(paint); + + canvas->clipRect(SkRect::MakeLTRB(drawCmd->ClipRect.x, drawCmd->ClipRect.y, + drawCmd->ClipRect.z, drawCmd->ClipRect.w)); + auto vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, + drawList->VtxBuffer.size(), + pos.begin(), uv.begin(), color.begin(), + drawCmd->ElemCount, + drawList->IdxBuffer.begin() + indexOffset); + canvas->drawVertices(vertices, SkBlendMode::kModulate, *paint); + indexOffset += drawCmd->ElemCount; + } } } } + + gCustomGuiPainters.reset(); } void Viewer::onIdle() { @@ -1409,7 +1623,7 @@ void Viewer::updateUIState() { state.append(softkeyState); state.append(fpsState); - fWindow->setUIState(state); + fWindow->setUIState(state.toStyledString().c_str()); } void Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateValue) { @@ -1437,7 +1651,7 @@ void Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateVa if (fBackendType != i) { fBackendType = (sk_app::Window::BackendType)i; fWindow->detach(); - fWindow->attach(fBackendType); + fWindow->attach(backend_type_for_window(fBackendType)); } break; } diff --git a/chromium/third_party/skia/tools/viewer/Viewer.h b/chromium/third_party/skia/tools/viewer/Viewer.h index 1b68007ede0..53b75b2de35 100644 --- a/chromium/third_party/skia/tools/viewer/Viewer.h +++ b/chromium/third_party/skia/tools/viewer/Viewer.h @@ -13,6 +13,8 @@ #include "sk_app/Window.h" #include "gm.h" #include "SkAnimTimer.h" +#include "SkExecutor.h" +#include "SkJSONCPP.h" #include "SkTouchGesture.h" #include "Slide.h" @@ -47,6 +49,7 @@ private: void setStartupSlide(); void setupCurrentSlide(int previousSlide); void listNames(); + void resetMeasurements(); void updateUIState(); @@ -57,13 +60,19 @@ private: void changeZoomLevel(float delta); SkMatrix computeMatrix(); + void resetExecutor() { + fExecutor = SkExecutor::MakeFIFOThreadPool(fThreadCnt == 0 ? fTileCnt : fThreadCnt); + } + sk_app::Window* fWindow; - static const int kMeasurementCount = 64; // should be power of 2 for fast mod + static const int kMeasurementCount = 1 << 6; // should be power of 2 for fast mod double fPaintTimes[kMeasurementCount]; double fFlushTimes[kMeasurementCount]; double fAnimateTimes[kMeasurementCount]; int fCurrentMeasurement; + double fCumulativeMeasurementTime; + int fCumulativeMeasurementCount; SkAnimTimer fAnimTimer; SkTArray<sk_sp<Slide>> fSlides; @@ -75,6 +84,7 @@ private: SkPaint fImGuiFontPaint; SkPaint fImGuiGamutPaint; bool fShowImGuiDebugWindow; + bool fShowSlidePicker; bool fShowImGuiTestWindow; bool fShowZoomWindow; @@ -85,6 +95,7 @@ private: // Color properties for slide rendering ColorMode fColorMode; SkColorSpacePrimaries fColorSpacePrimaries; + SkColorSpaceTransferFn fColorSpaceTransferFn; // transform data SkScalar fZoomLevel; @@ -106,6 +117,10 @@ private: SkTArray<std::function<void(void)>> fDeferredActions; Json::Value fAllSlideNames; // cache all slide names for fast updateUIState + + int fTileCnt; + int fThreadCnt; + std::unique_ptr<SkExecutor> fExecutor; }; |