1 /*
2 * Copyright 2016 Google Inc.
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 "Request.h"
9
10 #include "SkPictureRecorder.h"
11 #include "SkPM4fPriv.h"
12 #include "picture_utils.h"
13 #include "sk_tool_utils.h"
14
15 using namespace sk_gpu_test;
16
17 static int kDefaultWidth = 1920;
18 static int kDefaultHeight = 1080;
19 static int kMaxWidth = 8192;
20 static int kMaxHeight = 8192;
21
22
Request(SkString rootUrl)23 Request::Request(SkString rootUrl)
24 : fUploadContext(nullptr)
25 , fUrlDataManager(rootUrl)
26 , fGPUEnabled(false)
27 , fOverdraw(false)
28 , fColorMode(0) {
29 // create surface
30 #if SK_SUPPORT_GPU
31 GrContextOptions grContextOpts;
32 fContextFactory = new GrContextFactory(grContextOpts);
33 #else
34 fContextFactory = nullptr;
35 #endif
36 }
37
~Request()38 Request::~Request() {
39 #if SK_SUPPORT_GPU
40 if (fContextFactory) {
41 delete fContextFactory;
42 }
43 #endif
44 }
45
getBitmapFromCanvas(SkCanvas * canvas)46 SkBitmap* Request::getBitmapFromCanvas(SkCanvas* canvas) {
47 SkBitmap* bmp = new SkBitmap();
48 if (!bmp->tryAllocPixels(canvas->imageInfo()) || !canvas->readPixels(*bmp, 0, 0)) {
49 fprintf(stderr, "Can't read pixels\n");
50 delete bmp;
51 return nullptr;
52 }
53 return bmp;
54 }
55
writeCanvasToPng(SkCanvas * canvas)56 sk_sp<SkData> Request::writeCanvasToPng(SkCanvas* canvas) {
57 // capture pixels
58 std::unique_ptr<SkBitmap> bmp(this->getBitmapFromCanvas(canvas));
59 SkASSERT(bmp);
60
61 // Convert to format suitable for PNG output
62 sk_sp<SkData> encodedBitmap = sk_tools::encode_bitmap_for_png(*bmp);
63 SkASSERT(encodedBitmap.get());
64
65 // write to an opaque png (black background)
66 SkDynamicMemoryWStream buffer;
67 SkDrawCommand::WritePNG(encodedBitmap->bytes(), bmp->width(), bmp->height(),
68 buffer, true);
69 return buffer.detachAsData();
70 }
71
getCanvas()72 SkCanvas* Request::getCanvas() {
73 #if SK_SUPPORT_GPU
74 GrContextFactory* factory = fContextFactory;
75 GLTestContext* gl = factory->getContextInfo(GrContextFactory::kGL_ContextType,
76 GrContextFactory::ContextOverrides::kNone).glContext();
77 if (!gl) {
78 gl = factory->getContextInfo(GrContextFactory::kGLES_ContextType,
79 GrContextFactory::ContextOverrides::kNone).glContext();
80 }
81 if (gl) {
82 gl->makeCurrent();
83 }
84 #endif
85 SkASSERT(fDebugCanvas);
86
87 // create the appropriate surface if necessary
88 if (!fSurface) {
89 this->enableGPU(fGPUEnabled);
90 }
91 SkCanvas* target = fSurface->getCanvas();
92 return target;
93 }
94
drawToCanvas(int n,int m)95 void Request::drawToCanvas(int n, int m) {
96 SkCanvas* target = this->getCanvas();
97 fDebugCanvas->drawTo(target, n, m);
98 }
99
drawToPng(int n,int m)100 sk_sp<SkData> Request::drawToPng(int n, int m) {
101 //fDebugCanvas->setOverdrawViz(true);
102 this->drawToCanvas(n, m);
103 //fDebugCanvas->setOverdrawViz(false);
104 return writeCanvasToPng(this->getCanvas());
105 }
106
writeOutSkp()107 sk_sp<SkData> Request::writeOutSkp() {
108 // Playback into picture recorder
109 SkIRect bounds = this->getBounds();
110 SkPictureRecorder recorder;
111 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bounds.width()),
112 SkIntToScalar(bounds.height()));
113
114 fDebugCanvas->draw(canvas);
115
116 return recorder.finishRecordingAsPicture()->serialize();
117 }
118
getContext()119 GrContext* Request::getContext() {
120 #if SK_SUPPORT_GPU
121 GrContext* result = fContextFactory->get(GrContextFactory::kGL_ContextType,
122 GrContextFactory::ContextOverrides::kNone);
123 if (!result) {
124 result = fContextFactory->get(GrContextFactory::kGLES_ContextType,
125 GrContextFactory::ContextOverrides::kNone);
126 }
127 return result;
128 #else
129 return nullptr;
130 #endif
131 }
132
getBounds()133 SkIRect Request::getBounds() {
134 SkIRect bounds;
135 if (fPicture) {
136 bounds = fPicture->cullRect().roundOut();
137 if (fGPUEnabled) {
138 #if SK_SUPPORT_GPU
139 int maxRTSize = this->getContext()->caps()->maxRenderTargetSize();
140 bounds = SkIRect::MakeWH(SkTMin(bounds.width(), maxRTSize),
141 SkTMin(bounds.height(), maxRTSize));
142 #endif
143 }
144 } else {
145 bounds = SkIRect::MakeWH(kDefaultWidth, kDefaultHeight);
146 }
147
148 // We clip to kMaxWidth / kMaxHeight for performance reasons.
149 // TODO make this configurable
150 bounds = SkIRect::MakeWH(SkTMin(bounds.width(), kMaxWidth),
151 SkTMin(bounds.height(), kMaxHeight));
152 return bounds;
153 }
154
155 namespace {
156
157 struct ColorAndProfile {
158 SkColorType fColorType;
159 bool fSRGB;
160 };
161
162 ColorAndProfile ColorModes[] = {
163 { kN32_SkColorType, false },
164 { kN32_SkColorType, true },
165 { kRGBA_F16_SkColorType, true },
166 };
167
168 }
169
createCPUSurface()170 SkSurface* Request::createCPUSurface() {
171 SkIRect bounds = this->getBounds();
172 ColorAndProfile cap = ColorModes[fColorMode];
173 auto colorSpace = kRGBA_F16_SkColorType == cap.fColorType
174 ? SkColorSpace::MakeSRGBLinear()
175 : SkColorSpace::MakeSRGB();
176 SkImageInfo info = SkImageInfo::Make(bounds.width(), bounds.height(), cap.fColorType,
177 kPremul_SkAlphaType, cap.fSRGB ? colorSpace : nullptr);
178 return SkSurface::MakeRaster(info).release();
179 }
180
createGPUSurface()181 SkSurface* Request::createGPUSurface() {
182 GrContext* context = this->getContext();
183 SkIRect bounds = this->getBounds();
184 ColorAndProfile cap = ColorModes[fColorMode];
185 auto colorSpace = kRGBA_F16_SkColorType == cap.fColorType
186 ? SkColorSpace::MakeSRGBLinear()
187 : SkColorSpace::MakeSRGB();
188 SkImageInfo info = SkImageInfo::Make(bounds.width(), bounds.height(), cap.fColorType,
189 kPremul_SkAlphaType, cap.fSRGB ? colorSpace: nullptr);
190 SkSurface* surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info).release();
191 return surface;
192 }
193
setOverdraw(bool enable)194 bool Request::setOverdraw(bool enable) {
195 fOverdraw = enable;
196 return true;
197 }
198
setColorMode(int mode)199 bool Request::setColorMode(int mode) {
200 fColorMode = mode;
201 return enableGPU(fGPUEnabled);
202 }
203
enableGPU(bool enable)204 bool Request::enableGPU(bool enable) {
205 if (enable) {
206 SkSurface* surface = this->createGPUSurface();
207 if (surface) {
208 fSurface.reset(surface);
209 fGPUEnabled = true;
210
211 // When we switch to GPU, there seems to be some mystery draws in the canvas. So we
212 // draw once to flush the pipe
213 // TODO understand what is actually happening here
214 if (fDebugCanvas) {
215 fDebugCanvas->drawTo(this->getCanvas(), this->getLastOp());
216 this->getCanvas()->flush();
217 }
218
219 return true;
220 }
221 return false;
222 }
223 fSurface.reset(this->createCPUSurface());
224 fGPUEnabled = false;
225 return true;
226 }
227
initPictureFromStream(SkStream * stream)228 bool Request::initPictureFromStream(SkStream* stream) {
229 // parse picture from stream
230 fPicture = SkPicture::MakeFromStream(stream);
231 if (!fPicture) {
232 fprintf(stderr, "Could not create picture from stream.\n");
233 return false;
234 }
235
236 // reinitialize canvas with the new picture dimensions
237 this->enableGPU(fGPUEnabled);
238
239 // pour picture into debug canvas
240 SkIRect bounds = this->getBounds();
241 fDebugCanvas.reset(new SkDebugCanvas(bounds.width(), bounds.height()));
242 fDebugCanvas->drawPicture(fPicture);
243
244 // for some reason we need to 'flush' the debug canvas by drawing all of the ops
245 fDebugCanvas->drawTo(this->getCanvas(), this->getLastOp());
246 this->getCanvas()->flush();
247 return true;
248 }
249
getJsonOps(int n)250 sk_sp<SkData> Request::getJsonOps(int n) {
251 SkCanvas* canvas = this->getCanvas();
252 Json::Value root = fDebugCanvas->toJSON(fUrlDataManager, n, canvas);
253 root["mode"] = Json::Value(fGPUEnabled ? "gpu" : "cpu");
254 root["drawGpuOpBounds"] = Json::Value(fDebugCanvas->getDrawGpuOpBounds());
255 root["colorMode"] = Json::Value(fColorMode);
256 SkDynamicMemoryWStream stream;
257 stream.writeText(Json::FastWriter().write(root).c_str());
258
259 return stream.detachAsData();
260 }
261
getJsonOpList(int n)262 sk_sp<SkData> Request::getJsonOpList(int n) {
263 SkCanvas* canvas = this->getCanvas();
264 SkASSERT(fGPUEnabled);
265
266 Json::Value result = fDebugCanvas->toJSONOpList(n, canvas);
267
268 SkDynamicMemoryWStream stream;
269 stream.writeText(Json::FastWriter().write(result).c_str());
270
271 return stream.detachAsData();
272 }
273
getJsonInfo(int n)274 sk_sp<SkData> Request::getJsonInfo(int n) {
275 // drawTo
276 sk_sp<SkSurface> surface(this->createCPUSurface());
277 SkCanvas* canvas = surface->getCanvas();
278
279 // TODO this is really slow and we should cache the matrix and clip
280 fDebugCanvas->drawTo(canvas, n);
281
282 // make some json
283 SkMatrix vm = fDebugCanvas->getCurrentMatrix();
284 SkIRect clip = fDebugCanvas->getCurrentClip();
285 Json::Value info(Json::objectValue);
286 info["ViewMatrix"] = SkDrawCommand::MakeJsonMatrix(vm);
287 info["ClipRect"] = SkDrawCommand::MakeJsonIRect(clip);
288
289 std::string json = Json::FastWriter().write(info);
290
291 // We don't want the null terminator so strlen is correct
292 return SkData::MakeWithCopy(json.c_str(), strlen(json.c_str()));
293 }
294
getPixel(int x,int y)295 SkColor Request::getPixel(int x, int y) {
296 SkCanvas* canvas = this->getCanvas();
297 canvas->flush();
298 std::unique_ptr<SkBitmap> bitmap(this->getBitmapFromCanvas(canvas));
299 SkASSERT(bitmap);
300
301 // Convert to format suitable for inspection
302 sk_sp<SkData> encodedBitmap = sk_tools::encode_bitmap_for_png(*bitmap);
303 SkASSERT(encodedBitmap);
304
305 const uint8_t* start = encodedBitmap->bytes() + ((y * bitmap->width() + x) * 4);
306 SkColor result = SkColorSetARGB(start[3], start[0], start[1], start[2]);
307 return result;
308 }
309