skia2/gm/crbug_224618.cpp
Michael Ludwig 949ceb249f Add ClipToW0 utility function for GrQuads,
and improve perspective handling in rest of quad pipeline

This function produces 0, 1, or 2 quads clipped to w > 0, with proper
local coords. To make its signature a little easier to reason about,
I added a simple 'DrawQuad' struct that represents the combination of
device and local coordinates, and edge AA flags. I am open to suggestions
for the name.

GrQuad::bounds() remains perspective aware so that it is always correct.
I updated CropToRect to check for w < 0 and not attempt to crop in that
scenario. Theoretically, we could clip to w = 0 first and then go through
the CropToRect optimization path. However, with the current state of the
GrFillRectOp and GrTextureOp, that made it more annoying to have the bulk
APIs handle the w clipping as well.

So for now, the w plane clipping is entirely the responsibility of the
ops. A benefit of this approach is that GrRenderTargetContext doesn't need
to be modified, and in the case where the clipping produces 2 quads they
are automatically put in the same op w/o going through any batching code.

However, it is becoming clear to me that managing 4 effective code paths
(fill + texture X simple + bulk API) is more maintenance than it's worth.
I added skbug:9869 to work out how to simplify these op creation APIs further,
and if we succeed there, I think it will make applying the W plane clipping
more convenient as well.

For now, since this affects SkiaRenderer on Linux stable, I am pushing
for correctness.

Bug: skia:9779, chromium:224618
Change-Id: I4218a956cbe0bbc2b5c9cf133a069d54c93848e8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/268686
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
2020-02-07 19:06:51 +00:00

132 lines
4.8 KiB
C++

/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkImage.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkMatrix44.h"
#include "include/core/SkSurface.h"
#include "include/effects/SkGradientShader.h"
#include "include/private/SkM44.h"
#include "tools/timer/TimeUtils.h"
static SkM44 rotate_axis_angle(SkScalar x, SkScalar y, SkScalar z, SkScalar radians) {
// SkM44 doesn't expose any rotation factories yet
SkMatrix44 m;
m.setRotateAboutUnit(x, y, z, radians);
float a[16];
m.asColMajorf(a);
SkM44 m4;
m4.setColMajor(a);
return m4;
}
// Adapted from https://codepen.io/adamdupuis/pen/qLYzqB
class CrBug224618GM : public skiagm::GM {
public:
CrBug224618GM() : fTime(0.f) {}
protected:
SkString onShortName() override {
return SkString("crbug_224618");
}
SkISize onISize() override {
return SkISize::Make(kMaxVW, kMaxVW);
}
// This animates the FOV in viewer, to ensure the panorama covering rects are stable across
// a variety of perspective matrices
bool onAnimate(double nanos) override {
fTime = TimeUtils::Scaled(1e-9f * nanos, 0.5f);
return true;
}
void onOnceBeforeDraw() override {
static const SkColor kColors[2] = {SK_ColorTRANSPARENT, SkColorSetARGB(128, 255, 255, 255)};
sk_sp<SkShader> gradient = SkGradientShader::MakeRadial(
{200.f, 200.f}, 25.f, kColors, nullptr, 2, SkTileMode::kMirror,
SkGradientShader::kInterpolateColorsInPremul_Flag, nullptr);
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(400, 400);
SkPaint bgPaint;
bgPaint.setShader(gradient);
surface->getCanvas()->drawPaint(bgPaint);
fCubeImage = surface->makeImageSnapshot();
}
void onDraw(SkCanvas* canvas) override {
SkScalar viewportWidth = SkScalarMod(fTime, 10.f) / 10.f * (kMaxVW - kMinVW) + kMinVW;
SkScalar radius = viewportWidth / 2.f; // round?
// See https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/perspective
SkM44 proj{1.f, 0.f, 0.f, 0.f,
0.f, 1.f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
0.f, 0.f, -1.f / radius, 1.f};
SkM44 zoom = SkM44::Translate(0.f, 0.f, radius);
SkM44 postZoom = SkM44::Translate(0.f, 0.f, -radius - 1.f);
SkM44 rotateHorizontal = rotate_axis_angle(0.f, 1.f, 0.f, 2.356194490192345f);
// w in degrees will need to be converted to radians
SkV4 axisAngles[6] = {
{0.f, 1.f, 0.f, -90.f}, // rotateY(-90deg)
{1.f, 0.f, 0.f, 0.f}, // <none>
{0.f, 1.f, 0.f, 90.f}, // rotateY(90deg)
{0.f, 1.f, 0.f, 180.f}, // rotateY(180deg)
{1.f, 0.f, 0.f, -90.f}, // rotateX(-90deg)
{1.f, 0.f, 0.f, 90.f}, // rotateX(90deg)
};
SkColor faceColors[6] = {
SK_ColorRED,
SK_ColorGREEN,
SK_ColorBLUE,
SK_ColorYELLOW,
SkColorSetARGB(0xFF, 0xFF, 0xA5, 0x00), // orange css
SkColorSetARGB(0xFF, 0x80, 0x00, 0x80) // purple css
};
for (int i = 0; i < 6; ++i) {
SkM44 model = rotate_axis_angle(axisAngles[i].x, axisAngles[i].y, axisAngles[i].z,
SkDegreesToRadians(axisAngles[i].w));
model = SkM44::Translate(radius, radius) * proj * // project and place content
zoom * rotateHorizontal * model * postZoom * // main model matrix
SkM44::Translate(-radius, -radius); // center content
canvas->save();
canvas->experimental_concat44(model);
SkPaint fillPaint;
fillPaint.setAntiAlias(true);
fillPaint.setFilterQuality(kLow_SkFilterQuality);
fillPaint.setColor(faceColors[i]);
// Leverages GrFillRectOp on GPU backend
canvas->drawRect(SkRect::MakeWH(viewportWidth, viewportWidth), fillPaint);
// Leverages GrTextureOp on GPU backend, to ensure sure both quad paths handle clipping
canvas->drawImageRect(fCubeImage.get(),
SkRect::MakeWH(fCubeImage->width(), fCubeImage->height()),
SkRect::MakeWH(viewportWidth, viewportWidth), &fillPaint,
SkCanvas::kFast_SrcRectConstraint);
canvas->restore();
}
}
private:
static const int kMaxVW = 800;
static const int kMinVW = 300;
SkScalar fTime;
sk_sp<SkImage> fCubeImage;
};
DEF_GM(return new CrBug224618GM();)