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 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 38 Request::~Request() { 39 #if SK_SUPPORT_GPU 40 if (fContextFactory) { 41 delete fContextFactory; 42 } 43 #endif 44 } 45 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 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 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 95 void Request::drawToCanvas(int n, int m) { 96 SkCanvas* target = this->getCanvas(); 97 fDebugCanvas->drawTo(target, n, m); 98 } 99 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 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 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 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 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 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 194 bool Request::setOverdraw(bool enable) { 195 fOverdraw = enable; 196 return true; 197 } 198 199 bool Request::setColorMode(int mode) { 200 fColorMode = mode; 201 return enableGPU(fGPUEnabled); 202 } 203 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 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 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 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 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 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