1+ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O.
2+ // This file is part of the "Nabla Engine".
3+ // For conditions of distribution and use, see copyright notice in nabla.h
4+ #include " common.hpp"
5+
6+ #include " ../3rdparty/portable-file-dialogs/portable-file-dialogs.h"
7+
8+
9+ class MeshLoadersApp final : public MonoWindowApplication, public BuiltinResourcesApplication
10+ {
11+ using device_base_t = MonoWindowApplication;
12+ using asset_base_t = BuiltinResourcesApplication;
13+
14+ public:
15+ inline MeshLoadersApp (const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD)
16+ : IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD),
17+ device_base_t({1280 ,720 }, EF_UNKNOWN, _localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD) {}
18+
19+ inline bool onAppInitialized (smart_refctd_ptr<ISystem>&& system) override
20+ {
21+ if (!asset_base_t::onAppInitialized (smart_refctd_ptr (system)))
22+ return false ;
23+ if (!device_base_t::onAppInitialized (smart_refctd_ptr (system)))
24+ return false ;
25+
26+ m_semaphore = m_device->createSemaphore (m_realFrameIx);
27+ if (!m_semaphore)
28+ return logFail (" Failed to Create a Semaphore!" );
29+
30+ auto pool = m_device->createCommandPool (getGraphicsQueue ()->getFamilyIndex (),IGPUCommandPool::CREATE_FLAGS::RESET_COMMAND_BUFFER_BIT);
31+ for (auto i=0u ; i<MaxFramesInFlight; i++)
32+ {
33+ if (!pool)
34+ return logFail (" Couldn't create Command Pool!" );
35+ if (!pool->createCommandBuffers (IGPUCommandPool::BUFFER_LEVEL::PRIMARY,{m_cmdBufs.data ()+i,1 }))
36+ return logFail (" Couldn't create Command Buffer!" );
37+ }
38+
39+ // ! cache results -- speeds up mesh generation on second run
40+ m_qnc->loadCacheFromFile <EF_R8G8B8_SNORM>(m_system.get (),sharedOutputCWD/" ../../tmp/normalCache888.sse" );
41+
42+ //
43+ if (!reloadModel ())
44+ return false ;
45+ #if 0
46+ const uint32_t addtionalBufferOwnershipFamilies[] = {getGraphicsQueue()->getFamilyIndex()};
47+ // we want to use the vertex data through UTBs
48+ using usage_f = IGPUBuffer::E_USAGE_FLAGS;
49+ CAssetConverter::patch_t<asset::ICPUPolygonGeometry> patch = {};
50+ patch.positionBufferUsages = usage_f::EUF_UNIFORM_TEXEL_BUFFER_BIT;
51+ patch.indexBufferUsages = usage_f::EUF_INDEX_BUFFER_BIT;
52+ patch.otherBufferUsages = usage_f::EUF_UNIFORM_TEXEL_BUFFER_BIT;
53+ m_scene = CGeometryCreatorScene::create(
54+ {
55+ .transferQueue = getTransferUpQueue(),
56+ .utilities = m_utils.get(),
57+ .logger = m_logger.get(),
58+ .addtionalBufferOwnershipFamilies = addtionalBufferOwnershipFamilies
59+ },patch
60+ );
61+ #endif
62+
63+ auto scRes = static_cast <CDefaultSwapchainFramebuffers*>(m_surface->getSwapchainResources ());
64+ m_renderer = CSimpleDebugRenderer::create (m_assetMgr.get (),scRes->getRenderpass (),0 ,nullptr );
65+
66+ camera.mapKeysToArrows ();
67+
68+ onAppInitializedFinish ();
69+ return true ;
70+ }
71+
72+ inline IQueue::SSubmitInfo::SSemaphoreInfo renderFrame (const std::chrono::microseconds nextPresentationTimestamp) override
73+ {
74+ m_inputSystem->getDefaultMouse (&mouse);
75+ m_inputSystem->getDefaultKeyboard (&keyboard);
76+
77+ //
78+ const auto resourceIx = m_realFrameIx % MaxFramesInFlight;
79+
80+ auto * const cb = m_cmdBufs.data ()[resourceIx].get ();
81+ cb->reset (IGPUCommandBuffer::RESET_FLAGS::RELEASE_RESOURCES_BIT);
82+ cb->begin (IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT);
83+ // clear to black for both things
84+ {
85+ // begin renderpass
86+ {
87+ auto scRes = static_cast <CDefaultSwapchainFramebuffers*>(m_surface->getSwapchainResources ());
88+ auto * framebuffer = scRes->getFramebuffer (device_base_t::getCurrentAcquire ().imageIndex );
89+ const IGPUCommandBuffer::SClearColorValue clearValue = { .float32 = {1 .f ,0 .f ,1 .f ,1 .f } };
90+ const IGPUCommandBuffer::SClearDepthStencilValue depthValue = { .depth = 0 .f };
91+ const VkRect2D currentRenderArea =
92+ {
93+ .offset = {0 ,0 },
94+ .extent = {framebuffer->getCreationParameters ().width ,framebuffer->getCreationParameters ().height }
95+ };
96+ const IGPUCommandBuffer::SRenderpassBeginInfo info =
97+ {
98+ .framebuffer = framebuffer,
99+ .colorClearValues = &clearValue,
100+ .depthStencilClearValues = &depthValue,
101+ .renderArea = currentRenderArea
102+ };
103+ cb->beginRenderPass (info,IGPUCommandBuffer::SUBPASS_CONTENTS::INLINE);
104+
105+ const SViewport viewport = {
106+ .x = static_cast <float >(currentRenderArea.offset .x ),
107+ .y = static_cast <float >(currentRenderArea.offset .y ),
108+ .width = static_cast <float >(currentRenderArea.extent .width ),
109+ .height = static_cast <float >(currentRenderArea.extent .height )
110+ };
111+ cb->setViewport (0u ,1u ,&viewport);
112+
113+ cb->setScissor (0u ,1u ,¤tRenderArea);
114+ }
115+ // late latch input
116+ {
117+ camera.beginInputProcessing (nextPresentationTimestamp);
118+ mouse.consumeEvents ([&](const IMouseEventChannel::range_t & events) -> void { camera.mouseProcess (events); }, m_logger.get ());
119+ keyboard.consumeEvents ([&](const IKeyboardEventChannel::range_t & events) -> void
120+ {
121+ camera.keyboardProcess (events);
122+ },
123+ m_logger.get ()
124+ );
125+ camera.endInputProcessing (nextPresentationTimestamp);
126+ }
127+ // draw scene
128+ {
129+ float32_t3x4 viewMatrix;
130+ float32_t4x4 viewProjMatrix;
131+ // TODO: get rid of legacy matrices
132+ {
133+ memcpy (&viewMatrix,camera.getViewMatrix ().pointer (),sizeof (viewMatrix));
134+ memcpy (&viewProjMatrix,camera.getConcatenatedMatrix ().pointer (),sizeof (viewProjMatrix));
135+ }
136+ m_renderer->render (cb,CSimpleDebugRenderer::SViewParams (viewMatrix,viewProjMatrix));
137+ }
138+ cb->endRenderPass ();
139+ }
140+ cb->end ();
141+
142+ // updateGUIDescriptorSet();
143+
144+ IQueue::SSubmitInfo::SSemaphoreInfo retval =
145+ {
146+ .semaphore = m_semaphore.get (),
147+ .value = ++m_realFrameIx,
148+ .stageMask = PIPELINE_STAGE_FLAGS::ALL_GRAPHICS_BITS
149+ };
150+ const IQueue::SSubmitInfo::SCommandBufferInfo commandBuffers[] =
151+ {
152+ {.cmdbuf = cb }
153+ };
154+ const IQueue::SSubmitInfo::SSemaphoreInfo acquired[] = {
155+ {
156+ .semaphore = device_base_t::getCurrentAcquire ().semaphore ,
157+ .value = device_base_t::getCurrentAcquire ().acquireCount ,
158+ .stageMask = PIPELINE_STAGE_FLAGS::NONE
159+ }
160+ };
161+ const IQueue::SSubmitInfo infos[] =
162+ {
163+ {
164+ .waitSemaphores = acquired,
165+ .commandBuffers = commandBuffers,
166+ .signalSemaphores = {&retval,1 }
167+ }
168+ };
169+
170+ if (getGraphicsQueue ()->submit (infos) != IQueue::RESULT::SUCCESS)
171+ {
172+ retval.semaphore = nullptr ; // so that we don't wait on semaphore that will never signal
173+ m_realFrameIx--;
174+ }
175+
176+ std::string caption = " [Nabla Engine] Mesh Loaders" ;
177+ {
178+ caption += " , displaying [" ;
179+ caption += m_modelPath;
180+ caption += " ]" ;
181+ m_window->setCaption (caption);
182+ }
183+ return retval;
184+ }
185+
186+ protected:
187+ const video::IGPURenderpass::SCreationParams::SSubpassDependency* getDefaultSubpassDependencies () const override
188+ {
189+ // Subsequent submits don't wait for each other, hence its important to have External Dependencies which prevent users of the depth attachment overlapping.
190+ const static IGPURenderpass::SCreationParams::SSubpassDependency dependencies[] = {
191+ // wipe-transition of Color to ATTACHMENT_OPTIMAL and depth
192+ {
193+ .srcSubpass = IGPURenderpass::SCreationParams::SSubpassDependency::External,
194+ .dstSubpass = 0 ,
195+ .memoryBarrier = {
196+ // last place where the depth can get modified in previous frame, `COLOR_ATTACHMENT_OUTPUT_BIT` is implicitly later
197+ .srcStageMask = PIPELINE_STAGE_FLAGS::LATE_FRAGMENT_TESTS_BIT,
198+ // don't want any writes to be available, we'll clear
199+ .srcAccessMask = ACCESS_FLAGS::NONE,
200+ // destination needs to wait as early as possible
201+ // TODO: `COLOR_ATTACHMENT_OUTPUT_BIT` shouldn't be needed, because its a logically later stage, see TODO in `ECommonEnums.h`
202+ .dstStageMask = PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT | PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT,
203+ // because depth and color get cleared first no read mask
204+ .dstAccessMask = ACCESS_FLAGS::DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | ACCESS_FLAGS::COLOR_ATTACHMENT_WRITE_BIT
205+ }
206+ // leave view offsets and flags default
207+ },
208+ // color from ATTACHMENT_OPTIMAL to PRESENT_SRC
209+ {
210+ .srcSubpass = 0 ,
211+ .dstSubpass = IGPURenderpass::SCreationParams::SSubpassDependency::External,
212+ .memoryBarrier = {
213+ // last place where the color can get modified, depth is implicitly earlier
214+ .srcStageMask = PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT,
215+ // only write ops, reads can't be made available
216+ .srcAccessMask = ACCESS_FLAGS::COLOR_ATTACHMENT_WRITE_BIT
217+ // spec says nothing is needed when presentation is the destination
218+ }
219+ // leave view offsets and flags default
220+ },
221+ IGPURenderpass::SCreationParams::DependenciesEnd
222+ };
223+ return dependencies;
224+ }
225+
226+ private:
227+ inline bool reloadModel ()
228+ {
229+ pfd::open_file file (" Choose a supported Model File" , " ../../media" , { " All Supported Formats" , " *.ply *.stl *.serialized *.obj" ,
230+ " TODO (.ply)" , " *.ply" ,
231+ " TODO (.stl)" , " *.stl" ,
232+ " Mitsuba 0.6 Serialized (.serialized)" , " *.serialized" ,
233+ " Wavefront Object (.obj)" , " *.obj"
234+ });
235+ if (file.result ().empty ())
236+ return false ;
237+ m_modelPath = file.result ()[0 ];
238+
239+ // free up
240+ m_assetMgr->clearAllAssetCache ();
241+
242+ // ! load the geometry
243+ IAssetLoader::SAssetLoadParams params = {};
244+ params.meshManipulatorOverride = nullptr ; // TODO
245+ auto bundle = m_assetMgr->getAsset (m_modelPath,params);
246+ if (bundle.getContents ().empty ())
247+ return false ;
248+ // ! cache results -- speeds up mesh generation on second run
249+ m_qnc->saveCacheToFile <EF_R8G8B8_SNORM>(m_system.get (),sharedOutputCWD/" ../../tmp/normalCache888.sse" );
250+
251+ return true ;
252+ }
253+
254+ // Maximum frames which can be simultaneously submitted, used to cycle through our per-frame resources like command buffers
255+ constexpr static inline uint32_t MaxFramesInFlight = 3u ;
256+ //
257+ smart_refctd_ptr<CQuantNormalCache> m_qnc;
258+ smart_refctd_ptr<CSimpleDebugRenderer> m_renderer;
259+ //
260+ smart_refctd_ptr<ISemaphore> m_semaphore;
261+ uint64_t m_realFrameIx = 0 ;
262+ std::array<smart_refctd_ptr<IGPUCommandBuffer>,MaxFramesInFlight> m_cmdBufs;
263+ //
264+ InputSystem::ChannelReader<IMouseEventChannel> mouse;
265+ InputSystem::ChannelReader<IKeyboardEventChannel> keyboard;
266+ //
267+ Camera camera = Camera(core::vectorSIMDf(0 , 0 , 0 ), core::vectorSIMDf(0 , 0 , 0 ), core::matrix4SIMD());
268+ // mutables
269+ std::string m_modelPath;
270+ };
271+
272+ NBL_MAIN_FUNC (MeshLoadersApp)
0 commit comments