dbfd7ab108
'static const' means, there must be at most one of these, and initialize it at compile time if possible or runtime if necessary. This leads to unexpected code execution, and TSAN* will complain about races on the guard variables. Generally 'constexpr' or 'const' are better choices. Neither can cause races: they're either intialized at compile time (constexpr) or intialized each time independently (const). This CL prefers constexpr where possible, and uses const where not. It even prefers constexpr over const where they don't make a difference... I want to have lots of examples of constexpr for people to see and mimic. The scoped-to-class static has nothing to do with any of this, and is not changed. * Not yet on the bots, which use an older TSAN. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2300623005 Review-Url: https://codereview.chromium.org/2300623005
295 lines
10 KiB
C++
295 lines
10 KiB
C++
/*
|
|
* 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 "gm.h"
|
|
#if SK_SUPPORT_GPU
|
|
#include "GrFragmentProcessor.h"
|
|
#include "GrCoordTransform.h"
|
|
#include "GrInvariantOutput.h"
|
|
#include "effects/GrXfermodeFragmentProcessor.h"
|
|
#include "glsl/GrGLSLFragmentProcessor.h"
|
|
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
|
#include "glsl/GrGLSLProgramBuilder.h"
|
|
#include "glsl/GrGLSLProgramDataManager.h"
|
|
#include "Resources.h"
|
|
#include "SkReadBuffer.h"
|
|
#include "SkShader.h"
|
|
#include "SkStream.h"
|
|
#include "SkTypeface.h"
|
|
#include "SkWriteBuffer.h"
|
|
|
|
namespace skiagm {
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
class DCShader : public SkShader {
|
|
public:
|
|
DCShader(const SkMatrix& matrix) : fDeviceMatrix(matrix) {}
|
|
|
|
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DCShader);
|
|
|
|
void flatten(SkWriteBuffer& buf) const override {
|
|
buf.writeMatrix(fDeviceMatrix);
|
|
}
|
|
|
|
sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
|
|
|
|
#ifndef SK_IGNORE_TO_STRING
|
|
void toString(SkString* str) const override {
|
|
str->appendf("DCShader: ()");
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
const SkMatrix fDeviceMatrix;
|
|
};
|
|
|
|
sk_sp<SkFlattenable> DCShader::CreateProc(SkReadBuffer& buf) {
|
|
SkMatrix matrix;
|
|
buf.readMatrix(&matrix);
|
|
return sk_make_sp<DCShader>(matrix);
|
|
}
|
|
|
|
class DCFP : public GrFragmentProcessor {
|
|
public:
|
|
DCFP(const SkMatrix& m) : fDeviceTransform(kDevice_GrCoordSet, m) {
|
|
this->addCoordTransform(&fDeviceTransform);
|
|
this->initClassID<DCFP>();
|
|
}
|
|
|
|
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
|
|
class DCGLFP : public GrGLSLFragmentProcessor {
|
|
void emitCode(EmitArgs& args) override {
|
|
GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
|
|
fragBuilder->codeAppendf("vec2 c = %s;",
|
|
fragBuilder->ensureFSCoords2D(args.fCoords, 0).c_str());
|
|
fragBuilder->codeAppend("vec2 r = mod(c, vec2(20.0));");
|
|
fragBuilder->codeAppend("vec4 color = vec4(0.5*sin(c.x / 15.0) + 0.5,"
|
|
"0.5*cos((c.x + c.y) / 15.0) + 0.5,"
|
|
"(r.x + r.y) / 20.0,"
|
|
"distance(r, vec2(15.0)) / 20.0 + 0.2);");
|
|
fragBuilder->codeAppendf("color.rgb *= color.a;"
|
|
"%s = color * %s;",
|
|
args.fOutputColor, GrGLSLExpr4(args.fInputColor).c_str());
|
|
}
|
|
void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override {}
|
|
};
|
|
return new DCGLFP;
|
|
}
|
|
|
|
const char* name() const override { return "DCFP"; }
|
|
|
|
void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
|
|
inout->mulByUnknownFourComponents();
|
|
}
|
|
|
|
private:
|
|
void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
|
|
GrProcessorKeyBuilder* b) const override {}
|
|
|
|
bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
|
|
|
|
GrCoordTransform fDeviceTransform;
|
|
};
|
|
|
|
sk_sp<GrFragmentProcessor> DCShader::asFragmentProcessor(const AsFPArgs&) const {
|
|
sk_sp<GrFragmentProcessor> inner(new DCFP(fDeviceMatrix));
|
|
return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
|
|
}
|
|
|
|
class DCShaderGM : public GM {
|
|
public:
|
|
DCShaderGM() {
|
|
this->setBGColor(sk_tool_utils::color_to_565(0xFFAABBCC));
|
|
}
|
|
|
|
~DCShaderGM() override {
|
|
for (int i = 0; i < fPrims.count(); ++i) {
|
|
delete fPrims[i];
|
|
}
|
|
}
|
|
|
|
protected:
|
|
|
|
SkString onShortName() override {
|
|
return SkString("dcshader");
|
|
}
|
|
|
|
SkISize onISize() override { return SkISize::Make(1000, 900); }
|
|
|
|
void onOnceBeforeDraw() override {
|
|
struct Rect : public Prim {
|
|
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
|
|
SkRect rect = SkRect::MakeXYWH(0, 0, 50, 50);
|
|
canvas->drawRect(rect, paint);
|
|
return rect;
|
|
}
|
|
};
|
|
|
|
struct Circle : public Prim {
|
|
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
|
|
constexpr SkScalar radius = 25;
|
|
canvas->drawCircle(radius, radius, radius, paint);
|
|
return SkRect::MakeXYWH(0, 0, 2 * radius, 2 * radius);
|
|
}
|
|
};
|
|
|
|
struct RRect : public Prim {
|
|
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
|
|
SkRRect rrect;
|
|
rrect.setRectXY(SkRect::MakeXYWH(0, 0, 50, 50), 10, 10);
|
|
canvas->drawRRect(rrect, paint);
|
|
return rrect.getBounds();
|
|
}
|
|
};
|
|
|
|
struct DRRect : public Prim {
|
|
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
|
|
SkRRect outerRRect;
|
|
outerRRect.setRectXY(SkRect::MakeXYWH(0, 0, 50, 50), 5, 5);
|
|
SkRRect innerRRect;
|
|
innerRRect.setRectXY(SkRect::MakeXYWH(5, 8, 35, 30), 8, 3);
|
|
canvas->drawDRRect(outerRRect, innerRRect, paint);
|
|
return outerRRect.getBounds();
|
|
}
|
|
};
|
|
struct Path : public Prim {
|
|
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
|
|
SkPath path;
|
|
path.addCircle(15, 15, 10);
|
|
path.addOval(SkRect::MakeXYWH(2, 2, 22, 37));
|
|
path.setFillType(SkPath::kEvenOdd_FillType);
|
|
canvas->drawPath(path, paint);
|
|
return path.getBounds();
|
|
}
|
|
};
|
|
|
|
struct Points : public Prim {
|
|
Points(SkCanvas::PointMode mode) : fMode(mode) {}
|
|
|
|
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
|
|
SkRandom random;
|
|
SkPoint points[500];
|
|
SkRect bounds = SkRect::MakeWH(50, 50);
|
|
int count = SkToInt(SK_ARRAY_COUNT(points));
|
|
if (SkCanvas::kPoints_PointMode != fMode) {
|
|
count = SkTMin(count, 10);
|
|
}
|
|
for (int p = 0; p < count; ++p) {
|
|
points[p].fX = random.nextUScalar1() * bounds.width();
|
|
points[p].fY = random.nextUScalar1() * bounds.width();
|
|
}
|
|
canvas->drawPoints(fMode, count, points, paint);
|
|
return bounds;
|
|
}
|
|
SkCanvas::PointMode fMode;
|
|
};
|
|
|
|
struct Text : public Prim {
|
|
SkRect draw(SkCanvas* canvas, const SkPaint& origPaint) override {
|
|
SkPaint paint = origPaint;
|
|
paint.setTextSize(30.f);
|
|
this->setFont(&paint);
|
|
const char* text = this->text();
|
|
const SkVector offset = SkVector::Make(10, 10);
|
|
canvas->drawText(text, strlen(text), offset.fX, offset.fY, paint);
|
|
SkRect bounds;
|
|
paint.measureText(text, strlen(text), &bounds);
|
|
bounds.offset(offset);
|
|
return bounds;
|
|
}
|
|
|
|
virtual void setFont(SkPaint* paint) {
|
|
sk_tool_utils::set_portable_typeface(paint);
|
|
}
|
|
|
|
virtual const char* text() const { return "Hello, Skia!"; }
|
|
};
|
|
|
|
fPrims.push_back(new Rect);
|
|
fPrims.push_back(new Circle);
|
|
fPrims.push_back(new RRect);
|
|
fPrims.push_back(new DRRect);
|
|
fPrims.push_back(new Path);
|
|
fPrims.push_back(new Points(SkCanvas::kPoints_PointMode));
|
|
fPrims.push_back(new Points(SkCanvas::kLines_PointMode));
|
|
fPrims.push_back(new Points(SkCanvas::kPolygon_PointMode));
|
|
fPrims.push_back(new Text);
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
// This GM exists to test a specific feature of the GPU backend. It does not work with the
|
|
// sw rasterizer, tile modes, etc.
|
|
if (nullptr == canvas->getGrContext()) {
|
|
skiagm::GM::DrawGpuOnlyMessage(canvas);
|
|
return;
|
|
}
|
|
SkPaint paint;
|
|
SkTArray<SkMatrix> devMats;
|
|
devMats.push_back().reset();
|
|
devMats.push_back().setRotate(45, 500, 500);
|
|
devMats.push_back().setRotate(-30, 200, 200);
|
|
devMats.back().setPerspX(-SK_Scalar1 / 2000);
|
|
devMats.back().setPerspY(SK_Scalar1 / 1000);
|
|
|
|
|
|
SkTArray<SkMatrix> viewMats;
|
|
viewMats.push_back().setScale(0.75f, 0.75f);
|
|
viewMats.push_back().setRotate(45, 50, 50);
|
|
viewMats.back().postScale(0.5f, 1.1f);
|
|
|
|
canvas->translate(10, 20);
|
|
canvas->save();
|
|
SkScalar tx = 0, maxTy = 0;
|
|
constexpr SkScalar kW = 900;
|
|
|
|
for (int aa = 0; aa < 2; ++aa) {
|
|
for (int i = 0; i < fPrims.count(); ++i) {
|
|
for (int j = 0; j < devMats.count(); ++j) {
|
|
for (int k = 0; k < viewMats.count(); ++k) {
|
|
paint.setShader(sk_make_sp<DCShader>(devMats[j]));
|
|
paint.setAntiAlias(SkToBool(aa));
|
|
canvas->save();
|
|
canvas->concat(viewMats[k]);
|
|
SkRect bounds = fPrims[i]->draw(canvas, paint);
|
|
canvas->restore();
|
|
viewMats[k].mapRect(&bounds);
|
|
// add margins
|
|
bounds.fRight += 20;
|
|
bounds.fBottom += 20;
|
|
canvas->translate(bounds.fRight, 0);
|
|
tx += bounds.fRight;
|
|
maxTy = SkTMax(bounds.fBottom, maxTy);
|
|
if (tx > kW) {
|
|
tx = 0;
|
|
canvas->restore();
|
|
canvas->translate(0, maxTy);
|
|
canvas->save();
|
|
maxTy = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
canvas->restore();
|
|
}
|
|
|
|
private:
|
|
struct Prim {
|
|
virtual ~Prim() {}
|
|
virtual SkRect draw(SkCanvas*, const SkPaint&) = 0;
|
|
};
|
|
|
|
SkTArray<Prim*> fPrims;
|
|
|
|
typedef GM INHERITED;
|
|
};
|
|
|
|
DEF_GM(return new DCShaderGM;)
|
|
}
|
|
#endif
|