• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
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/viewer/MSKPSlide.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkStream.h"
12 #include "include/private/SkTPin.h"
13 #include "src/core/SkOSFile.h"
14 #include "imgui.h"
15 
MSKPSlide(const SkString & name,const SkString & path)16 MSKPSlide::MSKPSlide(const SkString& name, const SkString& path)
17         : MSKPSlide(name, SkStream::MakeFromFile(path.c_str())) {}
18 
MSKPSlide(const SkString & name,std::unique_ptr<SkStreamSeekable> stream)19 MSKPSlide::MSKPSlide(const SkString& name, std::unique_ptr<SkStreamSeekable> stream)
20         : fStream(std::move(stream)) {
21     fName = name;
22 }
23 
getDimensions() const24 SkISize MSKPSlide::getDimensions() const {
25     return fPlayer ? fPlayer->maxDimensions() : SkISize{0, 0};
26 }
27 
draw(SkCanvas * canvas)28 void MSKPSlide::draw(SkCanvas* canvas) {
29     if (!fPlayer) {
30         ImGui::Text("Could not read mskp file %s.\n", fName.c_str());
31         return;
32     }
33     ImGui::Begin("MSKP");
34 
35     ImGui::BeginGroup();
36     // Play/Pause button
37     if (ImGui::Button(fPaused ? "Play " : "Pause")) {
38         fPaused = !fPaused;
39         if (fPaused) {
40             // This will ensure that when playback is unpaused we start on the current frame.
41             fLastFrameTime = -1;
42         }
43     }
44     // Control the frame rate of MSKP playback
45     ImGui::Text("FPS: ");                   ImGui::SameLine();
46     ImGui::RadioButton(  "1", &fFPS,    1); ImGui::SameLine();
47     ImGui::RadioButton( "15", &fFPS,   15); ImGui::SameLine();
48     ImGui::RadioButton( "30", &fFPS,   30); ImGui::SameLine();
49     ImGui::RadioButton( "60", &fFPS,   60); ImGui::SameLine();
50     ImGui::RadioButton("120", &fFPS,  120); ImGui::SameLine();
51     ImGui::RadioButton("1:1", &fFPS,   -1); // Draw one MSKP frame for each real viewer frame.
52     if (fFPS < 0) {
53         // Like above, will cause onAnimate() to resume at current frame when FPS is changed
54         // back to another frame rate.
55         fLastFrameTime = -1;
56     }
57     // Frame control. Slider and +/- buttons. Ctrl-Click slider to type frame number.
58     ImGui::Text("Frame:");
59     ImGui::SameLine();
60     ImGui::PushButtonRepeat(true);  // Enable click-and-hold for frame arrows.
61     int oldFrame = fFrame;
62     if (ImGui::ArrowButton("-mksp_frame", ImGuiDir_Left)) {
63         fFrame = (fFrame + fPlayer->numFrames() - 1)%fPlayer->numFrames();
64     }
65     ImGui::SameLine();
66     if (ImGui::SliderInt("##msk_frameslider", &fFrame, 0, fPlayer->numFrames()-1, "% 3d")) {
67         fFrame = SkTPin(fFrame, 0, fPlayer->numFrames() - 1);
68     }
69     ImGui::SameLine();
70     if (ImGui::ArrowButton("+mskp_frame", ImGuiDir_Right)) {
71         fFrame = (fFrame + 1)%fPlayer->numFrames();
72     }
73     if (fFrame != oldFrame) {
74         // When manually adjusting frames force layers to redraw.
75         this->redrawLayers();
76     }
77 
78     ImGui::PopButtonRepeat();
79     ImGui::EndGroup();
80 
81     ImGui::BeginGroup();
82     ImGui::Checkbox("Show Frame Bounds", &fShowFrameBounds);
83     ImGui::SetNextItemWidth(200);
84     ImGui::ColorPicker4("background", fBackgroundColor, ImGuiColorEditFlags_AlphaBar);
85     // ImGui lets user enter out of range values by typing.
86     for (float& component : fBackgroundColor) {
87         component = SkTPin(component, 0.f, 1.f);
88     }
89     ImGui::EndGroup();
90 
91     // UI for visualizing contents of offscreen layers.
92     ImGui::Text("Offscreen Layers "); ImGui::SameLine();
93     ImGui::Checkbox("List All Layers", &fListAllLayers);
94     ImGui::RadioButton("root", &fDrawLayerID, -1);
95     const std::vector<int>& layerIDs = fListAllLayers ? fAllLayerIDs : fFrameLayerIDs[fFrame];
96     fLayerIDStrings.resize(layerIDs.size());
97     for (size_t i = 0; i < layerIDs.size(); ++i) {
98         fLayerIDStrings[i] = SkStringPrintf("%d", layerIDs[i]);
99         ImGui::RadioButton(fLayerIDStrings[i].c_str(), &fDrawLayerID, layerIDs[i]);
100     }
101     ImGui::End();
102 
103     auto bounds = SkIRect::MakeSize(fPlayer->frameDimensions(fFrame));
104 
105     if (fShowFrameBounds) {
106         SkPaint boundsPaint;
107         boundsPaint.setStyle(SkPaint::kStroke_Style);
108         boundsPaint.setColor(SK_ColorRED);
109         boundsPaint.setStrokeWidth(0.f);
110         boundsPaint.setAntiAlias(true);
111         // Outset so that at default scale we draw at pixel centers of the rows/cols surrounding the
112         // bounds.
113         canvas->drawRect(SkRect::Make(bounds).makeOutset(0.5f, 0.5f), boundsPaint);
114     }
115 
116     canvas->save();
117     if (fDrawLayerID >= 0) {
118         // clip out the root layer content, but still call playFrame so layer contents are updated
119         // to fFrame.
120         bounds = SkIRect::MakeEmpty();
121     }
122     canvas->clipIRect(bounds);
123     canvas->clear(SkColor4f{fBackgroundColor[0],
124                             fBackgroundColor[1],
125                             fBackgroundColor[2],
126                             fBackgroundColor[3]});
127     fPlayer->playFrame(canvas, fFrame);
128     canvas->restore();
129 
130     if (fDrawLayerID >= 0) {
131         if (sk_sp<SkImage> layerImage = fPlayer->layerSnapshot(fDrawLayerID)) {
132             canvas->save();
133             canvas->clipIRect(SkIRect::MakeSize(layerImage->dimensions()));
134             canvas->clear(SkColor4f{fBackgroundColor[0],
135                                     fBackgroundColor[1],
136                                     fBackgroundColor[2],
137                                     fBackgroundColor[3]});
138             canvas->drawImage(std::move(layerImage), 0, 0);
139             canvas->restore();
140         }
141         return;
142     }
143 }
144 
animate(double nanos)145 bool MSKPSlide::animate(double nanos) {
146     if (!fPlayer || fPaused) {
147         return false;
148     }
149     if (fLastFrameTime < 0) {
150         // We're coming off being paused or switching from 1:1 mode to steady FPS. Advance 1 frame
151         // and reset the frame time to start accumulating time from now.
152         fFrame = (fFrame + 1)%fPlayer->numFrames();
153         fLastFrameTime = nanos;
154         return this->fPlayer->numFrames() > 1;
155     }
156     if (fFPS < 0) {
157         // 1:1 mode. Always draw the next frame on each animation cycle.
158         fFrame = (fFrame + 1)%fPlayer->numFrames();
159         return this->fPlayer->numFrames() > 1;
160     }
161     double elapsed = nanos - fLastFrameTime;
162     double frameTime = 1E9/fFPS;
163     int framesToAdvance = elapsed/frameTime;
164     fFrame = fFrame + framesToAdvance;
165     if (fFrame >= fPlayer->numFrames()) {
166         this->redrawLayers();
167     }
168     fFrame %= fPlayer->numFrames();
169     // Instead of just adding elapsed, note the time when this frame should have begun.
170     fLastFrameTime += framesToAdvance*frameTime;
171     return framesToAdvance > 0;
172 }
173 
load(SkScalar,SkScalar)174 void MSKPSlide::load(SkScalar, SkScalar) {
175     if (!fStream) {
176         return;
177     }
178     fStream->rewind();
179     fPlayer = MSKPPlayer::Make(fStream.get());
180     if (!fPlayer) {
181         return;
182     }
183     fAllLayerIDs = fPlayer->layerIDs();
184     fFrameLayerIDs.clear();
185     fFrameLayerIDs.resize(fPlayer->numFrames());
186     for (int i = 0; i < fPlayer->numFrames(); ++i) {
187         fFrameLayerIDs[i] = fPlayer->layerIDs(i);
188     }
189 }
190 
unload()191 void MSKPSlide::unload() { fPlayer.reset(); }
192 
gpuTeardown()193 void MSKPSlide::gpuTeardown() { fPlayer->resetLayers(); }
194 
redrawLayers()195 void MSKPSlide::redrawLayers() {
196     if (fDrawLayerID >= 0) {
197         // Completely reset the layers so that we won't see content from later frames on layers
198         // that haven't been visited from frames 0..fFrames.
199         fPlayer->resetLayers();
200     } else {
201         // Just rewind layers so that we redraw any layer from scratch on the next frame that
202         // updates it. Important for benchmarking/profiling as otherwise if a layer is only
203         // drawn once in the frame sequence then it will never be updated after the first play
204         // through. This doesn't reallocate the layer backing stores.
205         fPlayer->rewindLayers();
206     }
207 }
208