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 "XLVkLoop.h"
24 : #include "XLVkGuiPlatform.h"
25 : #include "platform/linux/XLVkGuiViewImpl.h"
26 : #include "XLVkGuiApplication.h"
27 : #include "XLPlatform.h"
28 :
29 : #include "XLScene.h"
30 :
31 : #if MODULE_XENOLITH_RENDERER_BASIC2D
32 :
33 : #include "XL2dScene.h"
34 : #include "XL2dLabel.h"
35 : #include "XL2dSceneContent.h"
36 :
37 : namespace STAPPLER_VERSIONIZED stappler::xenolith::vk {
38 :
39 : class BootstrapScene : public basic2d::Scene2d {
40 : public:
41 0 : virtual ~BootstrapScene() = default;
42 :
43 : virtual bool init(Application *, const core::FrameContraints &constraints) override;
44 :
45 : virtual void onContentSizeDirty() override;
46 :
47 : protected:
48 : using Scene::init;
49 :
50 : basic2d::Label *_helloWorldLabel = nullptr;
51 : };
52 :
53 0 : bool BootstrapScene::init(Application *app, const core::FrameContraints &constraints) {
54 0 : if (!Scene2d::init(app, constraints)) {
55 0 : return false;
56 : }
57 :
58 0 : auto content = Rc<basic2d::SceneContent2d>::create();
59 :
60 0 : _helloWorldLabel = content->addChild(Rc<basic2d::Label>::create());
61 0 : _helloWorldLabel->setString("Hello World");
62 0 : _helloWorldLabel->setAnchorPoint(Anchor::Middle);
63 :
64 0 : setContent(content);
65 :
66 0 : auto color = Color4F::WHITE;
67 0 : color.a = 0.5f;
68 :
69 0 : auto light = Rc<basic2d::SceneLight>::create(basic2d::SceneLightType::Ambient, Vec2(0.0f, 0.3f), 1.5f, color);
70 0 : auto ambient = Rc<basic2d::SceneLight>::create(basic2d::SceneLightType::Ambient, Vec2(0.0f, 0.0f), 1.5f, color);
71 :
72 0 : content->setGlobalLight(Color4F::WHITE);
73 0 : content->removeAllLights();
74 0 : content->addLight(move(light));
75 0 : content->addLight(move(ambient));
76 :
77 0 : filesystem::mkdir(filesystem::cachesPath<Interface>());
78 :
79 0 : return true;
80 0 : }
81 :
82 0 : void BootstrapScene::onContentSizeDirty() {
83 0 : Scene2d::onContentSizeDirty();
84 :
85 0 : _helloWorldLabel->setPosition(_content->getContentSize() / 2.0f);
86 0 : }
87 :
88 : }
89 :
90 : #endif
91 :
92 : namespace STAPPLER_VERSIONIZED stappler::xenolith::vk {
93 :
94 10 : GuiApplication::~GuiApplication() { }
95 :
96 10 : bool GuiApplication::init(CommonInfo &&appInfo, Rc<core::Instance> &&instance) {
97 10 : if (instance) {
98 0 : if (Application::init(move(appInfo), move(instance))) {
99 0 : return true;
100 : }
101 : } else {
102 10 : auto inst = vk::platform::createInstance([&] (vk::platform::VulkanInstanceData &data, const vk::platform::VulkanInstanceInfo &info) {
103 10 : data.applicationName = appInfo.applicationName;
104 10 : data.applicationVersion = appInfo.applicationVersion;
105 10 : data.checkPresentationSupport = vk::platform::checkPresentationSupport;
106 10 : return vk::platform::initInstance(data, info);
107 10 : });
108 10 : if (Application::init(move(appInfo), move(inst))) {
109 10 : return true;
110 : }
111 10 : }
112 0 : return false;
113 : }
114 :
115 0 : bool GuiApplication::init(CommonInfo &&appInfo, const Callback<bool(VulkanInstanceData &, const VulkanInstanceInfo &)> &cb) {
116 0 : auto instance = vk::platform::createInstance([&] (VulkanInstanceData &data, const VulkanInstanceInfo &info) {
117 0 : if (cb(data, info)) {
118 0 : data.applicationName = appInfo.applicationName;
119 0 : data.applicationVersion = appInfo.applicationVersion;
120 0 : return true;
121 : }
122 0 : return false;
123 0 : });
124 :
125 0 : if (Application::init(move(appInfo), move(instance))) {
126 0 : return true;
127 : }
128 :
129 0 : return false;
130 0 : }
131 :
132 10 : void GuiApplication::run(const CallbackInfo &cb, uint32_t threadCount, TimeInterval ival) {
133 10 : core::LoopInfo info;
134 :
135 10 : run(cb, move(info), threadCount, ival);
136 10 : }
137 :
138 10 : void GuiApplication::run(const CallbackInfo &cb, core::LoopInfo &&info, uint32_t threadCount, TimeInterval ival) {
139 10 : if (!info.platformData) {
140 10 : auto data = Rc<LoopData>::alloc();
141 10 : data->deviceSupportCallback = [] (const vk::DeviceInfo &dev) {
142 20 : return dev.supportsPresentation() &&
143 10 : std::find(dev.availableExtensions.begin(), dev.availableExtensions.end(),
144 30 : String(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) != dev.availableExtensions.end();
145 10 : };
146 10 : data->deviceExtensionsCallback = [] (const vk::DeviceInfo &dev) -> Vector<StringView> {
147 10 : Vector<StringView> ret;
148 10 : ret.emplace_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
149 10 : return ret;
150 10 : };
151 10 : info.platformData = data;
152 10 : }
153 :
154 10 : Application::run(cb, move(info), threadCount, ival);
155 10 : }
156 :
157 : XL_DECLARE_EVENT_CLASS(BootstrapApplication, onSwapchainConfig);
158 :
159 0 : bool BootstrapApplication::init(ViewCommandLineData &&data, void *native) {
160 0 : _data = move(data);
161 :
162 0 : Application::CommonInfo info({
163 0 : .bundleName = _data.bundleName,
164 0 : .applicationName = _data.applicationName,
165 0 : .applicationVersion = _data.applicationVersion,
166 0 : .userAgent = _data.userAgent,
167 0 : .locale = _data.userLanguage,
168 : .nativeHandle = native
169 0 : });
170 :
171 0 : _storageParams = Value({
172 0 : pair("driver", Value("sqlite")),
173 0 : pair("dbname", Value(filesystem::cachesPath<Interface>("root.sqlite"))),
174 0 : pair("serverName", Value("RootStorage"))
175 0 : });
176 :
177 0 : return GuiApplication::init(move(info));
178 0 : }
179 :
180 0 : void BootstrapApplication::run(Function<void()> &&initCb) {
181 0 : if (_storageParams.getString("driver") == "sqlite") {
182 0 : auto path = _storageParams.getString("dbname");
183 0 : filesystem::mkdir(filepath::root(filepath::root(path)));
184 0 : filesystem::mkdir(filepath::root(path));
185 0 : }
186 :
187 : #if MODULE_XENOLITH_RESOURCES_STORAGE
188 0 : _storageServer = Rc<storage::Server>::create(static_cast<Application *>(this), _storageParams);
189 :
190 0 : if (!_storageServer) {
191 0 : log::error("Application", "Fail to create storage server");
192 : }
193 :
194 0 : addExtension(Rc<storage::Server>(_storageServer));
195 : #endif
196 :
197 : #if MODULE_XENOLITH_RESOURCES_NETWORK
198 0 : _networkController = Rc<network::Controller>::alloc(static_cast<Application *>(this), "Root");
199 0 : addExtension(Rc<network::Controller>(_networkController));
200 : #endif
201 :
202 : #if MODULE_XENOLITH_RESOURCES_ASSETS
203 0 : _assetLibrary = Rc<storage::AssetLibrary>::create(static_cast<Application *>(this), _networkController, Value({
204 0 : pair("driver", Value("sqlite")),
205 0 : pair("dbname", Value(filesystem::cachesPath<Interface>("assets.sqlite"))),
206 0 : pair("serverName", Value("AssetStorage"))
207 0 : }));
208 0 : addExtension(Rc<storage::AssetLibrary>(_assetLibrary));
209 : #endif
210 :
211 : GuiApplication::CallbackInfo callbacks({
212 0 : .initCallback = [&] (const Application &) {
213 0 : GuiApplication::addView(ViewInfo{
214 0 : .name = _data.applicationName,
215 0 : .bundleId = _data.bundleName,
216 0 : .rect = URect(UVec2{0, 0}, _data.screenSize),
217 : .decoration = _data.viewDecoration,
218 0 : .density = _data.density,
219 0 : .selectConfig = [this] (xenolith::View &view, const core::SurfaceInfo &info) -> core::SwapchainConfig {
220 0 : return selectConfig(static_cast<View &>(view), info);
221 : },
222 0 : .onCreated = [this] (xenolith::View &view, const core::FrameContraints &constraints) {
223 0 : auto scene = createSceneForView(static_cast<View &>(view), constraints);
224 0 : view.getDirector()->runScene(move(scene));
225 0 : },
226 0 : .onClosed = [this] (xenolith::View &view) {
227 0 : finalizeView(static_cast<View &>(view));
228 0 : end();
229 0 : }
230 : });
231 0 : if (initCb) {
232 0 : initCb();
233 : }
234 0 : },
235 0 : .updateCallback = [&] (const Application &, const UpdateTime &time) { }
236 0 : });
237 :
238 0 : GuiApplication::run(callbacks);
239 :
240 : #if MODULE_XENOLITH_RESOURCES_ASSETS
241 0 : _assetLibrary = nullptr;
242 : #endif
243 :
244 : #if MODULE_XENOLITH_RESOURCES_NETWORK
245 0 : _networkController = nullptr;
246 : #endif
247 :
248 : #if MODULE_XENOLITH_RESOURCES_STORAGE
249 0 : _storageServer = nullptr;
250 : #endif
251 0 : }
252 :
253 0 : void BootstrapApplication::setPreferredPresentMode(core::PresentMode mode) {
254 0 : std::unique_lock<Mutex> lock(_configMutex);
255 0 : _preferredPresentMode = mode;
256 0 : }
257 :
258 0 : Rc<Scene> BootstrapApplication::createSceneForView(vk::View &view, const core::FrameContraints &constraints) {
259 : #if MODULE_XENOLITH_RENDERER_BASIC2D
260 0 : return Rc<BootstrapScene>::create(this, constraints);
261 : #else
262 : return nullptr;
263 : #endif
264 : }
265 :
266 0 : void BootstrapApplication::finalizeView(vk::View &view) {
267 :
268 0 : }
269 :
270 0 : core::SwapchainConfig BootstrapApplication::selectConfig(vk::View &, const core::SurfaceInfo &info) {
271 0 : std::unique_lock<Mutex> lock(_configMutex);
272 0 : core::SwapchainConfig ret;
273 0 : ret.extent = info.currentExtent;
274 0 : ret.imageCount = std::max(uint32_t(3), info.minImageCount);
275 :
276 0 : ret.presentMode = info.presentModes.front();
277 0 : if (_preferredPresentMode != core::PresentMode::Unsupported) {
278 0 : for (auto &it : info.presentModes) {
279 0 : if (it == _preferredPresentMode) {
280 0 : ret.presentMode = it;
281 0 : break;
282 : }
283 : }
284 : }
285 :
286 0 : if (std::find(info.presentModes.begin(), info.presentModes.end(), core::PresentMode::Immediate) != info.presentModes.end()) {
287 0 : ret.presentModeFast = core::PresentMode::Immediate;
288 : }
289 :
290 0 : auto it = info.formats.begin();
291 0 : while (it != info.formats.end()) {
292 0 : if (it->first != xenolith::platform::getCommonFormat()) {
293 0 : ++ it;
294 : } else {
295 0 : break;
296 : }
297 : }
298 :
299 0 : if (it == info.formats.end()) {
300 0 : ret.imageFormat = info.formats.front().first;
301 0 : ret.colorSpace = info.formats.front().second;
302 : } else {
303 0 : ret.imageFormat = it->first;
304 0 : ret.colorSpace = it->second;
305 : }
306 :
307 0 : if ((info.supportedCompositeAlpha & core::CompositeAlphaFlags::Opaque) != core::CompositeAlphaFlags::None) {
308 0 : ret.alpha = core::CompositeAlphaFlags::Opaque;
309 0 : } else if ((info.supportedCompositeAlpha & core::CompositeAlphaFlags::Inherit) != core::CompositeAlphaFlags::None) {
310 0 : ret.alpha = core::CompositeAlphaFlags::Inherit;
311 : }
312 :
313 0 : ret.transfer = (info.supportedUsageFlags & core::ImageUsage::TransferDst) != core::ImageUsage::None;
314 :
315 0 : if (ret.presentMode == core::PresentMode::Mailbox) {
316 0 : ret.imageCount = std::max(uint32_t(3), ret.imageCount);
317 : }
318 :
319 0 : ret.transform = info.currentTransform;
320 :
321 0 : performOnMainThread([this, info, ret] {
322 0 : _surfaceInfo = info;
323 0 : _swapchainConfig = ret;
324 :
325 0 : onSwapchainConfig(this);
326 0 : });
327 :
328 0 : return ret;
329 0 : }
330 :
331 : }
|