/src/skia/tools/debugger/DebugLayerManager.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2019 Google Inc. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license that can be |
5 | | * found in the LICENSE file. |
6 | | */ |
7 | | |
8 | | #include "tools/debugger/DebugLayerManager.h" |
9 | | |
10 | | #include "include/core/SkAlphaType.h" |
11 | | #include "include/core/SkColorSpace.h" |
12 | | #include "include/core/SkColorType.h" |
13 | | #include "include/core/SkImage.h" |
14 | | #include "include/core/SkImageInfo.h" |
15 | | #include "include/core/SkPicture.h" |
16 | | #include "include/core/SkRect.h" |
17 | | #include "include/core/SkSurface.h" |
18 | | #include "include/core/SkTypes.h" |
19 | | #include "include/private/base/SkDebug.h" |
20 | | #include "src/core/SkTHash.h" |
21 | | #include "tools/debugger/DebugCanvas.h" |
22 | | |
23 | | #include <cstdint> |
24 | | #include <memory> |
25 | | #include <unordered_map> |
26 | | #include <utility> |
27 | | #include <vector> |
28 | | |
29 | 0 | void DebugLayerManager::setCommand(int nodeId, int frame, int command) { |
30 | 0 | auto* drawEvent = fDraws.find({frame, nodeId}); |
31 | 0 | if (!drawEvent) { |
32 | 0 | SkDebugf( |
33 | 0 | "Could not set command playhead for event {%d, %d}, it is not tracked by" |
34 | 0 | "DebugLayerManager.\n", |
35 | 0 | frame, |
36 | 0 | nodeId); |
37 | 0 | return; |
38 | 0 | } |
39 | 0 | const int count = drawEvent->debugCanvas->getSize(); |
40 | 0 | drawEvent->command = command < count ? command : count - 1; |
41 | | // Invalidate stored images that depended on this combination of node and frame. |
42 | | // actually this does all of the events for this nodeId, but close enough. |
43 | 0 | auto relevantFrames = listFramesForNode(nodeId); |
44 | 0 | for (const auto& f : relevantFrames) { |
45 | 0 | fDraws[{f, nodeId}].image = nullptr; |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | | void DebugLayerManager::storeSkPicture(int nodeId, |
50 | | int frame, |
51 | | const sk_sp<SkPicture>& picture, |
52 | 0 | SkIRect dirty) { |
53 | 0 | const LayerKey k = {frame, nodeId}; |
54 | | |
55 | | // Make debug canvas using bounds from SkPicture. This will be equal to whatever width and |
56 | | // height were passed into SkPictureRecorder::beginRecording(w, h) which is the layer bounds. |
57 | 0 | const auto& layerBounds = picture->cullRect().roundOut(); |
58 | 0 | auto debugCanvas = std::make_unique<DebugCanvas>(layerBounds); |
59 | | // Must be set or they end up undefined due to cosmic rays, bad luck, etc. |
60 | 0 | debugCanvas->setOverdrawViz(false); |
61 | 0 | debugCanvas->setDrawGpuOpBounds(false); |
62 | 0 | debugCanvas->setClipVizColor(SK_ColorTRANSPARENT); |
63 | | // Setting this allows a layer to contain another layer. TODO(nifong): write a test for this. |
64 | 0 | debugCanvas->setLayerManagerAndFrame(this, frame); |
65 | | // Only draw picture to the debug canvas once. |
66 | 0 | debugCanvas->drawPicture(picture); |
67 | 0 | int numCommands = debugCanvas->getSize(); |
68 | |
|
69 | 0 | DrawEvent event = { |
70 | 0 | frame == 0 || dirty == layerBounds, // fullRedraw |
71 | 0 | nullptr, // image |
72 | 0 | std::move(debugCanvas), // debugCanvas |
73 | 0 | numCommands - 1, // command |
74 | 0 | {layerBounds.width(), layerBounds.height()}, // layerBounds |
75 | 0 | }; |
76 | |
|
77 | 0 | fDraws.set(k, std::move(event)); |
78 | 0 | keys.push_back(k); |
79 | 0 | } |
80 | | |
81 | 0 | void DebugLayerManager::drawLayerEventTo(SkSurface* surface, const int nodeId, const int frame) { |
82 | 0 | auto& evt = fDraws[{frame, nodeId}]; |
83 | 0 | evt.debugCanvas->drawTo(surface->getCanvas(), evt.command); |
84 | 0 | } |
85 | | |
86 | 0 | sk_sp<SkImage> DebugLayerManager::getLayerAsImage(const int nodeId, const int frame) { |
87 | | // What is the last frame having an SkPicture for this layer? call it frame N |
88 | | // have cached image of it? if so, return it. |
89 | | // if not, draw it at frame N by the following method: |
90 | | // The picture at frame N could have been a full redraw, or it could have been clipped to a |
91 | | // dirty region. In order to know what the layer looked like on this frame, we must draw every |
92 | | // picture starting with the last full redraw, up to the last one before the current frame, since |
93 | | // any of those previous draws could be showing through. |
94 | | |
95 | | // list of frames this node was updated on. |
96 | 0 | auto relevantFrames = listFramesForNode(nodeId); |
97 | | // find largest one not greater than `frame`. |
98 | 0 | uint32_t i = relevantFrames.size()-1; |
99 | 0 | while (relevantFrames[i] > frame) { i--; } |
100 | 0 | const int frameN = relevantFrames[i]; |
101 | | // Fetch the draw event |
102 | 0 | auto& drawEvent = fDraws[{frameN, nodeId}]; |
103 | | // if an image of this is cached, return it. |
104 | 0 | if (drawEvent.image) { |
105 | 0 | return drawEvent.image; |
106 | 0 | } |
107 | | // when it's not cached, we'll have to render it in an offscreen surface. |
108 | | // start at the last full redraw. (pick up counting backwards from above) |
109 | 0 | while (i>0 && !(fDraws[{relevantFrames[i], nodeId}].fullRedraw)) { i--; } |
110 | | // The correct layer bounds can be obtained from any drawEvent on this layer. |
111 | | // the color type and alpha type are chosen here to match wasm-skp-debugger/cpu.js which was |
112 | | // chosen to match the capabilities of HTML canvas, which this ultimately has to be drawn into. |
113 | | // TODO(nifong): introduce a method of letting the user choose the backend for this. |
114 | 0 | auto surface = SkSurfaces::Raster(SkImageInfo::Make( |
115 | 0 | drawEvent.layerBounds, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, nullptr)); |
116 | | // draw everything from the last full redraw up to the current frame. |
117 | | // other frames drawn are partial, meaning they were clipped to not completely cover the layer. |
118 | | // count back up with i |
119 | 0 | for (; i<relevantFrames.size() && relevantFrames[i]<=frameN; i++) { |
120 | 0 | drawLayerEventTo(surface.get(), nodeId, relevantFrames[i]); |
121 | 0 | } |
122 | 0 | drawEvent.image = surface->makeImageSnapshot(); |
123 | 0 | return drawEvent.image; |
124 | 0 | } |
125 | | |
126 | 0 | DebugLayerManager::DrawEventSummary DebugLayerManager::event(int nodeId, int frame) const { |
127 | 0 | auto* evt = fDraws.find({frame, nodeId}); |
128 | 0 | if (!evt) { return {}; } |
129 | 0 | return { |
130 | 0 | true, evt->debugCanvas->getSize(), |
131 | 0 | evt->layerBounds.width(), evt->layerBounds.height() |
132 | 0 | }; |
133 | 0 | } |
134 | | |
135 | 0 | std::vector<DebugLayerManager::LayerSummary> DebugLayerManager::summarizeLayers(int frame) const { |
136 | | // Find the last update on or before `frame` for every node |
137 | | // key: nodeId, one entry for every layer |
138 | | // value: summary of the layer. |
139 | 0 | std::unordered_map<int, LayerSummary> summaryMap; |
140 | 0 | for (const auto& key : keys) { |
141 | 0 | auto* evt = fDraws.find(key); |
142 | 0 | if (!evt) { continue; } |
143 | | // -1 as a default value for the last update serves as a way of indicating that this layer |
144 | | // is present in the animation, but doesn't have an update less than or equal to `frame` |
145 | 0 | int lastUpdate = (key.frame <= frame ? key.frame : -1); |
146 | | |
147 | | // do we have an entry for this layer yet? is it later than the one we're looking at? |
148 | 0 | auto found = summaryMap.find(key.nodeId); |
149 | 0 | if (found != summaryMap.end()) { |
150 | 0 | LayerSummary& item = summaryMap[key.nodeId]; |
151 | 0 | if (lastUpdate > item.frameOfLastUpdate) { |
152 | 0 | item.frameOfLastUpdate = key.frame; |
153 | 0 | item.fullRedraw = evt->fullRedraw; |
154 | 0 | } |
155 | 0 | } else { |
156 | | // record first entry for this layer |
157 | 0 | summaryMap.insert({key.nodeId, { |
158 | 0 | key.nodeId, lastUpdate, evt->fullRedraw, |
159 | 0 | evt->layerBounds.width(), evt->layerBounds.height() |
160 | 0 | }}); |
161 | 0 | } |
162 | 0 | } |
163 | 0 | std::vector<LayerSummary> result; |
164 | 0 | for (auto it = summaryMap.begin(); it != summaryMap.end(); ++it) { |
165 | 0 | result.push_back(it->second); |
166 | 0 | } |
167 | 0 | return result; |
168 | 0 | } |
169 | | |
170 | 0 | std::vector<int> DebugLayerManager::listNodesForFrame(int frame) const { |
171 | 0 | std::vector<int> result; |
172 | 0 | for (const auto& key : keys) { |
173 | 0 | if (key.frame == frame) { |
174 | 0 | result.push_back(key.nodeId); |
175 | 0 | } |
176 | 0 | } |
177 | 0 | return result; |
178 | 0 | } |
179 | | |
180 | 0 | std::vector<int> DebugLayerManager::listFramesForNode(int nodeId) const { |
181 | 0 | std::vector<int> result; |
182 | 0 | for (const auto& key : keys) { |
183 | 0 | if (key.nodeId == nodeId) { |
184 | 0 | result.push_back(key.frame); |
185 | 0 | } |
186 | 0 | } |
187 | 0 | return result; |
188 | 0 | } |
189 | | |
190 | 0 | DebugCanvas* DebugLayerManager::getEventDebugCanvas(int nodeId, int frame) { |
191 | 0 | auto& evt = fDraws[{frame, nodeId}]; |
192 | 0 | return evt.debugCanvas.get(); |
193 | 0 | } |
194 | | |
195 | 0 | void DebugLayerManager::setOverdrawViz(bool overdrawViz) { |
196 | 0 | for (const auto& key : keys) { |
197 | 0 | auto& evt = fDraws[key]; |
198 | 0 | evt.debugCanvas->setOverdrawViz(overdrawViz); |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | 0 | void DebugLayerManager::setClipVizColor(SkColor clipVizColor) { |
203 | 0 | for (const auto& key : keys) { |
204 | 0 | auto& evt = fDraws[key]; |
205 | 0 | evt.debugCanvas->setClipVizColor(clipVizColor); |
206 | 0 | } |
207 | 0 | } |
208 | | |
209 | 0 | void DebugLayerManager::setDrawGpuOpBounds(bool drawGpuOpBounds) { |
210 | 0 | for (const auto& key : keys) { |
211 | 0 | auto& evt = fDraws[key]; |
212 | 0 | evt.debugCanvas->setDrawGpuOpBounds(drawGpuOpBounds); |
213 | 0 | } |
214 | 0 | } |