Add debugger as an optional module in canvaskit

Change-Id: I7d866aab2a74bbb4c8d4f2cf7901d4b679c0cbe9
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/515516
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Nathaniel Nifong <nifong@google.com>
This commit is contained in:
Nathaniel Nifong 2022-03-03 15:12:53 -05:00 committed by SkCQ
parent 7b40626ca9
commit 3bf76ab829
8 changed files with 616 additions and 10 deletions

View File

@ -61,6 +61,18 @@ if (skia_enable_tools) {
}
}
component("debugger") {
include_dirs = [ "../.." ]
sources = [
"../../tools/SkSharingProc.cpp",
"../../tools/UrlDataManager.cpp",
"../../tools/debugger/DebugCanvas.cpp",
"../../tools/debugger/DebugLayerManager.cpp",
"../../tools/debugger/DrawCommand.cpp",
"../../tools/debugger/JsonWriteBuffer.cpp",
]
}
action("create_notomono_cpp") {
script = "../../tools/embed_resources.py"
@ -127,6 +139,11 @@ skia_wasm_lib("canvaskit") {
}
if (skia_canvaskit_include_viewer) {
sources += [ "viewer_bindings.cpp" ]
# TODO(kjlubick): remove this
}
if (skia_canvaskit_enable_debugger) {
sources += [ "debugger_bindings.cpp" ]
deps += [ ":debugger" ]
}
if (skia_canvaskit_enable_embedded_font) {
sources += [
@ -253,6 +270,13 @@ skia_wasm_lib("canvaskit") {
]
}
if (skia_canvaskit_enable_debugger) {
ldflags += [
"--pre-js",
rebase_path("debugger.js"),
]
}
if (skia_canvaskit_enable_canvas_bindings) {
ldflags += [
"--pre-js",
@ -358,4 +382,10 @@ skia_wasm_lib("canvaskit") {
if (!skia_canvaskit_enable_font) {
defines += [ "CK_NO_FONTS" ]
}
if (skia_canvaskit_enable_debugger) {
defines += [
"SK_BUILD_FOR_DEBUGGER",
"SK_ENABLE_DUMP_GPU",
]
}
}

View File

@ -30,7 +30,7 @@ release_viewer:
debug:
# Does an incremental build where possible.
./compile.sh debug
./compile.sh debug --enable_debugger
- rm -rf build/
mkdir build
cp ../../out/canvaskit_wasm_debug/canvaskit.js ./build/
@ -164,3 +164,12 @@ bazel_test_canvaskit:
echo "test output in //bazel-testlogs/modules/canvaskit/canvaskit_js_wasms/test.outputs/"
ls ../../bazel-testlogs/modules/canvaskit/canvaskit_js_wasms/test.outputs/
with_debugger:
# Does an incremental build where possible.
./compile.sh debug enable_debugger
- rm -rf build/
mkdir build
cp ../../out/canvaskit_wasm_debug/canvaskit.js ./build/
cp ../../out/canvaskit_wasm_debug/canvaskit.wasm ./build/
cp ./build/canvaskit.js ${SKIA_INFRA_ROOT}/debugger-app/wasm_libs/local_build/
cp ./build/canvaskit.wasm ${SKIA_INFRA_ROOT}/debugger-app/wasm_libs/local_build/

View File

@ -17,6 +17,7 @@
using namespace emscripten;
// Self-documenting types
using JSColor = int32_t;
using JSArray = emscripten::val;
using JSObject = emscripten::val;
using JSString = emscripten::val;

View File

@ -3,18 +3,19 @@
# found in the LICENSE file.
declare_args() {
skia_canvaskit_enable_canvas_bindings = true
skia_canvaskit_enable_skottie = true
skia_canvaskit_enable_pathops = true
skia_canvaskit_enable_particles = true
skia_canvaskit_enable_rt_shader = true
skia_canvaskit_enable_sksl_trace = true
skia_canvaskit_enable_alias_font = true
skia_canvaskit_enable_skp_serialization = true
skia_canvaskit_enable_canvas_bindings = true
skia_canvaskit_enable_debugger = false
skia_canvaskit_enable_effects_deserialization = true
skia_canvaskit_enable_matrix_helper = true
skia_canvaskit_enable_font = true
skia_canvaskit_enable_embedded_font = true
skia_canvaskit_enable_font = true
skia_canvaskit_enable_matrix_helper = true
skia_canvaskit_enable_particles = true
skia_canvaskit_enable_pathops = true
skia_canvaskit_enable_rt_shader = true
skia_canvaskit_enable_skottie = true
skia_canvaskit_enable_skp_serialization = true
skia_canvaskit_enable_sksl_trace = true
skia_canvaskit_enable_paragraph = true
skia_canvaskit_include_viewer = false
skia_canvaskit_force_tracing = false

View File

@ -143,6 +143,11 @@ if [[ $@ == *legacy_draw_vertices* ]]; then
LEGACY_DRAW_VERTICES="true"
fi
DEBUGGER_ENABLED="false"
if [[ $@ == *enable_debugger* ]]; then
DEBUGGER_ENABLED="true"
fi
GN_SHAPER="skia_use_icu=true skia_use_system_icu=false skia_use_harfbuzz=true skia_use_system_harfbuzz=false"
if [[ $@ == *primitive_shaper* ]] || [[ $@ == *no_font* ]]; then
echo "Using the primitive shaper instead of the harfbuzz/icu one"
@ -248,6 +253,7 @@ echo "Compiling"
skia_canvaskit_enable_embedded_font=${ENABLE_EMBEDDED_FONT} \
skia_canvaskit_enable_alias_font=${ENABLE_ALIAS_FONT} \
skia_canvaskit_legacy_draw_vertices_blend_mode=${LEGACY_DRAW_VERTICES} \
skia_canvaskit_enable_debugger=${DEBUGGER_ENABLED} \
skia_canvaskit_enable_paragraph=${ENABLE_PARAGRAPH}"
${NINJA} -C ${BUILD_DIR} canvaskit.js

View File

@ -0,0 +1,26 @@
(function(CanvasKit){
CanvasKit.SkpFilePlayer = function(file_arraybuf) {
// Create the instance of SkpDebugPlayer
var player = new this.SkpDebugPlayer();
// Convert file (an ArrayBuffer) into a typedarray,
// otherwise fileMem.set() below seems to have no effect.
var fileContents = new Uint8Array(file_arraybuf);
var size = fileContents.byteLength;
// Allocate memory in wasm to hold the skp file selected by the user.
var fileMemPtr = this._malloc(size);
// Make a typed array view of that memory
var fileMem = new Uint8Array(CanvasKit.HEAPU8.buffer, fileMemPtr, size);
// Copy the file into it
fileMem.set(fileContents);
// Hand off pointer to wasm
var error = player.loadSkp(fileMemPtr, size);
// Free the memory that was used to hold the file, since it is now represented as an SkPicture
this._free(fileMemPtr)
return {
'error': error,
'player': player
};
}
}(Module)); // When this file is loaded in, the high level object is "Module";

View File

@ -0,0 +1,529 @@
/*
* This file defines SkpDebugPlayer, a class which loads a SKP or MSKP file and draws it
* to an SkSurface with annotation, and detailed playback controls. It holds as many DebugCanvases
* as there are frames in the file.
*
* It also defines emscripten bindings for SkpDebugPlayer and other classes necessary to us it.
*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkPicture.h"
#include "include/core/SkString.h"
#include "include/core/SkSurface.h"
#include "include/utils/SkBase64.h"
#include "src/core/SkPicturePriv.h"
#include "src/utils/SkJSONWriter.h"
#include "src/utils/SkMultiPictureDocument.h"
#include "tools/SkSharingProc.h"
#include "tools/UrlDataManager.h"
#include "tools/debugger/DebugCanvas.h"
#include "tools/debugger/DebugLayerManager.h"
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <map>
#include <emscripten.h>
#include <emscripten/bind.h>
#ifdef SK_GL
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/gl/GrGLInterface.h"
#include "include/gpu/gl/GrGLTypes.h"
#include <GL/gl.h>
#include <emscripten/html5.h>
#endif
#include "modules/canvaskit/WasmCommon.h"
// file signature for SkMultiPictureDocument
// TODO(nifong): make public and include from SkMultiPictureDocument.h
static constexpr char kMultiMagic[] = "Skia Multi-Picture Doc\n\n";
uint32_t MinVersion() { return SkPicturePriv::kMin_Version; }
struct ImageInfoNoColorspace {
int width;
int height;
SkColorType colorType;
SkAlphaType alphaType;
};
// TODO(kjlubick) Should this handle colorspace
ImageInfoNoColorspace toImageInfoNoColorspace(const SkImageInfo& ii) {
return (ImageInfoNoColorspace){ii.width(), ii.height(), ii.colorType(), ii.alphaType()};
}
class SkpDebugPlayer {
public:
SkpDebugPlayer() :
udm(UrlDataManager(SkString("/data"))){}
/* loadSkp deserializes a skp file that has been copied into the shared WASM memory.
* cptr - a pointer to the data to deserialize.
* length - length of the data in bytes.
* The caller must allocate the memory with M._malloc where M is the wasm module in javascript
* and copy the data into M.buffer at the pointer returned by malloc.
*
* uintptr_t is used here because emscripten will not allow binding of functions with pointers
* to primitive types. We can instead pass a number and cast it to whatever kind of
* pointer we're expecting.
*
* Returns an error string which is populated in the case that the file cannot be read.
*/
std::string loadSkp(uintptr_t cptr, int length) {
const uint8_t* data = reinterpret_cast<const uint8_t*>(cptr);
// Both traditional and multi-frame skp files have a magic word
SkMemoryStream stream(data, length);
SkDebugf("make stream at %p, with %d bytes\n",data, length);
const bool isMulti = memcmp(data, kMultiMagic, sizeof(kMultiMagic) - 1) == 0;
if (isMulti) {
SkDebugf("Try reading as a multi-frame skp\n");
const auto& error = loadMultiFrame(&stream);
if (!error.empty()) { return error; }
} else {
SkDebugf("Try reading as single-frame skp\n");
// TODO(nifong): Rely on SkPicture's return errors once it provides some.
frames.push_back(loadSingleFrame(&stream));
}
return "";
}
/* drawTo asks the debug canvas to draw from the beginning of the picture
* to the given command and flush the canvas.
*/
void drawTo(SkSurface* surface, int32_t index) {
// Set the command within the frame or layer event being drawn.
if (fInspectedLayer >= 0) {
fLayerManager->setCommand(fInspectedLayer, fp, index);
} else {
index = constrainFrameCommand(index);
}
auto* canvas = surface->getCanvas();
canvas->clear(SK_ColorTRANSPARENT);
if (fInspectedLayer >= 0) {
// when it's a layer event we're viewing, we use the layer manager to render it.
fLayerManager->drawLayerEventTo(surface, fInspectedLayer, fp);
} else {
// otherwise, its a frame at the top level.
frames[fp]->drawTo(surface->getCanvas(), index);
}
surface->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();
}
// Gets the bounds for the given frame
// (or layer update, assuming there is one at that frame for fInspectedLayer)
const SkIRect getBoundsForFrame(int32_t frame) {
if (fInspectedLayer < 0) {
return fBoundsArray[frame];
}
auto summary = fLayerManager->event(fInspectedLayer, fp);
return SkIRect::MakeWH(summary.layerWidth, summary.layerHeight);
}
// Gets the bounds for the current frame
const SkIRect getBounds() {
return getBoundsForFrame(fp);
}
// returns the debugcanvas of the current frame, or the current draw event when inspecting
// a layer.
DebugCanvas* visibleCanvas() {
if (fInspectedLayer >=0) {
return fLayerManager->getEventDebugCanvas(fInspectedLayer, fp);
} else {
return frames[fp].get();
}
}
// The following three operations apply to every debugcanvas because they are overdraw features.
// There is only one toggle for them on the app, they are global settings.
// However, there's not a simple way to make the debugcanvases pull settings from a central
// location so we set it on all of them at once.
void setOverdrawVis(bool on) {
for (int i=0; i < frames.size(); i++) {
frames[i]->setOverdrawViz(on);
}
fLayerManager->setOverdrawViz(on);
}
void setGpuOpBounds(bool on) {
for (int i=0; i < frames.size(); i++) {
frames[i]->setDrawGpuOpBounds(on);
}
fLayerManager->setDrawGpuOpBounds(on);
}
void setClipVizColor(JSColor color) {
for (int i=0; i < frames.size(); i++) {
frames[i]->setClipVizColor(SkColor(color));
}
fLayerManager->setClipVizColor(SkColor(color));
}
void setAndroidClipViz(bool on) {
for (int i=0; i < frames.size(); i++) {
frames[i]->setAndroidClipViz(on);
}
// doesn't matter in layers
}
void setOriginVisible(bool on) {
for (int i=0; i < frames.size(); i++) {
frames[i]->setOriginVisible(on);
}
}
// The two operations below only apply to the current frame, because they concern the command
// list, which is unique to each frame.
void deleteCommand(int index) {
visibleCanvas()->deleteDrawCommandAt(index);
}
void setCommandVisibility(int index, bool visible) {
visibleCanvas()->toggleCommand(index, visible);
}
int getSize() const {
if (fInspectedLayer >=0) {
return fLayerManager->event(fInspectedLayer, fp).commandCount;
} else {
return frames[fp]->getSize();
}
}
int getFrameCount() const {
return frames.size();
}
// Return the command list in JSON representation as a string
std::string jsonCommandList(sk_sp<SkSurface> surface) {
SkDynamicMemoryWStream stream;
SkJSONWriter writer(&stream, SkJSONWriter::Mode::kFast);
writer.beginObject(); // root
visibleCanvas()->toJSON(writer, udm, surface->getCanvas());
writer.endObject(); // root
writer.flush();
auto skdata = stream.detachAsData();
// Convert skdata to string_view, which accepts a length
std::string_view data_view(reinterpret_cast<const char*>(skdata->data()), skdata->size());
// and string_view to string, which emscripten understands.
return std::string(data_view);
}
// Gets the clip and matrix of the last command drawn
std::string lastCommandInfo() {
SkM44 vm = visibleCanvas()->getCurrentMatrix();
SkIRect clip = visibleCanvas()->getCurrentClip();
SkDynamicMemoryWStream stream;
SkJSONWriter writer(&stream, SkJSONWriter::Mode::kFast);
writer.beginObject(); // root
writer.appendName("ViewMatrix");
DrawCommand::MakeJsonMatrix44(writer, vm);
writer.appendName("ClipRect");
DrawCommand::MakeJsonIRect(writer, clip);
writer.endObject(); // root
writer.flush();
auto skdata = stream.detachAsData();
// Convert skdata to string_view, which accepts a length
std::string_view data_view(reinterpret_cast<const char*>(skdata->data()), skdata->size());
// and string_view to string, which emscripten understands.
return std::string(data_view);
}
void changeFrame(int index) {
fp = index;
}
// Return the png file at the requested index in
// the skp file's vector of shared images. this is the set of images referred to by the
// filenames like "\\1" in DrawImage commands.
// Return type is the PNG data as a base64 encoded string with prepended URI.
std::string getImageResource(int index) {
sk_sp<SkData> pngData = fImages[index]->encodeToData();
size_t len = SkBase64::Encode(pngData->data(), pngData->size(), nullptr);
SkString dst;
dst.resize(len);
SkBase64::Encode(pngData->data(), pngData->size(), dst.writable_str());
dst.prepend("data:image/png;base64,");
return std::string(dst.c_str());
}
int getImageCount() {
return fImages.size();
}
// Get the image info of one of the resource images.
ImageInfoNoColorspace getImageInfo(int index) {
return toImageInfoNoColorspace(fImages[index]->imageInfo());
}
// return data on which commands each image is used in.
// (frame, -1) returns info for the given frame,
// (frame, nodeid) return info for a layer update
// { imageid: [commandid, commandid, ...], ... }
JSObject imageUseInfo(int framenumber, int nodeid) {
JSObject result = emscripten::val::object();
DebugCanvas* debugCanvas = frames[framenumber].get();
if (nodeid >= 0) {
debugCanvas = fLayerManager->getEventDebugCanvas(nodeid, framenumber);
}
const auto& map = debugCanvas->getImageIdToCommandMap(udm);
for (auto it = map.begin(); it != map.end(); ++it) {
JSArray list = emscripten::val::array();
for (const int commandId : it->second) {
list.call<void>("push", commandId);
}
result.set(std::to_string(it->first), list);
}
return result;
}
// Return information on every layer (offscreeen buffer) that is available for drawing at
// the current frame.
JSArray getLayerSummariesJs() {
JSArray result = emscripten::val::array();
for (auto summary : fLayerManager->summarizeLayers(fp)) {
result.call<void>("push", summary);
}
return result;
}
JSArray getLayerKeys() {
JSArray result = emscripten::val::array();
for (auto key : fLayerManager->getKeys()) {
JSObject item = emscripten::val::object();
item.set("frame", key.frame);
item.set("nodeId", key.nodeId);
result.call<void>("push", item);
}
return result;
}
// When set to a valid layer index, causes this class to playback the layer draw event at nodeId
// on frame fp. No validation of nodeId or fp is performed, this must be valid values obtained
// from either fLayerManager.listNodesForFrame or fLayerManager.summarizeEvents
// Set to -1 to return to viewing the top level animation
void setInspectedLayer(int nodeId) {
fInspectedLayer = nodeId;
}
// Finds a command that left the given pixel in it's current state.
// Note that this method may fail to find the absolute last command that leaves a pixel
// the given color, but there is probably only one candidate in most cases, and the log(n)
// makes it worth it.
int findCommandByPixel(SkSurface* surface, int x, int y, int commandIndex) {
// What color is the pixel now?
SkColor finalColor = evaluateCommandColor(surface, commandIndex, x, y);
int lowerBound = 0;
int upperBound = commandIndex;
while (upperBound - lowerBound > 1) {
int command = (upperBound - lowerBound) / 2 + lowerBound;
auto c = evaluateCommandColor(surface, command, x, y);
if (c == finalColor) {
upperBound = command;
} else {
lowerBound = command;
}
}
// clean up after side effects
drawTo(surface, commandIndex);
return upperBound;
}
private:
// Helper for findCommandByPixel.
// Has side effect of flushing to surface.
// TODO(nifong) eliminate side effect.
SkColor evaluateCommandColor(SkSurface* surface, int command, int x, int y) {
drawTo(surface, command);
SkColor c;
SkImageInfo info = SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
SkPixmap pixmap(info, &c, 4);
surface->readPixels(pixmap, x, y);
return c;
}
// Loads a single frame (traditional) skp file from the provided data stream and returns
// a newly allocated DebugCanvas initialized with the SkPicture that was in the file.
std::unique_ptr<DebugCanvas> loadSingleFrame(SkMemoryStream* stream) {
// note overloaded = operator that actually does a move
sk_sp<SkPicture> picture = SkPicture::MakeFromStream(stream);
if (!picture) {
SkDebugf("Unable to deserialze frame.\n");
return nullptr;
}
SkDebugf("Parsed SKP file.\n");
// Make debug canvas using bounds from SkPicture
fBoundsArray.push_back(picture->cullRect().roundOut());
std::unique_ptr<DebugCanvas> debugCanvas = std::make_unique<DebugCanvas>(fBoundsArray.back());
// Only draw picture to the debug canvas once.
debugCanvas->drawPicture(picture);
return debugCanvas;
}
std::string 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();
int page_count = SkMultiPictureDocumentReadPageCount(stream);
if (!page_count) {
// MSKP's have a version separate from the SKP subpictures they contain.
return "Not a MultiPictureDocument, MultiPictureDocument file version too old, or MultiPictureDocument contained 0 frames.";
}
SkDebugf("Expecting %d frames\n", page_count);
std::vector<SkDocumentPage> pages(page_count);
if (!SkMultiPictureDocumentRead(stream, pages.data(), page_count, &procs)) {
return "Reading frames from MultiPictureDocument failed";
}
fLayerManager = std::make_unique<DebugLayerManager>();
int i = 0;
for (const auto& page : pages) {
// Make debug canvas using bounds from SkPicture
fBoundsArray.push_back(page.fPicture->cullRect().roundOut());
std::unique_ptr<DebugCanvas> debugCanvas = std::make_unique<DebugCanvas>(fBoundsArray.back());
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;
}
// If you don't set these, they're undefined.
debugCanvas->setOverdrawViz(false);
debugCanvas->setDrawGpuOpBounds(false);
debugCanvas->setClipVizColor(SK_ColorTRANSPARENT);
debugCanvas->setAndroidClipViz(false);
frames.push_back(std::move(debugCanvas));
i++;
}
fImages = deserialContext->fImages;
udm.indexImages(fImages);
return "";
}
// constrains the draw command index to the frame's command list length.
int constrainFrameCommand(int index) {
int cmdlen = frames[fp]->getSize();
if (index >= cmdlen) {
return cmdlen-1;
}
return index;
}
// A vector of DebugCanvas, each one initialized to a frame of the animation.
std::vector<std::unique_ptr<DebugCanvas>> frames;
// The index of the current frame (into the vector above)
int fp = 0;
// The width and height of every frame.
// frame sizes are known to change in Android Skia RenderEngine because it interleves pictures from different applications.
std::vector<SkIRect> fBoundsArray;
// image resources from a loaded file
std::vector<sk_sp<SkImage>> fImages;
// The URLDataManager here is a cache that accepts encoded data (pngs) and puts
// numbers on them. We have our own collection of images (fImages) that was populated by the
// SkSharingDeserialContext when mskp files are loaded which it can use for IDing images
// without having to serialize them.
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;
// The node id of a layer being inspected, if any.
// -1 means we are viewing the top level animation, not a layer.
// the exact draw event being inspected depends also on the selected frame `fp`.
int fInspectedLayer = -1;
};
using namespace emscripten;
EMSCRIPTEN_BINDINGS(my_module) {
function("MinVersion", &MinVersion);
// The main class that the JavaScript in index.html uses
class_<SkpDebugPlayer>("SkpDebugPlayer")
.constructor<>()
.function("changeFrame", &SkpDebugPlayer::changeFrame)
.function("deleteCommand", &SkpDebugPlayer::deleteCommand)
.function("draw", &SkpDebugPlayer::draw, allow_raw_pointers())
.function("drawTo", &SkpDebugPlayer::drawTo, allow_raw_pointers())
.function("findCommandByPixel", &SkpDebugPlayer::findCommandByPixel, allow_raw_pointers())
.function("getBounds", &SkpDebugPlayer::getBounds)
.function("getBoundsForFrame", &SkpDebugPlayer::getBoundsForFrame)
.function("getFrameCount", &SkpDebugPlayer::getFrameCount)
.function("getImageResource", &SkpDebugPlayer::getImageResource)
.function("getImageCount", &SkpDebugPlayer::getImageCount)
.function("getImageInfo", &SkpDebugPlayer::getImageInfo)
.function("getLayerKeys", &SkpDebugPlayer::getLayerKeys)
.function("getLayerSummariesJs", &SkpDebugPlayer::getLayerSummariesJs)
.function("getSize", &SkpDebugPlayer::getSize)
.function("imageUseInfo", &SkpDebugPlayer::imageUseInfo)
.function("imageUseInfoForFrameJs", optional_override([](SkpDebugPlayer& self, const int frame)->JSObject {
// -1 as a node id is used throughout the application to mean no layer inspected.
return self.imageUseInfo(frame, -1);
}))
.function("jsonCommandList", &SkpDebugPlayer::jsonCommandList, allow_raw_pointers())
.function("lastCommandInfo", &SkpDebugPlayer::lastCommandInfo)
.function("loadSkp", &SkpDebugPlayer::loadSkp, allow_raw_pointers())
.function("setClipVizColor", &SkpDebugPlayer::setClipVizColor)
.function("setCommandVisibility", &SkpDebugPlayer::setCommandVisibility)
.function("setGpuOpBounds", &SkpDebugPlayer::setGpuOpBounds)
.function("setInspectedLayer", &SkpDebugPlayer::setInspectedLayer)
.function("setOriginVisible", &SkpDebugPlayer::setOriginVisible)
.function("setOverdrawVis", &SkpDebugPlayer::setOverdrawVis)
.function("setAndroidClipViz", &SkpDebugPlayer::setAndroidClipViz);
// Structs used as arguments or returns to the functions above
// TODO(kjlubick) handle this rect like the ones in CanvasKit
value_object<SkIRect>("SkIRect")
.field("fLeft", &SkIRect::fLeft)
.field("fTop", &SkIRect::fTop)
.field("fRight", &SkIRect::fRight)
.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::LayerSummary>("VectorLayerSummary");
value_object<DebugLayerManager::LayerSummary>("LayerSummary")
.field("nodeId", &DebugLayerManager::LayerSummary::nodeId)
.field("frameOfLastUpdate", &DebugLayerManager::LayerSummary::frameOfLastUpdate)
.field("fullRedraw", &DebugLayerManager::LayerSummary::fullRedraw)
.field("layerWidth", &DebugLayerManager::LayerSummary::layerWidth)
.field("layerHeight", &DebugLayerManager::LayerSummary::layerHeight);
value_object<ImageInfoNoColorspace>("ImageInfoNoColorspace")
.field("width", &ImageInfoNoColorspace::width)
.field("height", &ImageInfoNoColorspace::height)
.field("colorType", &ImageInfoNoColorspace::colorType)
.field("alphaType", &ImageInfoNoColorspace::alphaType);
}

View File

@ -72,6 +72,10 @@ var CanvasKit = {
// Defined by emscripten.
createContext: function() {},
// Added by debugger when it extends canvaskit
MinVersion: function() {},
SkpFilePlayer: function() {},
// private API (i.e. things declared in the bindings that we use
// in the pre-js file)
_MakeGrContext: function() {},