Line data Source code
1 : /**
2 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "XL2dLayer.h"
24 : #include "XL2dLabel.h"
25 : #include "XL2dScene.h"
26 : #include "XL2dVectorCanvas.h"
27 : #include "XL2dVectorSprite.h"
28 : #include "XLView.h"
29 : #include "XLDirector.h"
30 : #include "XLInputListener.h"
31 : #include "XLSceneContent.h"
32 : #include "XLFrameInfo.h"
33 : #include "backend/vk/XL2dVkShadowPass.h"
34 :
35 : namespace STAPPLER_VERSIONIZED stappler::xenolith::basic2d {
36 :
37 : class Scene2d::FpsDisplay : public Layer {
38 : public:
39 : enum DisplayMode {
40 : Fps,
41 : Vertexes,
42 : Cache,
43 : Full,
44 : Disabled,
45 : };
46 :
47 20 : virtual ~FpsDisplay() { }
48 :
49 : virtual bool init(font::FontController *fontController);
50 : virtual void update(const UpdateTime &) override;
51 :
52 : virtual bool visitDraw(FrameInfo &, NodeFlags parentFlags) override;
53 :
54 : void incrementMode();
55 :
56 : protected:
57 : using Layer::init;
58 :
59 : uint32_t _frames = 0;
60 : Label *_label = nullptr;
61 : DisplayMode _mode = Fps;
62 : };
63 :
64 10 : bool Scene2d::FpsDisplay::init(font::FontController *fontController) {
65 10 : if (!Layer::init(Color::White)) {
66 0 : return false;
67 : }
68 :
69 10 : if (fontController) {
70 10 : _label = addChild(Rc<Label>::create(fontController), Node::ZOrderMax);
71 10 : _label->setString("0.0\n0.0\n0.0\n0 0 0 0");
72 10 : _label->setFontFamily("monospace");
73 10 : _label->setAnchorPoint(Anchor::BottomLeft);
74 10 : _label->setColor(Color::Black, true);
75 10 : _label->setFontSize(16);
76 10 : _label->setOnContentSizeDirtyCallback([this] {
77 1401 : setContentSize(_label->getContentSize());
78 1401 : });
79 10 : _label->setPersistentLayout(true);
80 10 : _label->addCommandFlags(CommandFlags::DoNotCount);
81 : }
82 :
83 10 : addCommandFlags(CommandFlags::DoNotCount);
84 10 : scheduleUpdate();
85 :
86 10 : return true;
87 : }
88 :
89 4650 : void Scene2d::FpsDisplay::update(const UpdateTime &) {
90 4650 : if (_director) {
91 4650 : auto fps = _director->getAvgFps();
92 4650 : auto spf = _director->getSpf();
93 4650 : auto local = _director->getLocalFrameTime();
94 4650 : auto stat = _director->getDrawStat();
95 4650 : auto tm = _director->getDirectorFrameTime();
96 4650 : auto vertex = stat.vertexInputTime / float(1000);
97 :
98 4650 : if (_label) {
99 4650 : String str;
100 4650 : switch (_mode) {
101 4650 : case Fps:
102 9300 : str = toString(std::setprecision(3),
103 : "FPS: ", fps, " SPF: ", spf, "\nGPU: ", local, "\nDir: ", tm, " Ver: ", vertex,
104 4650 : "\nF12 to switch");
105 4650 : break;
106 0 : case Vertexes:
107 0 : str = toString(std::setprecision(3),
108 : "V:", stat.vertexes, " T:", stat.triangles, "\nZ:", stat.zPaths, " C:", stat.drawCalls, " M: ", stat.materials, "\n",
109 : stat.solidCmds, "/", stat.surfaceCmds, "/", stat.transparentCmds,
110 0 : "\nF12 to switch");
111 0 : break;
112 0 : case Cache:
113 0 : str = toString(std::setprecision(3),
114 : "Cache:", stat.cachedFramebuffers, "/", stat.cachedImages, "/", stat.cachedImageViews,
115 0 : "\nF12 to switch");
116 0 : break;
117 0 : case Full:
118 0 : str = toString(std::setprecision(3),
119 : "FPS: ", fps, " SPF: ", spf, "\nGPU: ", local, "\nDir: ", tm, " Ver: ", vertex, "\n",
120 : "V:", stat.vertexes, " T:", stat.triangles, "\nZ:", stat.zPaths, " C:", stat.drawCalls, " M: ", stat.materials, "\n",
121 : stat.solidCmds, "/", stat.surfaceCmds, "/", stat.transparentCmds, "\n",
122 : "Cache:", stat.cachedFramebuffers, "/", stat.cachedImages, "/", stat.cachedImageViews,
123 0 : "\nF12 to switch");
124 0 : break;
125 0 : default:
126 0 : break;
127 : }
128 4650 : _label->setString(str);
129 4650 : }
130 4650 : ++ _frames;
131 : }
132 4650 : }
133 :
134 4650 : bool Scene2d::FpsDisplay::visitDraw(FrameInfo &frame, NodeFlags parentFlags) {
135 : // place above any shadows
136 4650 : frame.depthStack.emplace_back(100.0f);
137 4650 : auto ret = Layer::visitDraw(frame, parentFlags);
138 4650 : frame.depthStack.pop_back();
139 4650 : return ret;
140 : }
141 :
142 0 : void Scene2d::FpsDisplay::incrementMode() {
143 0 : _mode = DisplayMode(toInt(_mode) + 1);
144 0 : if (_mode > Disabled) {
145 0 : _mode = Fps;
146 : }
147 :
148 0 : setVisible(_mode != Disabled);
149 0 : }
150 :
151 0 : bool Scene2d::init(Application *app, const core::FrameContraints &constraints) {
152 0 : return init(app, [] (Queue::Builder &) { }, constraints);
153 : }
154 :
155 0 : bool Scene2d::init(Application *app, const Callback<void(Queue::Builder &)> &cb, const core::FrameContraints &constraints) {
156 0 : core::Queue::Builder builder("Loader");
157 :
158 0 : basic2d::vk::ShadowPass::RenderQueueInfo info{
159 0 : app, Extent2(constraints.extent.width, constraints.extent.height), basic2d::vk::ShadowPass::Flags::None
160 0 : };
161 :
162 0 : basic2d::vk::ShadowPass::makeDefaultRenderQueue(builder, info);
163 :
164 0 : cb(builder);
165 :
166 0 : if (!init(move(builder), constraints)) {
167 0 : return false;
168 : }
169 :
170 0 : return true;
171 0 : }
172 :
173 10 : bool Scene2d::init(Queue::Builder &&builder, const core::FrameContraints &constraints) {
174 10 : if (!xenolith::Scene::init(move(builder), constraints)) {
175 0 : return false;
176 : }
177 :
178 10 : initialize();
179 :
180 10 : return true;
181 : }
182 :
183 4650 : void Scene2d::update(const UpdateTime &time) {
184 4650 : xenolith::Scene::update(time);
185 4650 : }
186 :
187 10 : void Scene2d::onContentSizeDirty() {
188 10 : xenolith::Scene::onContentSizeDirty();
189 :
190 10 : if (_fps) {
191 10 : _fps->setPosition(Vec2(6.0f, 6.0f));
192 : }
193 :
194 10 : _pointerCenter->setPosition(_contentSize / 2.0f);
195 10 : }
196 :
197 0 : void Scene2d::setFpsVisible(bool value) {
198 0 : _fps->setVisible(value);
199 0 : }
200 :
201 0 : bool Scene2d::isFpsVisible() const {
202 0 : return _fps->isVisible();
203 : }
204 :
205 10 : void Scene2d::setContent(SceneContent *content) {
206 10 : xenolith::Scene::setContent(content);
207 :
208 10 : addContentNodes(_content);
209 10 : }
210 :
211 10 : void Scene2d::initialize() {
212 10 : _listener = addInputListener(Rc<InputListener>::create());
213 10 : _listener->addKeyRecognizer([this] (const GestureData &ev) {
214 0 : if (ev.event == GestureEvent::Ended) {
215 0 : _fps->incrementMode();
216 : }
217 0 : return true;
218 10 : }, InputListener::makeKeyMask({InputKeyCode::F12}));
219 :
220 10 : _listener->addKeyRecognizer([this] (const GestureData &ev) {
221 0 : _pointerReal->setVisible(ev.event != GestureEvent::Ended && ev.event != GestureEvent::Cancelled);
222 0 : _pointerVirtual->setVisible(ev.event != GestureEvent::Ended && ev.event != GestureEvent::Cancelled);
223 0 : _pointerCenter->setVisible(ev.event != GestureEvent::Ended && ev.event != GestureEvent::Cancelled);
224 0 : return true;
225 10 : }, InputListener::makeKeyMask({InputKeyCode::LEFT_CONTROL}));
226 :
227 10 : _listener->addTapRecognizer([this] (const GestureTap &ev) {
228 360 : if (_fps->isTouched(ev.input->currentLocation)) {
229 0 : _fps->incrementMode();
230 : }
231 360 : return true;
232 10 : }, InputListener::makeButtonMask({InputMouseButton::Touch}), 1);
233 :
234 10 : _listener->addTouchRecognizer([this] (const GestureData &ev) {
235 0 : if ((ev.input->data.modifiers & InputModifier::Ctrl) == InputModifier::None) {
236 0 : if (_data1.event != InputEventName::End && _data1.event != InputEventName::Cancel) {
237 :
238 0 : updateInputEventData(_data1, ev.input->data, _content->convertToWorldSpace(_pointerReal->getPosition().xy()), maxOf<uint32_t>() - 1);
239 0 : updateInputEventData(_data2, ev.input->data, _content->convertToWorldSpace(_pointerVirtual->getPosition().xy()), maxOf<uint32_t>() - 2);
240 :
241 0 : _data1.event = InputEventName::Cancel;
242 0 : _data2.event = InputEventName::Cancel;
243 :
244 0 : Vector<InputEventData> events{ _data1, _data2 };
245 :
246 0 : _scene->getDirector()->getView()->handleInputEvents(move(events));
247 0 : }
248 0 : return false;
249 : }
250 :
251 0 : if (ev.event == GestureEvent::Began) {
252 0 : _listener->setExclusiveForTouch(ev.input->data.id);
253 : }
254 :
255 0 : updateInputEventData(_data1, ev.input->data, _content->convertToWorldSpace(_pointerReal->getPosition().xy()), maxOf<uint32_t>() - 1);
256 0 : updateInputEventData(_data2, ev.input->data, _content->convertToWorldSpace(_pointerVirtual->getPosition().xy()), maxOf<uint32_t>() - 2);
257 :
258 0 : Vector<InputEventData> events{ _data1, _data2 };
259 :
260 0 : _scene->getDirector()->getView()->handleInputEvents(move(events));
261 :
262 0 : return true;
263 10 : }, InputListener::makeButtonMask({InputMouseButton::MouseRight}));
264 :
265 10 : _listener->addTapRecognizer([this] (const GestureTap &tap) {
266 0 : if ((tap.input->data.modifiers & InputModifier::Shift) != InputModifier::None
267 0 : && (tap.input->data.modifiers & InputModifier::Ctrl) != InputModifier::None) {
268 0 : _pointerCenter->setPosition(_content->convertToNodeSpace(tap.input->currentLocation));
269 : }
270 0 : return true;
271 10 : }, InputListener::makeButtonMask({InputMouseButton::MouseRight}), 1);
272 :
273 10 : _listener->addMoveRecognizer([this] (const GestureData &ev) {
274 367 : auto pos = _content->convertToNodeSpace(ev.input->currentLocation);
275 367 : auto diff = pos - _pointerCenter->getPosition().xy();
276 :
277 367 : _pointerReal->setPosition(pos);
278 367 : _pointerVirtual->setPosition(pos - diff * 2.0f);
279 367 : return true;
280 : });
281 :
282 : #if NDEBUG
283 : _listener->setEnabled(false);
284 : #endif
285 10 : }
286 :
287 10 : void Scene2d::addContentNodes(SceneContent *root) {
288 10 : if (_fps) {
289 0 : _fps->removeFromParent(true);
290 0 : _fps = nullptr;
291 : }
292 :
293 10 : if (_pointerReal) {
294 0 : _pointerReal->removeFromParent(true);
295 0 : _pointerReal = nullptr;
296 : }
297 :
298 10 : if (_pointerVirtual) {
299 0 : _pointerVirtual->removeFromParent(true);
300 0 : _pointerVirtual = nullptr;
301 : }
302 :
303 10 : if (_pointerCenter) {
304 0 : _pointerCenter->removeFromParent(true);
305 0 : _pointerCenter = nullptr;
306 : }
307 :
308 10 : if (root) {
309 10 : auto mainLoop = Application::getInstance();
310 :
311 10 : _fps = root->addChild(Rc<FpsDisplay>::create(mainLoop->getExtension<font::FontController>()), Node::ZOrderMax);
312 : #if NDEBUG
313 : _fps->setVisible(false);
314 : #endif
315 :
316 : do {
317 10 : auto image = Rc<VectorImage>::create(Size2(24, 24));
318 10 : image->addPath()->openForWriting([] (vg::PathWriter &writer) {
319 10 : writer.addCircle(12, 12, 12);
320 10 : });
321 :
322 10 : _pointerReal = root->addChild(Rc<VectorSprite>::create(move(image)), ZOrder::max());
323 10 : _pointerReal->setAnchorPoint(Anchor::Middle);
324 10 : _pointerReal->setContentSize(Size2(12, 12));
325 10 : _pointerReal->setColor(Color::Red_500);
326 10 : _pointerReal->setVisible(false);
327 10 : } while (0);
328 :
329 : do {
330 10 : auto image = Rc<VectorImage>::create(Size2(24, 24));
331 10 : image->addPath()->openForWriting([] (vg::PathWriter &writer) {
332 10 : writer.addCircle(12, 12, 12);
333 10 : });
334 :
335 10 : _pointerVirtual = root->addChild(Rc<VectorSprite>::create(move(image)), ZOrder::max());
336 10 : _pointerVirtual->setAnchorPoint(Anchor::Middle);
337 10 : _pointerVirtual->setContentSize(Size2(12, 12));
338 10 : _pointerVirtual->setColor(Color::Blue_500);
339 10 : _pointerVirtual->setVisible(false);
340 10 : } while (0);
341 :
342 : do {
343 10 : auto image = Rc<VectorImage>::create(Size2(24, 24));
344 10 : image->addPath()->openForWriting([] (vg::PathWriter &writer) {
345 10 : writer.addCircle(12, 12, 12);
346 10 : });
347 :
348 10 : _pointerCenter = root->addChild(Rc<VectorSprite>::create(move(image)), ZOrder::max());
349 10 : _pointerCenter->setAnchorPoint(Anchor::Middle);
350 10 : _pointerCenter->setContentSize(Size2(12, 12));
351 10 : _pointerCenter->setColor(Color::Green_500);
352 10 : _pointerCenter->setVisible(false);
353 10 : } while (0);
354 : }
355 10 : }
356 :
357 0 : void Scene2d::updateInputEventData(InputEventData &data, const InputEventData &source, Vec2 sourcePosition, uint32_t id) {
358 0 : auto pos = _inverse.transformPoint(sourcePosition);
359 :
360 0 : data = source;
361 0 : data.id = id;
362 0 : data.x = pos.x;
363 0 : data.y = pos.y;
364 0 : data.button = InputMouseButton::Touch;
365 0 : data.modifiers |= InputModifier::Unmanaged;
366 0 : }
367 :
368 : }
|