diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f68ba80c..ebb9866c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,7 +48,7 @@ jobs: build_macOS: name: Build macOS - runs-on: macos-13 + runs-on: macos-latest steps: - uses: actions/checkout@v4 with: diff --git a/android/examples/hostimagecopy/build.gradle b/android/examples/hostimagecopy/build.gradle index c4d9487e..e59aba39 100644 --- a/android/examples/hostimagecopy/build.gradle +++ b/android/examples/hostimagecopy/build.gradle @@ -60,6 +60,11 @@ task copyTask { include 'metalplate01_rgba.ktx' } + copy { + from rootProject.ext.assetPath + 'models' + into 'assets/models' + include 'plane_z.gltf' + } } diff --git a/base/Entrypoints.h b/base/Entrypoints.h index bd283810..7bbefcf2 100644 --- a/base/Entrypoints.h +++ b/base/Entrypoints.h @@ -3,7 +3,7 @@ * * Platform specific macros for the example main entry points * - * Copyright (C) 2024 by Sascha Willems - www.saschawillems.de + * Copyright (C) 2024-2025 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ @@ -42,12 +42,12 @@ int APIENTRY WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance VulkanExample *vulkanExample; \ void android_main(android_app* state) \ { \ + androidApp = state; \ + vks::android::getDeviceConfig(); \ vulkanExample = new VulkanExample(); \ state->userData = vulkanExample; \ state->onAppCmd = VulkanExample::handleAppCommand; \ state->onInputEvent = VulkanExample::handleAppInput; \ - androidApp = state; \ - vks::android::getDeviceConfig(); \ vulkanExample->renderLoop(); \ delete(vulkanExample); \ } diff --git a/base/VulkanAndroid.cpp b/base/VulkanAndroid.cpp index 0a5470e7..43d8c1c7 100644 --- a/base/VulkanAndroid.cpp +++ b/base/VulkanAndroid.cpp @@ -1,7 +1,7 @@ /* * Android Vulkan function pointer loader * -* Copyright (C) 2016-2024 by Sascha Willems - www.saschawillems.de +* Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ @@ -359,7 +359,6 @@ namespace vks jni->DeleteLocalRef(jmessage); androidApp->activity->vm->DetachCurrentThread(); - return; } } } diff --git a/base/VulkanAndroid.h b/base/VulkanAndroid.h index 9db7e82b..f3201bde 100644 --- a/base/VulkanAndroid.h +++ b/base/VulkanAndroid.h @@ -1,7 +1,7 @@ /* * Android Vulkan function pointer prototypes * -* Copyright (C) 2016-2024 by Sascha Willems - www.saschawillems.de +* Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ @@ -29,16 +29,6 @@ #include #include -// Missing from the NDK -namespace std -{ - template - std::unique_ptr make_unique(Args&&... args) - { - return std::unique_ptr(new T(std::forward(args)...)); - } -} - // Global reference to android application object extern android_app* androidApp; diff --git a/base/vulkanexamplebase.cpp b/base/vulkanexamplebase.cpp index 72928549..c45b928e 100644 --- a/base/vulkanexamplebase.cpp +++ b/base/vulkanexamplebase.cpp @@ -76,7 +76,7 @@ VkResult VulkanExampleBase::createInstance() #endif // Enabled requested instance extensions - if (enabledInstanceExtensions.size() > 0) + if (!enabledInstanceExtensions.empty()) { for (const char * enabledExtension : enabledInstanceExtensions) { @@ -120,7 +120,7 @@ VkResult VulkanExampleBase::createInstance() instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } - if (instanceExtensions.size() > 0) { + if (!instanceExtensions.empty()) { instanceCreateInfo.enabledExtensionCount = (uint32_t)instanceExtensions.size(); instanceCreateInfo.ppEnabledExtensionNames = instanceExtensions.data(); } @@ -316,7 +316,7 @@ void VulkanExampleBase::renderLoop() benchmark.run([=] { render(); }, vulkanDevice->properties); vkDeviceWaitIdle(device); - if (benchmark.filename != "") { + if (!benchmark.filename.empty()) { benchmark.saveResults(); } return; @@ -344,7 +344,7 @@ void VulkanExampleBase::renderLoop() } } #elif defined(VK_USE_PLATFORM_ANDROID_KHR) - while (1) + while (true) { int ident; int events; @@ -353,9 +353,9 @@ void VulkanExampleBase::renderLoop() focused = true; - while ((ident = ALooper_pollOnce(focused ? 0 : -1, NULL, &events, (void**)&source)) > ALOOPER_POLL_TIMEOUT) + while ((ident = ALooper_pollOnce(focused ? 0 : -1, nullptr, &events, (void**)&source)) > ALOOPER_POLL_TIMEOUT) { - if (source != NULL) + if (source != nullptr) { source->process(androidApp, source); } @@ -404,8 +404,6 @@ void VulkanExampleBase::renderLoop() updateOverlay(); - bool updateView = false; - // Check touch state (for movement) if (touchDown) { touchTimer += frameTimer; @@ -422,23 +420,20 @@ void VulkanExampleBase::renderLoop() if (std::abs(gamePadState.axisLeft.x) > deadZone) { camera.rotate(glm::vec3(0.0f, gamePadState.axisLeft.x * 0.5f, 0.0f)); - updateView = true; } if (std::abs(gamePadState.axisLeft.y) > deadZone) { camera.rotate(glm::vec3(gamePadState.axisLeft.y * 0.5f, 0.0f, 0.0f)); - updateView = true; } // Zoom if (std::abs(gamePadState.axisRight.y) > deadZone) { camera.translate(glm::vec3(0.0f, 0.0f, gamePadState.axisRight.y * 0.01f)); - updateView = true; } } else { - updateView = camera.updatePad(gamePadState.axisLeft, gamePadState.axisRight, frameTimer); + camera.updatePad(gamePadState.axisLeft, gamePadState.axisRight, frameTimer); } } } @@ -915,9 +910,9 @@ VulkanExampleBase::~VulkanExampleBase() { vkDestroyRenderPass(device, renderPass, nullptr); } - for (uint32_t i = 0; i < frameBuffers.size(); i++) + for (auto& frameBuffer : frameBuffers) { - vkDestroyFramebuffer(device, frameBuffers[i], nullptr); + vkDestroyFramebuffer(device, frameBuffer, nullptr); } for (auto& shaderModule : shaderModules) @@ -1509,7 +1504,6 @@ int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent* { int32_t keyCode = AKeyEvent_getKeyCode((const AInputEvent*)event); int32_t action = AKeyEvent_getAction((const AInputEvent*)event); - int32_t button = 0; if (action == AKEY_EVENT_ACTION_UP) return 0; @@ -1554,7 +1548,7 @@ int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent* void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd) { - assert(app->userData != NULL); + assert(app->userData != nullptr); VulkanExampleBase* vulkanExample = reinterpret_cast(app->userData); switch (cmd) { @@ -1568,7 +1562,7 @@ void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd) break; case APP_CMD_INIT_WINDOW: LOGD("APP_CMD_INIT_WINDOW"); - if (androidApp->window != NULL) + if (androidApp->window != nullptr) { if (vulkanExample->initVulkan()) { vulkanExample->prepare(); @@ -3195,8 +3189,8 @@ void VulkanExampleBase::windowResize() vkDestroyImage(device, depthStencil.image, nullptr); vkFreeMemory(device, depthStencil.memory, nullptr); setupDepthStencil(); - for (uint32_t i = 0; i < frameBuffers.size(); i++) { - vkDestroyFramebuffer(device, frameBuffers[i], nullptr); + for (auto& frameBuffer : frameBuffers) { + vkDestroyFramebuffer(device, frameBuffer, nullptr); } setupFrameBuffer(); diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h index 520637c5..2a983f28 100644 --- a/base/vulkanexamplebase.h +++ b/base/vulkanexamplebase.h @@ -77,8 +77,8 @@ class VulkanExampleBase { private: std::string getWindowTitle() const; - uint32_t destWidth; - uint32_t destHeight; + uint32_t destWidth{}; + uint32_t destHeight{}; bool resizing = false; void handleMouseMove(int32_t x, int32_t y); void nextFrame(); @@ -122,13 +122,13 @@ protected: // Handle to the device graphics queue that command buffers are submitted to VkQueue queue{ VK_NULL_HANDLE }; // Depth buffer format (selected during Vulkan initialization) - VkFormat depthFormat; + VkFormat depthFormat{VK_FORMAT_UNDEFINED}; // Command buffer pool VkCommandPool cmdPool{ VK_NULL_HANDLE }; /** @brief Pipeline stages used to wait at for graphics queue submissions */ VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; // Contains command buffers and semaphores to be presented to the queue - VkSubmitInfo submitInfo; + VkSubmitInfo submitInfo{}; // Command buffers used for rendering std::vector drawCmdBuffers; // Global render pass for frame buffer writes @@ -151,7 +151,7 @@ protected: VkSemaphore presentComplete; // Command buffer submission and execution VkSemaphore renderComplete; - } semaphores; + } semaphores{}; std::vector waitFences; bool requiresStencil{ false }; public: @@ -170,7 +170,7 @@ public: vks::Benchmark benchmark; /** @brief Encapsulated physical and logical vulkan device */ - vks::VulkanDevice *vulkanDevice; + vks::VulkanDevice *vulkanDevice{}; /** @brief Example settings that can be changed e.g. by command line arguments */ struct Settings { @@ -234,7 +234,7 @@ public: struct TouchPos { int32_t x; int32_t y; - } touchPos; + } touchPos{}; bool touchDown = false; double touchTimer = 0.0; int64_t lastTapTime = 0; diff --git a/examples/dynamicrenderingmultisampling/dynamicrenderingmultisampling.cpp b/examples/dynamicrenderingmultisampling/dynamicrenderingmultisampling.cpp index b9837eae..3b904335 100644 --- a/examples/dynamicrenderingmultisampling/dynamicrenderingmultisampling.cpp +++ b/examples/dynamicrenderingmultisampling/dynamicrenderingmultisampling.cpp @@ -40,7 +40,6 @@ public: VkDeviceMemory memory{ VK_NULL_HANDLE }; }; Image renderImage; - Image depthStencilRenderImage; VulkanExample() : VulkanExampleBase() { @@ -67,7 +66,7 @@ public: deviceCreatepNextChain = &enabledDynamicRenderingFeaturesKHR; } - ~VulkanExample() + ~VulkanExample() override { if (device) { vkDestroyPipeline(device, pipeline, nullptr); @@ -160,7 +159,7 @@ public: } // Enable physical device features required for this example - virtual void getEnabledFeatures() + void getEnabledFeatures() override { // Enable anisotropic filtering if supported if (deviceFeatures.samplerAnisotropy) { @@ -174,7 +173,7 @@ public: model.loadFromFile(getAssetPath() + "models/voyager.gltf", vulkanDevice, queue, glTFLoadingFlags); } - void buildCommandBuffers() + void buildCommandBuffers() override { VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); @@ -365,7 +364,7 @@ public: memcpy(uniformBuffer.mapped, &uniformData, sizeof(uniformData)); } - void prepare() + void prepare() override { VulkanExampleBase::prepare(); @@ -390,7 +389,7 @@ public: VulkanExampleBase::submitFrame(); } - virtual void render() + void render() override { if (!prepared) return; diff --git a/examples/hostimagecopy/hostimagecopy.cpp b/examples/hostimagecopy/hostimagecopy.cpp index 2dd96c34..cad565c5 100644 --- a/examples/hostimagecopy/hostimagecopy.cpp +++ b/examples/hostimagecopy/hostimagecopy.cpp @@ -70,7 +70,7 @@ public: deviceCreatepNextChain = &enabledPhysicalDeviceHostImageCopyFeaturesEXT; } - ~VulkanExample() + ~VulkanExample() override { if (device) { destroyTextureImage(texture); @@ -82,7 +82,7 @@ public: } // Enable physical device features required for this example - virtual void getEnabledFeatures() + void getEnabledFeatures() override { // Enable anisotropic filtering if supported if (deviceFeatures.samplerAnisotropy) { @@ -134,7 +134,6 @@ public: texture.height = ktxTexture->baseHeight; texture.mipLevels = ktxTexture->numLevels; ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); - ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); const VkFormat imageFormat = VK_FORMAT_R8G8B8A8_UNORM; @@ -271,7 +270,7 @@ public: vkFreeMemory(device, texture.deviceMemory, nullptr); } - void buildCommandBuffers() + void buildCommandBuffers() override { VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); @@ -371,11 +370,11 @@ public: VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); - std::array shaderStages; - - // Shaders - shaderStages[0] = loadShader(getShadersPath() + "texture/texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - shaderStages[1] = loadShader(getShadersPath() + "texture/texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + // Shaders + std::array shaderStages = { + loadShader(getShadersPath() + "texture/texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT), + loadShader(getShadersPath() + "texture/texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT) + }; VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; @@ -413,14 +412,14 @@ public: plane.loadFromFile(getAssetPath() + "models/plane_z.gltf", vulkanDevice, queue, glTFLoadingFlags); } - void prepare() + void prepare() override { VulkanExampleBase::prepare(); // Get the function pointers required host image copies vkCopyMemoryToImageEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCopyMemoryToImageEXT")); vkTransitionImageLayoutEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkTransitionImageLayoutEXT")); - vkGetPhysicalDeviceFormatProperties2 = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties2")); + vkGetPhysicalDeviceFormatProperties2 = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties2KHR")); loadAssets(); loadTexture(); @@ -440,7 +439,7 @@ public: VulkanExampleBase::submitFrame(); } - virtual void render() + void render() override { if (!prepared) return; @@ -448,7 +447,7 @@ public: draw(); } - virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) + void OnUpdateUIOverlay(vks::UIOverlay *overlay) override { if (overlay->header("Settings")) { if (overlay->sliderFloat("LOD bias", &uniformData.lodBias, 0.0f, (float)texture.mipLevels)) { diff --git a/examples/subpasses/subpasses.cpp b/examples/subpasses/subpasses.cpp index 72ab1109..e28d32d9 100644 --- a/examples/subpasses/subpasses.cpp +++ b/examples/subpasses/subpasses.cpp @@ -99,6 +99,8 @@ public: camera.setRotation(glm::vec3(0.5f, 210.05f, 0.0f)); camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); ui.subpass = 2; + + enabledFeatures.fragmentStoresAndAtomics = VK_TRUE; } ~VulkanExample() diff --git a/examples/triangle/triangle.cpp b/examples/triangle/triangle.cpp index 21c4954f..e7619c82 100644 --- a/examples/triangle/triangle.cpp +++ b/examples/triangle/triangle.cpp @@ -6,7 +6,7 @@ * Contrary to the other examples, this one won't make use of helper functions or initializers * Except in a few cases (swap chain setup e.g.) * -* Copyright (C) 2016-2024 by Sascha Willems - www.saschawillems.de +* Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ @@ -126,7 +126,7 @@ public: // Values not set here are initialized in the base class constructor } - ~VulkanExample() + ~VulkanExample() override { // Clean up used Vulkan resources // Note: Inherited destructor cleans up resources stored in base class @@ -452,7 +452,7 @@ public: // Create the depth (and stencil) buffer attachments used by our framebuffers // Note: Override of virtual function in the base class and called from within VulkanExampleBase::prepare - void setupDepthStencil() + void setupDepthStencil() override { // Create an optimal image used as the depth stencil attachment VkImageCreateInfo imageCI{}; @@ -502,7 +502,7 @@ public: // Create a frame buffer for each swap chain image // Note: Override of virtual function in the base class and called from within VulkanExampleBase::prepare - void setupFrameBuffer() + void setupFrameBuffer() override { // Create a frame buffer for every image in the swapchain frameBuffers.resize(swapChain.images.size()); @@ -533,7 +533,7 @@ public: // This allows the driver to know up-front what the rendering will look like and is a good opportunity to optimize especially on tile-based renderers (with multiple subpasses) // Using sub pass dependencies also adds implicit layout transitions for the attachment used, so we don't need to add explicit image memory barriers to transform them // Note: Override of virtual function in the base class and called from within VulkanExampleBase::prepare - void setupRenderPass() + void setupRenderPass() override { // This example will use a single render pass with one subpass @@ -622,7 +622,7 @@ public: // Vulkan loads its shaders from an immediate binary representation called SPIR-V // Shaders are compiled offline from e.g. GLSL using the reference glslang compiler // This function loads such a shader from a binary file and returns a shader module structure - VkShaderModule loadSPIRVShader(std::string filename) + VkShaderModule loadSPIRVShader(const std::string& filename) { size_t shaderSize; char* shaderCode{ nullptr }; @@ -886,7 +886,7 @@ public: } - void prepare() + void prepare() override { VulkanExampleBase::prepare(); createSynchronizationPrimitives(); @@ -900,7 +900,7 @@ public: prepared = true; } - virtual void render() + void render() override { if (!prepared) return; diff --git a/examples/trianglevulkan13/trianglevulkan13.cpp b/examples/trianglevulkan13/trianglevulkan13.cpp index 78824c60..6fa0a5bc 100644 --- a/examples/trianglevulkan13/trianglevulkan13.cpp +++ b/examples/trianglevulkan13/trianglevulkan13.cpp @@ -117,7 +117,7 @@ public: deviceCreatepNextChain = &enabledFeatures; } - ~VulkanExample() + ~VulkanExample() override { // Clean up used Vulkan resources // Note: Inherited destructor cleans up resources stored in base class @@ -140,6 +140,14 @@ public: } } + virtual void getEnabledFeatures() override + { + // Vulkan 1.3 device support is required for this example + if (deviceProperties.apiVersion < VK_API_VERSION_1_3) { + vks::tools::exitFatal("Selected GPU does not support support Vulkan 1.3", VK_ERROR_INCOMPATIBLE_DRIVER); + } + } + // This function is used to request a device memory type that supports all the property flags we request (e.g. device local, host visible) // Upon success it will return the index of the memory type that fits our requested memory properties // This is necessary as implementations can offer an arbitrary number of memory types with different memory properties @@ -381,7 +389,7 @@ public: // Create the depth (and stencil) buffer attachments // While we don't do any depth testing in this sample, having depth testing is very common so it's a good idea to learn it from the very start - void setupDepthStencil() + void setupDepthStencil() override { // Create an optimal tiled image used as the depth stencil attachment VkImageCreateInfo imageCI{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; @@ -428,7 +436,7 @@ public: // Vulkan loads its shaders from an immediate binary representation called SPIR-V // Shaders are compiled offline from e.g. GLSL using the reference glslang compiler // This function loads such a shader from a binary file and returns a shader module structure - VkShaderModule loadSPIRVShader(std::string filename) + VkShaderModule loadSPIRVShader(const std::string& filename) { size_t shaderSize; char* shaderCode{ nullptr }; @@ -661,7 +669,7 @@ public: } - void prepare() + void prepare() override { VulkanExampleBase::prepare(); createSynchronizationPrimitives(); @@ -673,7 +681,7 @@ public: prepared = true; } - virtual void render() override + void render() override { // Use a fence to wait until the command buffer has finished execution before using it again vkWaitForFences(device, 1, &waitFences[currentFrame], VK_TRUE, UINT64_MAX); diff --git a/shaders/glsl/meshshader/meshshader.mesh.spv b/shaders/glsl/meshshader/meshshader.mesh.spv index b95fc1c9..d9dcb786 100644 Binary files a/shaders/glsl/meshshader/meshshader.mesh.spv and b/shaders/glsl/meshshader/meshshader.mesh.spv differ diff --git a/shaders/hlsl/meshshader/meshshader.frag.spv b/shaders/hlsl/meshshader/meshshader.frag.spv index 93ce5e69..6c534184 100644 Binary files a/shaders/hlsl/meshshader/meshshader.frag.spv and b/shaders/hlsl/meshshader/meshshader.frag.spv differ diff --git a/shaders/hlsl/meshshader/meshshader.mesh.spv b/shaders/hlsl/meshshader/meshshader.mesh.spv index 110b8ee1..e29bbb2c 100644 Binary files a/shaders/hlsl/meshshader/meshshader.mesh.spv and b/shaders/hlsl/meshshader/meshshader.mesh.spv differ diff --git a/shaders/hlsl/meshshader/meshshader.task.spv b/shaders/hlsl/meshshader/meshshader.task.spv index 97278132..62823391 100644 Binary files a/shaders/hlsl/meshshader/meshshader.task.spv and b/shaders/hlsl/meshshader/meshshader.task.spv differ