Parse android layer annotations in debugger, play back layers
Bug: skia:9626 Change-Id: I3ae8fa83520690f9af534e9ab0b70834d7890fb0 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/256100 Commit-Queue: Nathaniel Nifong <nifong@google.com> Reviewed-by: Kevin Lubick <kjlubick@google.com>
This commit is contained in:
parent
ed58654e39
commit
20b177a9bf
4
BUILD.gn
4
BUILD.gn
@ -1034,6 +1034,7 @@ if (target_cpu == "wasm") {
|
|||||||
"tools/SkSharingProc.cpp",
|
"tools/SkSharingProc.cpp",
|
||||||
"tools/UrlDataManager.cpp",
|
"tools/UrlDataManager.cpp",
|
||||||
"tools/debugger/DebugCanvas.cpp",
|
"tools/debugger/DebugCanvas.cpp",
|
||||||
|
"tools/debugger/DebugLayerManager.cpp",
|
||||||
"tools/debugger/DrawCommand.cpp",
|
"tools/debugger/DrawCommand.cpp",
|
||||||
"tools/debugger/JsonWriteBuffer.cpp",
|
"tools/debugger/JsonWriteBuffer.cpp",
|
||||||
]
|
]
|
||||||
@ -1455,6 +1456,7 @@ if (skia_enable_tools) {
|
|||||||
"tools/ToolUtils.cpp",
|
"tools/ToolUtils.cpp",
|
||||||
"tools/UrlDataManager.cpp",
|
"tools/UrlDataManager.cpp",
|
||||||
"tools/debugger/DebugCanvas.cpp",
|
"tools/debugger/DebugCanvas.cpp",
|
||||||
|
"tools/debugger/DebugLayerManager.cpp",
|
||||||
"tools/debugger/DrawCommand.cpp",
|
"tools/debugger/DrawCommand.cpp",
|
||||||
"tools/debugger/JsonWriteBuffer.cpp",
|
"tools/debugger/JsonWriteBuffer.cpp",
|
||||||
"tools/fonts/RandomScalerContext.cpp",
|
"tools/fonts/RandomScalerContext.cpp",
|
||||||
@ -1998,6 +2000,7 @@ if (skia_enable_tools) {
|
|||||||
"fuzz/oss_fuzz/FuzzTextBlobDeserialize.cpp",
|
"fuzz/oss_fuzz/FuzzTextBlobDeserialize.cpp",
|
||||||
"tools/UrlDataManager.cpp",
|
"tools/UrlDataManager.cpp",
|
||||||
"tools/debugger/DebugCanvas.cpp",
|
"tools/debugger/DebugCanvas.cpp",
|
||||||
|
"tools/debugger/DebugLayerManager.cpp",
|
||||||
"tools/debugger/DrawCommand.cpp",
|
"tools/debugger/DrawCommand.cpp",
|
||||||
"tools/debugger/JsonWriteBuffer.cpp",
|
"tools/debugger/JsonWriteBuffer.cpp",
|
||||||
]
|
]
|
||||||
@ -2416,6 +2419,7 @@ if (skia_enable_tools) {
|
|||||||
sources = [
|
sources = [
|
||||||
"tools/UrlDataManager.cpp",
|
"tools/UrlDataManager.cpp",
|
||||||
"tools/debugger/DebugCanvas.cpp",
|
"tools/debugger/DebugCanvas.cpp",
|
||||||
|
"tools/debugger/DebugLayerManager.cpp",
|
||||||
"tools/debugger/DrawCommand.cpp",
|
"tools/debugger/DrawCommand.cpp",
|
||||||
"tools/debugger/JsonWriteBuffer.cpp",
|
"tools/debugger/JsonWriteBuffer.cpp",
|
||||||
"tools/mdbviz/MainWindow.cpp",
|
"tools/mdbviz/MainWindow.cpp",
|
||||||
|
@ -13,7 +13,12 @@
|
|||||||
#include "tools/SkSharingProc.h"
|
#include "tools/SkSharingProc.h"
|
||||||
#include "tools/UrlDataManager.h"
|
#include "tools/UrlDataManager.h"
|
||||||
#include "tools/debugger/DebugCanvas.h"
|
#include "tools/debugger/DebugCanvas.h"
|
||||||
|
#include "tools/debugger/DebugLayerManager.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
#include <emscripten/bind.h>
|
#include <emscripten/bind.h>
|
||||||
|
|
||||||
@ -98,10 +103,20 @@ class SkpDebugPlayer {
|
|||||||
SkDebugf("Constrained command index (%d) within this frame's length (%d)\n", index, cmdlen);
|
SkDebugf("Constrained command index (%d) within this frame's length (%d)\n", index, cmdlen);
|
||||||
index = cmdlen-1;
|
index = cmdlen-1;
|
||||||
}
|
}
|
||||||
|
auto* canvas = surface->getCanvas();
|
||||||
|
canvas->clear(SK_ColorTRANSPARENT);
|
||||||
frames[fp]->drawTo(surface->getCanvas(), index);
|
frames[fp]->drawTo(surface->getCanvas(), index);
|
||||||
surface->getCanvas()->flush();
|
surface->getCanvas()->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draws to the end of the current frame.
|
||||||
|
void draw(SkSurface* surface) {
|
||||||
|
auto* canvas = surface->getCanvas();
|
||||||
|
canvas->clear(SK_ColorTRANSPARENT);
|
||||||
|
frames[fp]->draw(surface->getCanvas());
|
||||||
|
surface->getCanvas()->flush();
|
||||||
|
}
|
||||||
|
|
||||||
const SkIRect& getBounds() { return fBounds; }
|
const SkIRect& getBounds() { return fBounds; }
|
||||||
|
|
||||||
// The following three operations apply to every frame because they are overdraw features.
|
// The following three operations apply to every frame because they are overdraw features.
|
||||||
@ -201,6 +216,11 @@ class SkpDebugPlayer {
|
|||||||
return toSimpleImageInfo(fImages[index]->imageInfo(), fImages[index].get());
|
return toSimpleImageInfo(fImages[index]->imageInfo(), fImages[index].get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return a list of layer draw events that happened at the beginning of this frame.
|
||||||
|
std::vector<DebugLayerManager::DrawEventSummary> getLayerDrawEvents() {
|
||||||
|
return fLayerManager->summarizeEvents(fp);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Loads a single frame (traditional) skp file from the provided data stream and returns
|
// Loads a single frame (traditional) skp file from the provided data stream and returns
|
||||||
@ -215,54 +235,57 @@ class SkpDebugPlayer {
|
|||||||
SkDebugf("Parsed SKP file.\n");
|
SkDebugf("Parsed SKP file.\n");
|
||||||
// Make debug canvas using bounds from SkPicture
|
// Make debug canvas using bounds from SkPicture
|
||||||
fBounds = picture->cullRect().roundOut();
|
fBounds = picture->cullRect().roundOut();
|
||||||
std::unique_ptr<DebugCanvas> debugDanvas = std::make_unique<DebugCanvas>(fBounds);
|
std::unique_ptr<DebugCanvas> debugCanvas = std::make_unique<DebugCanvas>(fBounds);
|
||||||
SkDebugf("DebugCanvas created.\n");
|
|
||||||
|
|
||||||
// Only draw picture to the debug canvas once.
|
// Only draw picture to the debug canvas once.
|
||||||
debugDanvas->drawPicture(picture);
|
debugCanvas->drawPicture(picture);
|
||||||
SkDebugf("Added picture with %d commands.\n", debugDanvas->getSize());
|
return debugCanvas;
|
||||||
return debugDanvas;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadMultiFrame(SkMemoryStream* stream) {
|
void loadMultiFrame(SkMemoryStream* stream) {
|
||||||
|
// Attempt to deserialize with an image sharing serial proc.
|
||||||
|
auto deserialContext = std::make_unique<SkSharingDeserialContext>();
|
||||||
|
SkDeserialProcs procs;
|
||||||
|
procs.fImageProc = SkSharingDeserialContext::deserializeImage;
|
||||||
|
procs.fImageCtx = deserialContext.get();
|
||||||
|
|
||||||
// Attempt to deserialize with an image sharing serial proc.
|
int page_count = SkMultiPictureDocumentReadPageCount(stream);
|
||||||
auto deserialContext = std::make_unique<SkSharingDeserialContext>();
|
if (!page_count) {
|
||||||
SkDeserialProcs procs;
|
SkDebugf("Not a MultiPictureDocument");
|
||||||
procs.fImageProc = SkSharingDeserialContext::deserializeImage;
|
return;
|
||||||
procs.fImageCtx = deserialContext.get();
|
}
|
||||||
|
SkDebugf("Expecting %d frames\n", page_count);
|
||||||
|
|
||||||
int page_count = SkMultiPictureDocumentReadPageCount(stream);
|
std::vector<SkDocumentPage> pages(page_count);
|
||||||
if (!page_count) {
|
if (!SkMultiPictureDocumentRead(stream, pages.data(), page_count, &procs)) {
|
||||||
SkDebugf("Not a MultiPictureDocument");
|
SkDebugf("Reading frames from MultiPictureDocument failed");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fLayerManager = std::make_unique<DebugLayerManager>();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& page : pages) {
|
||||||
|
i++;
|
||||||
|
// Make debug canvas using bounds from SkPicture
|
||||||
|
fBounds = page.fPicture->cullRect().roundOut();
|
||||||
|
std::unique_ptr<DebugCanvas> debugCanvas = std::make_unique<DebugCanvas>(fBounds);
|
||||||
|
debugCanvas->setLayerManagerAndFrame(fLayerManager.get(), i);
|
||||||
|
|
||||||
|
// Only draw picture to the debug canvas once.
|
||||||
|
debugCanvas->drawPicture(page.fPicture);
|
||||||
|
|
||||||
|
if (debugCanvas->getSize() <=0 ){
|
||||||
|
SkDebugf("Skipped corrupted frame, had %d commands \n", debugCanvas->getSize());
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
SkDebugf("Expecting %d frames\n", page_count);
|
// If you don't set these, they're undefined.
|
||||||
|
debugCanvas->setOverdrawViz(false);
|
||||||
std::vector<SkDocumentPage> pages(page_count);
|
debugCanvas->setDrawGpuOpBounds(false);
|
||||||
if (!SkMultiPictureDocumentRead(stream, pages.data(), page_count, &procs)) {
|
debugCanvas->setClipVizColor(SK_ColorTRANSPARENT);
|
||||||
SkDebugf("Reading frames from MultiPictureDocument failed");
|
frames.push_back(std::move(debugCanvas));
|
||||||
return;
|
}
|
||||||
}
|
fImages = deserialContext->fImages;
|
||||||
|
|
||||||
for (const auto& page : pages) {
|
|
||||||
// Make debug canvas using bounds from SkPicture
|
|
||||||
fBounds = page.fPicture->cullRect().roundOut();
|
|
||||||
std::unique_ptr<DebugCanvas> debugDanvas = std::make_unique<DebugCanvas>(fBounds);
|
|
||||||
// Only draw picture to the debug canvas once.
|
|
||||||
debugDanvas->drawPicture(page.fPicture);
|
|
||||||
SkDebugf("Added picture with %d commands.\n", debugDanvas->getSize());
|
|
||||||
|
|
||||||
if (debugDanvas->getSize() <=0 ){
|
|
||||||
SkDebugf("Skipped corrupted frame, had %d commands \n", debugDanvas->getSize());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
debugDanvas->setOverdrawViz(false);
|
|
||||||
debugDanvas->setDrawGpuOpBounds(false);
|
|
||||||
debugDanvas->setClipVizColor(SK_ColorTRANSPARENT);
|
|
||||||
frames.push_back(std::move(debugDanvas));
|
|
||||||
}
|
|
||||||
fImages = deserialContext->fImages;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A vector of DebugCanvas, each one initialized to a frame of the animation.
|
// A vector of DebugCanvas, each one initialized to a frame of the animation.
|
||||||
@ -285,6 +308,10 @@ class SkpDebugPlayer {
|
|||||||
// find anything. TODO(nifong): Unify these two numbering schemes in CollatingCanvas.
|
// find anything. TODO(nifong): Unify these two numbering schemes in CollatingCanvas.
|
||||||
UrlDataManager udm;
|
UrlDataManager udm;
|
||||||
|
|
||||||
|
// A structure holding the picture information needed to draw any layers used in an mskp file
|
||||||
|
// individual frames hold a pointer to it, store draw events, and request images from it.
|
||||||
|
// it is stateful and is set to the current frame at all times.
|
||||||
|
std::unique_ptr<DebugLayerManager> fLayerManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if SK_SUPPORT_GPU
|
#if SK_SUPPORT_GPU
|
||||||
@ -356,6 +383,7 @@ EMSCRIPTEN_BINDINGS(my_module) {
|
|||||||
.constructor<>()
|
.constructor<>()
|
||||||
.function("loadSkp", &SkpDebugPlayer::loadSkp, allow_raw_pointers())
|
.function("loadSkp", &SkpDebugPlayer::loadSkp, allow_raw_pointers())
|
||||||
.function("drawTo", &SkpDebugPlayer::drawTo, allow_raw_pointers())
|
.function("drawTo", &SkpDebugPlayer::drawTo, allow_raw_pointers())
|
||||||
|
.function("draw", &SkpDebugPlayer::draw, allow_raw_pointers())
|
||||||
.function("getBounds", &SkpDebugPlayer::getBounds)
|
.function("getBounds", &SkpDebugPlayer::getBounds)
|
||||||
.function("setOverdrawVis", &SkpDebugPlayer::setOverdrawVis)
|
.function("setOverdrawVis", &SkpDebugPlayer::setOverdrawVis)
|
||||||
.function("setClipVizColor", &SkpDebugPlayer::setClipVizColor)
|
.function("setClipVizColor", &SkpDebugPlayer::setClipVizColor)
|
||||||
@ -369,7 +397,8 @@ EMSCRIPTEN_BINDINGS(my_module) {
|
|||||||
.function("getFrameCount", &SkpDebugPlayer::getFrameCount)
|
.function("getFrameCount", &SkpDebugPlayer::getFrameCount)
|
||||||
.function("getImageResource", &SkpDebugPlayer::getImageResource)
|
.function("getImageResource", &SkpDebugPlayer::getImageResource)
|
||||||
.function("getImageCount", &SkpDebugPlayer::getImageCount)
|
.function("getImageCount", &SkpDebugPlayer::getImageCount)
|
||||||
.function("getImageInfo", &SkpDebugPlayer::getImageInfo);
|
.function("getImageInfo", &SkpDebugPlayer::getImageInfo)
|
||||||
|
.function("getLayerDrawEvents", &SkpDebugPlayer::getLayerDrawEvents);
|
||||||
|
|
||||||
// Structs used as arguments or returns to the functions above
|
// Structs used as arguments or returns to the functions above
|
||||||
value_object<SkIRect>("SkIRect")
|
value_object<SkIRect>("SkIRect")
|
||||||
@ -377,6 +406,15 @@ EMSCRIPTEN_BINDINGS(my_module) {
|
|||||||
.field("fTop", &SkIRect::fTop)
|
.field("fTop", &SkIRect::fTop)
|
||||||
.field("fRight", &SkIRect::fRight)
|
.field("fRight", &SkIRect::fRight)
|
||||||
.field("fBottom", &SkIRect::fBottom);
|
.field("fBottom", &SkIRect::fBottom);
|
||||||
|
// emscripten provided the following convenience function for binding vector<T>
|
||||||
|
// https://emscripten.org/docs/api_reference/bind.h.html#_CPPv415register_vectorPKc
|
||||||
|
register_vector<DebugLayerManager::DrawEventSummary>("VectorDrawEventSummary");
|
||||||
|
value_object<DebugLayerManager::DrawEventSummary>("DebugLayerManager::DrawEventSummary")
|
||||||
|
.field("nodeId", &DebugLayerManager::DrawEventSummary::nodeId)
|
||||||
|
.field("fullRedraw", &DebugLayerManager::DrawEventSummary::fullRedraw)
|
||||||
|
.field("commandCount", &DebugLayerManager::DrawEventSummary::commandCount)
|
||||||
|
.field("layerWidth", &DebugLayerManager::DrawEventSummary::layerWidth)
|
||||||
|
.field("layerHeight", &DebugLayerManager::DrawEventSummary::layerHeight);
|
||||||
|
|
||||||
// Symbols needed by cpu.js to perform surface creation and flushing.
|
// Symbols needed by cpu.js to perform surface creation and flushing.
|
||||||
enum_<SkColorType>("ColorType")
|
enum_<SkColorType>("ColorType")
|
||||||
|
@ -50,6 +50,7 @@ tests_sources = [
|
|||||||
"$_tests/ColorTest.cpp",
|
"$_tests/ColorTest.cpp",
|
||||||
"$_tests/CopySurfaceTest.cpp",
|
"$_tests/CopySurfaceTest.cpp",
|
||||||
"$_tests/CubicMapTest.cpp",
|
"$_tests/CubicMapTest.cpp",
|
||||||
|
"$_tests/DebugLayerManagerTest.cpp",
|
||||||
"$_tests/DashPathEffectTest.cpp",
|
"$_tests/DashPathEffectTest.cpp",
|
||||||
"$_tests/DataRefTest.cpp",
|
"$_tests/DataRefTest.cpp",
|
||||||
"$_tests/DefaultPathRendererTest.cpp",
|
"$_tests/DefaultPathRendererTest.cpp",
|
||||||
|
84
tests/DebugLayerManagerTest.cpp
Normal file
84
tests/DebugLayerManagerTest.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "include/core/SkPaint.h"
|
||||||
|
#include "include/core/SkPicture.h"
|
||||||
|
#include "include/core/SkPictureRecorder.h"
|
||||||
|
#include "include/core/SkRect.h"
|
||||||
|
#include "tests/Test.h"
|
||||||
|
#include "tools/debugger/DebugLayerManager.h"
|
||||||
|
|
||||||
|
// Adds one full update, one partial update, and requests one image a few frames later.
|
||||||
|
static void test_basic(skiatest::Reporter* reporter) {
|
||||||
|
// prepare supporting objects
|
||||||
|
int layerWidth = 100;
|
||||||
|
int layerHeight = 100;
|
||||||
|
|
||||||
|
// make a picture that fully updates the layer
|
||||||
|
SkPictureRecorder rec;
|
||||||
|
SkCanvas* canvas = rec.beginRecording(layerWidth, layerHeight);
|
||||||
|
canvas->clear(0x00000000);
|
||||||
|
SkPaint paint;
|
||||||
|
paint.setColor(SK_ColorBLUE);
|
||||||
|
canvas->drawOval(SkRect::MakeLTRB(1,1,99,99), paint);
|
||||||
|
canvas->flush();
|
||||||
|
auto picture1 = rec.finishRecordingAsPicture();
|
||||||
|
SkIRect dirtyRectFull = SkIRect::MakeLTRB(0, 0, layerWidth, layerHeight);
|
||||||
|
|
||||||
|
// make a second picture that acts as a partial update.
|
||||||
|
SkPictureRecorder rec2;
|
||||||
|
canvas = rec2.beginRecording(layerWidth, layerHeight);
|
||||||
|
paint.setColor(SK_ColorGREEN);
|
||||||
|
canvas->drawOval(SkRect::MakeLTRB(40,40,60,60), paint);
|
||||||
|
canvas->flush();
|
||||||
|
auto picture2 = rec2.finishRecordingAsPicture();
|
||||||
|
SkIRect dirtyRectPartial = SkIRect::MakeLTRB(40,40,60,60);
|
||||||
|
|
||||||
|
int node = 2;
|
||||||
|
|
||||||
|
// create and exercise DebugLayerManager
|
||||||
|
DebugLayerManager dlm;
|
||||||
|
dlm.storeSkPicture(node, 0, picture1, dirtyRectFull);
|
||||||
|
dlm.storeSkPicture(node, 10, picture2, dirtyRectPartial);
|
||||||
|
auto frames = dlm.listFramesForNode(node);
|
||||||
|
|
||||||
|
// Confirm the layer manager stored these at the right places.
|
||||||
|
REPORTER_ASSERT(reporter, frames.size() == 2);
|
||||||
|
REPORTER_ASSERT(reporter, frames[0] == 0);
|
||||||
|
REPORTER_ASSERT(reporter, frames[1] == 10);
|
||||||
|
|
||||||
|
SkPixmap pixmap;
|
||||||
|
// request an image of the layer between the two updates.
|
||||||
|
for (int i=0; i<10; i++) {
|
||||||
|
auto image = dlm.getLayerAsImage(node, i);
|
||||||
|
REPORTER_ASSERT(reporter, image->width() == layerWidth);
|
||||||
|
REPORTER_ASSERT(reporter, image->height() == layerHeight);
|
||||||
|
// confirm center is blue, proving only first pic was drawn.
|
||||||
|
image->peekPixels(&pixmap);
|
||||||
|
SkColor paintColor = pixmap.getColor(50, 50);
|
||||||
|
REPORTER_ASSERT(reporter, paintColor == SK_ColorBLUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For any images after the second draw, confirm the center is green, but the area just outside
|
||||||
|
// that smaller circle is still blue, proving dlm drew both pictures.
|
||||||
|
for (int i=10; i<12; i++) {
|
||||||
|
auto image = dlm.getLayerAsImage(node, i);
|
||||||
|
REPORTER_ASSERT(reporter, image->width() == layerWidth);
|
||||||
|
REPORTER_ASSERT(reporter, image->height() == layerHeight);
|
||||||
|
image->peekPixels(&pixmap);
|
||||||
|
auto innerColor = pixmap.getColor(50, 50);
|
||||||
|
REPORTER_ASSERT(reporter, innerColor == SK_ColorGREEN);
|
||||||
|
auto outerColor = pixmap.getColor(10, 50);
|
||||||
|
REPORTER_ASSERT(reporter, outerColor == SK_ColorBLUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_TEST(DebugLayerManagerTest, reporter) {
|
||||||
|
test_basic(reporter);
|
||||||
|
}
|
@ -13,6 +13,7 @@
|
|||||||
#include "src/core/SkRectPriv.h"
|
#include "src/core/SkRectPriv.h"
|
||||||
#include "src/utils/SkJSONWriter.h"
|
#include "src/utils/SkJSONWriter.h"
|
||||||
#include "tools/debugger/DebugCanvas.h"
|
#include "tools/debugger/DebugCanvas.h"
|
||||||
|
#include "tools/debugger/DebugLayerManager.h"
|
||||||
#include "tools/debugger/DrawCommand.h"
|
#include "tools/debugger/DrawCommand.h"
|
||||||
|
|
||||||
#include "include/gpu/GrContext.h"
|
#include "include/gpu/GrContext.h"
|
||||||
@ -20,11 +21,19 @@
|
|||||||
#include "src/gpu/GrContextPriv.h"
|
#include "src/gpu/GrContextPriv.h"
|
||||||
#include "src/gpu/GrRenderTargetContext.h"
|
#include "src/gpu/GrRenderTargetContext.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#define SKDEBUGCANVAS_VERSION 1
|
#define SKDEBUGCANVAS_VERSION 1
|
||||||
#define SKDEBUGCANVAS_ATTRIBUTE_VERSION "version"
|
#define SKDEBUGCANVAS_ATTRIBUTE_VERSION "version"
|
||||||
#define SKDEBUGCANVAS_ATTRIBUTE_COMMANDS "commands"
|
#define SKDEBUGCANVAS_ATTRIBUTE_COMMANDS "commands"
|
||||||
#define SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL "auditTrail"
|
#define SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL "auditTrail"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Constants used in Annotations by Android for keeping track of layers
|
||||||
|
static constexpr char kOffscreenLayerDraw[] = "OffscreenLayerDraw";
|
||||||
|
static constexpr char kSurfaceID[] = "SurfaceID";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
class DebugPaintFilterCanvas : public SkPaintFilterCanvas {
|
class DebugPaintFilterCanvas : public SkPaintFilterCanvas {
|
||||||
public:
|
public:
|
||||||
DebugPaintFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) {}
|
DebugPaintFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) {}
|
||||||
@ -53,7 +62,9 @@ DebugCanvas::DebugCanvas(int width, int height)
|
|||||||
: INHERITED(width, height)
|
: INHERITED(width, height)
|
||||||
, fOverdrawViz(false)
|
, fOverdrawViz(false)
|
||||||
, fClipVizColor(SK_ColorTRANSPARENT)
|
, fClipVizColor(SK_ColorTRANSPARENT)
|
||||||
, fDrawGpuOpBounds(false) {
|
, fDrawGpuOpBounds(false)
|
||||||
|
, fnextDrawPictureLayerId(-1)
|
||||||
|
, fnextDrawImageRectLayerId(-1) {
|
||||||
// SkPicturePlayback uses the base-class' quickReject calls to cull clipped
|
// SkPicturePlayback uses the base-class' quickReject calls to cull clipped
|
||||||
// operations. This can lead to problems in the debugger which expects all
|
// operations. This can lead to problems in the debugger which expects all
|
||||||
// the operations in the captured skp to appear in the debug canvas. To
|
// the operations in the captured skp to appear in the debug canvas. To
|
||||||
@ -73,7 +84,9 @@ DebugCanvas::DebugCanvas(int width, int height)
|
|||||||
this->INHERITED::onClipRect(large, kReplace_SkClipOp, kHard_ClipEdgeStyle);
|
this->INHERITED::onClipRect(large, kReplace_SkClipOp, kHard_ClipEdgeStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugCanvas::DebugCanvas(SkIRect bounds) { DebugCanvas(bounds.width(), bounds.height()); }
|
DebugCanvas::DebugCanvas(SkIRect bounds) {
|
||||||
|
DebugCanvas(bounds.width(), bounds.height());
|
||||||
|
}
|
||||||
|
|
||||||
DebugCanvas::~DebugCanvas() { fCommandVector.deleteAll(); }
|
DebugCanvas::~DebugCanvas() { fCommandVector.deleteAll(); }
|
||||||
|
|
||||||
@ -94,7 +107,6 @@ void DebugCanvas::drawTo(SkCanvas* originalCanvas, int index, int m) {
|
|||||||
SkRect windowRect = SkRect::MakeWH(SkIntToScalar(originalCanvas->getBaseLayerSize().width()),
|
SkRect windowRect = SkRect::MakeWH(SkIntToScalar(originalCanvas->getBaseLayerSize().width()),
|
||||||
SkIntToScalar(originalCanvas->getBaseLayerSize().height()));
|
SkIntToScalar(originalCanvas->getBaseLayerSize().height()));
|
||||||
|
|
||||||
originalCanvas->clear(SK_ColorTRANSPARENT);
|
|
||||||
originalCanvas->resetMatrix();
|
originalCanvas->resetMatrix();
|
||||||
if (!windowRect.isEmpty()) {
|
if (!windowRect.isEmpty()) {
|
||||||
originalCanvas->clipRect(windowRect, kReplace_SkClipOp);
|
originalCanvas->clipRect(windowRect, kReplace_SkClipOp);
|
||||||
@ -307,6 +319,23 @@ void DebugCanvas::didConcat(const SkMatrix& matrix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DebugCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
|
void DebugCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
|
||||||
|
// Parse layer-releated annotations added in SkiaPipeline.cpp and RenderNodeDrawable.cpp
|
||||||
|
// the format of the annotations is <Indicator|RenderNodeId>
|
||||||
|
SkTArray<SkString> tokens;
|
||||||
|
SkStrSplit(key, "|", kStrict_SkStrSplitMode, &tokens);
|
||||||
|
if (tokens.size() == 2) {
|
||||||
|
if (tokens[0].equals(kOffscreenLayerDraw)) {
|
||||||
|
// Indicates that the next drawPicture command contains the SkPicture to render the node
|
||||||
|
// at this id in an offscreen buffer.
|
||||||
|
fnextDrawPictureLayerId = std::stoi(tokens[1].c_str());
|
||||||
|
fnextDrawPictureDirtyRect = rect.roundOut();
|
||||||
|
return; // don't record it
|
||||||
|
} else if (tokens[0].equals(kSurfaceID)) {
|
||||||
|
// Indicates that the following drawImageRect should draw the offscreen buffer.
|
||||||
|
fnextDrawImageRectLayerId = std::stoi(tokens[1].c_str());
|
||||||
|
return; // don't record it
|
||||||
|
}
|
||||||
|
}
|
||||||
this->addDrawCommand(new DrawAnnotationCommand(rect, key, sk_ref_sp(value)));
|
this->addDrawCommand(new DrawAnnotationCommand(rect, key, sk_ref_sp(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +388,22 @@ void DebugCanvas::onDrawImageRect(const SkImage* image,
|
|||||||
const SkRect& dst,
|
const SkRect& dst,
|
||||||
const SkPaint* paint,
|
const SkPaint* paint,
|
||||||
SrcRectConstraint constraint) {
|
SrcRectConstraint constraint) {
|
||||||
this->addDrawCommand(new DrawImageRectCommand(image, src, dst, paint, constraint));
|
if (fnextDrawImageRectLayerId != -1 && fLayerManager) {
|
||||||
|
// This drawImageRect command would have drawn the offscreen buffer for a layer.
|
||||||
|
// On Android, we recorded an SkPicture of the commands that drew to the layer.
|
||||||
|
// To render the layer as it would have looked on the frame this DebugCanvas draws, we need
|
||||||
|
// to call fLayerManager->getLayerAsImage(id). This must be done just before
|
||||||
|
// drawTo(command), since it depends on the index into the layer's commands
|
||||||
|
// (managed by fLayerManager)
|
||||||
|
// Instead of adding a DrawImageRectCommand, we need a deferred command, that when
|
||||||
|
// executed, will call drawImageRect(fLayerManager->getLayerAsImage())
|
||||||
|
this->addDrawCommand(new DrawImageRectLayerCommand(
|
||||||
|
fLayerManager, fnextDrawImageRectLayerId, fFrame, src, dst, paint, constraint));
|
||||||
|
} else {
|
||||||
|
this->addDrawCommand(new DrawImageRectCommand(image, src, dst, paint, constraint));
|
||||||
|
}
|
||||||
|
// Reset expectation so next drawImageRect is not special.
|
||||||
|
fnextDrawImageRectLayerId = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugCanvas::onDrawImageNine(const SkImage* image,
|
void DebugCanvas::onDrawImageNine(const SkImage* image,
|
||||||
@ -400,10 +444,16 @@ void DebugCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
|
|||||||
void DebugCanvas::onDrawPicture(const SkPicture* picture,
|
void DebugCanvas::onDrawPicture(const SkPicture* picture,
|
||||||
const SkMatrix* matrix,
|
const SkMatrix* matrix,
|
||||||
const SkPaint* paint) {
|
const SkPaint* paint) {
|
||||||
this->addDrawCommand(new BeginDrawPictureCommand(picture, matrix, paint));
|
if (fnextDrawPictureLayerId != -1 && fLayerManager) {
|
||||||
SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
|
fLayerManager->storeSkPicture(fnextDrawPictureLayerId, fFrame, sk_ref_sp(picture),
|
||||||
picture->playback(this);
|
fnextDrawPictureDirtyRect);
|
||||||
this->addDrawCommand(new EndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint)));
|
} else {
|
||||||
|
this->addDrawCommand(new BeginDrawPictureCommand(picture, matrix, paint));
|
||||||
|
SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
|
||||||
|
picture->playback(this);
|
||||||
|
this->addDrawCommand(new EndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint)));
|
||||||
|
}
|
||||||
|
fnextDrawPictureLayerId = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugCanvas::onDrawPoints(PointMode mode,
|
void DebugCanvas::onDrawPoints(PointMode mode,
|
||||||
|
@ -16,11 +16,13 @@
|
|||||||
#include "include/pathops/SkPathOps.h"
|
#include "include/pathops/SkPathOps.h"
|
||||||
#include "include/private/SkTArray.h"
|
#include "include/private/SkTArray.h"
|
||||||
#include "tools/UrlDataManager.h"
|
#include "tools/UrlDataManager.h"
|
||||||
|
#include "tools/debugger/DebugLayerManager.h"
|
||||||
#include "tools/debugger/DrawCommand.h"
|
#include "tools/debugger/DrawCommand.h"
|
||||||
|
|
||||||
class GrAuditTrail;
|
class GrAuditTrail;
|
||||||
class SkNWayCanvas;
|
class SkNWayCanvas;
|
||||||
class SkPicture;
|
class SkPicture;
|
||||||
|
class DebugLayerManager;
|
||||||
|
|
||||||
class DebugCanvas : public SkCanvasVirtualEnforcer<SkCanvas> {
|
class DebugCanvas : public SkCanvasVirtualEnforcer<SkCanvas> {
|
||||||
public:
|
public:
|
||||||
@ -30,6 +32,21 @@ public:
|
|||||||
|
|
||||||
~DebugCanvas() override;
|
~DebugCanvas() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a DebugLayerManager for mskp files containing layer information
|
||||||
|
* when set this DebugCanvas will attempt to parse layer info from annotations.
|
||||||
|
* it will store layer pictures to the layer manager, and interpret some drawImageRects
|
||||||
|
* as layer draws, deferring to the layer manager for images.
|
||||||
|
* Provide a frame number that will be passed to all layer manager functions to identify this
|
||||||
|
* DebugCanvas.
|
||||||
|
*
|
||||||
|
* Used only in wasm debugger animations.
|
||||||
|
*/
|
||||||
|
void setLayerManagerAndFrame(DebugLayerManager* lm, int frame) {
|
||||||
|
fLayerManager = lm;
|
||||||
|
fFrame = frame;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable or disable overdraw visualization
|
* Enable or disable overdraw visualization
|
||||||
*/
|
*/
|
||||||
@ -54,6 +71,8 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Executes the draw calls up to the specified index.
|
Executes the draw calls up to the specified index.
|
||||||
|
Does not clear the canvas to transparent black first,
|
||||||
|
if needed, caller should do that first.
|
||||||
@param canvas The canvas being drawn to
|
@param canvas The canvas being drawn to
|
||||||
@param index The index of the final command being executed
|
@param index The index of the final command being executed
|
||||||
@param m an optional Mth gpu op to highlight, or -1
|
@param m an optional Mth gpu op to highlight, or -1
|
||||||
@ -207,6 +226,17 @@ private:
|
|||||||
SkColor fClipVizColor;
|
SkColor fClipVizColor;
|
||||||
bool fDrawGpuOpBounds;
|
bool fDrawGpuOpBounds;
|
||||||
|
|
||||||
|
// When not negative, indicates the render node id of the layer represented by the next
|
||||||
|
// drawPicture call.
|
||||||
|
int fnextDrawPictureLayerId = -1;
|
||||||
|
int fnextDrawImageRectLayerId = -1;
|
||||||
|
SkIRect fnextDrawPictureDirtyRect;
|
||||||
|
// may be null, in which case layer annotations are ignored.
|
||||||
|
DebugLayerManager* fLayerManager = nullptr;
|
||||||
|
// May be set when DebugCanvas is used in playing back an animation.
|
||||||
|
// Only used for passing to fLayerManager to identify itself.
|
||||||
|
int fFrame = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Adds the command to the class' vector of commands.
|
Adds the command to the class' vector of commands.
|
||||||
@param command The draw command for execution
|
@param command The draw command for execution
|
||||||
|
142
tools/debugger/DebugLayerManager.cpp
Normal file
142
tools/debugger/DebugLayerManager.cpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tools/debugger/DebugLayerManager.h"
|
||||||
|
|
||||||
|
#include "include/core/SkImage.h"
|
||||||
|
#include "include/core/SkImageInfo.h"
|
||||||
|
#include "include/core/SkPicture.h"
|
||||||
|
#include "include/core/SkSurface.h"
|
||||||
|
#include "include/private/SkTHash.h"
|
||||||
|
#include "tools/debugger/DebugCanvas.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
void DebugLayerManager::setCommand(int nodeId, int frame, int command) {
|
||||||
|
auto* drawEvent = fDraws.find({frame, nodeId});
|
||||||
|
if (!drawEvent) {
|
||||||
|
SkDebugf("Could not set command playhead for event {%d, %d}, it is not tracked by"
|
||||||
|
"DebugLayerManager.\n", frame, nodeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int count = drawEvent->debugCanvas->getSize();
|
||||||
|
drawEvent->command = command < count ? command : count - 1;
|
||||||
|
// Invalidate stored images that depended on this combination of node and frame.
|
||||||
|
// actually this does all of the events for this nodeId, but close enough.
|
||||||
|
auto relevantFrames = listFramesForNode(nodeId);
|
||||||
|
for (const auto& frame : relevantFrames) {
|
||||||
|
fDraws[{frame, nodeId}].image = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugLayerManager::storeSkPicture(int nodeId, int frame, sk_sp<SkPicture> picture,
|
||||||
|
SkIRect dirty) {
|
||||||
|
const LayerKey k = {frame, nodeId};
|
||||||
|
|
||||||
|
// Make debug canvas using bounds from SkPicture. This will be equal to whatever width and
|
||||||
|
// height were passed into SkPictureRecorder::beginRecording(w, h) which is the layer bounds.
|
||||||
|
const auto& layerBounds = picture->cullRect().roundOut();
|
||||||
|
auto debugCanvas = std::make_unique<DebugCanvas>(layerBounds);
|
||||||
|
// Must be set or they end up undefined due to cosmic rays, bad luck, etc.
|
||||||
|
debugCanvas->setOverdrawViz(false);
|
||||||
|
debugCanvas->setDrawGpuOpBounds(false);
|
||||||
|
debugCanvas->setClipVizColor(SK_ColorTRANSPARENT);
|
||||||
|
// Setting this allows a layer to contain another layer. TODO(nifong): write a test for this.
|
||||||
|
debugCanvas->setLayerManagerAndFrame(this, frame);
|
||||||
|
// Only draw picture to the debug canvas once.
|
||||||
|
debugCanvas->drawPicture(picture);
|
||||||
|
int numCommands = debugCanvas->getSize();
|
||||||
|
|
||||||
|
DrawEvent event = {
|
||||||
|
frame == 0 || dirty==layerBounds, // fullRedraw
|
||||||
|
nullptr, // image
|
||||||
|
std::move(debugCanvas), // debugCanvas
|
||||||
|
numCommands-1, // command
|
||||||
|
{layerBounds.height(), layerBounds.width()}, // layerBounds
|
||||||
|
};
|
||||||
|
|
||||||
|
fDraws.set(k, std::move(event));
|
||||||
|
keys.push_back(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
sk_sp<SkImage> DebugLayerManager::getLayerAsImage(const int nodeId, const int frame) {
|
||||||
|
// What is the last frame having an SkPicture for this layer? call it frame N
|
||||||
|
// have cached image of it? if so, return it.
|
||||||
|
// if not, draw it at frame N by the following method:
|
||||||
|
// The picture at frame N could have been a full redraw, or it could have been clipped to a
|
||||||
|
// dirty region. In order to know what the layer looked like on this frame, we must draw every
|
||||||
|
// picture starting with the last full redraw, up to the last one before the current frame, since
|
||||||
|
// any of those previous draws could be showing through.
|
||||||
|
|
||||||
|
// list of frames this node was updated on.
|
||||||
|
auto relevantFrames = listFramesForNode(nodeId);
|
||||||
|
// find largest one not greater than `frame`.
|
||||||
|
uint32_t i = relevantFrames.size()-1;
|
||||||
|
while (relevantFrames[i] > frame) { i--; }
|
||||||
|
const int frameN = relevantFrames[i];
|
||||||
|
// Fetch the draw event
|
||||||
|
auto& drawEvent = fDraws[{frameN, nodeId}];
|
||||||
|
// if an image of this is cached, return it.
|
||||||
|
if (drawEvent.image) {
|
||||||
|
return drawEvent.image;
|
||||||
|
}
|
||||||
|
// when it's not cached, we'll have to render it in an offscreen surface.
|
||||||
|
// start at the last full redraw. (pick up counting backwards from above)
|
||||||
|
while (i>0 && !(fDraws[{relevantFrames[i], nodeId}].fullRedraw)) { i--; }
|
||||||
|
// The correct layer bounds can be obtained from any drawEvent on this layer.
|
||||||
|
// the color type and alpha type are chosen here to match wasm-skp-debugger/cpu.js which was
|
||||||
|
// chosen to match the capabilities of HTML canvas, which this ultimately has to be drawn into.
|
||||||
|
// TODO(nifong): introduce a method of letting the user choose the backend for this.
|
||||||
|
auto surface = SkSurface::MakeRaster(SkImageInfo::Make(drawEvent.layerBounds,
|
||||||
|
kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, nullptr));
|
||||||
|
// draw everything from the last full redraw up to the current frame.
|
||||||
|
// other frames drawn are partial, meaning they were clipped to not completely cover the layer.
|
||||||
|
// count back up with i
|
||||||
|
auto* canvas = surface->getCanvas();
|
||||||
|
for (; i<relevantFrames.size() && relevantFrames[i]<=frameN; i++) {
|
||||||
|
auto& evt = fDraws[{relevantFrames[i], nodeId}];
|
||||||
|
evt.debugCanvas->drawTo(canvas, evt.command);
|
||||||
|
canvas->flush();
|
||||||
|
}
|
||||||
|
drawEvent.image = surface->makeImageSnapshot();
|
||||||
|
return drawEvent.image;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DebugLayerManager::DrawEventSummary> DebugLayerManager::summarizeEvents(int frame) const {
|
||||||
|
std::vector<DrawEventSummary> result;
|
||||||
|
for (const auto& node : listNodesForFrame(frame)) {
|
||||||
|
auto* evt = fDraws.find({frame, node});
|
||||||
|
if (!evt) { continue; }
|
||||||
|
result.push_back({
|
||||||
|
node, evt->fullRedraw, evt->debugCanvas->getSize(),
|
||||||
|
evt->layerBounds.width(), evt->layerBounds.height()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> DebugLayerManager::listNodesForFrame(int frame) const {
|
||||||
|
std::vector<int> result;
|
||||||
|
for (const auto& key : keys) {
|
||||||
|
if (key.frame == frame) {
|
||||||
|
result.push_back(key.nodeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> DebugLayerManager::listFramesForNode(int nodeId) const {
|
||||||
|
std::vector<int> result;
|
||||||
|
for (const auto& key : keys) {
|
||||||
|
if (key.nodeId == nodeId) {
|
||||||
|
result.push_back(key.frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
126
tools/debugger/DebugLayerManager.h
Normal file
126
tools/debugger/DebugLayerManager.h
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DEBUGLAYERMANAGER_H_
|
||||||
|
#define DEBUGLAYERMANAGER_H_
|
||||||
|
|
||||||
|
#include "include/core/SkImage.h"
|
||||||
|
#include "include/private/SkTHash.h"
|
||||||
|
#include "tools/debugger/DebugCanvas.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// A class to assist in playing back and debugging an mskp file containing offscreen layer commands.
|
||||||
|
|
||||||
|
// Holds SkPictures necessary to draw layers in one or more DebugCanvases. During
|
||||||
|
// recording of the mskp file on Android, each layer had a RenderNode id, which is recorded with
|
||||||
|
// the layer's draw commands.
|
||||||
|
// Creates one surface (cpu only for now) for each layer, and renders
|
||||||
|
// pictures to it up to the requested command using a DebugCanvas.
|
||||||
|
|
||||||
|
// Animations are expected to, but may not always use a layer on more than frame.
|
||||||
|
// the layer may be drawn to more than once, and each different draw is saved for reconstructing the
|
||||||
|
// layer as it was on any frame. Draws may be partial, meaning their commands were clipped to not
|
||||||
|
// cover the entire layer.
|
||||||
|
|
||||||
|
// Clients may ask for a rendering of a given layer by it's RenderNode id and frame, and
|
||||||
|
// this class will return a rendering of how it looked on that frame.
|
||||||
|
// returning an SkImage snapshot of the internally managed surface.
|
||||||
|
|
||||||
|
class DebugCanvas;
|
||||||
|
|
||||||
|
class DebugLayerManager {
|
||||||
|
public:
|
||||||
|
DebugLayerManager() {}
|
||||||
|
|
||||||
|
// Store an SkPicture under a given nodeId (and under the currently set frame number)
|
||||||
|
// `dirty` is the recorded rect that was used to call androidFramework_setDeviceClipRestriction
|
||||||
|
// when the layer was drawn.
|
||||||
|
void storeSkPicture(int nodeId, int frame, sk_sp<SkPicture> picture, SkIRect dirty);
|
||||||
|
|
||||||
|
// Set's the command playback head for a given picture/draw event.
|
||||||
|
void setCommand(int nodeId, int frame, int command);
|
||||||
|
|
||||||
|
// getLayerAsImage draws the given layer as it would have looked on frame and returns an image.
|
||||||
|
// Though each picture can be played back in as many ways as there are commands, we will let
|
||||||
|
// that be determined by the user who sets an independent playhead for each draw event, tracked
|
||||||
|
// here, so it stays how they left it.
|
||||||
|
// For example: Say we are drawing a layer at frame 10.
|
||||||
|
// Frame 0: Layer was completely redrawn. By default we draw it to it's last command. We always
|
||||||
|
// save the result by (nodeId, frame)
|
||||||
|
// Frame 5: Layer was partially redrawn, and the user has inspected this draw event, leaving
|
||||||
|
// its command playhead at command 50/100. We have drew this at the time and save how
|
||||||
|
// the result looked (all of the commands at frame 0, then half of the commands in the
|
||||||
|
// partial draw at frame 5)
|
||||||
|
// Frame 10: Another partial redraw, un-altered, drawn on top of the result from frame 5. We
|
||||||
|
// return this as the image of how the layer should look on frame 10
|
||||||
|
// Frame 15: A full redraw
|
||||||
|
//
|
||||||
|
// If the user then comes along and moves the command playhead of the picture at frame 0,
|
||||||
|
// we invalidate the stored images for 0, 5, and 10, but we can leave 15 alone if we have it.
|
||||||
|
//
|
||||||
|
// Which leaves us with one less degree of freedom to think about when implementing this
|
||||||
|
// function: We can assume there is only one way to play back a given picture. :)
|
||||||
|
//
|
||||||
|
// The reason the public version of this function doesn't let you specify the frame, is that
|
||||||
|
// I expect DebugCanvas to call it, which doesn't know which frame it's rendering. The code in
|
||||||
|
// debugger_bindings.cpp does know, which it why I'm having it set the frame via setFrame(int)
|
||||||
|
sk_sp<SkImage> getLayerAsImage(const int nodeId, const int frame);
|
||||||
|
|
||||||
|
// Mean to be bindable by emscripted and returned to the javascript side
|
||||||
|
struct DrawEventSummary {
|
||||||
|
int nodeId;
|
||||||
|
bool fullRedraw;
|
||||||
|
int commandCount;
|
||||||
|
int layerWidth;
|
||||||
|
int layerHeight;
|
||||||
|
};
|
||||||
|
// Return a list summarizing the layer draw events on the current frame.
|
||||||
|
std::vector<DrawEventSummary> summarizeEvents(int frame) const;
|
||||||
|
|
||||||
|
// Return the list of node ids which have DrawEvents on the given frame
|
||||||
|
std::vector<int> listNodesForFrame(int frame) const;
|
||||||
|
// Return the list of frames on which the given node had DrawEvents.
|
||||||
|
std::vector<int> listFramesForNode(int nodeId) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This class is basically a map from (frame, node) to draw-event
|
||||||
|
// during recording, at the beginning of any frame, one or more layers could have been drawn on.
|
||||||
|
// every draw event was recorded, and when reading the mskp file they are stored and organized
|
||||||
|
// here.
|
||||||
|
|
||||||
|
struct LayerKey{
|
||||||
|
int frame; // frame of animation on which this event was recorded.
|
||||||
|
int nodeId; // the render node id of the layer which was drawn to.
|
||||||
|
|
||||||
|
bool operator==(const LayerKey& b) const {
|
||||||
|
return this->frame==b.frame && this->nodeId==b.nodeId;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DrawEvent {
|
||||||
|
// true the pic's clip equals the layer bounds.
|
||||||
|
bool fullRedraw;
|
||||||
|
// the saved result of how the layer looks on this frame.
|
||||||
|
// null if we don't have it.
|
||||||
|
sk_sp<SkImage> image;
|
||||||
|
// A debug canvas used for drawing this picture.
|
||||||
|
// the SkPicture itself isn't saved, since it's in the DebugCanvas.
|
||||||
|
std::unique_ptr<DebugCanvas> debugCanvas;
|
||||||
|
// the command index where the debugCanvas was left off.
|
||||||
|
int command;
|
||||||
|
// the size of the layer this drew into. redundant between multiple DrawEvents on the same
|
||||||
|
// layer but helpful.
|
||||||
|
SkISize layerBounds;
|
||||||
|
};
|
||||||
|
|
||||||
|
SkTHashMap<LayerKey, DrawEvent> fDraws;
|
||||||
|
// The list of all keys in the map above (it has no keys() method)
|
||||||
|
std::vector<LayerKey> keys;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -28,6 +28,7 @@
|
|||||||
#include "src/core/SkRectPriv.h"
|
#include "src/core/SkRectPriv.h"
|
||||||
#include "src/core/SkTextBlobPriv.h"
|
#include "src/core/SkTextBlobPriv.h"
|
||||||
#include "src/core/SkWriteBuffer.h"
|
#include "src/core/SkWriteBuffer.h"
|
||||||
|
#include "tools/debugger/DebugLayerManager.h"
|
||||||
#include "tools/debugger/JsonWriteBuffer.h"
|
#include "tools/debugger/JsonWriteBuffer.h"
|
||||||
|
|
||||||
#define DEBUGCANVAS_ATTRIBUTE_COMMAND "command"
|
#define DEBUGCANVAS_ATTRIBUTE_COMMAND "command"
|
||||||
@ -125,6 +126,7 @@
|
|||||||
#define DEBUGCANVAS_ATTRIBUTE_AMBIENTCOLOR "ambientColor"
|
#define DEBUGCANVAS_ATTRIBUTE_AMBIENTCOLOR "ambientColor"
|
||||||
#define DEBUGCANVAS_ATTRIBUTE_SPOTCOLOR "spotColor"
|
#define DEBUGCANVAS_ATTRIBUTE_SPOTCOLOR "spotColor"
|
||||||
#define DEBUGCANVAS_ATTRIBUTE_LIGHTRADIUS "lightRadius"
|
#define DEBUGCANVAS_ATTRIBUTE_LIGHTRADIUS "lightRadius"
|
||||||
|
#define DEBUGCANVAS_ATTRIBUTE_LAYERNODEID "layerNodeId"
|
||||||
|
|
||||||
#define DEBUGCANVAS_VERB_MOVE "move"
|
#define DEBUGCANVAS_VERB_MOVE "move"
|
||||||
#define DEBUGCANVAS_VERB_LINE "line"
|
#define DEBUGCANVAS_VERB_LINE "line"
|
||||||
@ -225,6 +227,7 @@ const char* DrawCommand::GetCommandString(OpType type) {
|
|||||||
case kDrawImageLattice_OpType: return "DrawImageLattice";
|
case kDrawImageLattice_OpType: return "DrawImageLattice";
|
||||||
case kDrawImageNine_OpType: return "DrawImageNine";
|
case kDrawImageNine_OpType: return "DrawImageNine";
|
||||||
case kDrawImageRect_OpType: return "DrawImageRect";
|
case kDrawImageRect_OpType: return "DrawImageRect";
|
||||||
|
case kDrawImageRectLayer_OpType: return "DrawImageRectLayer";
|
||||||
case kDrawOval_OpType: return "DrawOval";
|
case kDrawOval_OpType: return "DrawOval";
|
||||||
case kDrawPaint_OpType: return "DrawPaint";
|
case kDrawPaint_OpType: return "DrawPaint";
|
||||||
case kDrawPatch_OpType: return "DrawPatch";
|
case kDrawPatch_OpType: return "DrawPatch";
|
||||||
@ -1448,6 +1451,65 @@ void DrawImageRectCommand::toJSON(SkJSONWriter& writer, UrlDataManager& urlDataM
|
|||||||
writer.appendString(DEBUGCANVAS_ATTRIBUTE_SHORTDESC, str_append(&desc, fDst)->c_str());
|
writer.appendString(DEBUGCANVAS_ATTRIBUTE_SHORTDESC, str_append(&desc, fDst)->c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DrawImageRectLayerCommand::DrawImageRectLayerCommand(DebugLayerManager* layerManager,
|
||||||
|
const int nodeId,
|
||||||
|
const int frame,
|
||||||
|
const SkRect* src,
|
||||||
|
const SkRect& dst,
|
||||||
|
const SkPaint* paint,
|
||||||
|
SkCanvas::SrcRectConstraint constraint)
|
||||||
|
: INHERITED(kDrawImageRectLayer_OpType)
|
||||||
|
, fLayerManager(layerManager)
|
||||||
|
, fNodeId(nodeId)
|
||||||
|
, fFrame(frame)
|
||||||
|
, fSrc(src)
|
||||||
|
, fDst(dst)
|
||||||
|
, fPaint(paint)
|
||||||
|
, fConstraint(constraint) {}
|
||||||
|
|
||||||
|
void DrawImageRectLayerCommand::execute(SkCanvas* canvas) const {
|
||||||
|
sk_sp<SkImage> snapshot = fLayerManager->getLayerAsImage(fNodeId, fFrame);
|
||||||
|
canvas->legacy_drawImageRect(
|
||||||
|
snapshot.get(), fSrc.getMaybeNull(), fDst, fPaint.getMaybeNull(), fConstraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DrawImageRectLayerCommand::render(SkCanvas* canvas) const {
|
||||||
|
SkAutoCanvasRestore acr(canvas, true);
|
||||||
|
canvas->clear(0xFFFFFFFF);
|
||||||
|
|
||||||
|
xlate_and_scale_to_bounds(canvas, fDst);
|
||||||
|
|
||||||
|
this->execute(canvas);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawImageRectLayerCommand::toJSON(SkJSONWriter& writer, UrlDataManager& urlDataManager) const {
|
||||||
|
INHERITED::toJSON(writer, urlDataManager);
|
||||||
|
|
||||||
|
// Don't append an image attribute here, the image can be rendered in as many different ways
|
||||||
|
// as there are commands in the layer, at least. the urlDataManager would save each one under
|
||||||
|
// a different URL.
|
||||||
|
// Append the node id, and the layer inspector of the debugger will know what to do with it.
|
||||||
|
writer.appendS64(DEBUGCANVAS_ATTRIBUTE_LAYERNODEID, fNodeId);
|
||||||
|
|
||||||
|
if (fSrc.isValid()) {
|
||||||
|
writer.appendName(DEBUGCANVAS_ATTRIBUTE_SRC);
|
||||||
|
MakeJsonRect(writer, *fSrc);
|
||||||
|
}
|
||||||
|
writer.appendName(DEBUGCANVAS_ATTRIBUTE_DST);
|
||||||
|
MakeJsonRect(writer, fDst);
|
||||||
|
if (fPaint.isValid()) {
|
||||||
|
writer.appendName(DEBUGCANVAS_ATTRIBUTE_PAINT);
|
||||||
|
MakeJsonPaint(writer, *fPaint, urlDataManager);
|
||||||
|
}
|
||||||
|
if (fConstraint == SkCanvas::kStrict_SrcRectConstraint) {
|
||||||
|
writer.appendBool(DEBUGCANVAS_ATTRIBUTE_STRICT, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
SkString desc;
|
||||||
|
writer.appendString(DEBUGCANVAS_ATTRIBUTE_SHORTDESC, str_append(&desc, fDst)->c_str());
|
||||||
|
}
|
||||||
|
|
||||||
DrawImageNineCommand::DrawImageNineCommand(const SkImage* image,
|
DrawImageNineCommand::DrawImageNineCommand(const SkImage* image,
|
||||||
const SkIRect& center,
|
const SkIRect& center,
|
||||||
const SkRect& dst,
|
const SkRect& dst,
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include "src/utils/SkJSONWriter.h"
|
#include "src/utils/SkJSONWriter.h"
|
||||||
#include "tools/UrlDataManager.h"
|
#include "tools/UrlDataManager.h"
|
||||||
|
|
||||||
|
class DebugLayerManager;
|
||||||
|
|
||||||
class DrawCommand {
|
class DrawCommand {
|
||||||
public:
|
public:
|
||||||
enum OpType {
|
enum OpType {
|
||||||
@ -43,6 +45,7 @@ public:
|
|||||||
kDrawImageLattice_OpType,
|
kDrawImageLattice_OpType,
|
||||||
kDrawImageNine_OpType,
|
kDrawImageNine_OpType,
|
||||||
kDrawImageRect_OpType,
|
kDrawImageRect_OpType,
|
||||||
|
kDrawImageRectLayer_OpType, // unique to DebugCanvas
|
||||||
kDrawOval_OpType,
|
kDrawOval_OpType,
|
||||||
kDrawArc_OpType,
|
kDrawArc_OpType,
|
||||||
kDrawPaint_OpType,
|
kDrawPaint_OpType,
|
||||||
@ -369,6 +372,34 @@ private:
|
|||||||
typedef DrawCommand INHERITED;
|
typedef DrawCommand INHERITED;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Command for resolving the deferred SkImage representing an android layer
|
||||||
|
// Functions like DrawImageRect except it uses the saved UrlDataManager to resolve the image
|
||||||
|
// at the time execute() is called.
|
||||||
|
class DrawImageRectLayerCommand : public DrawCommand {
|
||||||
|
public:
|
||||||
|
DrawImageRectLayerCommand(DebugLayerManager* layerManager,
|
||||||
|
const int nodeId,
|
||||||
|
const int frame,
|
||||||
|
const SkRect* src,
|
||||||
|
const SkRect& dst,
|
||||||
|
const SkPaint* paint,
|
||||||
|
SkCanvas::SrcRectConstraint constraint);
|
||||||
|
void execute(SkCanvas* canvas) const override;
|
||||||
|
bool render(SkCanvas* canvas) const override;
|
||||||
|
void toJSON(SkJSONWriter& writer, UrlDataManager& urlDataManager) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DebugLayerManager* fLayerManager;
|
||||||
|
int fNodeId;
|
||||||
|
int fFrame;
|
||||||
|
SkTLazy<SkRect> fSrc;
|
||||||
|
SkRect fDst;
|
||||||
|
SkTLazy<SkPaint> fPaint;
|
||||||
|
SkCanvas::SrcRectConstraint fConstraint;
|
||||||
|
|
||||||
|
typedef DrawCommand INHERITED;
|
||||||
|
};
|
||||||
|
|
||||||
class DrawOvalCommand : public DrawCommand {
|
class DrawOvalCommand : public DrawCommand {
|
||||||
public:
|
public:
|
||||||
DrawOvalCommand(const SkRect& oval, const SkPaint& paint);
|
DrawOvalCommand(const SkRect& oval, const SkPaint& paint);
|
||||||
|
@ -71,7 +71,9 @@ SkCanvas* Request::getCanvas() {
|
|||||||
|
|
||||||
sk_sp<SkData> Request::drawToPng(int n, int m) {
|
sk_sp<SkData> Request::drawToPng(int n, int m) {
|
||||||
//fDebugCanvas->setOverdrawViz(true);
|
//fDebugCanvas->setOverdrawViz(true);
|
||||||
fDebugCanvas->drawTo(this->getCanvas(), n, m);
|
auto* canvas = this->getCanvas();
|
||||||
|
canvas->clear(SK_ColorTRANSPARENT);
|
||||||
|
fDebugCanvas->drawTo(canvas, n, m);
|
||||||
//fDebugCanvas->setOverdrawViz(false);
|
//fDebugCanvas->setOverdrawViz(false);
|
||||||
return writeCanvasToPng(this->getCanvas());
|
return writeCanvasToPng(this->getCanvas());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user