PeriDyno 1.0.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
VkApp.cpp
Go to the documentation of this file.
1/*
2* Vulkan Example base class
3*
4* Copyright (C) by Sascha Willems - www.saschawillems.de
5*
6* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
7*/
8
9#include "VkApp.h"
10#include "VkSystem.h"
11#include "VkVisualModule.h"
12#include "SceneGraph.h"
13#include "SceneGraphFactory.h"
14
16{
18 submitInfo.commandBufferCount = 1;
19 submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
20 VK_CHECK_RESULT(vkQueueSubmit(ctx->graphicsQueueHandle(), 1, &submitInfo, VK_NULL_HANDLE));
22}
23
25{
26 std::string device(dyno::VkSystem::instance()->getDeviceProperties().deviceName);
27 std::string windowTitle;
28 windowTitle = title + " - " + device;
29 if (!settings.overlay) {
30 windowTitle += " - " + std::to_string(frameCounter) + " fps";
31 }
32 return windowTitle;
33}
34
36{
37 // Create one command buffer for each swap chain image and reuse for rendering
38 drawCmdBuffers.resize(swapChain.imageCount);
39
40 VkCommandBufferAllocateInfo cmdBufAllocateInfo =
42 cmdPool,
43 VK_COMMAND_BUFFER_LEVEL_PRIMARY,
44 static_cast<uint32_t>(drawCmdBuffers.size()));
45
46 VK_CHECK_RESULT(vkAllocateCommandBuffers(ctx->deviceHandle(), &cmdBufAllocateInfo, drawCmdBuffers.data()));
47}
48
50{
51 vkFreeCommandBuffers(ctx->deviceHandle(), cmdPool, static_cast<uint32_t>(drawCmdBuffers.size()), drawCmdBuffers.data());
52}
53
54std::string VkApp::getShadersPath() const
55{
56 return getAssetPath() + "shaders/" + shaderDir + "/";
57}
58
60{
61 if (ctx->enableDebugMarkers) {
62 //vks::debugmarker::setup(ctx->deviceHandle());
63 }
71// createPipelineCache();
73 settings.overlay = settings.overlay;
74 if (settings.overlay) {
75 UIOverlay.shaders = {
76 loadShader(getShadersPath() + "base/uioverlay.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
77 loadShader(getShadersPath() + "base/uioverlay.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT),
78 };
79 UIOverlay.prepareResources();
80 UIOverlay.preparePipeline(ctx->pipelineCacheHandle(), renderPass);
81 }
82
83 // Make sure the code works properly both with different queues families for graphics and compute and the same queue family
84#ifdef DEBUG_FORCE_SHARED_GRAPHICS_COMPUTE_QUEUE
85 ctx->queueFamilyIndices.compute = ctx->queueFamilyIndices.graphics;
86#endif
87
88 // Initialize the all nodes and modules in a scenegraph.
90
91 this->prepare(renderPass);
92 this->viewChanged();
93
94 this->buildCommandBuffers();
95
96 prepared = true;
97}
98
99VkPipelineShaderStageCreateInfo VkApp::loadShader(std::string fileName, VkShaderStageFlagBits stage)
100{
101 VkPipelineShaderStageCreateInfo shaderStage = {};
102 shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
103 shaderStage.stage = stage;
104 shaderStage.module = vks::tools::loadShaderModule(fileName, ctx->deviceHandle());
105 shaderStage.pName = "main";
106 assert(shaderStage.module != VK_NULL_HANDLE);
107 shaderModules.push_back(shaderStage.module);
108 return shaderStage;
109}
110
112{
113 auto tStart = std::chrono::high_resolution_clock::now();
114 if (viewUpdated)
115 {
116 viewUpdated = false;
117 viewChanged();
118 }
119
120 render();
121 frameCounter++;
122 auto tEnd = std::chrono::high_resolution_clock::now();
123 auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
124 frameTimer = (float)tDiff / 1000.0f;
125 camera.update(frameTimer);
126 if (camera.moving())
127 {
128 viewUpdated = true;
129 }
130 // Convert to clamped timer value
131 if (!paused)
132 {
134 if (timer > 1.0)
135 {
136 timer -= 1.0f;
137 }
138 }
139 float fpsTimer = (float)(std::chrono::duration<double, std::milli>(tEnd - lastTimestamp).count());
140 if (fpsTimer > 1000.0f)
141 {
142 lastFPS = static_cast<uint32_t>((float)frameCounter * (1000.0f / fpsTimer));
143#if defined(_WIN32)
144 if (!settings.overlay) {
145 std::string windowTitle = getWindowTitle();
146 SetWindowText(window, windowTitle.c_str());
147 }
148#endif
149 frameCounter = 0;
150 lastTimestamp = tEnd;
151 }
152 // TODO: Cap UI overlay update rates
154}
155
157{
160 lastTimestamp = std::chrono::high_resolution_clock::now();
161#if defined(_WIN32)
162 MSG msg;
163 bool quitMessageReceived = false;
164 while (!quitMessageReceived) {
165 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
166 TranslateMessage(&msg);
167 DispatchMessage(&msg);
168 if (msg.message == WM_QUIT) {
169 quitMessageReceived = true;
170 break;
171 }
172 }
173 if (prepared && !IsIconic(window)) {
174 nextFrame();
175 }
176 }
177#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
178 while (1)
179 {
180 int ident;
181 int events;
182 struct android_poll_source* source;
183 bool destroy = false;
184
185 focused = true;
186
187 while ((ident = ALooper_pollAll(focused ? 0 : -1, NULL, &events, (void**)&source)) >= 0)
188 {
189 if (source != NULL)
190 {
191 source->process(androidApp, source);
192 }
193 if (androidApp->destroyRequested != 0)
194 {
195 LOGD("Android app destroy requested");
196 destroy = true;
197 break;
198 }
199 }
200
201 // App destruction requested
202 // Exit loop, example will be destroyed in application main
203 if (destroy)
204 {
205 ANativeActivity_finish(androidApp->activity);
206 break;
207 }
208
209 // Render frame
210 if (prepared)
211 {
212 auto tStart = std::chrono::high_resolution_clock::now();
213 render();
214 frameCounter++;
215 auto tEnd = std::chrono::high_resolution_clock::now();
216 auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
217 frameTimer = tDiff / 1000.0f;
218 camera.update(frameTimer);
219 // Convert to clamped timer value
220 if (!paused)
221 {
223 if (timer > 1.0)
224 {
225 timer -= 1.0f;
226 }
227 }
228 float fpsTimer = std::chrono::duration<double, std::milli>(tEnd - lastTimestamp).count();
229 if (fpsTimer > 1000.0f)
230 {
231 lastFPS = (float)frameCounter * (1000.0f / fpsTimer);
232 frameCounter = 0;
233 lastTimestamp = tEnd;
234 }
235
236 // TODO: Cap UI overlay update rates/only issue when update requested
238
239 bool updateView = false;
240
241 // Check touch state (for movement)
242 if (touchDown) {
243 touchTimer += frameTimer;
244 }
245 if (touchTimer >= 1.0) {
246 camera.keys.up = true;
247 viewChanged();
248 }
249
250 // Check gamepad state
251 const float deadZone = 0.0015f;
252 // todo : check if gamepad is present
253 // todo : time based and relative axis positions
255 {
256 // Rotate
257 if (std::abs(gamePadState.axisLeft.x) > deadZone)
258 {
259 camera.rotate(glm::vec3(0.0f, gamePadState.axisLeft.x * 0.5f, 0.0f));
260 updateView = true;
261 }
262 if (std::abs(gamePadState.axisLeft.y) > deadZone)
263 {
264 camera.rotate(glm::vec3(gamePadState.axisLeft.y * 0.5f, 0.0f, 0.0f));
265 updateView = true;
266 }
267 // Zoom
268 if (std::abs(gamePadState.axisRight.y) > deadZone)
269 {
270 camera.translate(glm::vec3(0.0f, 0.0f, gamePadState.axisRight.y * 0.01f));
271 updateView = true;
272 }
273 if (updateView)
274 {
275 viewChanged();
276 }
277 }
278 else
279 {
280 updateView = camera.updatePad(gamePadState.axisLeft, gamePadState.axisRight, frameTimer);
281 if (updateView)
282 {
283 viewChanged();
284 }
285 }
286 }
287 }
288#endif
289 // Flush device to make sure all resources can be freed
290 if (ctx->deviceHandle() != VK_NULL_HANDLE) {
291 vkDeviceWaitIdle(ctx->deviceHandle());
292 }
293}
294
296{
297 if (!settings.overlay)
298 return;
299
300 ImGuiIO& io = ImGui::GetIO();
301
302 io.DisplaySize = ImVec2((float)width, (float)height);
303 io.DeltaTime = frameTimer;
304
305 io.MousePos = ImVec2(mousePos.x, mousePos.y);
306 io.MouseDown[0] = mouseButtons.left;
307 io.MouseDown[1] = mouseButtons.right;
308
309 ImGui::NewFrame();
310
311 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0);
312 ImGui::SetNextWindowPos(ImVec2(10, 10));
313 ImGui::SetNextWindowSize(ImVec2(0, 0));
314 ImGui::Begin("Vulkan Example", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove);
315 ImGui::TextUnformatted(title.c_str());
316 ImGui::TextUnformatted(dyno::VkSystem::instance()->getDeviceProperties().deviceName);
317 ImGui::Text("%.2f ms/frame (%.1d fps)", (1000.0f / lastFPS), lastFPS);
318
319#if defined(VK_USE_PLATFORM_ANDROID_KHR)
320 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 5.0f * UIOverlay.scale));
321#endif
322 ImGui::PushItemWidth(110.0f * UIOverlay.scale);
324 ImGui::PopItemWidth();
325#if defined(VK_USE_PLATFORM_ANDROID_KHR)
326 ImGui::PopStyleVar();
327#endif
328
329 ImGui::End();
330 ImGui::PopStyleVar();
331 ImGui::Render();
332
333 if (UIOverlay.update() || UIOverlay.updated) {
335 UIOverlay.updated = false;
336 }
337
338#if defined(VK_USE_PLATFORM_ANDROID_KHR)
339 if (mouseButtons.left) {
340 mouseButtons.left = false;
341 }
342#endif
343}
344
345void VkApp::drawUI(const VkCommandBuffer commandBuffer)
346{
347 if (settings.overlay) {
348 const VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
349 const VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
350 vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
351 vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
352
353 UIOverlay.draw(commandBuffer);
354 }
355}
356
358{
359 // Acquire the next image from the swap chain
360 VkResult result = swapChain.acquireNextImage(semaphores.presentComplete, &currentBuffer);
361 // Recreate the swapchain if it's no longer compatible with the surface (OUT_OF_DATE) or no longer optimal for presentation (SUBOPTIMAL)
362 if ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)) {
363 windowResize();
364 }
365 else {
366 VK_CHECK_RESULT(result);
367 }
368}
369
371{
372 VkResult result = swapChain.queuePresent(ctx->graphicsQueueHandle(), currentBuffer, semaphores.renderComplete);
373 if (!((result == VK_SUCCESS) || (result == VK_SUBOPTIMAL_KHR))) {
374 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
375 // Swap chain is no longer compatible with the surface and needs to be recreated
376 windowResize();
377 return;
378 } else {
379 VK_CHECK_RESULT(result);
380 }
381 }
382 VK_CHECK_RESULT(vkQueueWaitIdle(ctx->graphicsQueueHandle()));
383}
384
385VkApp::VkApp(bool enableValidation)
386{
387#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
388 // Check for a valid asset path
389 struct stat info;
390 if (stat(getAssetPath().c_str(), &info) != 0)
391 {
392#if defined(_WIN32)
393 std::string msg = "Could not locate asset path in \"" + getAssetPath() + "\" !";
394 MessageBox(NULL, msg.c_str(), "Fatal error", MB_OK | MB_ICONERROR);
395#else
396 std::cerr << "Error: Could not find asset path in " << getAssetPath() << "\n";
397#endif
398 exit(-1);
399 }
400#endif
401
402 settings.validation = enableValidation;
403
404#if defined(_WIN32)
405 // Enable console if validation is active
406// Debug message callback will output to it
407 if (this->settings.validation)
408 {
409 setupConsole("Vulkan validation output");
410 }
411 setupDPIAwareness();
412
413 paused = true;
414#endif
415
416 title = "Compute shader Height FieldFluid simulation";
418 camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 512.0f);
419 camera.setRotation(glm::vec3(-30.0f, -45.0f, 0.0f));
420 camera.setTranslation(glm::vec3(0.0f, 0.0f, -5.0f));
421 settings.overlay = true;
422
424}
425
427{
428 // Clean up Vulkan resources
429 swapChain.cleanup();
430 if (descriptorPool != VK_NULL_HANDLE)
431 {
432 vkDestroyDescriptorPool(ctx->deviceHandle(), descriptorPool, nullptr);
433 }
435 vkDestroyRenderPass(ctx->deviceHandle(), renderPass, nullptr);
436 for (uint32_t i = 0; i < frameBuffers.size(); i++)
437 {
438 vkDestroyFramebuffer(ctx->deviceHandle(), frameBuffers[i], nullptr);
439 }
440
441 for (auto& shaderModule : shaderModules)
442 {
443 vkDestroyShaderModule(ctx->deviceHandle(), shaderModule, nullptr);
444 }
445 vkDestroyImageView(ctx->deviceHandle(), depthStencil.view, nullptr);
446 vkDestroyImage(ctx->deviceHandle(), depthStencil.image, nullptr);
447 vkFreeMemory(ctx->deviceHandle(), depthStencil.mem, nullptr);
448
449 vkDestroyPipelineCache(ctx->deviceHandle(), ctx->pipelineCacheHandle(), nullptr);
450
451 vkDestroyCommandPool(ctx->deviceHandle(), cmdPool, nullptr);
452
453 vkDestroySemaphore(ctx->deviceHandle(), semaphores.presentComplete, nullptr);
454 vkDestroySemaphore(ctx->deviceHandle(), semaphores.renderComplete, nullptr);
455 for (auto& fence : waitFences) {
456 vkDestroyFence(ctx->deviceHandle(), fence, nullptr);
457 }
458
459 if (settings.overlay) {
460 UIOverlay.freeResources();
461 }
462
463#if defined(VK_USE_PLATFORM_ANDROID_KHR)
464 // todo : android cleanup (if required)
465#endif
466}
467
469{
470#if defined(VK_USE_PLATFORM_ANDROID_KHR)
471 vks::android::loadVulkanFunctions(dyno::VkSystem::instance()->instanceHandle());
472#endif
473
474 // Find a suitable depth format
475 VkBool32 validDepthFormat = vks::tools::getSupportedDepthFormat(ctx->physicalDevice, &depthFormat);
476 assert(validDepthFormat);
477
478 swapChain.connect(dyno::VkSystem::instance()->instanceHandle(), ctx->physicalDevice, ctx->deviceHandle());
479
480 // Create synchronization objects
481 VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo();
482 // Create a semaphore used to synchronize image presentation
483 // Ensures that the image is displayed before we start submitting new commands to the queue
484 VK_CHECK_RESULT(vkCreateSemaphore(ctx->deviceHandle(), &semaphoreCreateInfo, nullptr, &semaphores.presentComplete));
485 // Create a semaphore used to synchronize command submission
486 // Ensures that the image is not presented until all commands have been submitted and executed
487 VK_CHECK_RESULT(vkCreateSemaphore(ctx->deviceHandle(), &semaphoreCreateInfo, nullptr, &semaphores.renderComplete));
488
489 // Set up submit info structure
490 // Semaphores will stay the same during application lifetime
491 // Command buffer submission info is set by each example
493 submitInfo.pWaitDstStageMask = &submitPipelineStages;
494 submitInfo.waitSemaphoreCount = 1;
495 submitInfo.pWaitSemaphores = &semaphores.presentComplete;
496 submitInfo.signalSemaphoreCount = 1;
497 submitInfo.pSignalSemaphores = &semaphores.renderComplete;
498
499 return true;
500}
501
502#if defined(_WIN32)
503// Win32 : Sets up a console window and redirects standard output to it
504void VkApp::setupConsole(std::string title)
505{
506 AllocConsole();
507 AttachConsole(GetCurrentProcessId());
508 FILE *stream;
509 freopen_s(&stream, "CONOUT$", "w+", stdout);
510 freopen_s(&stream, "CONOUT$", "w+", stderr);
511 SetConsoleTitle(TEXT(title.c_str()));
512}
513
514void VkApp::setupDPIAwareness()
515{
516 typedef HRESULT *(__stdcall *SetProcessDpiAwarenessFunc)(PROCESS_DPI_AWARENESS);
517
518 HMODULE shCore = LoadLibraryA("Shcore.dll");
519 if (shCore)
520 {
521 SetProcessDpiAwarenessFunc setProcessDpiAwareness =
522 (SetProcessDpiAwarenessFunc)GetProcAddress(shCore, "SetProcessDpiAwareness");
523
524 if (setProcessDpiAwareness != nullptr)
525 {
526 setProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
527 }
528
529 FreeLibrary(shCore);
530 }
531}
532
533HWND VkApp::setupWindow(HINSTANCE hinstance, WNDPROC wndproc)
534{
535 this->windowInstance = hinstance;
536
537 WNDCLASSEX wndClass;
538
539 wndClass.cbSize = sizeof(WNDCLASSEX);
540 wndClass.style = CS_HREDRAW | CS_VREDRAW;
541 wndClass.lpfnWndProc = wndproc;
542 wndClass.cbClsExtra = 0;
543 wndClass.cbWndExtra = 0;
544 wndClass.hInstance = hinstance;
545 wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
546 wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
547 wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
548 wndClass.lpszMenuName = NULL;
549 wndClass.lpszClassName = name.c_str();
550 wndClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
551
552 if (!RegisterClassEx(&wndClass))
553 {
554 std::cout << "Could not register window class!\n";
555 fflush(stdout);
556 exit(1);
557 }
558
559 int screenWidth = GetSystemMetrics(SM_CXSCREEN);
560 int screenHeight = GetSystemMetrics(SM_CYSCREEN);
561
562 if (settings.fullscreen)
563 {
564 if ((width != (uint32_t)screenWidth) && (height != (uint32_t)screenHeight))
565 {
566 DEVMODE dmScreenSettings;
567 memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
568 dmScreenSettings.dmSize = sizeof(dmScreenSettings);
569 dmScreenSettings.dmPelsWidth = width;
570 dmScreenSettings.dmPelsHeight = height;
571 dmScreenSettings.dmBitsPerPel = 32;
572 dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
573 if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
574 {
575 if (MessageBox(NULL, "Fullscreen Mode not supported!\n Switch to window mode?", "Error", MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
576 {
577 settings.fullscreen = false;
578 }
579 else
580 {
581 return nullptr;
582 }
583 }
584 screenWidth = width;
585 screenHeight = height;
586 }
587
588 }
589
590 DWORD dwExStyle;
591 DWORD dwStyle;
592
593 if (settings.fullscreen)
594 {
595 dwExStyle = WS_EX_APPWINDOW;
596 dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
597 }
598 else
599 {
600 dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
601 dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
602 }
603
604 RECT windowRect;
605 windowRect.left = 0L;
606 windowRect.top = 0L;
607 windowRect.right = settings.fullscreen ? (long)screenWidth : (long)width;
608 windowRect.bottom = settings.fullscreen ? (long)screenHeight : (long)height;
609
610 AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
611
612 std::string windowTitle = getWindowTitle();
613 window = CreateWindowEx(0,
614 name.c_str(),
615 windowTitle.c_str(),
616 dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
617 0,
618 0,
619 windowRect.right - windowRect.left,
620 windowRect.bottom - windowRect.top,
621 NULL,
622 NULL,
623 hinstance,
624 NULL);
625
626 if (!settings.fullscreen)
627 {
628 // Center on screen
629 uint32_t x = (GetSystemMetrics(SM_CXSCREEN) - windowRect.right) / 2;
630 uint32_t y = (GetSystemMetrics(SM_CYSCREEN) - windowRect.bottom) / 2;
631 SetWindowPos(window, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
632 }
633
634 if (!window)
635 {
636 printf("Could not create window!\n");
637 fflush(stdout);
638 return nullptr;
639 }
640
641 ShowWindow(window, SW_SHOW);
642 SetForegroundWindow(window);
643 SetFocus(window);
644
645 return window;
646}
647
648void VkApp::handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
649{
650 switch (uMsg)
651 {
652 case WM_CLOSE:
653 prepared = false;
654 DestroyWindow(hWnd);
655 PostQuitMessage(0);
656 break;
657 case WM_PAINT:
658 ValidateRect(window, NULL);
659 break;
660 case WM_KEYDOWN:
661 switch (wParam)
662 {
663 case KEY_P:
664 paused = !paused;
665 break;
666 case KEY_N:
667 dyno::SceneGraphFactory::instance()->active()->takeOneFrame();
668 break;
669 case KEY_F1:
670 if (settings.overlay) {
671 UIOverlay.visible = !UIOverlay.visible;
672 }
673 break;
674 case KEY_ESCAPE:
675 PostQuitMessage(0);
676 break;
677 }
678
679 if (camera.type == Camera::firstperson)
680 {
681 switch (wParam)
682 {
683 case KEY_W:
684 camera.keys.up = true;
685 break;
686 case KEY_S:
687 camera.keys.down = true;
688 break;
689 case KEY_A:
690 camera.keys.left = true;
691 break;
692 case KEY_D:
693 camera.keys.right = true;
694 break;
695 }
696 }
697
698 keyPressed((uint32_t)wParam);
699 break;
700 case WM_KEYUP:
701 if (camera.type == Camera::firstperson)
702 {
703 switch (wParam)
704 {
705 case KEY_W:
706 camera.keys.up = false;
707 break;
708 case KEY_S:
709 camera.keys.down = false;
710 break;
711 case KEY_A:
712 camera.keys.left = false;
713 break;
714 case KEY_D:
715 camera.keys.right = false;
716 break;
717 }
718 }
719 break;
720 case WM_LBUTTONDOWN:
721 mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam));
722 mouseButtons.left = true;
723 break;
724 case WM_RBUTTONDOWN:
725 mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam));
726 mouseButtons.right = true;
727 break;
728 case WM_MBUTTONDOWN:
729 mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam));
730 mouseButtons.middle = true;
731 break;
732 case WM_LBUTTONUP:
733 mouseButtons.left = false;
734 break;
735 case WM_RBUTTONUP:
736 mouseButtons.right = false;
737 break;
738 case WM_MBUTTONUP:
739 mouseButtons.middle = false;
740 break;
741 case WM_MOUSEWHEEL:
742 {
743 short wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
744 camera.translate(glm::vec3(0.0f, 0.0f, (float)wheelDelta * 0.005f));
745 viewUpdated = true;
746 break;
747 }
748 case WM_MOUSEMOVE:
749 {
750 handleMouseMove(LOWORD(lParam), HIWORD(lParam));
751 break;
752 }
753 case WM_SIZE:
754 if ((prepared) && (wParam != SIZE_MINIMIZED))
755 {
756 if ((resizing) || ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED)))
757 {
758 destWidth = LOWORD(lParam);
759 destHeight = HIWORD(lParam);
760 windowResize();
761 }
762 }
763 break;
764 case WM_GETMINMAXINFO:
765 {
766 LPMINMAXINFO minMaxInfo = (LPMINMAXINFO)lParam;
767 minMaxInfo->ptMinTrackSize.x = 64;
768 minMaxInfo->ptMinTrackSize.y = 64;
769 break;
770 }
771 case WM_ENTERSIZEMOVE:
772 resizing = true;
773 break;
774 case WM_EXITSIZEMOVE:
775 resizing = false;
776 break;
777 }
778}
779#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
780int32_t VkApp::handleAppInput(struct android_app* app, AInputEvent* event)
781{
782 VkApp* vulkanExample = reinterpret_cast<VkApp*>(app->userData);
783 if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION)
784 {
785 int32_t eventSource = AInputEvent_getSource(event);
786 switch (eventSource) {
787 case AINPUT_SOURCE_JOYSTICK: {
788 // Left thumbstick
789 vulkanExample->gamePadState.axisLeft.x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_X, 0);
790 vulkanExample->gamePadState.axisLeft.y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Y, 0);
791 // Right thumbstick
792 vulkanExample->gamePadState.axisRight.x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Z, 0);
793 vulkanExample->gamePadState.axisRight.y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_RZ, 0);
794 break;
795 }
796
797 case AINPUT_SOURCE_TOUCHSCREEN: {
798 int32_t action = AMotionEvent_getAction(event);
799
800 switch (action) {
801 case AMOTION_EVENT_ACTION_UP: {
802 vulkanExample->lastTapTime = AMotionEvent_getEventTime(event);
803 vulkanExample->touchPos.x = AMotionEvent_getX(event, 0);
804 vulkanExample->touchPos.y = AMotionEvent_getY(event, 0);
805 vulkanExample->touchTimer = 0.0;
806 vulkanExample->touchDown = false;
807 vulkanExample->camera.keys.up = false;
808
809 // Detect single tap
810 int64_t eventTime = AMotionEvent_getEventTime(event);
811 int64_t downTime = AMotionEvent_getDownTime(event);
812 if (eventTime - downTime <= vks::android::TAP_TIMEOUT) {
813 float deadZone = (160.f / vks::android::screenDensity) * vks::android::TAP_SLOP * vks::android::TAP_SLOP;
814 float x = AMotionEvent_getX(event, 0) - vulkanExample->touchPos.x;
815 float y = AMotionEvent_getY(event, 0) - vulkanExample->touchPos.y;
816 if ((x * x + y * y) < deadZone) {
817 vulkanExample->mouseButtons.left = true;
818 }
819 };
820
821 return 1;
822 break;
823 }
824 case AMOTION_EVENT_ACTION_DOWN: {
825 // Detect double tap
826 int64_t eventTime = AMotionEvent_getEventTime(event);
827 if (eventTime - vulkanExample->lastTapTime <= vks::android::DOUBLE_TAP_TIMEOUT) {
828 float deadZone = (160.f / vks::android::screenDensity) * vks::android::DOUBLE_TAP_SLOP * vks::android::DOUBLE_TAP_SLOP;
829 float x = AMotionEvent_getX(event, 0) - vulkanExample->touchPos.x;
830 float y = AMotionEvent_getY(event, 0) - vulkanExample->touchPos.y;
831 if ((x * x + y * y) < deadZone) {
832 vulkanExample->keyPressed(TOUCH_DOUBLE_TAP);
833 vulkanExample->touchDown = false;
834 }
835 }
836 else {
837 vulkanExample->touchDown = true;
838 }
839 vulkanExample->touchPos.x = AMotionEvent_getX(event, 0);
840 vulkanExample->touchPos.y = AMotionEvent_getY(event, 0);
841 vulkanExample->mousePos.x = AMotionEvent_getX(event, 0);
842 vulkanExample->mousePos.y = AMotionEvent_getY(event, 0);
843 break;
844 }
845 case AMOTION_EVENT_ACTION_MOVE: {
846 bool handled = false;
847 if (vulkanExample->settings.overlay) {
848 ImGuiIO& io = ImGui::GetIO();
849 handled = io.WantCaptureMouse;
850 }
851 if (!handled) {
852 int32_t eventX = AMotionEvent_getX(event, 0);
853 int32_t eventY = AMotionEvent_getY(event, 0);
854
855 float deltaX = (float)(vulkanExample->touchPos.y - eventY) * vulkanExample->camera.rotationSpeed * 0.5f;
856 float deltaY = (float)(vulkanExample->touchPos.x - eventX) * vulkanExample->camera.rotationSpeed * 0.5f;
857
858 vulkanExample->camera.rotate(glm::vec3(deltaX, 0.0f, 0.0f));
859 vulkanExample->camera.rotate(glm::vec3(0.0f, -deltaY, 0.0f));
860
861 vulkanExample->viewChanged();
862
863 vulkanExample->touchPos.x = eventX;
864 vulkanExample->touchPos.y = eventY;
865 }
866 break;
867 }
868 default:
869 return 1;
870 break;
871 }
872 }
873
874 return 1;
875 }
876 }
877
878 if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY)
879 {
880 int32_t keyCode = AKeyEvent_getKeyCode((const AInputEvent*)event);
881 int32_t action = AKeyEvent_getAction((const AInputEvent*)event);
882 int32_t button = 0;
883
884 if (action == AKEY_EVENT_ACTION_UP)
885 return 0;
886
887 switch (keyCode)
888 {
889 case AKEYCODE_BUTTON_A:
890 vulkanExample->keyPressed(GAMEPAD_BUTTON_A);
891 break;
892 case AKEYCODE_BUTTON_B:
893 vulkanExample->keyPressed(GAMEPAD_BUTTON_B);
894 break;
895 case AKEYCODE_BUTTON_X:
896 vulkanExample->keyPressed(GAMEPAD_BUTTON_X);
897 break;
898 case AKEYCODE_BUTTON_Y:
899 vulkanExample->keyPressed(GAMEPAD_BUTTON_Y);
900 break;
901 case AKEYCODE_BUTTON_L1:
902 vulkanExample->keyPressed(GAMEPAD_BUTTON_L1);
903 break;
904 case AKEYCODE_BUTTON_R1:
905 vulkanExample->keyPressed(GAMEPAD_BUTTON_R1);
906 break;
907 case AKEYCODE_BUTTON_START:
908 vulkanExample->paused = !vulkanExample->paused;
909 break;
910 };
911
912 LOGD("Button %d pressed", keyCode);
913 }
914
915 return 0;
916}
917
918void VkApp::handleAppCommand(android_app * app, int32_t cmd)
919{
920 assert(app->userData != NULL);
921 VkApp* vulkanExample = reinterpret_cast<VkApp*>(app->userData);
922 switch (cmd)
923 {
924 case APP_CMD_SAVE_STATE:
925 LOGD("APP_CMD_SAVE_STATE");
926 /*
927 vulkanExample->app->savedState = malloc(sizeof(struct saved_state));
928 *((struct saved_state*)vulkanExample->app->savedState) = vulkanExample->state;
929 vulkanExample->app->savedStateSize = sizeof(struct saved_state);
930 */
931 break;
932 case APP_CMD_INIT_WINDOW:
933 LOGD("APP_CMD_INIT_WINDOW");
934 if (androidApp->window != NULL)
935 {
936 if (vulkanExample->initVulkan()) {
937 vulkanExample->prepare();
938 assert(vulkanExample->prepared);
939 vulkanExample->swapChainCleaned = false;
940 }
941 else {
942 LOGE("Could not initialize Vulkan, exiting!");
943 androidApp->destroyRequested = 1;
944 }
945 }
946 else
947 {
948 LOGE("No window assigned!");
949 }
950 break;
951 case APP_CMD_LOST_FOCUS:
952 LOGD("APP_CMD_LOST_FOCUS");
953 vulkanExample->focused = false;
954 break;
955 case APP_CMD_GAINED_FOCUS:
956 LOGD("APP_CMD_GAINED_FOCUS");
957 vulkanExample->focused = true;
958 break;
959 case APP_CMD_TERM_WINDOW:
960 // Window is hidden or closed, clean up resources
961 LOGD("APP_CMD_TERM_WINDOW");
962 if (vulkanExample->prepared) {
963 vulkanExample->swapChain.cleanup();
964 vulkanExample->swapChainCleaned = true;
965 }
966 break;
967 case APP_CMD_CONFIG_CHANGED:
968 LOGD("APP_CMD_CONFIG_CHANGED");
969 break;
970 case APP_CMD_START:
971 LOGD("APP_CMD_START");
972 break;
973 case APP_CMD_RESUME:
974 LOGD("APP_CMD_RESUME");
975 break;
976 case APP_CMD_PAUSE:
977 LOGD("APP_CMD_PAUSE");
978 break;
979 case APP_CMD_STOP:
980 LOGD("APP_CMD_STOP");
981 break;
982 case APP_CMD_DESTROY:
983 LOGD("APP_CMD_DESTROY");
985 break;
986 }
987}
988#endif
989
991{
992#if defined(_WIN32)
993 if (!paused) {
994 dyno::SceneGraphFactory::instance()->active()->takeOneFrame();
995 }
996#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
997 dyno::SceneGraphFactory::instance()->active()->takeOneFrame();
998#endif
999 dyno::SceneGraphFactory::instance()->active()->updateGraphicsContext();
1000 if (!prepared || swapChainCleaned)
1001 return;
1002
1003 // Submit graphics commands
1005
1006 submitInfo.commandBufferCount = 1;
1007 submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
1008 VK_CHECK_RESULT(vkQueueSubmit(ctx->graphicsQueueHandle(), 1, &submitInfo, VK_NULL_HANDLE));
1009
1011}
1012
1014{
1016
1017 for (auto it = scn->begin(); it != scn->end(); it++)
1018 {
1019 for (auto iter : it->graphicsPipeline()->activeModules())
1020 {
1021 auto m = dynamic_cast<dyno::VkVisualModule*>(iter.get());
1022 if (m && m->isVisible())
1023 {
1024 m->viewChanged(camera.matrices.perspective, camera.matrices.view);
1025 }
1026 }
1027 }
1028}
1029
1030void VkApp::keyPressed(uint32_t) {}
1031
1032void VkApp::mouseMoved(double x, double y, bool & handled) {}
1033
1035{
1036 VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
1037
1038 VkClearValue clearValues[2];
1039 clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };;
1040 clearValues[1].depthStencil = { 1.0f, 0 };
1041
1042 VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
1043 renderPassBeginInfo.renderPass = renderPass;
1044 renderPassBeginInfo.renderArea.offset.x = 0;
1045 renderPassBeginInfo.renderArea.offset.y = 0;
1046 renderPassBeginInfo.renderArea.extent.width = width;
1047 renderPassBeginInfo.renderArea.extent.height = height;
1048 renderPassBeginInfo.clearValueCount = 2;
1049 renderPassBeginInfo.pClearValues = clearValues;
1050
1051 for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
1052 {
1053 // Set target frame buffer
1054 renderPassBeginInfo.framebuffer = frameBuffers[i];
1055
1056 VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
1057
1058 // Draw the particle system using the update vertex buffer
1059
1060 vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
1061
1062 VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
1063 vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
1064
1065 VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
1066 vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
1067
1069
1071
1072 vkCmdEndRenderPass(drawCmdBuffers[i]);
1073
1074 VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
1075 }
1076
1077}
1078
1080{
1081 // Wait fences to sync command buffer access
1082 VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo(VK_FENCE_CREATE_SIGNALED_BIT);
1083 waitFences.resize(drawCmdBuffers.size());
1084 for (auto& fence : waitFences) {
1085 VK_CHECK_RESULT(vkCreateFence(ctx->deviceHandle(), &fenceCreateInfo, nullptr, &fence));
1086 }
1087}
1088
1090{
1091 VkCommandPoolCreateInfo cmdPoolInfo = {};
1092 cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1093 cmdPoolInfo.queueFamilyIndex = swapChain.queueNodeIndex;
1094 cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1095 VK_CHECK_RESULT(vkCreateCommandPool(ctx->deviceHandle(), &cmdPoolInfo, nullptr, &cmdPool));
1096}
1097
1099{
1100 VkImageCreateInfo imageCI{};
1101 imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1102 imageCI.imageType = VK_IMAGE_TYPE_2D;
1103 imageCI.format = depthFormat;
1104 imageCI.extent = { width, height, 1 };
1105 imageCI.mipLevels = 1;
1106 imageCI.arrayLayers = 1;
1107 imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
1108 imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
1109 imageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
1110
1111 VK_CHECK_RESULT(vkCreateImage(ctx->deviceHandle(), &imageCI, nullptr, &depthStencil.image));
1112 VkMemoryRequirements memReqs{};
1113 vkGetImageMemoryRequirements(ctx->deviceHandle(), depthStencil.image, &memReqs);
1114
1115 VkMemoryAllocateInfo memAllloc{};
1116 memAllloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1117 memAllloc.allocationSize = memReqs.size;
1118 memAllloc.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1119 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllloc, nullptr, &depthStencil.mem));
1120 VK_CHECK_RESULT(vkBindImageMemory(ctx->deviceHandle(), depthStencil.image, depthStencil.mem, 0));
1121
1122 VkImageViewCreateInfo imageViewCI{};
1123 imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1124 imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
1125 imageViewCI.image = depthStencil.image;
1126 imageViewCI.format = depthFormat;
1127 imageViewCI.subresourceRange.baseMipLevel = 0;
1128 imageViewCI.subresourceRange.levelCount = 1;
1129 imageViewCI.subresourceRange.baseArrayLayer = 0;
1130 imageViewCI.subresourceRange.layerCount = 1;
1131 imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
1132 // Stencil aspect should only be set on depth + stencil formats (VK_FORMAT_D16_UNORM_S8_UINT..VK_FORMAT_D32_SFLOAT_S8_UINT
1133 if (depthFormat >= VK_FORMAT_D16_UNORM_S8_UINT) {
1134 imageViewCI.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
1135 }
1136 VK_CHECK_RESULT(vkCreateImageView(ctx->deviceHandle(), &imageViewCI, nullptr, &depthStencil.view));
1137}
1138
1140{
1141 VkImageView attachments[2];
1142
1143 // Depth/Stencil attachment is the same for all frame buffers
1144 attachments[1] = depthStencil.view;
1145
1146 VkFramebufferCreateInfo frameBufferCreateInfo = {};
1147 frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1148 frameBufferCreateInfo.pNext = NULL;
1149 frameBufferCreateInfo.renderPass = renderPass;
1150 frameBufferCreateInfo.attachmentCount = 2;
1151 frameBufferCreateInfo.pAttachments = attachments;
1152 frameBufferCreateInfo.width = width;
1153 frameBufferCreateInfo.height = height;
1154 frameBufferCreateInfo.layers = 1;
1155
1156 // Create frame buffers for every swap chain image
1157 frameBuffers.resize(swapChain.imageCount);
1158 for (uint32_t i = 0; i < frameBuffers.size(); i++)
1159 {
1160 attachments[0] = swapChain.buffers[i].view;
1161 VK_CHECK_RESULT(vkCreateFramebuffer(ctx->deviceHandle(), &frameBufferCreateInfo, nullptr, &frameBuffers[i]));
1162 }
1163}
1164
1166{
1167 std::array<VkAttachmentDescription, 2> attachments = {};
1168 // Color attachment
1169 attachments[0].format = swapChain.colorFormat;
1170 attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
1171 attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1172 attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1173 attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1174 attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1175 attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1176 attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1177 // Depth attachment
1178 attachments[1].format = depthFormat;
1179 attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
1180 attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1181 attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1182 attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1183 attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1184 attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1185 attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1186
1187 VkAttachmentReference colorReference = {};
1188 colorReference.attachment = 0;
1189 colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1190
1191 VkAttachmentReference depthReference = {};
1192 depthReference.attachment = 1;
1193 depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1194
1195 VkSubpassDescription subpassDescription = {};
1196 subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1197 subpassDescription.colorAttachmentCount = 1;
1198 subpassDescription.pColorAttachments = &colorReference;
1199 subpassDescription.pDepthStencilAttachment = &depthReference;
1200 subpassDescription.inputAttachmentCount = 0;
1201 subpassDescription.pInputAttachments = nullptr;
1202 subpassDescription.preserveAttachmentCount = 0;
1203 subpassDescription.pPreserveAttachments = nullptr;
1204 subpassDescription.pResolveAttachments = nullptr;
1205
1206 // Subpass dependencies for layout transitions
1207 std::array<VkSubpassDependency, 2> dependencies;
1208
1209 dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
1210 dependencies[0].dstSubpass = 0;
1211 dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
1212 dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1213 dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
1214 dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1215 dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
1216
1217 dependencies[1].srcSubpass = 0;
1218 dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
1219 dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1220 dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
1221 dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1222 dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
1223 dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
1224
1225 VkRenderPassCreateInfo renderPassInfo = {};
1226 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1227 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1228 renderPassInfo.pAttachments = attachments.data();
1229 renderPassInfo.subpassCount = 1;
1230 renderPassInfo.pSubpasses = &subpassDescription;
1231 renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
1232 renderPassInfo.pDependencies = dependencies.data();
1233
1234 VK_CHECK_RESULT(vkCreateRenderPass(ctx->deviceHandle(), &renderPassInfo, nullptr, &renderPass));
1235}
1236
1238
1240{
1241 if (!prepared)
1242 {
1243 return;
1244 }
1245 prepared = false;
1246 resized = true;
1247
1248 // Ensure all operations on the device have been finished before destroying resources
1249 vkDeviceWaitIdle(ctx->deviceHandle());
1250
1251 // Recreate swap chain
1252 width = destWidth;
1255
1256 // Recreate the frame buffers
1257 vkDestroyImageView(ctx->deviceHandle(), depthStencil.view, nullptr);
1258 vkDestroyImage(ctx->deviceHandle(), depthStencil.image, nullptr);
1259 vkFreeMemory(ctx->deviceHandle(), depthStencil.mem, nullptr);
1261 for (uint32_t i = 0; i < frameBuffers.size(); i++) {
1262 vkDestroyFramebuffer(ctx->deviceHandle(), frameBuffers[i], nullptr);
1263 }
1265
1266 if ((width > 0.0f) && (height > 0.0f)) {
1267 if (settings.overlay) {
1268 UIOverlay.resize(width, height);
1269 }
1270 }
1271
1272 // Command buffers need to be recreated as they may store
1273 // references to the recreated frame buffer
1277
1278 vkDeviceWaitIdle(ctx->deviceHandle());
1279
1280 if ((width > 0.0f) && (height > 0.0f)) {
1281 camera.updateAspectRatio((float)width / (float)height);
1282 }
1283
1284 // Notify derived class
1285 windowResized();
1286 viewChanged();
1287
1288 prepared = true;
1289}
1290
1291void VkApp::handleMouseMove(int32_t x, int32_t y)
1292{
1293 int32_t dx = (int32_t)mousePos.x - x;
1294 int32_t dy = (int32_t)mousePos.y - y;
1295
1296 bool handled = false;
1297
1298 if (settings.overlay) {
1299 ImGuiIO& io = ImGui::GetIO();
1300 handled = io.WantCaptureMouse;
1301 }
1302 mouseMoved((float)x, (float)y, handled);
1303
1304 if (handled) {
1305 mousePos = glm::vec2((float)x, (float)y);
1306 return;
1307 }
1308
1309 if (mouseButtons.left) {
1310 camera.rotate(glm::vec3(dy * camera.rotationSpeed, -dx * camera.rotationSpeed, 0.0f));
1311 viewUpdated = true;
1312 }
1313 if (mouseButtons.right) {
1314 camera.translate(glm::vec3(-0.0f, 0.0f, dy * .005f));
1315 viewUpdated = true;
1316 }
1317 if (mouseButtons.middle) {
1318 camera.translate(glm::vec3(-dx * 0.01f, -dy * 0.01f, 0.0f));
1319 viewUpdated = true;
1320 }
1321 mousePos = glm::vec2((float)x, (float)y);
1322}
1323
1325
1327{
1328#if defined(_WIN32)
1329 swapChain.initSurface(windowInstance, window);
1330#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
1331 swapChain.initSurface(androidApp->window);
1332#endif
1333}
1334
1336{
1337 swapChain.create(&width, &height, settings.vsync);
1338}
1339
1341
1342void VkApp::buildCustomCommandBuffer(VkCommandBuffer commandBuffer)
1343{
1345
1346 for (auto it = scn->begin(); it != scn->end(); it++)
1347 {
1348 for (auto iter : it->graphicsPipeline()->activeModules())
1349 {
1350 auto m = dynamic_cast<dyno::VkVisualModule*>(iter.get());
1351 if (m && m->isVisible())
1352 {
1353 //m->update();
1354 m->buildCommandBuffers(commandBuffer);
1355 }
1356 }
1357 }
1358}
1359
1360
1361void VkApp::prepare(VkRenderPass renderPass)
1362{
1364
1365 for (auto it = scn->begin(); it != scn->end(); it++)
1366 {
1367 for (auto iter : it->graphicsPipeline()->activeModules())
1368 {
1369 auto m = dynamic_cast<dyno::VkVisualModule*>(iter.get());
1370 if (m && m->isVisible())
1371 {
1372 m->prepare(renderPass);
1373 }
1374 }
1375 }
1376}
1377
1378
1380{
1381 title = name;
1382}
1383
1384void VkApp::setSceneGraph(std::shared_ptr<dyno::SceneGraph> scn)
1385{
1387}
assert(queueCount >=1)
#define VK_CHECK_RESULT(f)
Definition VulkanTools.h:55
bool up
Definition Camera.h:74
float rotationSpeed
Definition Camera.h:58
struct Camera::@053002266270240026173313023376237034104174045346 keys
void rotate(glm::vec3 delta)
Definition Camera.h:122
@ lookat
Definition Camera.h:51
@ firstperson
Definition Camera.h:51
void prepareFrame()
Definition VkApp.cpp:357
void drawUI(const VkCommandBuffer commandBuffer)
Adds the drawing commands for the ImGui overlay to the given command buffer.
Definition VkApp.cpp:345
dyno::VkContext * ctx
Definition VkApp.h:153
uint32_t destHeight
Definition VkApp.h:77
std::string getWindowTitle()
Definition VkApp.cpp:24
uint32_t lastFPS
Definition VkApp.h:96
VkDescriptorPool descriptorPool
Definition VkApp.h:126
VkSubmitInfo submitInfo
Definition VkApp.h:116
bool left
Definition VkApp.h:197
void nextFrame()
Definition VkApp.cpp:111
bool prepared
Definition VkApp.h:143
bool resized
Definition VkApp.h:144
void destroyCommandBuffers()
Definition VkApp.cpp:49
void buildCustomCommandBuffer(VkCommandBuffer commandBuffer)
Definition VkApp.cpp:1342
float timer
Definition VkApp.h:173
virtual void mouseMoved(double x, double y, bool &handled)
(Virtual) Called after the mouse cursor moved and before internal events (like camera rotation) is ha...
Definition VkApp.cpp:1032
virtual void buildCommandBuffers()
(Virtual) Called when resources have been recreated that require a rebuild of the command buffers (e....
Definition VkApp.cpp:1034
virtual void getEnabledFeatures()
(Virtual) Called after the physical device features have been read, can be used to set features to en...
Definition VkApp.cpp:1237
virtual void setupFrameBuffer()
(Virtual) Setup default framebuffers for all requested swapchain images
Definition VkApp.cpp:1139
glm::vec2 mousePos
Definition VkApp.h:179
void initSwapchain()
Definition VkApp.cpp:1326
std::string getShadersPath() const
Definition VkApp.cpp:54
virtual ~VkApp()
Definition VkApp.cpp:426
vks::UIOverlay UIOverlay
Definition VkApp.h:148
VkPipelineShaderStageCreateInfo loadShader(std::string fileName, VkShaderStageFlagBits stage)
Loads a SPIR-V shader file for the given shader stage.
Definition VkApp.cpp:99
bool paused
Definition VkApp.h:176
virtual void renderFrame()
(Virtual) Default image acquire + submission and command buffer submission function
Definition VkApp.cpp:15
virtual void viewChanged()
(Virtual) Called when the camera view has changed
Definition VkApp.cpp:1013
void setWindowTitle(std::string name)
Definition VkApp.cpp:1379
virtual void keyPressed(uint32_t)
(Virtual) Called after a key was pressed, can be used to do custom key handling
Definition VkApp.cpp:1030
float frameTimer
Last frame time measured using a high performance timer (if available)
Definition VkApp.h:151
void submitFrame()
Presents the current image to the swap chain.
Definition VkApp.cpp:370
void windowResize()
Definition VkApp.cpp:1239
void handleMouseMove(int32_t x, int32_t y)
Definition VkApp.cpp:1291
VkPipelineStageFlags submitPipelineStages
Pipeline stages used to wait at for graphics queue submissions.
Definition VkApp.h:114
virtual void setupDepthStencil()
(Virtual) Setup default depth and stencil views
Definition VkApp.cpp:1098
void setupSwapChain()
Definition VkApp.cpp:1335
void renderLoop()
Entry point for the main render loop.
Definition VkApp.cpp:156
uint32_t currentBuffer
Definition VkApp.h:124
std::vector< VkFence > waitFences
Definition VkApp.h:140
void createSynchronizationPrimitives()
Definition VkApp.cpp:1079
VkApp(bool enableValidation=false)
Definition VkApp.cpp:385
virtual void windowResized()
(Virtual) Called when the window has been resized, can be used by the sample application to recreate ...
Definition VkApp.cpp:1324
struct VkApp::@035006017256177105240376270267042317070052055111 depthStencil
std::chrono::time_point< std::chrono::high_resolution_clock > lastTimestamp
Definition VkApp.h:97
virtual void prepare()
Prepares all Vulkan resources and functions required to run the sample.
Definition VkApp.cpp:59
bool viewUpdated
Definition VkApp.h:75
void createCommandPool()
Definition VkApp.cpp:1089
uint32_t width
Definition VkApp.h:145
VkFormat depthFormat
Logical device, application's view of the physical device (GPU)
Definition VkApp.h:110
bool swapChainCleaned
Definition VkApp.h:142
std::vector< VkFramebuffer > frameBuffers
Definition VkApp.h:122
Camera camera
Definition VkApp.h:178
std::vector< VkCommandBuffer > drawCmdBuffers
Definition VkApp.h:118
float timerSpeed
Definition VkApp.h:175
std::string name
Definition VkApp.h:182
virtual void render()
(Pure virtual) Render function to be implemented by the sample application
Definition VkApp.cpp:990
glm::vec2 axisLeft
Definition VkApp.h:192
VulkanSwapChain swapChain
Definition VkApp.h:132
uint32_t height
Definition VkApp.h:146
uint32_t frameCounter
Definition VkApp.h:95
uint32_t destWidth
Definition VkApp.h:76
VkCommandPool cmdPool
Definition VkApp.h:112
void updateOverlay()
Definition VkApp.cpp:295
std::string title
Definition VkApp.h:181
void setSceneGraph(std::shared_ptr< dyno::SceneGraph > scn)
Definition VkApp.cpp:1384
struct VkApp::@024362050273107367271176263277065044127044370222 semaphores
VkRenderPass renderPass
Definition VkApp.h:120
bool resizing
Definition VkApp.h:78
struct VkApp::@236151146162240273010117313013267214200120303363 gamePadState
std::string shaderDir
Definition VkApp.h:89
void createCommandBuffers()
Definition VkApp.cpp:35
glm::vec2 axisRight
Definition VkApp.h:193
virtual void setupRenderPass()
(Virtual) Setup a default renderpass
Definition VkApp.cpp:1165
struct VkApp::Settings settings
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
(Virtual) Called when the UI overlay is updating, can be used to add custom elements to the overlay
Definition VkApp.cpp:1340
bool initVulkan()
Setup the vulkan instance, enable required extensions and connect to the physical device (GPU)
Definition VkApp.cpp:468
struct VkApp::@033204162100256106143271256032327056257013175042 mouseButtons
std::vector< VkShaderModule > shaderModules
Definition VkApp.h:128
static SceneGraphFactory * instance()
void pushScene(std::shared_ptr< SceneGraph > scn)
std::shared_ptr< SceneGraph > active()
VkContext * currentContext()
Definition VkSystem.h:21
static VkSystem * instance()
Definition VkSystem.cpp:10
VkCommandBufferAllocateInfo commandBufferAllocateInfo(VkCommandPool commandPool, VkCommandBufferLevel level, uint32_t bufferCount)
VkFenceCreateInfo fenceCreateInfo(VkFenceCreateFlags flags=0)
VkRenderPassBeginInfo renderPassBeginInfo()
VkCommandBufferBeginInfo commandBufferBeginInfo()
VkSemaphoreCreateInfo semaphoreCreateInfo()
VkRect2D rect2D(int32_t width, int32_t height, int32_t offsetX, int32_t offsetY)
VkViewport viewport(float width, float height, float minDepth, float maxDepth)
VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *depthFormat)
VkShaderModule loadShaderModule(const std::string fileName, VkDevice device)
bool overlay
Enable UI overlay.
Definition VkApp.h:164