blob: 7c803f089bfd82f249db7358196f5079b5cc6e6e [file] [log] [blame]
Robert Phillips94e67912021-01-21 13:39:08 -05001/*
2 * Copyright 2021 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "gm/gm.h"
9#include "include/core/SkCanvas.h"
10#include "include/core/SkPaint.h"
11#include "include/core/SkRect.h"
12#include "include/core/SkSize.h"
13#include "include/core/SkString.h"
14#include "include/gpu/GrDirectContext.h"
15
Robert Phillips7a0d3c32021-07-21 15:39:51 -040016#include "src/core/SkCanvasPriv.h"
Robert Phillips94e67912021-01-21 13:39:08 -050017#include "src/core/SkConvertPixels.h"
18#include "src/gpu/GrDirectContextPriv.h"
19#include "src/gpu/GrPaint.h"
20#include "src/gpu/GrProxyProvider.h"
21#include "src/gpu/GrResourceProvider.h"
Robert Phillips94e67912021-01-21 13:39:08 -050022#include "src/gpu/SkGr.h"
23#include "src/gpu/effects/GrTextureEffect.h"
Robert Phillips4dca8312021-07-28 15:13:20 -040024#include "src/gpu/v1/SurfaceDrawContext_v1.h"
Robert Phillips94e67912021-01-21 13:39:08 -050025
26#include "tools/gpu/ProxyUtils.h"
27
28static GrSurfaceProxyView create_view(GrDirectContext* dContext,
29 const SkBitmap& src,
30 GrSurfaceOrigin origin) {
31 SkASSERT(src.colorType() == kRGBA_8888_SkColorType);
32
33#define USE_LAZY_PROXIES 1 // Toggle this to generate the reference images
34
35 if (USE_LAZY_PROXIES) {
36 auto format = dContext->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
37 GrRenderable::kNo);
38 if (!format.isValid()) {
39 return {};
40 }
41
42 sk_sp<GrTextureProxy> proxy = GrProxyProvider::MakeFullyLazyProxy(
43 [src](GrResourceProvider* rp,
44 const GrSurfaceProxy::LazySurfaceDesc& desc)
45 -> GrSurfaceProxy::LazyCallbackResult {
46 SkASSERT(desc.fMipmapped == GrMipmapped::kNo);
Brian Salomon2c673402021-04-02 14:36:58 -040047 GrMipLevel mipLevel = {src.getPixels(), src.rowBytes(), nullptr};
Robert Phillips94e67912021-01-21 13:39:08 -050048 auto colorType = SkColorTypeToGrColorType(src.colorType());
49
50 return rp->createTexture(src.dimensions(), desc.fFormat, colorType,
51 desc.fRenderable, desc.fSampleCnt, desc.fBudgeted,
52 desc.fFit, desc.fProtected, mipLevel);
53 },
54 format, GrRenderable::kNo, 1, GrProtected::kNo, *dContext->priv().caps(),
55 GrSurfaceProxy::UseAllocator::kYes);
56
57 if (!proxy) {
58 return {};
59 }
60
61 auto swizzle = dContext->priv().caps()->getReadSwizzle(proxy->backendFormat(),
62 GrColorType::kRGBA_8888);
63 return GrSurfaceProxyView(std::move(proxy), origin, swizzle);
64 }
65
66 return sk_gpu_test::MakeTextureProxyViewFromData(dContext,
67 GrRenderable::kNo,
68 origin,
69 src.pixmap());
70}
71
72// Create an over large texture which is initialized to opaque black outside of the content
73// rect. The inside of the content rect consists of a grey coordinate frame lacking the -Y axis.
74// The -X and +X axes have a red and green dot at their ends (respectively). Finally, the content
75// rect has a 1-pixel wide blue border.
76static SkBitmap create_bitmap(SkIRect contentRect, SkISize fullSize, GrSurfaceOrigin origin) {
77
78 const int kContentSize = contentRect.width();
79 SkBitmap contentBM;
80
81 {
82 SkImageInfo contentInfo = SkImageInfo::Make(kContentSize, kContentSize,
83 kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
84 contentBM.allocPixels(contentInfo);
85
86 contentBM.eraseColor(SK_ColorWHITE);
87
88 const int halfM1 = kContentSize/2 - 1;
89
90 // The coordinate frame
91 contentBM.eraseArea(SkIRect::MakeXYWH(halfM1, 2,2, halfM1), SK_ColorGRAY);
92 contentBM.eraseArea(SkIRect::MakeXYWH(2, halfM1, kContentSize-4, 2), SK_ColorGRAY);
93
94 contentBM.eraseArea(SkIRect::MakeXYWH(2, halfM1, 2, 2), SK_ColorRED);
95 contentBM.eraseArea(SkIRect::MakeXYWH(kContentSize-4, halfM1, 2, 2), SK_ColorGREEN);
96
97 // The 1-pixel wide rim around the content rect
98 contentBM.eraseArea(SkIRect::MakeXYWH(0, 0, kContentSize, 1), SK_ColorBLUE);
99 contentBM.eraseArea(SkIRect::MakeXYWH(0, 0, 1, kContentSize), SK_ColorBLUE);
100 contentBM.eraseArea(SkIRect::MakeXYWH(kContentSize-1, 0, 1, kContentSize), SK_ColorBLUE);
101 contentBM.eraseArea(SkIRect::MakeXYWH(0, kContentSize-1, kContentSize, 1), SK_ColorBLUE);
102 }
103
104 SkBitmap bigBM;
105
106 {
107 const int kLeft = contentRect.fLeft;
108 const int kTop = contentRect.fTop;
109
110 SkImageInfo bigInfo = SkImageInfo::Make(fullSize.fWidth, fullSize.fHeight,
111 kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
112
113 bigBM.allocPixels(bigInfo);
114
115 bigBM.eraseColor(SK_ColorBLACK);
116
117 const char* src = static_cast<const char*>(contentBM.getPixels());
118 size_t srcRB = contentBM.rowBytes();
119 size_t dstRB = bigBM.rowBytes();
120
121 if (USE_LAZY_PROXIES && origin == kBottomLeft_GrSurfaceOrigin) {
122 char* dst = static_cast<char*>(bigBM.getAddr(kLeft, fullSize.height() - kTop - 1));
123 for (int y = 0; y < contentBM.height(); ++y) {
124 memcpy(dst, src, srcRB);
125 src = src + srcRB;
126 dst = dst - dstRB;
127 }
128 } else {
129 char* dst = static_cast<char*>(bigBM.getAddr(kLeft, kTop));
130 SkRectMemcpy(dst, dstRB, src, srcRB,
131 contentBM.rowBytes(), contentBM.height());
132 }
133
134 bigBM.setAlphaType(kOpaque_SkAlphaType);
135 bigBM.setImmutable();
136 }
137
138 return bigBM;
139}
140
141static void draw_texture(const GrCaps* caps,
Robert Phillips4dca8312021-07-28 15:13:20 -0400142 skgpu::v1::SurfaceDrawContext* sdc,
Robert Phillips94e67912021-01-21 13:39:08 -0500143 const GrSurfaceProxyView& src,
144 const SkIRect& srcRect,
145 const SkIRect& drawRect,
146 const SkMatrix& mat,
147 GrSamplerState::WrapMode xTileMode,
148 GrSamplerState::WrapMode yTileMode) {
149 GrSamplerState sampler(xTileMode, yTileMode, SkFilterMode::kNearest);
150
151 auto fp = GrTextureEffect::MakeSubset(src, kOpaque_SkAlphaType, mat,
152 sampler, SkRect::Make(srcRect), *caps);
153 GrPaint paint;
154 paint.setColorFragmentProcessor(std::move(fp));
155
156 sdc->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(), SkRect::Make(drawRect));
157}
158
159namespace skiagm {
160
161// This GM exercises all the different tile modes for a texture that cannot be normalized
162// early (i.e., rectangle or fully-lazy).
163// TODO: should we also test w/ mipmapping?
164class LazyTilingGM : public GpuGM {
165public:
166 LazyTilingGM(GrSurfaceOrigin origin)
167 : fOrigin(origin)
168 , fContentRect(SkIRect::MakeXYWH(kLeftContentOffset, kTopContentOffset,
169 kContentSize, kContentSize)) {
170 this->setBGColor(0xFFCCCCCC);
171 }
172
173protected:
174
175 SkString onShortName() override {
176 return SkStringPrintf("lazytiling_%s", fOrigin == kTopLeft_GrSurfaceOrigin ? "tl" : "bl");
177 }
178
179 SkISize onISize() override {
180 return SkISize::Make(kTotalWidth, kTotalHeight);
181 }
182
183 DrawResult onGpuSetup(GrDirectContext* dContext, SkString* errorMsg) override {
184 if (!dContext || dContext->abandoned()) {
185 return DrawResult::kSkip;
186 }
187
188 auto bm = create_bitmap(fContentRect,
189 { kLeftContentOffset + kContentSize + kRightContentPadding,
190 kTopContentOffset + kContentSize + kBottomContentPadding },
191 fOrigin);
192
193 fView = create_view(dContext, bm, fOrigin);
194 if (!fView.proxy()) {
195 *errorMsg = "Failed to create proxy";
196 return DrawResult::kFail;
197 }
198
199 return DrawResult::kOk;
200 }
201
Robert Phillips7a0d3c32021-07-21 15:39:51 -0400202 DrawResult onDraw(GrRecordingContext* rContext, SkCanvas* canvas, SkString* errorMsg) override {
Robert Phillips94e67912021-01-21 13:39:08 -0500203 SkSamplingOptions sampling(SkFilterMode::kNearest, SkMipmapMode::kNone);
204 SkPaint p;
205
Robert Phillips7a0d3c32021-07-21 15:39:51 -0400206 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
207 if (!sdc) {
208 *errorMsg = kErrorMsg_DrawSkippedGpuOnly;
209 return DrawResult::kSkip;
210 }
211
Robert Phillips94e67912021-01-21 13:39:08 -0500212 int y = kPad;
213 for (auto yMode : { SkTileMode::kClamp, SkTileMode::kRepeat,
214 SkTileMode::kMirror, SkTileMode::kDecal }) {
215 int x = kPad;
216 for (auto xMode : { SkTileMode::kClamp, SkTileMode::kRepeat,
217 SkTileMode::kMirror, SkTileMode::kDecal }) {
218 SkIRect cellRect = SkIRect::MakeXYWH(x, y, 2*kContentSize, 2*kContentSize);
219 SkRect contentRect = SkRect::MakeXYWH(x+kContentSize/2, y+kContentSize/2,
220 kContentSize, kContentSize);
221
222 SkMatrix texMatrix = SkMatrix::RectToRect(contentRect, SkRect::Make(fContentRect));
223
224 draw_texture(rContext->priv().caps(),
225 sdc,
226 fView,
227 fContentRect,
228 cellRect,
229 texMatrix,
230 SkTileModeToWrapMode(xMode),
231 SkTileModeToWrapMode(yMode));
232
233 x += 2*kContentSize+kPad;
234 }
235
236 y += 2*kContentSize+kPad;
237 }
238
Robert Phillips7a0d3c32021-07-21 15:39:51 -0400239 return DrawResult::kOk;
Robert Phillips94e67912021-01-21 13:39:08 -0500240 }
241
242private:
243 static constexpr int kLeftContentOffset = 8;
244 static constexpr int kTopContentOffset = 16;
245 static constexpr int kRightContentPadding = 24;
246 static constexpr int kBottomContentPadding = 80;
247
248 static constexpr int kPad = 4; // on-screen padding between cells
249
250 static constexpr int kContentSize = 32;
251
252 // Each cell in this GM's grid is a square - 2*kContentSize on a side
253 static constexpr int kTotalWidth = (2*kContentSize+kPad) * kSkTileModeCount + kPad;
254 static constexpr int kTotalHeight = (2*kContentSize+kPad) * kSkTileModeCount + kPad;
255
256 GrSurfaceOrigin fOrigin;
257 SkIRect fContentRect;
258 GrSurfaceProxyView fView;
259
260 using INHERITED = GM;
261};
262
263//////////////////////////////////////////////////////////////////////////////
264
265DEF_GM(return new LazyTilingGM(kTopLeft_GrSurfaceOrigin);)
266DEF_GM(return new LazyTilingGM(kBottomLeft_GrSurfaceOrigin);)
267
268} // namespace skiagm