Line data Source code
1 : /**
2 : Copyright (c) 2021-2022 Roman Katuntsev <sbkarr@stappler.org>
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 "XLVkAttachment.h"
24 : #include "XLVkPipeline.h"
25 : #include "XLVkRenderPass.h"
26 : #include "XLVkRenderQueueCompiler.h"
27 : #include "XLVkTransferQueue.h"
28 : #include "XLVkMaterialCompiler.h"
29 : #include "XLCoreFrameEmitter.h"
30 : #include "XLCoreFrameQueue.h"
31 : #include "XLCoreFrameCache.h"
32 : #include "XLCoreFrameRequest.h"
33 :
34 : namespace STAPPLER_VERSIONIZED stappler::xenolith::vk {
35 :
36 : class RenderQueueAttachment : public core::GenericAttachment {
37 : public:
38 : virtual ~RenderQueueAttachment();
39 :
40 : virtual Rc<core::AttachmentHandle> makeFrameHandle(const FrameQueue &) override;
41 : };
42 :
43 : class RenderQueueAttachmentHandle : public core::AttachmentHandle {
44 : public:
45 : virtual ~RenderQueueAttachmentHandle();
46 :
47 : virtual bool setup(FrameQueue &handle, Function<void(bool)> &&) override;
48 : virtual void submitInput(FrameQueue &, Rc<core::AttachmentInputData> &&, Function<void(bool)> &&) override;
49 :
50 756 : const Rc<core::Queue> &getRenderQueue() const { return _input->queue; }
51 189 : const Rc<TransferResource> &getTransferResource() const { return _resource; }
52 :
53 : protected:
54 : void runShaders(FrameHandle &frame);
55 : void runPipelines(FrameHandle &frame);
56 :
57 : Device *_device = nullptr;
58 : std::atomic<size_t> _programsInQueue = 0;
59 : std::atomic<size_t> _pipelinesInQueue = 0;
60 : Rc<TransferResource> _resource;
61 : Rc<RenderQueueInput> _input;
62 : };
63 :
64 : class RenderQueuePass : public QueuePass {
65 : public:
66 : virtual ~RenderQueuePass();
67 :
68 : virtual bool init(QueuePassBuilder &, const AttachmentData *);
69 :
70 : virtual Rc<QueuePassHandle> makeFrameHandle(const FrameQueue &) override;
71 :
72 189 : const AttachmentData *getAttachment() const {
73 189 : return _attachment;
74 : }
75 :
76 : protected:
77 : using QueuePass::init;
78 :
79 : const AttachmentData *_attachment = nullptr;
80 : };
81 :
82 : class RenderQueuePassHandle : public QueuePassHandle {
83 : public:
84 : virtual ~RenderQueuePassHandle();
85 :
86 : virtual bool init(QueuePass &, const FrameQueue &) override;
87 :
88 : virtual bool prepare(FrameQueue &, Function<void(bool)> &&) override;
89 : virtual void submit(FrameQueue &, Rc<FrameSync> &&, Function<void(bool)> &&onSubmited, Function<void(bool)> &&onComplete) override;
90 :
91 : virtual void finalize(FrameQueue &, bool successful) override;
92 :
93 : protected:
94 : virtual bool prepareMaterials(FrameHandle &frame, VkCommandBuffer buf,
95 : const Rc<core::MaterialAttachment> &attachment, Vector<VkBufferMemoryBarrier> &outputBufferBarriers);
96 :
97 : Rc<TransferResource> _resource;
98 : Rc<core::Queue> _queue;
99 : RenderQueueAttachmentHandle *_attachment;
100 : };
101 :
102 84 : RenderQueueCompiler::~RenderQueueCompiler() { }
103 :
104 42 : bool RenderQueueCompiler::init(Device &dev, TransferQueue *transfer, MaterialCompiler *compiler) {
105 : using namespace core;
106 :
107 42 : Queue::Builder builder("RenderQueueCompiler");
108 :
109 84 : auto attachment = builder.addAttachemnt("RenderQueueAttachment", [&] (AttachmentBuilder &attachmentBuilder) -> Rc<Attachment> {
110 42 : attachmentBuilder.defineAsInput();
111 42 : attachmentBuilder.defineAsOutput();
112 84 : return Rc<RenderQueueAttachment>::create(attachmentBuilder);
113 42 : });
114 :
115 42 : builder.addPass("RenderQueueRenderPass", PassType::Transfer, RenderOrdering(0), [&] (QueuePassBuilder &passBuilder) -> Rc<core::QueuePass> {
116 84 : return Rc<RenderQueuePass>::create(passBuilder, attachment);
117 : });
118 :
119 42 : if (Queue::init(move(builder))) {
120 42 : _attachment = attachment;
121 :
122 42 : prepare(dev);
123 :
124 84 : for (auto &it : getPasses()) {
125 42 : auto pass = Rc<RenderPass>::create(dev, *it);
126 42 : it->impl = pass.get();
127 42 : }
128 :
129 42 : _transfer = transfer;
130 42 : _materialCompiler = compiler;
131 42 : return true;
132 : }
133 0 : return false;
134 42 : }
135 :
136 189 : auto RenderQueueCompiler::makeRequest(Rc<RenderQueueInput> &&input) -> Rc<FrameRequest> {
137 189 : auto ret = Rc<FrameRequest>::create(this);
138 189 : ret->addInput(_attachment, move(input));
139 189 : return ret;
140 0 : }
141 :
142 84 : RenderQueueAttachment::~RenderQueueAttachment() { }
143 :
144 189 : auto RenderQueueAttachment::makeFrameHandle(const FrameQueue &handle) -> Rc<AttachmentHandle> {
145 378 : return Rc<RenderQueueAttachmentHandle>::create(this, handle);
146 : }
147 :
148 378 : RenderQueueAttachmentHandle::~RenderQueueAttachmentHandle() { }
149 :
150 189 : bool RenderQueueAttachmentHandle::setup(FrameQueue &handle, Function<void(bool)> &&) {
151 189 : _device = static_cast<Device *>(handle.getFrame()->getDevice());
152 189 : return true;
153 : }
154 :
155 189 : void RenderQueueAttachmentHandle::submitInput(FrameQueue &q, Rc<core::AttachmentInputData> &&data, Function<void(bool)> &&cb) {
156 189 : _input = (RenderQueueInput *)data.get();
157 189 : if (!_input || q.isFinalized()) {
158 0 : cb(false);
159 0 : return;
160 : }
161 :
162 189 : q.getFrame()->waitForDependencies(data->waitDependencies, [this, cb = move(cb)] (FrameHandle &handle, bool success) {
163 189 : if (!success || !handle.isValidFlag()) {
164 0 : cb(false);
165 0 : return;
166 : }
167 :
168 189 : if (_input->queue->getInternalResource()) {
169 84 : handle.performInQueue([this] (FrameHandle &frame) -> bool {
170 42 : runShaders(frame);
171 42 : _resource = Rc<TransferResource>::create(_device->getAllocator(), _input->queue->getInternalResource());
172 42 : if (_resource->initialize()) {
173 42 : return true;
174 : }
175 0 : return false;
176 42 : }, [cb = move(cb)] (FrameHandle &frame, bool success) {
177 42 : cb(success);
178 42 : }, nullptr, "RenderQueueAttachmentHandle::submitInput _input->queue->getInternalResource");
179 : } else {
180 147 : handle.performOnGlThread([this, cb = move(cb)] (FrameHandle &frame) {
181 147 : cb(true);
182 147 : runShaders(frame);
183 147 : }, this, true, "RenderQueueAttachmentHandle::submitInput");
184 : }
185 : });
186 : }
187 :
188 189 : void RenderQueueAttachmentHandle::runShaders(FrameHandle &frame) {
189 189 : size_t tasksCount = 0;
190 189 : Vector<core::ProgramData *> programs;
191 :
192 : // count phase-1 tasks
193 189 : _programsInQueue += _input->queue->getPasses().size();
194 189 : tasksCount += _input->queue->getPasses().size();
195 :
196 1785 : for (auto &it : _input->queue->getPrograms()) {
197 1596 : if (auto p = _device->getProgram(it->key)) {
198 0 : it->program = p;
199 : } else {
200 1596 : ++ tasksCount;
201 1596 : ++ _programsInQueue;
202 1596 : programs.emplace_back(it);
203 1596 : }
204 : }
205 :
206 1785 : for (auto &it : programs) {
207 1596 : frame.performRequiredTask([this, req = it] (FrameHandle &frame) {
208 1596 : auto ret = Rc<Shader>::create(*_device, *req);
209 1596 : if (!ret) {
210 0 : log::error("Gl-Device", "Fail to compile shader program ", req->key);
211 0 : return false;
212 : } else {
213 1596 : req->program = _device->addProgram(ret);
214 3192 : if (_programsInQueue.fetch_sub(1) == 1) {
215 0 : runPipelines(frame);
216 : }
217 : }
218 1596 : return true;
219 1596 : }, this, "RenderQueueAttachmentHandle::runShaders - programs");
220 : }
221 :
222 189 : _input->queue->prepare(*_device);
223 :
224 504 : for (auto &it : _input->queue->getPasses()) {
225 315 : frame.performRequiredTask([this, req = it] (FrameHandle &frame) -> bool {
226 315 : auto ret = Rc<RenderPass>::create(*_device, *req);
227 315 : if (!ret) {
228 0 : log::error("Gl-Device", "Fail to compile render pass ", req->key);
229 0 : return false;
230 : } else {
231 315 : req->impl = ret.get();
232 630 : if (_programsInQueue.fetch_sub(1) == 1) {
233 189 : runPipelines(frame);
234 : }
235 : }
236 315 : return true;
237 315 : }, this, "RenderQueueAttachmentHandle::runShaders - passes");
238 : }
239 :
240 189 : if (tasksCount == 0) {
241 0 : runPipelines(frame);
242 : }
243 189 : }
244 :
245 189 : void RenderQueueAttachmentHandle::runPipelines(FrameHandle &frame) {
246 189 : [[maybe_unused]] size_t tasksCount = _pipelinesInQueue.load();
247 504 : for (auto &pit : _input->queue->getPasses()) {
248 588 : for (auto &sit : pit->subpasses) {
249 273 : _pipelinesInQueue += sit->graphicPipelines.size() + sit->computePipelines.size();
250 273 : tasksCount += sit->graphicPipelines.size() + sit->computePipelines.size();
251 : }
252 : }
253 :
254 504 : for (auto &pit : _input->queue->getPasses()) {
255 588 : for (auto &sit : pit->subpasses) {
256 357 : for (auto &it : sit->graphicPipelines) {
257 84 : frame.performRequiredTask([this, pass = sit, pipeline = it] (FrameHandle &frame) -> bool {
258 84 : auto ret = Rc<GraphicPipeline>::create(*_device, *pipeline, *pass, *_input->queue);
259 84 : if (!ret) {
260 0 : log::error("Gl-Device", "Fail to compile pipeline ", pipeline->key);
261 0 : return false;
262 : } else {
263 84 : pipeline->pipeline = ret.get();
264 : }
265 84 : return true;
266 84 : }, this, "RenderQueueAttachmentHandle::runPipelines");
267 : }
268 1785 : for (auto &it : sit->computePipelines) {
269 1512 : frame.performRequiredTask([this, pass = sit, pipeline = it] (FrameHandle &frame) -> bool {
270 1511 : auto ret = Rc<ComputePipeline>::create(*_device, *pipeline, *pass, *_input->queue);
271 1512 : if (!ret) {
272 0 : log::error("Gl-Device", "Fail to compile pipeline ", pipeline->key);
273 0 : return false;
274 : } else {
275 1512 : pipeline->pipeline = ret.get();
276 : }
277 1512 : return true;
278 1512 : }, this, "RenderQueueAttachmentHandle::runPipelines");
279 : }
280 : }
281 : }
282 189 : }
283 :
284 84 : RenderQueuePass::~RenderQueuePass() { }
285 :
286 42 : bool RenderQueuePass::init(QueuePassBuilder &passBuilder, const AttachmentData *attachment) {
287 42 : passBuilder.addAttachment(attachment);
288 :
289 42 : if (!QueuePass::init(passBuilder)) {
290 0 : return false;
291 : }
292 :
293 42 : _attachment = attachment;
294 42 : return true;
295 : }
296 :
297 189 : auto RenderQueuePass::makeFrameHandle(const FrameQueue &handle) -> Rc<QueuePassHandle> {
298 378 : return Rc<RenderQueuePassHandle>::create(*this, handle);
299 : }
300 :
301 378 : RenderQueuePassHandle::~RenderQueuePassHandle() {
302 189 : if (_resource) {
303 42 : _resource->invalidate(*_device);
304 : }
305 378 : }
306 :
307 189 : bool RenderQueuePassHandle::init(QueuePass &pass, const FrameQueue &queue) {
308 189 : if (!QueuePassHandle::init(pass, queue)) {
309 0 : return false;
310 : }
311 :
312 189 : _isAsync = true;
313 189 : return true;
314 : }
315 :
316 189 : bool RenderQueuePassHandle::prepare(FrameQueue &frame, Function<void(bool)> &&cb) {
317 189 : if (auto a = frame.getAttachment(static_cast<RenderQueuePass *>(_queuePass.get())->getAttachment())) {
318 189 : _attachment = static_cast<RenderQueueAttachmentHandle *>(a->handle.get());
319 : }
320 :
321 189 : _loop = static_cast<Loop *>(frame.getLoop());
322 189 : _device = static_cast<Device *>(frame.getFrame()->getDevice());
323 189 : _queue = _attachment->getRenderQueue();
324 :
325 189 : auto hasMaterials = false;
326 189 : auto &res = _attachment->getTransferResource();
327 796 : for (auto &it : _queue->getAttachments()) {
328 628 : if (auto v = it->attachment.cast<core::MaterialAttachment>()) {
329 :
330 21 : v->setCompiler(static_cast<RenderQueueCompiler *>(_data->queue->queue)->getMaterialCompiler());
331 :
332 21 : if (!v->getPredefinedMaterials().empty()) {
333 21 : hasMaterials = true;
334 21 : break;
335 : }
336 628 : }
337 : }
338 :
339 189 : if (res || hasMaterials) {
340 42 : _resource = res;
341 42 : _pool = _device->acquireCommandPool(QueueOperations::Transfer);
342 42 : if (!_pool) {
343 0 : invalidate();
344 0 : return false;
345 : }
346 :
347 84 : frame.getFrame()->performInQueue([this, hasMaterials] (FrameHandle &frame) {
348 42 : auto buf = _pool->recordBuffer(*_device, [&, this] (CommandBuffer &buf) {
349 42 : Vector<VkImageMemoryBarrier> outputImageBarriers;
350 42 : Vector<VkBufferMemoryBarrier> outputBufferBarriers;
351 :
352 42 : if (_resource) {
353 42 : if (!_resource->prepareCommands(_pool->getFamilyIdx(), buf.getBuffer(), outputImageBarriers, outputBufferBarriers)) {
354 0 : log::error("vk::RenderQueueCompiler", "Fail to compile resource for ", _queue->getName());
355 0 : return false;
356 : }
357 42 : _resource->compile();
358 : }
359 :
360 42 : if (hasMaterials) {
361 210 : for (auto &it : _queue->getAttachments()) {
362 189 : if (auto v = it->attachment.cast<core::MaterialAttachment>()) {
363 21 : if (!prepareMaterials(frame, buf.getBuffer(), v, outputBufferBarriers)) {
364 0 : log::error("vk::RenderQueueCompiler", "Fail to compile predefined materials for ", _queue->getName());
365 0 : return false;
366 : }
367 189 : }
368 : }
369 : }
370 :
371 84 : _device->getTable()->vkCmdPipelineBarrier(buf.getBuffer(), VK_PIPELINE_STAGE_TRANSFER_BIT,
372 : VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0,
373 : 0, nullptr,
374 42 : outputBufferBarriers.size(), outputBufferBarriers.data(),
375 42 : outputImageBarriers.size(), outputImageBarriers.data());
376 42 : return true;
377 84 : });
378 :
379 42 : if (buf) {
380 42 : _buffers.emplace_back(buf);
381 : }
382 42 : return true;
383 42 : }, [this, cb = move(cb)] (FrameHandle &frame, bool success) {
384 42 : if (success) {
385 42 : _commandsReady = true;
386 42 : _descriptorsReady = true;
387 : } else {
388 0 : log::error("VK-Error", "Fail to doPrepareCommands");
389 : }
390 42 : cb(success);
391 42 : }, this, "RenderPass::doPrepareCommands _attachment->getTransferResource");
392 : } else {
393 147 : frame.getFrame()->performOnGlThread([cb = move(cb)] (FrameHandle &frame) {
394 147 : cb(true);
395 147 : }, this, false, "RenderPass::doPrepareCommands");
396 : }
397 :
398 189 : return false;
399 : }
400 :
401 189 : void RenderQueuePassHandle::submit(FrameQueue &queue, Rc<FrameSync> &&sync, Function<void(bool)> &&onSubmited, Function<void(bool)> &&onComplete) {
402 189 : if (_buffers.empty()) {
403 147 : onSubmited(true);
404 147 : onComplete(true);
405 : } else {
406 42 : QueuePassHandle::submit(queue, move(sync), move(onSubmited), move(onComplete));
407 : }
408 189 : }
409 :
410 189 : void RenderQueuePassHandle::finalize(FrameQueue &frame, bool successful) {
411 189 : QueuePassHandle::finalize(frame, successful);
412 189 : Vector<uint64_t> passIds;
413 189 : auto &cache = frame.getLoop()->getFrameCache();
414 504 : for (auto &it : _attachment->getRenderQueue()->getPasses()) {
415 315 : if (it->impl && it->pass->getType() != core::PassType::Generic) {
416 293 : passIds.emplace_back(it->impl->getIndex());
417 293 : cache->addRenderPass(it->impl->getIndex());
418 : }
419 : }
420 :
421 189 : Vector<uint64_t> attachmentIds;
422 903 : for (auto &it : _attachment->getRenderQueue()->getAttachments()) {
423 714 : if (it->type == core::AttachmentType::Image) {
424 105 : attachmentIds.emplace_back(it->id);
425 105 : cache->addAttachment(it->id);
426 : }
427 : }
428 :
429 567 : _attachment->getRenderQueue()->setCompiled(*_device, [loop = Rc<core::Loop>(frame.getLoop()),
430 378 : passIds = move(passIds), attachmentIds = move(attachmentIds)] () mutable {
431 189 : loop->performOnGlThread([loop, passIds = move(passIds), attachmentIds = move(attachmentIds)] () mutable {
432 105 : auto &cache = loop->getFrameCache();
433 314 : for (auto &id : passIds) {
434 209 : cache->removeRenderPass(id);
435 : }
436 210 : for (auto &id : attachmentIds) {
437 105 : cache->removeAttachment(id);
438 : }
439 105 : cache->removeUnreachableFramebuffers();
440 105 : });
441 189 : });
442 189 : }
443 :
444 21 : bool RenderQueuePassHandle::prepareMaterials(FrameHandle &iframe, VkCommandBuffer buf,
445 : const Rc<core::MaterialAttachment> &attachment, Vector<VkBufferMemoryBarrier> &outputBufferBarriers) {
446 21 : auto table = _device->getTable();
447 21 : auto &initial = attachment->getPredefinedMaterials();
448 21 : if (initial.empty()) {
449 0 : return true;
450 : }
451 :
452 21 : auto data = attachment->allocateSet(*_device);
453 :
454 21 : auto buffers = updateMaterials(iframe, data, initial, SpanView<core::MaterialId>(), SpanView<core::MaterialId>());
455 :
456 : VkBufferCopy indexesCopy;
457 21 : indexesCopy.srcOffset = 0;
458 21 : indexesCopy.dstOffset = 0;
459 21 : indexesCopy.size = buffers.stagingBuffer->getSize();
460 :
461 21 : auto stagingBuf = buffers.stagingBuffer->getBuffer();
462 21 : auto targetBuf = buffers.targetBuffer->getBuffer();
463 :
464 21 : Vector<VkImageMemoryBarrier> outputImageBarriers;
465 21 : table->vkCmdCopyBuffer(buf, stagingBuf, targetBuf, 1, &indexesCopy);
466 :
467 21 : QueueOperations ops = QueueOperations::None;
468 42 : for (auto &it : attachment->getRenderPasses()) {
469 21 : ops |= static_cast<vk::QueuePass *>(it->pass.get())->getQueueOps();
470 21 : }
471 :
472 21 : if (auto q = _device->getQueueFamily(ops)) {
473 21 : if (q->index == _pool->getFamilyIdx()) {
474 0 : outputBufferBarriers.emplace_back(VkBufferMemoryBarrier({
475 : VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, nullptr,
476 : VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
477 : VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
478 0 : buffers.targetBuffer->getBuffer(), 0, VK_WHOLE_SIZE
479 : }));
480 : } else {
481 21 : auto &b = outputBufferBarriers.emplace_back(VkBufferMemoryBarrier({
482 : VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, nullptr,
483 : VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
484 21 : _pool->getFamilyIdx(), q->index,
485 21 : buffers.targetBuffer->getBuffer(), 0, VK_WHOLE_SIZE
486 : }));
487 21 : buffers.targetBuffer->setPendingBarrier(b);
488 : }
489 :
490 21 : auto tmpBuffer = new Rc<Buffer>(move(buffers.targetBuffer));
491 21 : auto tmpOrder = new HashMap<core::MaterialId, uint32_t>(move(buffers.ordering));
492 21 : iframe.performOnGlThread([attachment, data, tmpBuffer, tmpOrder] (FrameHandle &) {
493 21 : data->setBuffer(move(*tmpBuffer), move(*tmpOrder));
494 21 : attachment->setMaterials(data);
495 21 : delete tmpBuffer;
496 21 : delete tmpOrder;
497 21 : }, nullptr, false, "RenderQueueRenderPassHandle::prepareMaterials");
498 :
499 21 : return true;
500 : }
501 0 : return false;
502 21 : }
503 :
504 : }
|