| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "tools/skiaserve/Request.h" |
| |
| #include <memory> |
| |
| #include "include/core/SkBitmap.h" |
| #include "include/core/SkColorSpace.h" |
| #include "include/core/SkPictureRecorder.h" |
| #include "include/gpu/GrDirectContext.h" |
| #include "include/gpu/ganesh/SkSurfaceGanesh.h" |
| #include "src/utils/SkJSONWriter.h" |
| #include "tools/ToolUtils.h" |
| #include "tools/debugger/DrawCommand.h" |
| |
| using namespace sk_gpu_test; |
| |
| static int kDefaultWidth = 1920; |
| static int kDefaultHeight = 1080; |
| static int kMaxWidth = 8192; |
| static int kMaxHeight = 8192; |
| |
| |
| Request::Request(SkString rootUrl) |
| : fUploadContext(nullptr) |
| , fUrlDataManager(rootUrl) |
| , fGPUEnabled(false) |
| , fOverdraw(false) |
| , fColorMode(0) { |
| // create surface |
| GrContextOptions grContextOpts; |
| fContextFactory = new GrContextFactory(grContextOpts); |
| } |
| |
| Request::~Request() { |
| if (fContextFactory) { |
| delete fContextFactory; |
| } |
| } |
| |
| sk_sp<SkData> Request::writeCanvasToPng(SkCanvas* canvas) { |
| // capture pixels |
| SkBitmap bmp; |
| bmp.allocPixels(canvas->imageInfo()); |
| SkAssertResult(canvas->readPixels(bmp, 0, 0)); |
| |
| // write to an opaque png (black background) |
| SkDynamicMemoryWStream buffer; |
| DrawCommand::WritePNG(bmp, buffer); |
| return buffer.detachAsData(); |
| } |
| |
| SkCanvas* Request::getCanvas() { |
| #ifdef SK_GL |
| GrContextFactory* factory = fContextFactory; |
| GLTestContext* gl = factory->getContextInfo(skgpu::ContextType::kGL, |
| GrContextFactory::ContextOverrides::kNone).glContext(); |
| if (!gl) { |
| gl = factory->getContextInfo(skgpu::ContextType::kGLES, |
| GrContextFactory::ContextOverrides::kNone).glContext(); |
| } |
| if (gl) { |
| gl->makeCurrent(); |
| } |
| #endif |
| SkASSERT(fDebugCanvas); |
| |
| // create the appropriate surface if necessary |
| if (!fSurface) { |
| this->enableGPU(fGPUEnabled); |
| } |
| SkCanvas* target = fSurface->getCanvas(); |
| return target; |
| } |
| |
| sk_sp<SkData> Request::drawToPng(int n, int m) { |
| //fDebugCanvas->setOverdrawViz(true); |
| auto* canvas = this->getCanvas(); |
| canvas->clear(SK_ColorTRANSPARENT); |
| fDebugCanvas->drawTo(canvas, n, m); |
| //fDebugCanvas->setOverdrawViz(false); |
| return writeCanvasToPng(this->getCanvas()); |
| } |
| |
| sk_sp<SkData> Request::writeOutSkp() { |
| // Playback into picture recorder |
| SkIRect bounds = this->getBounds(); |
| SkPictureRecorder recorder; |
| SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bounds.width()), |
| SkIntToScalar(bounds.height())); |
| |
| fDebugCanvas->draw(canvas); |
| |
| return recorder.finishRecordingAsPicture()->serialize(); |
| } |
| |
| GrDirectContext* Request::directContext() { |
| auto result = fContextFactory->get(skgpu::ContextType::kGL, |
| GrContextFactory::ContextOverrides::kNone); |
| if (!result) { |
| result = fContextFactory->get(skgpu::ContextType::kGLES, |
| GrContextFactory::ContextOverrides::kNone); |
| } |
| return result; |
| } |
| |
| SkIRect Request::getBounds() { |
| SkIRect bounds; |
| if (fPicture) { |
| bounds = fPicture->cullRect().roundOut(); |
| if (fGPUEnabled) { |
| int maxRTSize = this->directContext()->maxRenderTargetSize(); |
| bounds = SkIRect::MakeWH(std::min(bounds.width(), maxRTSize), |
| std::min(bounds.height(), maxRTSize)); |
| } |
| } else { |
| bounds = SkIRect::MakeWH(kDefaultWidth, kDefaultHeight); |
| } |
| |
| // We clip to kMaxWidth / kMaxHeight for performance reasons. |
| // TODO make this configurable |
| bounds = SkIRect::MakeWH(std::min(bounds.width(), kMaxWidth), |
| std::min(bounds.height(), kMaxHeight)); |
| return bounds; |
| } |
| |
| namespace { |
| |
| struct ColorAndProfile { |
| SkColorType fColorType; |
| bool fSRGB; |
| }; |
| |
| ColorAndProfile ColorModes[] = { |
| { kN32_SkColorType, false }, |
| { kN32_SkColorType, true }, |
| { kRGBA_F16_SkColorType, true }, |
| }; |
| |
| } // namespace |
| |
| SkSurface* Request::createCPUSurface() { |
| SkIRect bounds = this->getBounds(); |
| ColorAndProfile cap = ColorModes[fColorMode]; |
| auto colorSpace = kRGBA_F16_SkColorType == cap.fColorType |
| ? SkColorSpace::MakeSRGBLinear() |
| : SkColorSpace::MakeSRGB(); |
| SkImageInfo info = SkImageInfo::Make(bounds.size(), cap.fColorType, kPremul_SkAlphaType, |
| cap.fSRGB ? colorSpace : nullptr); |
| return SkSurfaces::Raster(info).release(); |
| } |
| |
| SkSurface* Request::createGPUSurface() { |
| auto context = this->directContext(); |
| SkIRect bounds = this->getBounds(); |
| ColorAndProfile cap = ColorModes[fColorMode]; |
| auto colorSpace = kRGBA_F16_SkColorType == cap.fColorType |
| ? SkColorSpace::MakeSRGBLinear() |
| : SkColorSpace::MakeSRGB(); |
| SkImageInfo info = SkImageInfo::Make(bounds.size(), cap.fColorType, kPremul_SkAlphaType, |
| cap.fSRGB ? colorSpace : nullptr); |
| SkSurface* surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kNo, info).release(); |
| return surface; |
| } |
| |
| bool Request::setOverdraw(bool enable) { |
| fOverdraw = enable; |
| return true; |
| } |
| |
| bool Request::setColorMode(int mode) { |
| fColorMode = mode; |
| return enableGPU(fGPUEnabled); |
| } |
| |
| bool Request::enableGPU(bool enable) { |
| if (enable) { |
| SkSurface* surface = this->createGPUSurface(); |
| if (surface) { |
| fSurface.reset(surface); |
| fGPUEnabled = true; |
| |
| // When we switch to GPU, there seems to be some mystery draws in the canvas. So we |
| // draw once to flush the pipe |
| // TODO understand what is actually happening here |
| if (fDebugCanvas) { |
| fDebugCanvas->drawTo(this->getCanvas(), this->getLastOp()); |
| this->directContext()->flush(fSurface.get()); |
| } |
| |
| return true; |
| } |
| return false; |
| } |
| fSurface.reset(this->createCPUSurface()); |
| fGPUEnabled = false; |
| return true; |
| } |
| |
| bool Request::initPictureFromStream(SkStream* stream) { |
| // parse picture from stream |
| fPicture = SkPicture::MakeFromStream(stream); |
| if (!fPicture) { |
| fprintf(stderr, "Could not create picture from stream.\n"); |
| return false; |
| } |
| |
| // reinitialize canvas with the new picture dimensions |
| this->enableGPU(fGPUEnabled); |
| |
| // pour picture into debug canvas |
| SkIRect bounds = this->getBounds(); |
| fDebugCanvas = std::make_unique<DebugCanvas>(bounds.width(), bounds.height()); |
| fDebugCanvas->drawPicture(fPicture); |
| |
| // for some reason we need to 'flush' the debug canvas by drawing all of the ops |
| fDebugCanvas->drawTo(this->getCanvas(), this->getLastOp()); |
| this->directContext()->flush(fSurface.get()); |
| return true; |
| } |
| |
| sk_sp<SkData> Request::getJsonOps() { |
| SkCanvas* canvas = this->getCanvas(); |
| SkDynamicMemoryWStream stream; |
| SkJSONWriter writer(&stream, SkJSONWriter::Mode::kFast); |
| writer.beginObject(); // root |
| |
| writer.appendCString("mode", fGPUEnabled ? "gpu" : "cpu"); |
| writer.appendBool("drawGpuOpBounds", fDebugCanvas->getDrawGpuOpBounds()); |
| writer.appendS32("colorMode", fColorMode); |
| fDebugCanvas->toJSON(writer, fUrlDataManager, canvas); |
| |
| writer.endObject(); // root |
| writer.flush(); |
| return stream.detachAsData(); |
| } |
| |
| sk_sp<SkData> Request::getJsonOpsTask() { |
| SkCanvas* canvas = this->getCanvas(); |
| SkASSERT(fGPUEnabled); |
| SkDynamicMemoryWStream stream; |
| SkJSONWriter writer(&stream, SkJSONWriter::Mode::kFast); |
| |
| fDebugCanvas->toJSONOpsTask(writer, canvas); |
| |
| writer.flush(); |
| return stream.detachAsData(); |
| } |
| |
| sk_sp<SkData> Request::getJsonInfo(int n) { |
| // drawTo |
| sk_sp<SkSurface> surface(this->createCPUSurface()); |
| SkCanvas* canvas = surface->getCanvas(); |
| |
| // TODO this is really slow and we should cache the matrix and clip |
| fDebugCanvas->drawTo(canvas, n); |
| |
| // make some json |
| SkDynamicMemoryWStream stream; |
| SkJSONWriter writer(&stream, SkJSONWriter::Mode::kFast); |
| |
| SkM44 vm = fDebugCanvas->getCurrentMatrix(); |
| SkIRect clip = fDebugCanvas->getCurrentClip(); |
| |
| writer.beginObject(); // root |
| writer.appendName("ViewMatrix"); |
| DrawCommand::MakeJsonMatrix44(writer, vm); |
| writer.appendName("ClipRect"); |
| DrawCommand::MakeJsonIRect(writer, clip); |
| writer.endObject(); // root |
| |
| // TODO: Old code explicitly avoided the null terminator in the returned data. Important? |
| writer.flush(); |
| return stream.detachAsData(); |
| } |
| |
| SkColor Request::getPixel(int x, int y) { |
| SkBitmap bmp; |
| bmp.allocPixels(this->getCanvas()->imageInfo().makeWH(1, 1)); |
| SkAssertResult(this->getCanvas()->readPixels(bmp, x, y)); |
| return bmp.getColor(0, 0); |
| } |