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