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