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 "XLFrameContext.h"
24 : #include "XLFrameInfo.h"
25 : #include "XLDirector.h"
26 : #include "XLScene.h"
27 :
28 : namespace STAPPLER_VERSIONIZED stappler::xenolith {
29 :
30 21 : FrameContext::~FrameContext() { }
31 :
32 21 : bool FrameContext::init() {
33 21 : return true;
34 : }
35 :
36 21 : void FrameContext::onEnter(Scene *scene) {
37 21 : _scene = scene;
38 21 : _queue = _scene->getQueue();
39 21 : }
40 :
41 21 : void FrameContext::onExit() {
42 21 : _queue = nullptr;
43 21 : _scene = nullptr;
44 21 : }
45 :
46 : SP_COVERAGE_TRIVIAL
47 : Rc<FrameContextHandle> FrameContext::makeHandle(FrameInfo &) {
48 : return nullptr;
49 : }
50 :
51 9538 : void FrameContext::submitHandle(FrameInfo &info, FrameContextHandle *handle) {
52 9538 : submitMaterials(info);
53 :
54 9538 : if (!handle->waitDependencies.empty()) {
55 1088 : info.director->getApplication()->wakeup();
56 : }
57 9538 : }
58 :
59 42202 : uint64_t FrameContext::getMaterial(const MaterialInfo &info) const {
60 42202 : auto hash = info.hash();
61 42202 : auto it = _materials.find(hash);
62 42202 : if (it != _materials.end()) {
63 42070 : for (auto &m : it->second) {
64 42070 : if (m.info == info) {
65 42070 : return m.id;
66 : }
67 : }
68 : }
69 132 : return 0;
70 : }
71 :
72 132 : uint64_t FrameContext::acquireMaterial(const MaterialInfo &info, Vector<core::MaterialImage> &&images, Ref *data, bool revokable) {
73 132 : auto pipeline = getPipelineForMaterial(info);
74 132 : if (!pipeline) {
75 0 : return 0;
76 : }
77 :
78 264 : for (uint32_t idx = 0; idx < uint32_t(images.size()); ++ idx) {
79 132 : if (images[idx].image != nullptr) {
80 132 : auto &image = images[idx];
81 132 : image.info = getImageViewForMaterial(info, idx, images[idx].image);
82 132 : image.view = nullptr;
83 132 : image.sampler = info.samplers[idx];
84 : }
85 : }
86 :
87 132 : core::MaterialId newId = 0;
88 132 : if (revokable) {
89 27 : if (!_revokedIds.empty()) {
90 0 : newId = _revokedIds.back();
91 0 : _revokedIds.pop_back();
92 : }
93 : }
94 :
95 132 : if (newId == 0) {
96 132 : newId = _materialAttachment->getNextMaterialId();
97 : }
98 :
99 132 : if (auto m = Rc<core::Material>::create(newId, pipeline, move(images), data)) {
100 132 : auto id = m->getId();
101 132 : addPendingMaterial(move(m));
102 132 : addMaterial(info, id, revokable);
103 132 : return id;
104 132 : }
105 0 : return 0;
106 : }
107 :
108 21 : void FrameContext::readMaterials(core::MaterialAttachment *a) {
109 21 : _materialAttachment = a;
110 :
111 21 : auto renderPass = a->getLastRenderPass();
112 42 : while (renderPass) {
113 21 : auto &layouts = renderPass->pipelineLayouts;
114 42 : for (auto &layout : layouts) {
115 21 : bool isUsable = false;
116 42 : for (auto &set : layout->sets) {
117 42 : for (auto &desc : set->descriptors) {
118 42 : if (desc->attachment->attachment->attachment == a) {
119 21 : isUsable = true;
120 21 : break;
121 : }
122 : }
123 : }
124 :
125 21 : if (!isUsable) {
126 0 : break;
127 : }
128 :
129 21 : auto &sp = _layouts.emplace_back(PipelineLayoutData({layout}));
130 :
131 105 : for (auto &pipeline : layout->graphicPipelines) {
132 84 : auto hash = pipeline->material.hash();
133 84 : auto it = sp.pipelines.find(hash);
134 84 : if (it == sp.pipelines.end()) {
135 84 : it = sp.pipelines.emplace(hash, Vector<const core::GraphicPipelineData *>()).first;
136 : }
137 84 : it->second.emplace_back(pipeline);
138 : }
139 : }
140 :
141 21 : renderPass = a->getPrevRenderPass(renderPass);
142 : }
143 105 : for (auto &m : a->getPredefinedMaterials()) {
144 84 : addMaterial(getMaterialInfo(m), m->getId(), false);
145 : }
146 21 : }
147 :
148 84 : MaterialInfo FrameContext::getMaterialInfo(const Rc<core::Material> &material) const {
149 84 : MaterialInfo ret;
150 :
151 84 : size_t idx = 0;
152 168 : for (auto &it : material->getImages()) {
153 84 : if (idx < config::MaxMaterialImages) {
154 84 : ret.images[idx] = it.image->image->getIndex();
155 84 : ret.samplers[idx] = it.sampler;
156 84 : ret.colorModes[idx] = it.info.getColorMode();
157 : }
158 84 : ++ idx;
159 : }
160 :
161 84 : ret.pipeline = material->getPipeline()->material;
162 84 : return ret;
163 : }
164 :
165 132 : void FrameContext::addPendingMaterial(Rc<core::Material> &&material) {
166 132 : _pendingMaterialsToAdd.emplace_back(move(material));
167 132 : if (!_materialDependency) {
168 424 : _materialDependency = Rc<core::DependencyEvent>::alloc(core::DependencyEvent::QueueSet{
169 212 : Rc<core::Queue>(_materialAttachment->getCompiler())
170 318 : });
171 : }
172 132 : }
173 :
174 216 : void FrameContext::addMaterial(const MaterialInfo &info, core::MaterialId id, bool revokable) {
175 216 : auto materialHash = info.hash();
176 216 : auto it = _materials.find(info.hash());
177 216 : if (it != _materials.end()) {
178 0 : it->second.emplace_back(ContextMaterialInfo{info, id, revokable});
179 : } else {
180 216 : Vector<ContextMaterialInfo> ids({ContextMaterialInfo{info, id, revokable}});
181 216 : _materials.emplace(materialHash, move(ids));
182 216 : }
183 216 : }
184 :
185 21 : String FrameContext::listMaterials() const {
186 21 : StringStream out;
187 237 : for (auto &it : _materials) {
188 216 : out << it.first << ":\n";
189 432 : for (auto &iit : it.second) {
190 216 : out << "\t" << iit.info.description() << " -> " << iit.id << "\n";
191 : }
192 : }
193 42 : return out.str();
194 21 : }
195 :
196 132 : core::ImageViewInfo FrameContext::getImageViewForMaterial(const MaterialInfo &info, uint32_t idx, const core::ImageData *image) const {
197 132 : return core::ImageViewInfo(image->format, info.colorModes[idx]);
198 : }
199 :
200 132 : auto FrameContext::getPipelineForMaterial(const MaterialInfo &info) const -> const core::GraphicPipelineData * {
201 132 : auto hash = info.pipeline.hash();
202 132 : for (auto &it : _layouts) {
203 132 : auto hashIt = it.pipelines.find(hash);
204 132 : if (hashIt != it.pipelines.end()) {
205 132 : for (auto &pipeline : hashIt->second) {
206 132 : if (pipeline->material == info.pipeline && isPipelineMatch(pipeline, info)) {
207 132 : return pipeline;
208 : }
209 : }
210 : }
211 : }
212 0 : log::warn("Scene", "No pipeline for attachment '", _materialAttachment->getName(), "': ",
213 0 : info.pipeline.description(), " : ", info.pipeline.data());
214 0 : return nullptr;
215 : }
216 :
217 132 : bool FrameContext::isPipelineMatch(const core::GraphicPipelineData *data, const MaterialInfo &info) const {
218 132 : return true; // TODO: true match
219 : }
220 :
221 9538 : void FrameContext::submitMaterials(const FrameInfo &info) {
222 : // submit material updates
223 9538 : if (!_pendingMaterialsToAdd.empty() || !_pendingMaterialsToRemove.empty()) {
224 127 : Vector<Rc<core::DependencyEvent>> events;
225 127 : if (_materialDependency) {
226 106 : events.emplace_back(_materialDependency);
227 : }
228 :
229 127 : if (!_pendingMaterialsToAdd.empty() || !_pendingMaterialsToRemove.empty()) {
230 127 : auto req = Rc<core::MaterialInputData>::alloc();
231 127 : req->attachment = _materialAttachment;
232 127 : req->materialsToAddOrUpdate = move(_pendingMaterialsToAdd);
233 127 : req->materialsToRemove = move(_pendingMaterialsToRemove);
234 254 : req->callback = [app = Rc<Application>(info.director->getApplication())] {
235 127 : app->wakeup();
236 254 : };
237 :
238 154 : for (auto &it : req->materialsToRemove) {
239 27 : emplace_ordered(_revokedIds, it);
240 : }
241 :
242 127 : info.director->getGlLoop()->compileMaterials(move(req), events);
243 127 : }
244 :
245 127 : _pendingMaterialsToAdd.clear();
246 127 : _pendingMaterialsToRemove.clear();
247 127 : _materialDependency = nullptr;
248 127 : }
249 9538 : }
250 :
251 21 : void FrameContext::revokeImages(SpanView<uint64_t> vec) {
252 : #ifdef COVERAGE
253 21 : listMaterials();
254 : #endif
255 54 : auto shouldRevoke = [&, this] (const ContextMaterialInfo &iit) {
256 27 : for (auto &id : vec) {
257 27 : if (iit.info.hasImage(id)) {
258 27 : emplace_ordered(_pendingMaterialsToRemove, iit.id);
259 27 : return true;
260 : }
261 : }
262 0 : return false;
263 21 : };
264 :
265 237 : for (auto &it : _materials) {
266 216 : auto iit = it.second.begin();
267 432 : while (iit != it.second.end()) {
268 216 : if (iit->revokable) {
269 27 : if (shouldRevoke(*iit)) {
270 27 : iit = it.second.erase(iit);
271 : } else {
272 0 : ++ iit;
273 : }
274 : } else {
275 189 : ++ iit;
276 : }
277 : }
278 : }
279 21 : }
280 :
281 : }
|