diff --git a/00_Introduction.md b/00_Introduction.md index d507ce21..fec682f6 100644 --- a/00_Introduction.md +++ b/00_Introduction.md @@ -38,7 +38,7 @@ tutorial: This tutorial will not assume knowledge of OpenGL or Direct3D concepts, but it does require you to know the basics of 3D computer graphics. It will not explain -the math behind perspective projection, for example. See [this online book](https://www.docdroid.net/UKocmTz/arcsynthesis.pdf.html) +the math behind perspective projection, for example. See [this online book](http://opengl.datenwolf.net/gltut/html/index.html) for a great introduction of computer graphics concepts. You can use C instead of C++ if you want, but you will have to use a different @@ -46,6 +46,14 @@ linear algebra library and you will be on your own in terms of code structuring. We will use C++ features like classes and RAII to organize logic and resource lifetimes. +## E-book + +If you prefer to read this tutorial as an e-book, then you can download an EPUB +or PDF version here: + +* [EPUB](https://raw.githubusercontent.com/Overv/VulkanTutorial/master/ebook/Vulkan%20Tutorial.epub) +* [PDF](https://raw.githubusercontent.com/Overv/VulkanTutorial/master/ebook/Vulkan%20Tutorial.pdf) + ## Tutorial structure We'll start with an overview of how Vulkan works and the work we'll have to do diff --git a/02_Development_environment.md b/02_Development_environment.md index be964b44..34a2f500 100644 --- a/02_Development_environment.md +++ b/02_Development_environment.md @@ -199,8 +199,9 @@ The number of extensions should be non-zero. Congratulations, you're all set for playing with Vulkan! To avoid having to repeat this work all over again every time, you can create a -template from it. Select `File -> Export Template...`. Select `Project template` -and fill in a nice name and description for the template. +template from it. Select `File -> Export Template...` in Visual Studio 2015 or +`Project -> Export Template...` in Visual Studio 2017. Then select +`Project template` and fill in a nice name and description for the template. ![](/images/vs_export_template.png) diff --git a/03_Drawing_a_triangle/00_Setup/00_Base_code.md b/03_Drawing_a_triangle/00_Setup/00_Base_code.md index c5e6d59f..840e3b06 100644 --- a/03_Drawing_a_triangle/00_Setup/00_Base_code.md +++ b/03_Drawing_a_triangle/00_Setup/00_Base_code.md @@ -16,6 +16,7 @@ public: void run() { initVulkan(); mainLoop(); + cleanup(); } private: @@ -26,6 +27,10 @@ private: void mainLoop() { } + + void cleanup() { + + } }; int main() { @@ -52,6 +57,8 @@ as private class members and add functions to initiate each of them, which will be called from the `initVulkan` function. Once everything has been prepared, we enter the main loop to start rendering frames. We'll fill in the `mainLoop` function to include a loop that iterates until the window is closed in a moment. +Once the window is closed and `mainLoop` returns, we'll make sure to deallocate +the resources we've used in the `cleanup` function. If any kind of fatal error occurs during execution then we'll throw a `std::runtime_error` exception with a descriptive message, which will propagate @@ -61,157 +68,33 @@ extension is not supported. Roughly every chapter that follows after this one will add one new function that will be called from `initVulkan` and one or more new Vulkan objects to the -private class members. +private class members that need to be freed at the end in `cleanup`. ## Resource management -You may have noticed that there's no cleanup function anywhere to be seen and -that is intentional. Every Vulkan object needs to be destroyed with a function -call when it's no longer needed, just like each chunk of memory allocated with -`malloc` requires a call to `free`. Doing that manually is a lot of work and is -very error-prone, but we can completely avoid that by taking advantage of the -C++ [RAII](https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization) -principle. To do that, we're going to create a class that wraps Vulkan objects -and automatically cleans them up when it goes out of scope, for example because -the application was closed. - -First consider the interface we want from this `VDeleter` wrapper class. -Let's say we want to store a `VkInstance` object that should be destroyed with -`vkDestroyInstance` at some point. Then we would add the following class member: - -```c++ -VDeleter instance{vkDestroyInstance}; -``` - -The template argument specifies the type of Vulkan object we want to wrap and -the constructor argument specifies the function to use to clean up the object -when it goes out of scope. - -To assign an object to the wrapper, we would simply want to pass its pointer to -the creation function as if it was a normal `VkInstance` variable: - -```c++ -vkCreateInstance(&instanceCreateInfo, nullptr, &instance); -``` - -Unfortunately, taking the address of the handle in the wrapper doesn't -necessarily mean that we want to overwrite its existing value. A common pattern -is to simply use `&instance` as short-hand for an array of instances with 1 -item. If we intend to write a new handle, then the wrapper should clean up any -previous object to not leak memory. Therefore it would be better to have the `&` -operator return a constant pointer and have an explicit function to state that -we wish to replace the handle. The `replace` function calls clean up for any -existing handle and then gives you a non-const pointer to overwrite the handle: - -```c++ -vkCreateInstance(&instanceCreateInfo, nullptr, instance.replace()); -``` - -Just like that we can now use the `instance` variable wherever a `VkInstance` -would normally be accepted. We no longer have to worry about cleaning up -anymore, because that will automatically happen once the `instance` variable -becomes unreachable! That's pretty easy, right? - -The implementation of such a wrapper class is fairly straightforward. It just -requires a bit of lambda magic to shorten the syntax for specifying the cleanup -functions. - -```c++ -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; -``` - -The three non-default constructors allow you to specify all three types of -deletion functions used in Vulkan: - -* `vkDestroyXXX(object, callbacks)`: Only the object itself needs to be passed -to the cleanup function, so we can simply construct a `VDeleter` with just the -function as argument. -* `vkDestroyXXX(instance, object, callbacks)`: A `VkInstance` also -needs to be passed to the cleanup function, so we use the `VDeleter` constructor -that takes the `VkInstance` reference and cleanup function as parameters. -* `vkDestroyXXX(device, object, callbacks)`: Similar to the previous case, but a -`VkDevice` must be passed instead of a `VkInstance`. - -The `callbacks` parameter is optional and we always pass `nullptr` to it, as you -can see in the `VDeleter` definition. - -All of the constructors initialize the object handle with the equivalent of -`nullptr` in Vulkan: `VK_NULL_HANDLE`. Any extra arguments that are needed for -the deleter functions must also be passed, usually the parent object. It -overloads the address-of, assignment, comparison and casting operators to make -the wrapper as transparent as possible. When the wrapped object goes out of -scope, the destructor is invoked, which in turn calls the cleanup function we -specified. - -The address-of operator returns a constant pointer to make sure that the object -within the wrapper is not unexpectedly changed. If you want to replace the -handle within the wrapper through a pointer, then you should use the `replace()` -function instead. It will invoke the cleanup function for the existing handle so -that you can safely overwrite it afterwards. - -There is also a default constructor with a dummy deleter function that can be -used to initialize it later, which will be useful for lists of deleters. - -I've added the class code between the headers and the `HelloTriangleApplication` -class definition. You can also choose to put it in a separate header file. We'll -use it for the first time in the next chapter where we'll create the very first -Vulkan object! +Just like each chunk of memory allocated with `malloc` requires a call to +`free`, every Vulkan object that we create needs to be explicitly destroyed when +we no longer need it. In modern C++ code it is possible to do automatic resource +management through the utilities in the `` header, but I've chosen to be +explicit about allocation and deallocation of Vulkan objects in this tutorial. +After all, Vulkan's niche is to be explicit about every operation to avoid +mistakes, so it's good to be explicit about the lifetime of objects to learn how +the API works. + +After following this tutorial, you could implement automatic resource management +by overloading `std::shared_ptr` for example. Using [RAII](https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization) +to your advantage is the recommended approach for larger Vulkan programs, but +for learning purposes it's always good to know what's going on behind the +scenes. + +Vulkan objects are either created directly with functions like `vkCreateXXX`, or +allocated through another object with functions like `vkAllocateXXX`. After +making sure that an object is no longer used anywhere, you need to destroy it +with the counterparts `vkDestroyXXX` and `vkFreeXXX`. The parameters for these +functions generally vary for different types of objects, but there is one +parameter that they all share: `pAllocator`. This is an optional parameter that +allows you to specify callbacks for a custom memory allocator. We will ignore +this parameter in the tutorial and always pass `nullptr` as argument. ## Integrating GLFW @@ -234,6 +117,7 @@ void run() { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: @@ -305,19 +189,24 @@ void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } +} +``` +This code should be fairly self-explanatory. It loops and checks for events like +pressing the X button until the window has been closed by the user. This is also +the loop where we'll later call a function to render a single frame. + +Once the window is closed, we need to clean up resources by destroying it and +terminating GLFW itself. This will be our first `cleanup` code: + +```c++ +void cleanup() { glfwDestroyWindow(window); glfwTerminate(); } ``` -This code should be fairly self-explanatory. It loops and checks for events like -pressing the X button until the window has been closed by the user. This is also -the loop where we'll later call a function to render a single frame. Once the -window is closed, we need to clean up resources by destroying it and GLFW] -itself. - When you run the program now you should see a window titled `Vulkan` show up until the application is terminated by closing the window. Now that we have the skeleton for the Vulkan application, let's [create the first Vulkan object](!Drawing_a_triangle/Setup/Instance)! diff --git a/03_Drawing_a_triangle/00_Setup/01_Instance.md b/03_Drawing_a_triangle/00_Setup/01_Instance.md index 19c8a7e5..251cbc23 100644 --- a/03_Drawing_a_triangle/00_Setup/01_Instance.md +++ b/03_Drawing_a_triangle/00_Setup/01_Instance.md @@ -14,20 +14,13 @@ void initVulkan() { } ``` -Additionally add a class member to hold the handle to the instance, like we saw -in the resource management section of the previous chapter. +Additionally add a class member to hold the handle to the instance: ```c++ private: -VDeleter instance {vkDestroyInstance}; +VkInstance instance; ``` -The `vkDestroyInstance` function, as you might imagine, will clean up the -instance that we'll create in a moment. The second parameter is optional and -allows you to specify callbacks for a custom allocator. You'll see that most of -the creation and destroy functions have such a callback parameter and we'll -always pass a `nullptr` as argument, as seen in the `VDeleter` definition. - Now, to create an instance we'll first have to fill in a struct with some information about our application. This data is technically optional, but it may provide some useful information to the driver to optimize for our specific @@ -90,7 +83,7 @@ We've now specified everything Vulkan needs to create an instance and we can finally issue the `vkCreateInstance` call: ```c++ -VkResult result = vkCreateInstance(&createInfo, nullptr, instance.replace()); +VkResult result = vkCreateInstance(&createInfo, nullptr, &instance); ``` As you'll see, the general pattern that object creation function parameters in @@ -101,12 +94,13 @@ Vulkan follow is: * Pointer to the variable that stores the handle to the new object If everything went well then the handle to the instance was stored in the -wrapped `VkInstance` class member. Nearly all Vulkan functions return a value of -type `VkResult` that is either `VK_SUCCESS` or an error code. To check if the -instance was created successfully, simply add a check for the success value: +`VkInstance` class member. Nearly all Vulkan functions return a value of type +`VkResult` that is either `VK_SUCCESS` or an error code. To check if the +instance was created successfully, we don't need to store the result and can +just use a check for the success value instead: ```c++ -if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { +if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } ``` @@ -167,6 +161,27 @@ that checks if all of the extensions returned by `glfwGetRequiredInstanceExtensions` are included in the supported extensions list. +## Cleaning up + +The `VkInstance` should be only destroyed right before the program exits. It can +be destroyed in `cleanup` with the `vkDestroyInstance` function: + +```c++ +void cleanup() { + vkDestroyInstance(instance, nullptr); + + glfwDestroyWindow(window); + + glfwTerminate(); +} +``` + +The parameters for the `vkDestroyInstance` function are straightforward. As +mentioned in the previous chapter, the allocation and deallocation functions +in Vulkan have an optional allocator callback that we'll ignore by passing +`nullptr` to it. All of the other Vulkan resources that we'll create in the +following chapters should be cleaned up before the instance is destroyed. + Before continuing with the more complex steps after instance creation, it's time to evaluate our debugging options by checking out [validation layers](!Drawing_a_triangle/Setup/Validation_layers). diff --git a/03_Drawing_a_triangle/00_Setup/02_Validation_layers.md b/03_Drawing_a_triangle/00_Setup/02_Validation_layers.md index 5a9fa21f..ae2e7d47 100644 --- a/03_Drawing_a_triangle/00_Setup/02_Validation_layers.md +++ b/03_Drawing_a_triangle/00_Setup/02_Validation_layers.md @@ -54,10 +54,10 @@ Validation layers can only be used if they have been installed onto the system. For example, the LunarG validation layers are only available on PCs with the Vulkan SDK installed. -There were formerly two different types of validation layers in Vulkan. Instance -and device specific layers. The idea was that instance layers would only check -calls related to global Vulkan objects like instances and device specific layers -only calls related to a specific GPU. Device specific layers have now been +There were formerly two different types of validation layers in Vulkan: instance +and device specific. The idea was that instance layers would only check +calls related to global Vulkan objects like instances, and device specific layers +would only check calls related to a specific GPU. Device specific layers have now been deprecated, which means that instance validation layers apply to all Vulkan calls. The specification document still recommends that you enable validation layers at device level as well for compatibility, which is required by some @@ -155,7 +155,7 @@ validation layer names if they are enabled: ```c++ if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; @@ -205,7 +205,7 @@ We can now use this function in `createInstance`: ```c++ auto extensions = getRequiredExtensions(); -createInfo.enabledExtensionCount = extensions.size(); +createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); ``` @@ -248,10 +248,15 @@ any of the following bit flags: The `objType` parameter specifies the type of object that is the subject of the message. For example if `obj` is a `VkPhysicalDevice` then `objType` would be `VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT`. This works because internally all -Vulkan handles are typedef'd as `uint64_t`. +Vulkan handles are typedef'd as `uint64_t`. The `msg` parameter contains the +pointer to the message itself. Finally, there's a `userData` parameter to pass +your own data to the callback. -The `msg` parameter contains the pointer to the message itself. Finally, there's -a `userData` parameter to pass your own data to the callback. +The callback returns a boolean that indicates if the Vulkan call that triggered +the validation layer message should be aborted. If the callback returns true, +then the call is aborted with the `VK_ERROR_VALIDATION_FAILED_EXT` error. This +is normally only used to test the validation layers themselves, so you should +always return `VK_FALSE`. All that remains now is telling Vulkan about the callback function. Perhaps somewhat surprisingly, even the debug callback in Vulkan is managed with a @@ -297,7 +302,7 @@ create the `VkDebugReportCallbackEXT` object. Unfortunately, because this function is an extension function, it is not automatically loaded. We have to look up its address ourselves using `vkGetInstanceProcAddr`. We're going to create our own proxy function that handles this in the background. I've added it -right above the `VDeleter` definition. +right above the `HelloTriangleApplication` class definition. ```c++ VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { @@ -320,18 +325,21 @@ if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != V } ``` -Let's see if it works... Run the program and close the window once you're fed up -with staring at the blank window. You'll see that the following message is -printed to the command prompt: +The second to last parameter is again the optional allocator callback that we +set to `nullptr`, other than that the parameters are fairly straightforward. +Since the debug callback is specific to our Vulkan instance and its layers, it +needs to be explicitly specified as first argument. You will also see this +pattern with other *child* objects later on. Let's see if it works... Run the +program and close the window once you're fed up with staring at the blank +window. You'll see that the following message is printed to the command prompt: ![](/images/validation_layer_test.png) Oops, it has already spotted a bug in our program! The `VkDebugReportCallbackEXT` object needs to be cleaned up with a call to -`vkDestroyDebugReportCallbackEXT`. Change the `callback` variable to use our -deleter wrapper. Similarly to `vkCreateDebugReportCallbackEXT` the function -needs to be explicitly loaded. Create another proxy function right below -`CreateDebugReportCallbackEXT`: +`vkDestroyDebugReportCallbackEXT`. Similarly to `vkCreateDebugReportCallbackEXT` +the function needs to be explicitly loaded. Create another proxy function right +below `CreateDebugReportCallbackEXT`: ```c++ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { @@ -343,17 +351,17 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT ``` Make sure that this function is either a static class function or a function -outside the class. We can then specify it as cleanup function: +outside the class. We can then call it in the `cleanup` function: ```c++ -VDeleter callback{instance, DestroyDebugReportCallbackEXT}; -``` +void cleanup() { + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroyInstance(instance, nullptr); -Make sure to change the line that creates the debug report callback to use the -`replace()` method of the wrapper: + glfwDestroyWindow(window); -```c++ -if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + glfwTerminate(); +} ``` When you run the program again you'll see that the error message has diff --git a/03_Drawing_a_triangle/00_Setup/03_Physical_devices_and_queue_families.md b/03_Drawing_a_triangle/00_Setup/03_Physical_devices_and_queue_families.md index 4f090b7f..0eadf032 100644 --- a/03_Drawing_a_triangle/00_Setup/03_Physical_devices_and_queue_families.md +++ b/03_Drawing_a_triangle/00_Setup/03_Physical_devices_and_queue_families.md @@ -22,8 +22,8 @@ void pickPhysicalDevice() { The graphics card that we'll end up selecting will be stored in a VkPhysicalDevice handle that is added as a new class member. This object will be -implicitly destroyed when the VkInstance is destroyed, so we don't need to add a -delete wrapper. +implicitly destroyed when the VkInstance is destroyed, so we won't need to do +anything new in the `cleanup` function. ```c++ VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; diff --git a/03_Drawing_a_triangle/00_Setup/04_Logical_device_and_queues.md b/03_Drawing_a_triangle/00_Setup/04_Logical_device_and_queues.md index 9bfd17c7..8e587973 100644 --- a/03_Drawing_a_triangle/00_Setup/04_Logical_device_and_queues.md +++ b/03_Drawing_a_triangle/00_Setup/04_Logical_device_and_queues.md @@ -7,13 +7,10 @@ need to specify which queues to create now that we've queried which queue families are available. You can even create multiple logical devices from the same physical device if you have varying requirements. -Start by adding a new class member to store the logical device handle in. Make -sure to place the declaration below the `VkInstance` member, because it needs to -be cleaned up before the instance is cleaned up. See [C++ destruction order](https://msdn.microsoft.com/en-us/library/6t4fe76c.aspx). -Logical devices are cleaned up with the `vkDestroyDevice` function. +Start by adding a new class member to store the logical device handle in. ```c++ -VDeleter device{vkDestroyDevice}; +VkDevice device; ``` Next, add a `createLogicalDevice` function that is called from `initVulkan`. @@ -111,7 +108,7 @@ device specific extensions for now. createInfo.enabledExtensionCount = 0; if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; @@ -122,7 +119,7 @@ That's it, we're now ready to instantiate the logical device with a call to the appropriately named `vkCreateDevice` function. ```c++ -if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { +if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } ``` @@ -133,6 +130,18 @@ to a variable to store the logical device handle in. Similarly to the instance creation function, this call can return errors based on enabling non-existent extensions or specifying the desired usage of unsupported features. +The device should be destroyed in `cleanup` with the `vkDestroyDevice` function: + +```c++ +void cleanup() { + vkDestroyDevice(device, nullptr); + ... +} +``` + +Logical devices don't interact directly with instances, which is why it's not +included as a parameter. + ## Retrieving queue handles The queues are automatically created along with the logical device, but we don't @@ -144,7 +153,7 @@ VkQueue graphicsQueue; ``` Device queues are implicitly cleaned up when the device is destroyed, so we -don't need to wrap it in a deleter object. +don't need to do anything in `cleanup`. We can use the `vkGetDeviceQueue` function to retrieve queue handles for each queue family. The parameters are the logical device, queue family, queue index diff --git a/03_Drawing_a_triangle/01_Presentation/00_Window_surface.md b/03_Drawing_a_triangle/01_Presentation/00_Window_surface.md index b0480c57..fda6fe5c 100644 --- a/03_Drawing_a_triangle/01_Presentation/00_Window_surface.md +++ b/03_Drawing_a_triangle/01_Presentation/00_Window_surface.md @@ -23,10 +23,9 @@ allows you to do that without hacks like creating an invisible window ## Window surface creation Start by adding a `surface` class member right below the debug callback. -Surfaces are destroyed using the `vkDestroySurfaceKHR` call. ```c++ -VDeleter surface{instance, vkDestroySurfaceKHR}; +VkSurfaceKHR surface; ``` Although the `VkSurfaceKHR` object and its usage is platform agnostic, its @@ -67,8 +66,7 @@ allocators and the variable for the surface handle to be stored in. ```c++ auto CreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"); -if (!CreateWin32SurfaceKHR || CreateWin32SurfaceKHR(instance, &createInfo, - nullptr, surface.replace()) != VK_SUCCESS) { +if (!CreateWin32SurfaceKHR || CreateWin32SurfaceKHR(instance, &createInfo, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } ``` @@ -101,7 +99,7 @@ implementation of the function very straightforward: ```c++ void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -109,7 +107,19 @@ void createSurface() { The parameters are the `VkInstance`, GLFW window pointer, custom allocators and pointer to `VkSurfaceKHR` variable. It simply passes through the `VkResult` from -the relevant platform call. +the relevant platform call. GLFW doesn't offer a special function for destroying +a surface, but that can easily be done through the original API: + +```c++ +void cleanup() { + ... + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + ... + } +``` + +Make sure that the surface is destroyed before the instance. ## Querying for presentation support @@ -200,8 +210,8 @@ for (int queueFamily : uniqueQueueFamilies) { And modify `VkDeviceCreateInfo` to point to the vector: ```c++ +createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); -createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); ``` If the queue families are the same, then we only need to pass its index once. diff --git a/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.md b/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.md index 67675070..8eaaef6a 100644 --- a/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.md +++ b/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.md @@ -85,7 +85,7 @@ Enabling the extension just requires a small change to the logical device creation structure: ```c++ -createInfo.enabledExtensionCount = deviceExtensions.size(); +createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); ``` @@ -216,7 +216,7 @@ VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector } ``` -Each `VkSurfaceFormatKHR` entry contains `format` and `colorSpace` member. The +Each `VkSurfaceFormatKHR` entry contains a `format` and a `colorSpace` member. The `format` member specifies the color channels and types. For example, `VK_FORMAT_B8G8R8A8_UNORM` means that we store the B, G, R and alpha channels in that order with an 8 bit unsigned integer for a total of 32 bits per pixel. The @@ -279,14 +279,16 @@ There are four possible modes available in Vulkan: * `VK_PRESENT_MODE_IMMEDIATE_KHR`: Images submitted by your application are transferred to the screen right away, which may result in tearing. * `VK_PRESENT_MODE_FIFO_KHR`: The swap chain is a queue where the display takes -an image from the front of the queue on a vertical blank and the program inserts -rendered images at the back of the queue. If the queue is full then the program -has to wait. This is most similar to vertical sync as found in modern games. -* `VK_PRESENT_MODE_FIFO_RELAXED_KHR`: This mode only differs from the first one -if the application is late and the queue was empty at the last vertical blank. -Instead of waiting for the next vertical blank, the image is transferred right -away when it finally arrives. This may result in visible tearing. -* `VK_PRESENT_MODE_MAILBOX_KHR`: This is another variation of the first mode. +an image from the front of the queue when the display is refreshed and the +program inserts rendered images at the back of the queue. If the queue is full +then the program has to wait. This is most similar to vertical sync as found in +modern games. The moment that the display is refreshed is known as "vertical +blank". +* `VK_PRESENT_MODE_FIFO_RELAXED_KHR`: This mode only differs from the previous +one if the application is late and the queue was empty at the last vertical +blank. Instead of waiting for the next vertical blank, the image is transferred +right away when it finally arrives. This may result in visible tearing. +* `VK_PRESENT_MODE_MAILBOX_KHR`: This is another variation of the second mode. Instead of blocking the application when the queue is full, the images that are already queued are simply replaced with the newer ones. This mode can be used to implement triple buffering, which allows you to avoid tearing with significantly @@ -531,26 +533,32 @@ be specified in this field. This is a complex topic that we'll learn more about in [a future chapter](!Drawing_a_triangle/Swap_chain_recreation). For now we'll assume that we'll only ever create one swap chain. -Now add a class member to store the `VkSwapchainKHR` object with a proper -deleter. Make sure to add it after `device` so that it gets cleaned up before -the logical device is. +Now add a class member to store the `VkSwapchainKHR` object: ```c++ -VDeleter swapChain{device, vkDestroySwapchainKHR}; +VkSwapchainKHR swapChain; ``` -Now creating the swap chain is as simple as calling `vkCreateSwapchainKHR`: +Creating the swap chain is now as simple as calling `vkCreateSwapchainKHR`: ```c++ -if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { +if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } ``` The parameters are the logical device, swap chain creation info, optional custom allocators and a pointer to the variable to store the handle in. No surprises -there. Now run the application to ensure that the swap chain is created -successfully! +there. It should be cleaned up using `vkDestroySwapchainKHR` before the device: + +```c++ +void cleanup() { + vkDestroySwapchainKHR(device, swapChain, nullptr); + ... +} +``` + +Now run the application to ensure that the swap chain is created successfully! Try removing the `createInfo.imageExtent = extent;` line with validation layers enabled. You'll see that one of the validation layers immediately catches the @@ -570,7 +578,7 @@ std::vector swapChainImages; The images were created by the implementation for the swap chain and they will be automatically cleaned up once the swap chain has been destroyed, therefore we -don't need a deleter here. +don't need to add any cleanup code. I'm adding the code to retrieve the handles to the end of the `createSwapChain` function, right after the `vkCreateSwapchainKHR` call. Retrieving them is very @@ -593,7 +601,7 @@ One last thing, store the format and extent we've chosen for the swap chain images in member variables. We'll need them in future chapters. ```c++ -VDeleter swapChain{device, vkDestroySwapchainKHR}; +VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; @@ -605,7 +613,8 @@ swapChainExtent = extent; ``` We now have a set of images that can be drawn onto and can be presented to the -window. The next two chapters will cover how we can set up the images as render -targets and then we start looking into the actual drawing commands! +window. The next chapter will begin to cover how we can set up the images as +render targets and then we start looking into the actual graphics pipeline and +drawing commands! [C++ code](/code/swap_chain_creation.cpp) diff --git a/03_Drawing_a_triangle/01_Presentation/02_Image_views.md b/03_Drawing_a_triangle/01_Presentation/02_Image_views.md index ede9da42..c44aa8a9 100644 --- a/03_Drawing_a_triangle/01_Presentation/02_Image_views.md +++ b/03_Drawing_a_triangle/01_Presentation/02_Image_views.md @@ -8,12 +8,10 @@ In this chapter we'll write a `createImageViews` function that creates a basic image view for every image in the swap chain so that we can use them as color targets later on. -First add a class member to store the image views in. Unlike the `VkImage`s, the -`VkImageView` objects are created by us so we need to clean them up ourselves -later. +First add a class member to store the image views in: ```c++ -std::vector> swapChainImageViews; +std::vector swapChainImageViews; ``` Create the `createImageViews` function and call it right after swap chain @@ -36,21 +34,19 @@ void createImageViews() { ``` The first thing we need to do is resize the list to fit all of the image views -we'll be creating. This is also the place where we'll actually define the -deleter function. +we'll be creating: ```c++ void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); } ``` -The `resize` function initializes all of the list items with the right deleter. Next, set up the loop that iterates over all of the swap chain images. ```c++ -for (uint32_t i = 0; i < swapChainImages.size(); i++) { +for (size_t i = 0; i < swapChainImages.size(); i++) { } ``` @@ -105,14 +101,23 @@ different layers. Creating the image view is now a matter of calling `vkCreateImageView`: ```c++ -if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { +if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } ``` -That's it, now run the program to verify that the image views are created -properly and destroyed properly. Checking the latter requires enabling the -validation layers, or putting a print statement in the deleter function. +Unlike images, the image views were explicitly created by us, so we need to add +a similar loop to destroy them again at the end of the program: + +```c++ +void cleanup() { + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + ... +} +``` An image view is sufficient to start using an image as a texture, but it's not quite ready to be used as a render target just yet. That requires one more step diff --git a/03_Drawing_a_triangle/02_Graphics_pipeline_basics/01_Shader_modules.md b/03_Drawing_a_triangle/02_Graphics_pipeline_basics/01_Shader_modules.md index 66440e6d..f3169df9 100644 --- a/03_Drawing_a_triangle/02_Graphics_pipeline_basics/01_Shader_modules.md +++ b/03_Drawing_a_triangle/02_Graphics_pipeline_basics/01_Shader_modules.md @@ -53,11 +53,14 @@ on to the fragment shader, like color and texture coordinates. These values will then be interpolated over the fragments by the rasterizer to produce a smooth gradient. -Clip coordinates are [homogeneous coordinates](https://en.wikipedia.org/wiki/Homogeneous_coordinates) +A *clip coordinate* is a four dimensional vector from the vertex shader that is +subsequently turned into a *normalized device coordinate* by dividing the whole +vector by its last component. These normalized device coordinates are +[homogeneous coordinates](https://en.wikipedia.org/wiki/Homogeneous_coordinates) that map the framebuffer to a [-1, 1] by [-1, 1] coordinate system that looks like the following: -![](/images/clip_coordinates.svg) +![](/images/normalized_device_coordinates.svg) You should already be familiar with these if you have dabbed in computer graphics before. If you have used OpenGL before, then you'll notice that the @@ -65,11 +68,16 @@ sign of the Y coordinates is now flipped. The Z coordinate now uses the same range as it does in Direct3D, from 0 to 1. For our first triangle we won't be applying any transformations, we'll just -specify the positions of the three vertices directly in clip coordinates to -create the following shape: +specify the positions of the three vertices directly as normalized device +coordinates to create the following shape: ![](/images/triangle_coordinates.svg) +We can directly output normalized device coordinates by outputting them as clip +coordinates from the vertex shader with the last component set to `1`. That way +the division to transform clip coordinates to normalized device coordinates will +not change anything. + Normally these coordinates would be stored in a vertex buffer, but creating a vertex buffer in Vulkan and filling it with data is not trivial. Therefore I've decided to postpone that until after we've had the satisfaction of seeing a @@ -350,37 +358,36 @@ Before we can pass the code to the pipeline, we have to wrap it in a do that. ```c++ -void createShaderModule(const std::vector& code, VDeleter& shaderModule) { +VkShaderModule createShaderModule(const std::vector& code) { } ``` The function will take a buffer with the bytecode as parameter and create a -`VkShaderModule` from it. Instead of returning this handle directly, it's -written to the variable specified for the second parameter, which makes it -easier to wrap it in a deleter variable when calling `createShaderModule`. +`VkShaderModule` from it. Creating a shader module is simple, we only need to specify a pointer to the buffer with the bytecode and the length of it. This information is specified in a `VkShaderModuleCreateInfo` structure. The one catch is that the size of the bytecode is specified in bytes, but the bytecode pointer is a `uint32_t` pointer -rather than a `char` pointer. Therefore we need to temporarily copy the bytecode -to a container that has the right alignment for `uint32_t`: +rather than a `char` pointer. Therefore we will need to cast the pointer with +`reinterpret_cast` as shown below. When you perform a cast like this, you also +need to ensure that the data satisfies the alignment requirements of `uint32_t`. +Lucky for us, the data is stored in an `std::vector` where the default allocator +already ensures that the data satisfies the worst case alignment requirements. ```c++ VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); - -std::vector codeAligned(code.size() / sizeof(uint32_t) + 1); -memcpy(codeAligned.data(), code.data(), code.size()); -createInfo.pCode = codeAligned.data(); +createInfo.pCode = reinterpret_cast(code.data()); ``` The `VkShaderModule` can then be created with a call to `vkCreateShaderModule`: ```c++ -if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { +VkShaderModule shaderModule; +if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } ``` @@ -388,24 +395,38 @@ if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) ! The parameters are the same as those in previous object creation functions: the logical device, pointer to create info structure, optional pointer to custom allocators and handle output variable. The buffer with the code can be freed -immediately after creating the shader module. +immediately after creating the shader module. Don't forget to return the created +shader module: + +```c++ +return shaderModule; +``` The shader module objects are only required during the pipeline creation process, so instead of declaring them as class members, we'll make them local variables in the `createGraphicsPipeline` function: ```c++ -VDeleter vertShaderModule{device, vkDestroyShaderModule}; -VDeleter fragShaderModule{device, vkDestroyShaderModule}; +VkShaderModule vertShaderModule; +VkShaderModule fragShaderModule; ``` -They will be automatically cleaned up when the graphics pipeline has been -created and `createGraphicsPipeline` returns. Now just call the helper function -we created and we're done: +Call the helper function we created to load the shader modules: ```c++ -createShaderModule(vertShaderCode, vertShaderModule); -createShaderModule(fragShaderCode, fragShaderModule); +vertShaderModule = createShaderModule(vertShaderCode); +fragShaderModule = createShaderModule(fragShaderCode); +``` + +They should be cleaned up when the graphics pipeline has been created and +`createGraphicsPipeline` returns, so make sure that they are deleted at the end +of the function: + +```c++ + ... + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); +} ``` ## Shader stage creation diff --git a/03_Drawing_a_triangle/02_Graphics_pipeline_basics/02_Fixed_functions.md b/03_Drawing_a_triangle/02_Graphics_pipeline_basics/02_Fixed_functions.md index f5df4293..4af43f80 100644 --- a/03_Drawing_a_triangle/02_Graphics_pipeline_basics/02_Fixed_functions.md +++ b/03_Drawing_a_triangle/02_Graphics_pipeline_basics/02_Fixed_functions.md @@ -41,12 +41,12 @@ like: * `VK_PRIMITIVE_TOPOLOGY_POINT_LIST`: points from vertices * `VK_PRIMITIVE_TOPOLOGY_LINE_LIST`: line from every 2 vertices without reuse -* `VK_PRIMITIVE_TOPOLOGY_LINE_STRIP`: every second vertex is used as start -vertex for the next line +* `VK_PRIMITIVE_TOPOLOGY_LINE_STRIP`: the end vertex of every line is used as +start vertex for the next line * `VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST`: triangle from every 3 vertices without reuse -* `VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP `: every third vertex is used as first -vertex for the next triangle +* `VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP `: the second and third vertex of every +triangle are used as first two vertices of the next triangle Normally, the vertices are loaded from the vertex buffer by index in sequential order, but with an *element buffer* you can specify the indices to use yourself. @@ -213,7 +213,7 @@ multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.sampleShadingEnable = VK_FALSE; multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; multisampling.minSampleShading = 1.0f; // Optional -multisampling.pSampleMask = nullptr; /// Optional +multisampling.pSampleMask = nullptr; // Optional multisampling.alphaToCoverageEnable = VK_FALSE; // Optional multisampling.alphaToOneEnable = VK_FALSE; // Optional ``` @@ -301,7 +301,7 @@ You can find all of the possible operations in the `VkBlendFactor` and The second structure references the array of structures for all of the framebuffers and allows you to set blend constants that you can use as blend -factors in the aforementioned calculations. +factors in the aforementioned calculations. ```c++ VkPipelineColorBlendStateCreateInfo colorBlending = {}; @@ -365,7 +365,7 @@ Create a class member to hold this object, because we'll refer to it from other functions at a later point in time: ```c++ -VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; +VkPipelineLayout pipelineLayout; ``` And then create the object in the `createGraphicsPipeline` function: @@ -378,14 +378,22 @@ pipelineLayoutInfo.pSetLayouts = nullptr; // Optional pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional pipelineLayoutInfo.pPushConstantRanges = 0; // Optional -if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, - pipelineLayout.replace()) != VK_SUCCESS) { +if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } ``` The structure also specifies *push constants*, which are another way of passing -dynamic values to shaders that we'll get into later. +dynamic values to shaders that we may get into in a future chapter. The pipeline +layout is may be referenced throughout the program's lifetime, so it should be +destroyed at the end: + +```c++ +void cleanup() { + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + ... +} +``` ## Conclusion @@ -400,4 +408,4 @@ graphics pipeline and that is a [render pass](!Drawing_a_triangle/Graphics_pipel [C++ code](/code/fixed_functions.cpp) / [Vertex shader](/code/shader_base.vert) / -[Fragment shader](/code/shader_base.frag) \ No newline at end of file +[Fragment shader](/code/shader_base.frag) diff --git a/03_Drawing_a_triangle/02_Graphics_pipeline_basics/03_Render_passes.md b/03_Drawing_a_triangle/02_Graphics_pipeline_basics/03_Render_passes.md index 91506657..4ff24b0f 100644 --- a/03_Drawing_a_triangle/02_Graphics_pipeline_basics/03_Render_passes.md +++ b/03_Drawing_a_triangle/02_Graphics_pipeline_basics/03_Render_passes.md @@ -41,8 +41,9 @@ void createRenderPass() { } ``` -The `format` of the color attachment should match the one of the swap chain -images and we're not doing anything with multisampling, so we stick to 1 sample. +The `format` of the color attachment should match the format of the swap chain +images, and we're not doing anything with multisampling, so we'll stick to 1 +sample. ```c++ colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; @@ -173,7 +174,8 @@ we can create the render pass itself. Create a new class member variable to hold the `VkRenderPass` object right above the `pipelineLayout` variable: ```c++ -VDeleter renderPass{device, vkDestroyRenderPass}; +VkRenderPass renderPass; +VkPipelineLayout pipelineLayout; ``` The render pass object can then be created by filling in the @@ -189,14 +191,25 @@ renderPassInfo.pAttachments = &colorAttachment; renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; -if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { +if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } ``` +Just like the pipeline layout, the render pass will be referenced throughout the +program, so it should only be cleaned up at the end: + +```c++ +void cleanup() { + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + ... +} +``` + That was a lot of work, but in the next chapter it all comes together to finally create the graphics pipeline object! [C++ code](/code/render_passes.cpp) / [Vertex shader](/code/shader_base.vert) / -[Fragment shader](/code/shader_base.frag) \ No newline at end of file +[Fragment shader](/code/shader_base.frag) diff --git a/03_Drawing_a_triangle/02_Graphics_pipeline_basics/04_Conclusion.md b/03_Drawing_a_triangle/02_Graphics_pipeline_basics/04_Conclusion.md index 6ff4db9c..d77e1e8d 100644 --- a/03_Drawing_a_triangle/02_Graphics_pipeline_basics/04_Conclusion.md +++ b/03_Drawing_a_triangle/02_Graphics_pipeline_basics/04_Conclusion.md @@ -73,13 +73,13 @@ Now prepare for the final step by creating a class member to hold the `VkPipeline` object: ```c++ -VDeleter graphicsPipeline{device, vkDestroyPipeline}; +VkPipeline graphicsPipeline; ``` And finally create the graphics pipeline: ```c++ -if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { +if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } ``` @@ -96,6 +96,17 @@ store and reuse data relevant to pipeline creation across multiple calls to stored to a file. This makes it possible to significantly speed up pipeline creation at a later time. We'll get into this in the pipeline cache chapter. +The graphics pipeline is required for all common drawing operations, so it +should also only be destroyed at the end of the program: + +```c++ +void cleanup() { + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + ... +} +``` + Now run your program to confirm that all this hard work has resulted in a successful pipeline creation! We are already getting quite close to seeing something pop up on the screen. In the next couple of chapters we'll set up the diff --git a/03_Drawing_a_triangle/03_Drawing/00_Framebuffers.md b/03_Drawing_a_triangle/03_Drawing/00_Framebuffers.md index cbf65363..2fe9262d 100644 --- a/03_Drawing_a_triangle/03_Drawing/00_Framebuffers.md +++ b/03_Drawing_a_triangle/03_Drawing/00_Framebuffers.md @@ -14,7 +14,7 @@ at drawing time. To that end, create another `std::vector` class member to hold the framebuffers: ```c++ -std::vector> swapChainFramebuffers; +std::vector swapChainFramebuffers; ``` We'll create the objects for this array in a new function `createFramebuffers` @@ -45,7 +45,7 @@ Start by resizing the container to hold all of the framebuffers: ```c++ void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); } ``` @@ -66,7 +66,7 @@ for (size_t i = 0; i < swapChainImageViews.size(); i++) { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -85,6 +85,19 @@ The `width` and `height` parameters are self-explanatory and `layers` refers to the number of layers in image arrays. Our swap chain images are single images, so the number of layers is `1`. +We should delete the framebuffers before the image views and render pass that +they are based on, but only after we've finished rendering: + +```c++ +void cleanup() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + ... +} +``` + We've now reached the milestone where we have all of the objects that are required for rendering. In the next chapter we're going to write the first actual drawing commands. diff --git a/03_Drawing_a_triangle/03_Drawing/01_Command_buffers.md b/03_Drawing_a_triangle/03_Drawing/01_Command_buffers.md index 740495b2..8ea78920 100644 --- a/03_Drawing_a_triangle/03_Drawing/01_Command_buffers.md +++ b/03_Drawing_a_triangle/03_Drawing/01_Command_buffers.md @@ -12,7 +12,7 @@ pools manage the memory that is used to store the buffers and command buffers are allocated from them. Add a new class member to store a `VkCommandPool`: ```c++ -VDeleter commandPool{device, vkDestroyCommandPool}; +VkCommandPool commandPool; ``` Then create a new function `createCommandPool` and call it from `initVulkan` @@ -69,13 +69,23 @@ execute them many times in the main loop, so we're not going to use either of these flags. ```c++ -if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { +if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create command pool!"); } ``` Finish creating the command pool using the `vkCreateCommandPool` function. It -doesn't have any special parameters. +doesn't have any special parameters. Commands will be used throughout the +program to draw things on the screen, so the pool should only be destroyed at +the end: + +```c++ +void cleanup() { + vkDestroyCommandPool(device, commandPool, nullptr); + + ... +} +``` ## Command buffer allocation @@ -84,7 +94,7 @@ them. Because one of the drawing commands involves binding the right `VkFramebuffer`, we'll actually have to record a command buffer for every image in the swap chain once again. To that end, create a list of `VkCommandBuffer` objects as class member. Command buffers will be automatically freed when their -command pool is destroyed, so we don't need a `VDeleter`. +command pool is destroyed, so we don't need an explicit cleanup. ```c++ std::vector commandBuffers; @@ -116,10 +126,6 @@ void createCommandBuffers() { } ``` -Cleaning up command buffers involves a slightly different function than other -objects. The `vkFreeCommandBuffers` function takes the command pool and an array -of command buffers as parameters. - Command buffers are allocated with the `vkAllocateCommandBuffers` function, which takes a `VkCommandBufferAllocateInfo` struct as parameter that specifies the command pool and number of buffers to allocate: diff --git a/03_Drawing_a_triangle/03_Drawing/02_Rendering_and_presentation.md b/03_Drawing_a_triangle/03_Drawing/02_Rendering_and_presentation.md index 9252153e..bee86c7a 100644 --- a/03_Drawing_a_triangle/03_Drawing/02_Rendering_and_presentation.md +++ b/03_Drawing_a_triangle/03_Drawing/02_Rendering_and_presentation.md @@ -10,8 +10,6 @@ void mainLoop() { glfwPollEvents(); drawFrame(); } - - glfwDestroyWindow(window); } ... @@ -55,8 +53,8 @@ presentation can happen. Create two class members to store these semaphore objects: ```c++ -VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; -VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; +VkSemaphore imageAvailableSemaphore; +VkSemaphore renderFinishedSemaphore; ``` To create the semaphores, we'll add the last `create` function for this part of @@ -102,13 +100,22 @@ Future versions of the Vulkan API or extensions may add functionality for the the semaphores follows the familiar pattern with `vkCreateSemaphore`: ```c++ -if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { +if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } ``` +The semaphores should be cleaned up at the end of the program, when all commands +have finished and no more synchronization is necessary: + +```c++ +void cleanup() { + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); +``` + ## Acquiring an image from the swap chain As mentioned before, the first thing we need to do in the `drawFrame` function @@ -208,11 +215,11 @@ start of the render pass and at the end of the render pass, but the former does not occur at the right time. It assumes that the transition occurs at the start of the pipeline, but we haven't acquired the image yet at that point! There are two ways to deal with this problem. We could change the `waitStages` for the -`imageAvailableSemaphore` to `VK_PIPELINE_STAGE_TOP_OF_PIPELINE_BIT` to ensure -that the render passes don't begin until the image is available, or we can make -the render pass wait for the `VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` -stage. I've decided to go with the second option here, because it's a good -excuse to have a look at subpass dependencies and how they work. +`imageAvailableSemaphore` to `VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT` to ensure that +the render passes don't begin until the image is available, or we can make the +render pass wait for the `VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` stage. +I've decided to go with the second option here, because it's a good excuse to +have a look at subpass dependencies and how they work. Subpass dependencies are specified in `VkSubpassDependency` structs. Go to the `createRenderPass` function and add one: @@ -310,8 +317,8 @@ something resembling the following when you run your program: ![](/images/triangle.png) Yay! Unfortunately, you'll see that when validation layers are enabled, the -program crashes as soon as you close it. The message printed to the terminal -from `debugCallback` tells us why: +program crashes as soon as you close it. The messages printed to the terminal +from `debugCallback` tell us why: ![](/images/semaphore_in_use.png) @@ -331,8 +338,6 @@ void mainLoop() { } vkDeviceWaitIdle(device); - - glfwDestroyWindow(window); } ``` @@ -341,6 +346,48 @@ You can also wait for operations in a specific command queue to be finished with perform synchronization. You'll see that the program now exits without problems when closing the window. +## Memory leak + +If you run your application with validation layers enabled and you monitor the +memory usage of your application, you may notice that it is slowly growing. The +reason for this is that the validation layer implementation expects the +application to explicitly synchronize with the GPU. Although this is technically +not required, doing so once a frame will not noticeably affect performance. + +We can do this by explicitly waiting for presentation to finish before starting +to draw the next frame: + +```c++ +void drawFrame() { + ... + + vkQueuePresentKHR(presentQueue, &presentInfo); + + vkQueueWaitIdle(presentQueue); +} +``` + +The state of the application is also updated every frame in many applications. +In that case it would be most efficient to draw a frame this way: + +```c++ +void drawFrame() { + updateAppState(); + + vkQueueWaitIdle(presentQueue); + + vkAcquireNextImageKHR(...) + + submitDrawCommands(); + + vkQueuePresentKHR(presentQueue, &presentInfo); +} +``` + +This approach allows you to update the state of the application, for example run +the AI routines in a game, while the previous frame is being rendered. That way +you keep both the GPU and CPU busy at all times. + ## Conclusion About 800 lines of code later, we've finally gotten to the stage of seeing diff --git a/03_Drawing_a_triangle/04_Swap_chain_recreation.md b/03_Drawing_a_triangle/04_Swap_chain_recreation.md index 322dc929..a2277d44 100644 --- a/03_Drawing_a_triangle/04_Swap_chain_recreation.md +++ b/03_Drawing_a_triangle/04_Swap_chain_recreation.md @@ -36,58 +36,82 @@ avoid this by using dynamic state for the viewports and scissor rectangles. Finally, the framebuffers and command buffers also directly depend on the swap chain images. -Because of our handy `VDeleter` construct, most of the functions will work fine -for recreation and will automatically clean up the old objects. However, the -`createSwapChain` and `createCommandBuffers` functions still need some -adjustments. +To make sure that the old versions of these objects are cleaned up before +recreating them, we should move some of the cleanup code to a separate function +that we can call from the `recreateSwapChain` function. Let's call it +`cleanupSwapChain`: ```c++ -VkSwapchainKHR oldSwapChain = swapChain; -createInfo.oldSwapchain = oldSwapChain; - -VkSwapchainKHR newSwapChain; -if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { - throw std::runtime_error("failed to create swap chain!"); +void cleanupSwapChain() { + } -swapChain = newSwapChain; +void recreateSwapChain() { + vkDeviceWaitIdle(device); + + cleanupSwapChain(); + + createSwapChain(); + createImageViews(); + createRenderPass(); + createGraphicsPipeline(); + createFramebuffers(); + createCommandBuffers(); +} ``` -We need to pass the previous swap chain object in the `oldSwapchain` parameter -of `VkSwapchainCreateInfoKHR` to indicate that we intend to replace it. The old -swap chain needs to stick around until after the new swap chain has been -created, which means that we can't directly write the new handle to `swapChain`. -The `VDeleter` would clear the old object before `vkCreateSwapchainKHR` has a -chance to execute. That's why we use the temporary `newSwapChain` variable. +we'll move the cleanup code of all objects that are recreated as part of a swap +chain refresh from `cleanup` to `cleanupSwapChain`: ```c++ -swapChain = newSwapChain; -``` - -This line will actually destroy the old swap chain and replace the handle with -the handle of the new swap chain. +void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } -The problem with `createCommandBuffers` is that it doesn't free the old command -buffers. There are two ways to solve this: + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); -* Call `createCommandPool` as well, which will automatically free the old -command buffers -* Extend `createCommandBuffers` to free any previous command buffers + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); -As there isn't really a need to recreate the command pool itself, I've chosen to -go for the second solution in this tutorial. + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } -```c++ -if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); + vkDestroySwapchainKHR(device, swapChain, nullptr); } -commandBuffers.resize(swapChainFramebuffers.size()); +void cleanup() { + cleanupSwapChain(); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + + glfwDestroyWindow(window); + + glfwTerminate(); +} ``` -The `createCommandBuffers` function now first checks if the `commandBuffers` -vector already contains previous command buffers, and if so, frees them. That's -all it takes to recreate the swap chain! +We could recreate the command pool from scratch, but that is rather wasteful. +Instead I've opted to clean up the existing command buffers with the +`vkFreeCommandBuffers` function. This way we can reuse the existing pool to +allocate the new command buffers. + +That's all it takes to recreate the swap chain! However, the disadvantage of +this approach is that we need to stop all rendering before creating the new swap +chain. It is possible to create a new swap chain while drawing commands on an +image from the old swap chain are still in-flight. You need to pass the previous +swap chain to the `oldSwapChain` field in the `VkSwapchainCreateInfoKHR` struct +and destroy the old swap chain as soon as you've finished using it. ## Window resizing @@ -180,6 +204,8 @@ if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + +vkQueueWaitIdle(presentQueue); ``` The `vkQueuePresentKHR` function returns the same values with the same meaning. diff --git a/04_Vertex_buffers/00_Vertex_input_description.md b/04_Vertex_buffers/00_Vertex_input_description.md index a6c47c64..c52b6fa2 100644 --- a/04_Vertex_buffers/00_Vertex_input_description.md +++ b/04_Vertex_buffers/00_Vertex_input_description.md @@ -202,7 +202,7 @@ auto bindingDescription = Vertex::getBindingDescription(); auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; -vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); +vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); ``` diff --git a/04_Vertex_buffers/01_Vertex_buffer_creation.md b/04_Vertex_buffers/01_Vertex_buffer_creation.md index 2fcd41f2..14baf126 100644 --- a/04_Vertex_buffers/01_Vertex_buffer_creation.md +++ b/04_Vertex_buffers/01_Vertex_buffer_creation.md @@ -74,7 +74,7 @@ We can now create the buffer with `vkCreateBuffer`. Define a class member to hold the buffer handle and call it `vertexBuffer`. ```c++ -VDeleter vertexBuffer{device, vkDestroyBuffer}; +VkBuffer vertexBuffer; ... @@ -85,12 +85,26 @@ void createVertexBuffer() { bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, vertexBuffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &vertexBuffer) != VK_SUCCESS) { throw std::runtime_error("failed to create vertex buffer!"); } } ``` +The buffer should be available for use in rendering commands until the end of +the program and it does not depend on the swap chain, so we'll clean it up in +the original `cleanup` function: + +```c++ +void cleanup() { + cleanupSwapChain(); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + + ... +} +``` + ## Memory requirements The buffer has been created, but it doesn't actually have any memory assigned to @@ -175,11 +189,11 @@ for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { } ``` -In the future we may have more than one desirable property, so we should check -if the result of the bitwise AND is not just non-zero, but equal to the desired -properties bit field. If there is a memory type suitable for the buffer that -also has all of the properties we need, then we return its index, otherwise we -throw an exception. +We may have more than one desirable property, so we should check if the result +of the bitwise AND is not just non-zero, but equal to the desired properties bit +field. If there is a memory type suitable for the buffer that also has all of +the properties we need, then we return its index, otherwise we throw an +exception. ## Memory allocation @@ -199,20 +213,16 @@ desired property. Create a class member to store the handle to the memory and allocate it with `vkAllocateMemory`. ```c++ -VDeleter vertexBuffer{device, vkDestroyBuffer}; -VDeleter vertexBufferMemory{device, vkFreeMemory}; +VkBuffer vertexBuffer; +VkDeviceMemory vertexBufferMemory; ... -if (vkAllocateMemory(device, &allocInfo, nullptr, vertexBufferMemory.replace()) != VK_SUCCESS) { +if (vkAllocateMemory(device, &allocInfo, nullptr, &vertexBufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate vertex buffer memory!"); } ``` -Note that specifying the `vertexBuffer` and `vertexBufferMemory` members in this -order will cause the memory to be freed before the buffer is destroyed, but -that's allowed as long as the buffer is no longer used. - If memory allocation was successful, then we can now associate this memory with the buffer using `vkBindBufferMemory`: @@ -220,11 +230,24 @@ the buffer using `vkBindBufferMemory`: vkBindBufferMemory(device, vertexBuffer, vertexBufferMemory, 0); ``` -The first two parameters are self-explanatory and the third parameter is the +The first three parameters are self-explanatory and the fourth parameter is the offset within the region of memory. Since this memory is allocated specifically for this the vertex buffer, the offset is simply `0`. If the offset is non-zero, then it is required to be divisible by `memRequirements.alignment`. +Of course, just like dynamic memory allocation in C++, the memory should be +freed at some point. Memory that is bound to a buffer object may be freed once +the buffer is no longer used, so let's free it after the buffer has been +destroyed: + +```c++ +void cleanup() { + cleanupSwapChain(); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); +``` + ## Filling the vertex buffer It is now time to copy the vertex data to the buffer. This is done by [mapping @@ -279,7 +302,7 @@ VkBuffer vertexBuffers[] = {vertexBuffer}; VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); -vkCmdDraw(commandBuffers[i], vertices.size(), 1, 0, 0); +vkCmdDraw(commandBuffers[i], static_cast(vertices.size()), 1, 0, 0); ``` The `vkCmdBindVertexBuffers` function is used to bind vertex buffers to @@ -314,4 +337,4 @@ vertex buffer that results in better performance, but takes some more work. [C++ code](/code/vertex_buffer.cpp) / [Vertex shader](/code/shader_vertexbuffer.vert) / -[Fragment shader](/code/shader_vertexbuffer.frag) \ No newline at end of file +[Fragment shader](/code/shader_vertexbuffer.frag) diff --git a/04_Vertex_buffers/02_Staging_buffer.md b/04_Vertex_buffers/02_Staging_buffer.md index ea166480..7019fc2a 100644 --- a/04_Vertex_buffers/02_Staging_buffer.md +++ b/04_Vertex_buffers/02_Staging_buffer.md @@ -44,14 +44,14 @@ to move buffer creation to a helper function. Create a new function `createBuffer` and move the code in `createVertexBuffer` (except mapping) to it. ```c++ -void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& buffer, VDeleter& bufferMemory) { +void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = usage; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, buffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } @@ -63,7 +63,7 @@ void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyF allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, bufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } @@ -101,8 +101,8 @@ as temporary buffer and use a device local one as actual vertex buffer. void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -229,6 +229,19 @@ createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERT copyBuffer(stagingBuffer, vertexBuffer, bufferSize); ``` +After copying the data from the staging buffer to the device buffer, we should +clean it up: + +```c++ + ... + + copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); +} +``` + Run your program to verify that you're seeing the familiar triangle again. It may not be visible, but its vertex data is now being loaded from high performance memory. This will matter when we're going to start rendering more @@ -245,10 +258,10 @@ objects at the same time is to create a custom allocator that splits up a single allocation among many different objects by using the `offset` parameters that we've seen in many functions. -You will currently have to write such an allocator yourself, but the author -expects that there will be a library at some point that can be integrated into -any Vulkan program to properly handle allocations. It's okay to use a separate -allocation for every resource for this tutorial, because we won't come close to +You can either implement such an allocator yourself, or use the +[VulkanMemoryAllocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) +library provided by the GPUOpen initiative. However, for this tutorial it's okay +to use a separate allocation for every resource, because we won't come close to hitting any of these limits for now. [C++ code](/code/staging_buffer.cpp) / diff --git a/04_Vertex_buffers/03_Index_buffer.md b/04_Vertex_buffers/03_Index_buffer.md index e0323de8..cd110dbb 100644 --- a/04_Vertex_buffers/03_Index_buffer.md +++ b/04_Vertex_buffers/03_Index_buffer.md @@ -54,10 +54,10 @@ the GPU to be able to access them. Define two new class members to hold the resources for the index buffer: ```c++ -VDeleter vertexBuffer{device, vkDestroyBuffer}; -VDeleter vertexBufferMemory{device, vkFreeMemory}; -VDeleter indexBuffer{device, vkDestroyBuffer}; -VDeleter indexBufferMemory{device, vkFreeMemory}; +VkBuffer vertexBuffer; +VkDeviceMemory vertexBufferMemory; +VkBuffer indexBuffer; +VkDeviceMemory indexBufferMemory; ``` The `createIndexBuffer` function that we'll add now is almost identical to @@ -74,8 +74,8 @@ void initVulkan() { void createIndexBuffer() { VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -86,6 +86,9 @@ void createIndexBuffer() { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } ``` @@ -97,6 +100,23 @@ number of indices times the size of the index type, either `uint16_t` or process is exactly the same. We create a staging buffer to copy the contents of `indices` to and then copy it to the final device local index buffer. +The index buffer should be cleaned up at the end of the program, just like the +vertex buffer: + +```c++ +void cleanup() { + cleanupSwapChain(); + + vkDestroyBuffer(device, indexBuffer, nullptr); + vkFreeMemory(device, indexBufferMemory, nullptr); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + ... +} +``` + ## Using an index buffer Using an index buffer for drawing involves two changes to @@ -122,7 +142,7 @@ the drawing command to tell Vulkan to use the index buffer. Remove the `vkCmdDraw` line and replace it with `vkCmdDrawIndexed`: ```c++ -vkCmdDrawIndexed(commandBuffers[i], indices.size(), 1, 0, 0, 0); +vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); ``` A call to this function is very similar to `vkCmdDraw`. The first two parameters diff --git a/05_Uniform_buffers/00_Descriptor_layout_and_buffer.md b/05_Uniform_buffers/00_Descriptor_layout_and_buffer.md index eb275d5d..b27807df 100644 --- a/05_Uniform_buffers/00_Descriptor_layout_and_buffer.md +++ b/05_Uniform_buffers/00_Descriptor_layout_and_buffer.md @@ -93,7 +93,11 @@ Note that the order of the `uniform`, `in` and `out` declarations doesn't matter. The `binding` directive is similar to the `location` directive for attributes. We're going to reference this binding in the descriptor layout. The line with `gl_Position` is changed to use the transformations to compute the -final position in clip coordinates. +final position in clip coordinates. Unlike the 2D triangles, the last component +of the clip coordinates may not be `1`, which will result in a division when +converted to the final normalized device coordinates on the screen. This is used +in perspective projection as the *perspective division* and is essential for +making closer objects look larger than objects that are further away. ## Descriptor set layout @@ -174,8 +178,8 @@ All of the descriptor bindings are combined into a single `pipelineLayout`: ```c++ -VDeleter descriptorSetLayout{device, vkDestroyDescriptorSetLayout}; -VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; +VkDescriptorSetLayout descriptorSetLayout; +VkPipelineLayout pipelineLayout; ``` We can then create it using `vkCreateDescriptorSetLayout`. This function accepts @@ -187,7 +191,7 @@ layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layoutInfo.bindingCount = 1; layoutInfo.pBindings = &uboLayoutBinding; -if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, descriptorSetLayout.replace()) != VK_SUCCESS) { +if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor set layout!"); } ``` @@ -198,11 +202,10 @@ specified in the pipeline layout object. Modify the `VkPipelineLayoutCreateInfo` to reference the layout object: ```c++ -VkDescriptorSetLayout setLayouts[] = {descriptorSetLayout}; VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; -pipelineLayoutInfo.pSetLayouts = setLayouts; +pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; ``` You may be wondering why it's possible to specify multiple descriptor set @@ -210,28 +213,39 @@ layouts here, because a single one already includes all of the bindings. We'll get back to that in the next chapter, where we'll look into descriptor pools and descriptor sets. +The descriptor layout should stick around while we may create new graphics +pipelines i.e. until the program ends: + +```c++ +void cleanup() { + cleanupSwapChain(); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + + ... +} +``` + ## Uniform buffer In the next chapter we'll specify the buffer that contains the UBO data for the shader, but we need to create this buffer first. We're going to copy new data to -the uniform buffer every frame, so this time the staging buffer actually needs -to stick around. +the uniform buffer every frame, so it doesn't really make any sense to have a +staging buffer. It would just add extra overhead in this case and likely degrade +performance instead of improving it. -Add new class members for `uniformStagingBuffer`, `uniformStagingBufferMemory`, -`uniformBuffer`, and `uniformBufferMemory`: +Add new class members for `uniformBuffer`, and `uniformBufferMemory`: ```c++ -VDeleter indexBuffer{device, vkDestroyBuffer}; -VDeleter indexBufferMemory{device, vkFreeMemory}; +VkBuffer indexBuffer; +VkDeviceMemory indexBufferMemory; -VDeleter uniformStagingBuffer{device, vkDestroyBuffer}; -VDeleter uniformStagingBufferMemory{device, vkFreeMemory}; -VDeleter uniformBuffer{device, vkDestroyBuffer}; -VDeleter uniformBufferMemory{device, vkFreeMemory}; +VkBuffer uniformBuffer; +VkDeviceMemory uniformBufferMemory; ``` Similarly, create a new function `createUniformBuffer` that is called after -`createIndexBuffer` and allocates the buffers: +`createIndexBuffer` and allocates the buffer: ```c++ void initVulkan() { @@ -246,15 +260,31 @@ void initVulkan() { void createUniformBuffer() { VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformStagingBuffer, uniformStagingBufferMemory); - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, uniformBuffer, uniformBufferMemory); + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffer, uniformBufferMemory); } ``` We're going to write a separate function that updates the uniform buffer with a -new transformation every frame, so there will be no `vkMapMemory` and -`copyBuffer` operations here. +new transformation every frame, so there will be no `vkMapMemory` here. The +uniform data will be used for all draw calls, so the buffer containing it should +only be destroyed at the end: + +```c++ +void cleanup() { + cleanupSwapChain(); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyBuffer(device, uniformBuffer, nullptr); + vkFreeMemory(device, uniformBufferMemory, nullptr); + + ... +} +``` + +## Updating uniform data + +Create a new function `updateUniformBuffer` and add a call to it from the main +loop: ```c++ void mainLoop() { @@ -266,8 +296,6 @@ void mainLoop() { } vkDeviceWaitIdle(device); - - glfwDestroyWindow(window); } ... @@ -277,10 +305,7 @@ void updateUniformBuffer() { } ``` -## Updating uniform data - -Create a new function `updateUniformBuffer` and add a call to it from the main -loop. This function will generate a new transformation every frame to make the +This function will generate a new transformation every frame to make the geometry spin around. We need to include two new headers to implement this functionality: @@ -322,11 +347,11 @@ Z-axis using the `time` variable: ```c++ UniformBufferObject ubo = {}; -ubo.model = glm::rotate(glm::mat4(), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); +ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ``` The `glm::rotate` function takes an existing transformation, rotation angle and -rotation axis as parameters. The `glm::mat4()` default constructor returns an +rotation axis as parameters. The `glm::mat4(1.0f)` constructor returns an identity matrix. Using a rotation angle of `time * glm::radians(90.0f)` accomplishes the purpose of rotation 90 degrees per second. @@ -359,21 +384,18 @@ do this, then the image will be rendered upside down. All of the transformations are defined now, so we can copy the data in the uniform buffer object to the uniform buffer. This happens in exactly the same -way as we did for vertex buffers with a staging buffer: +way as we did for vertex buffers, except without a staging buffer: ```c++ void* data; -vkMapMemory(device, uniformStagingBufferMemory, 0, sizeof(ubo), 0, &data); +vkMapMemory(device, uniformBufferMemory, 0, sizeof(ubo), 0, &data); memcpy(data, &ubo, sizeof(ubo)); -vkUnmapMemory(device, uniformStagingBufferMemory); - -copyBuffer(uniformStagingBuffer, uniformBuffer, sizeof(ubo)); +vkUnmapMemory(device, uniformBufferMemory); ``` -Using a staging buffer and final buffer this way is not the most efficient way -to pass frequently changing values to the shader. A more efficient way to pass a -small buffer of data to shaders are *push constants*. We may look at these in a -future chapter. +Using a UBO this way is not the most efficient way to pass frequently changing +values to the shader. A more efficient way to pass a small buffer of data to +shaders are *push constants*. We may look at these in a future chapter. In the next chapter we'll look at descriptor sets, which will actually bind the `VkBuffer` to the uniform buffer descriptor so that the shader can access this @@ -381,4 +403,4 @@ transformation data. [C++ code](/code/descriptor_layout.cpp) / [Vertex shader](/code/shader_ubo.vert) / -[Fragment shader](/code/shader_ubo.frag) +[Fragment shader](/code/shader_ubo.frag) \ No newline at end of file diff --git a/05_Uniform_buffers/01_Descriptor_pool_and_sets.md b/05_Uniform_buffers/01_Descriptor_pool_and_sets.md index 119bd8f7..ee581dc9 100644 --- a/05_Uniform_buffers/01_Descriptor_pool_and_sets.md +++ b/05_Uniform_buffers/01_Descriptor_pool_and_sets.md @@ -60,17 +60,28 @@ the descriptor set after creating it, so we don't need this flag. You can leave `flags` to its default value of `0`. ```c++ -VDeleter descriptorPool{device, vkDestroyDescriptorPool}; +VkDescriptorPool descriptorPool; ... -if (vkCreateDescriptorPool(device, &poolInfo, nullptr, descriptorPool.replace()) != VK_SUCCESS) { +if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor pool!"); } ``` Add a new class member to store the handle of the descriptor pool and call -`vkCreateDescriptorPool` to create it. +`vkCreateDescriptorPool` to create it. The descriptor pool should be destroyed +only at the end of the program, much like the other drawing related resources: + +```c++ +void cleanup() { + cleanupSwapChain(); + + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + + ... +} +``` ## Descriptor set @@ -109,7 +120,7 @@ Add a class member to hold the descriptor set handle and allocate it with `vkAllocateDescriptorSets`: ```c++ -VDeleter descriptorPool{device, vkDestroyDescriptorPool}; +VkDescriptorPool descriptorPool; VkDescriptorSet descriptorSet; ... @@ -119,7 +130,7 @@ if (vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet) != VK_SUCCESS) } ``` -You don't need to use a deleter for descriptor sets, because they will be +You don't need to explicitly clean up descriptor sets, because they will be automatically freed when the descriptor pool is destroyed. The call to `vkAllocateDescriptorSets` will allocate one descriptor set with one uniform buffer descriptor. @@ -182,8 +193,8 @@ vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr); The updates are applied using `vkUpdateDescriptorSets`. It accepts two kinds of arrays as parameters: an array of `VkWriteDescriptorSet` and an array of -`VkCopyDescriptorSet`. The latter can be used to copy the configuration of -descriptors, as its name implies. +`VkCopyDescriptorSet`. The latter can be used to copy descriptors to each other, +as its name implies. ## Using a descriptor set @@ -208,7 +219,7 @@ visible. The problem is that because of the Y-flip we did in the projection matrix, the vertices are now being drawn in clockwise order instead of counter-clockwise order. This causes backface culling to kick in and prevents any geometry from being drawn. Go to the `createGraphicsPipeline` function and -modify the `cullFace` in `VkPipelineRasterizationStateCreateInfo` to correct +modify the `frontFace` in `VkPipelineRasterizationStateCreateInfo` to correct this: ```c++ diff --git a/06_Texture_mapping/00_Images.md b/06_Texture_mapping/00_Images.md index 84ab051d..923e2bb6 100644 --- a/06_Texture_mapping/00_Images.md +++ b/06_Texture_mapping/00_Images.md @@ -14,12 +14,16 @@ Adding a texture to our application will involve the following steps: We've already worked with image objects before, but those were automatically created by the swap chain extension. This time we'll have to create one by -ourselves. Creating an image and filling it with data is very similar to vertex -buffer creation. You create a `VkImage`, query its memory requirements, allocate -device memory, bind the memory to the image, and finally map the memory to -upload the pixel data. We'll use a staging and final image again, to make sure -that the texture image itself ends up in fast device local memory. There is a -command to copy the contents of images similar to `vkCmdCopyBuffer`. +ourselves. Creating an image and filling it with data is similar to vertex +buffer creation. We'll start by creating a staging resource and filling it with +pixel data and then we copy this to the final image object that we'll use for +rendering. Although it is possible to create a staging image for this purpose, +Vulkan also allows you to copy pixels from a `VkBuffer` to an image and the API +for this is actually [faster on some hardware](https://developer.nvidia.com/vulkan-memory-management). +We'll first create this buffer and fill it with pixel values, and then we'll +create an image to copy the pixels to. Creating an image is not very different +from creating buffers. It involves querying the memory requirements, allocating +device memory and binding it, just like we've seen before. However, there is something extra that we'll have to take care of when working with images. Images can have different *layouts* that affect how the pixels are @@ -33,9 +37,9 @@ these layouts when we specified the render pass: * `VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL`: Optimal as attachment for writing colors from the fragment shader * `VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL`: Optimal as source in a transfer -operation, like `vkCmdCopyImage` +operation, like `vkCmdCopyImageToBuffer` * `VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL`: Optimal as destination in a transfer -operation, like `vkCmdCopyImage` +operation, like `vkCmdCopyBufferToImage` * `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL`: Optimal for sampling from a shader One of the most common ways to transition the layout of an image is a *pipeline @@ -140,16 +144,51 @@ returned is the first element in an array of pixel values. The pixels are laid out row by row with 4 bytes per pixel in the case of `STBI_rgba_alpha` for a total of `texWidth * texHeight * 4` values. -## Staging image +## Staging buffer -We're now going to create an image in host visible memory so that we can use -`vkMapMemory` and copy the pixels to it. Pixels within an image object are known -as texels and we'll use that name from this point on. Add the following two -variables in the `createTextureImage` function: +We're now going to create a buffer in host visible memory so that we can use +`vkMapMemory` and copy the pixels to it. Add variables for this temporary buffer +to the `createTextureImage` function: ```c++ -VDeleter stagingImage{device, vkDestroyImage}; -VDeleter stagingImageMemory{device, vkFreeMemory}; +VkBuffer stagingBuffer; +VkDeviceMemory stagingBufferMemory; +``` + +The buffer should be in host visible memory so that we can map it and it should +be usable as a transfer source so that we can copy it to an image later on: + +```c++ +createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); +``` + +We can then directly copy the pixel values that we got from the image loading +library to the buffer: + +```c++ +void* data; +vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); +vkUnmapMemory(device, stagingBufferMemory); +``` + +Don't forget to clean up the original pixel array now: + +```c++ +stbi_image_free(pixels); +``` + +## Texture Image + +Although we could set up the shader to access the pixel values in the buffer, +it's better to use image objects in Vulkan for this purpose. Image objects will +make it easier and faster to retrieve colors by allowing us to use 2D +coordinates, for one. Pixels within an image object are known as texels and +we'll use that name from this point on. Add the following new class members: + +```c++ +VkImage textureImage; +VkDeviceMemory textureImageMemory; ``` The parameters for an image are specified in a `VkImageCreateInfo` struct: @@ -158,14 +197,14 @@ The parameters for an image are specified in a `VkImageCreateInfo` struct: VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.imageType = VK_IMAGE_TYPE_2D; -imageInfo.extent.width = texWidth; -imageInfo.extent.height = texHeight; +imageInfo.extent.width = static_cast(texWidth); +imageInfo.extent.height = static_cast(texHeight); imageInfo.extent.depth = 1; imageInfo.mipLevels = 1; imageInfo.arrayLayers = 1; ``` -The image type, specified in the `imageType` field, tells Vulkan with that kind +The image type, specified in the `imageType` field, tells Vulkan with what kind of coordinate system the texels in the image are going to be addressed. It is possible to create 1D, 2D and 3D images. One dimensional images can be used to store an array of data or gradient, two dimensional images are mainly used for @@ -178,11 +217,12 @@ many texels there are on each axis. That's why `depth` must be `1` instead of imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM; ``` -Vulkan supports many possible image formats, but it makes the most sense to use -exactly the same format for the texels as the pixels loaded with the library. +Vulkan supports many possible image formats, but we should use the same format +for the texels as the pixels in the buffer, otherwise the copy operation will +fail. ```c++ -imageInfo.tiling = VK_IMAGE_TILING_LINEAR; +imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; ``` The `tiling` field can have one of two values: @@ -192,14 +232,14 @@ The `tiling` field can have one of two values: * `VK_IMAGE_TILING_OPTIMAL`: Texels are laid out in an implementation defined order for optimal access -If you want to be able to directly access texels in the memory of the image, -then you must use `VK_IMAGE_TILING_LINEAR`. We want to be able to directly copy -the data in `pixels` to the staging image memory, so we should use it. Unlike -the layout of an image, the tiling mode cannot be changed at a later time. We're -going to use `VK_IMAGE_TILING_OPTIMAL` for the final image. +Unlike the layout of an image, the tiling mode cannot be changed at a later +time. If you want to be able to directly access texels in the memory of the +image, then you must use `VK_IMAGE_TILING_LINEAR`. We will be using a staging +buffer instead of a staging image, so this won't be necessary. We will be using +`VK_IMAGE_TILING_OPTIMAL` for efficient access from the shader. ```c++ -imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; +imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; ``` There are only two possible values for the `initialLayout` of an image: @@ -209,26 +249,31 @@ transition will discard the texels. * `VK_IMAGE_LAYOUT_PREINITIALIZED`: Not usable by the GPU, but the first transition will preserve the texels. -An initially undefined layout is suitable for images that will be used as -attachments, like color and depth buffers. In that case we don't care about any -initial data, because it'll probably be cleared by a render pass before use. If -you want to fill it with data, like a texture, then you should use the -preinitialized layout. +There are few situations where it is necessary for the texels to be preserved +during the first transition. One example, however, would be if you wanted to use +an image as a staging image in combination with the `VK_IMAGE_TILING_LINEAR` +layout. In that case, you'd want to upload the texel data to it and then +transition the image to be a transfer source without losing the data. In our +case, however, we're first going to transition the image to be a transfer +destination and then copy texel data to it from a buffer object, so we don't +need this property and can safely use `VK_IMAGE_LAYOUT_UNDEFINED`. ```c++ -imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; +imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; ``` The `usage` field has the same semantics as the one during buffer creation. The -staging image is going to be copied to the final texture image, so it should be -set up as a transfer source. +image is going to be used as destination for the buffer copy, so it should be +set up as a transfer destination. We also want to be able to access the image +from the shader to color our mesh, so the usage should include +`VK_IMAGE_USAGE_SAMPLED_BIT`. ```c++ imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; ``` -The staging image will only be used by one queue family: the one that supports -transfer operations. +The image will only be used by one queue family: the one that supports graphics +(and therefore also) transfer operations. ```c++ imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; @@ -244,7 +289,7 @@ avoid allocating memory to store large volumes of "air" values. We won't be using it in this tutorial, so leave it to its default value of `0`. ```c++ -if (vkCreateImage(device, &imageInfo, nullptr, stagingImage.replace()) != VK_SUCCESS) { +if (vkCreateImage(device, &imageInfo, nullptr, &textureImage) != VK_SUCCESS) { throw std::runtime_error("failed to create image!"); } ``` @@ -259,126 +304,32 @@ this in the depth buffer chapter, where we'll implement such a system. ```c++ VkMemoryRequirements memRequirements; -vkGetImageMemoryRequirements(device, stagingImage, &memRequirements); +vkGetImageMemoryRequirements(device, textureImage, &memRequirements); VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; -allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); +allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); -if (vkAllocateMemory(device, &allocInfo, nullptr, stagingImageMemory.replace()) != VK_SUCCESS) { +if (vkAllocateMemory(device, &allocInfo, nullptr, &textureImageMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate image memory!"); } -vkBindImageMemory(device, stagingImage, stagingImageMemory, 0); +vkBindImageMemory(device, textureImage, textureImageMemory, 0); ``` Allocating memory for an image works in exactly the same way as allocating memory for a buffer. Use `vkGetImageMemoryRequirements` instead of `vkGetBufferMemoryRequirements`, and use `vkBindImageMemory` instead of -`vkBindBufferMemory`. Remember that we need the memory to be host visible to be -able to use `vkMapMemory`, so you should specify that property when looking for -the right memory type. - -We can now use the `vkMapMemory` function to (temporarily) access the memory of -the staging image directly from our application. It returns a pointer to the -first byte in the memory buffer: - -```c++ -void* data; -vkMapMemory(device, stagingImageMemory, 0, imageSize, 0, &data); -``` - -Unfortunately we can't just copy the pixel bytes directly into the image memory -with `memcpy` and assume that this works correctly. The problem is that there -may be padding bytes between rows of pixels. In other words, the graphics card -may assume that one row of pixels is not `texWidth * 4` bytes wide, but rather -`texWidth * 4 + paddingBytes`. To handle this correctly, we need to query how -bytes are arranged in our staging image using `vkGetImageSubresourceLayout`: - -```c++ -VkImageSubresource subresource = {}; -subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; -subresource.mipLevel = 0; -subresource.arrayLayer = 0; - -VkSubresourceLayout stagingImageLayout; -vkGetImageSubresourceLayout(device, stagingImage, &subresource, &stagingImageLayout); -``` - -Images contain one or more *subresources*, which are specific images within an -image. For example, there is one subresource for every entry in an array image. -In this case we don't have an array image, so there is simply one subresource at -entry 0 and the base mipmapping level. - -The `rowPitch` member of the `VkSubresourceLayout` struct specifies the total -number of bytes of each row of pixels in the image. If this value is equal to -`texWidth * 4`, then we're lucky and we *can* use `memcpy`, because there are no -padding bytes in that case. - -```c++ -if (stagingImageLayout.rowPitch == texWidth * 4) { - memcpy(data, pixels, (size_t) imageSize); -} else { - -} -``` - -This is usually the case when your images have a power-of-2 size (e.g. 512 or -1024). Otherwise, we'll have to copy the pixels row-by-row using the right -offset: - -```c++ -uint8_t* dataBytes = reinterpret_cast(data); - -for (int y = 0; y < texHeight; y++) { - memcpy( - &dataBytes[y * stagingImageLayout.rowPitch], - &pixels[y * texWidth * 4], - texWidth * 4 - ); -} -``` - -Each subsequent row in the image memory is offset by `rowPitch` and the original -pixels are offset by `texWidth * 4` without padding bytes. - -If you're done accessing the memory buffer, then you should unmap it with -`vkUnmapMemory`. It is not necessary to call `vkUnmapMemory` now if you want to -access the staging image memory again later on. The writes to the buffer will -already be visible without calling this function. - -```c++ -void* data; -vkMapMemory(device, stagingImageMemory, 0, imageSize, 0, &data); - - if (stagingImageLayout.rowPitch == texWidth * 4) { - memcpy(data, pixels, (size_t) imageSize); - } else { - uint8_t* dataBytes = reinterpret_cast(data); - - for (int y = 0; y < texHeight; y++) { - memcpy(&dataBytes[y * stagingImageLayout.rowPitch], &pixels[y * texWidth * 4], texWidth * 4); - } - } - -vkUnmapMemory(device, stagingImageMemory); -``` +`vkBindBufferMemory`. -Don't forget to clean up the original pixel array now: - -```c++ -stbi_image_free(pixels); -``` - -## Texture image - -We will now abstract image creation into a `createImage` function, like we did -for buffers. Create the function and move the image object creation and memory -allocation to it: +This function is already getting quite large and there'll be a need to create +more images in later chapters, so we should abstract image creation into a +`createImage` function, like we did for buffers. Create the function and move +the image object creation and memory allocation to it: ```c++ -void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& image, VDeleter& imageMemory) { +void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) { VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.imageType = VK_IMAGE_TYPE_2D; @@ -389,12 +340,12 @@ void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling imageInfo.arrayLayers = 1; imageInfo.format = format; imageInfo.tiling = tiling; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.usage = usage; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateImage(device, &imageInfo, nullptr, image.replace()) != VK_SUCCESS) { + if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) { throw std::runtime_error("failed to create image!"); } @@ -406,7 +357,7 @@ void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, imageMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate image memory!"); } @@ -430,71 +381,21 @@ void createTextureImage() { throw std::runtime_error("failed to load texture image!"); } - VDeleter stagingImage{device, vkDestroyImage}; - VDeleter stagingImageMemory{device, vkFreeMemory}; - createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingImage, stagingImageMemory); - - VkImageSubresource subresource = {}; - subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresource.mipLevel = 0; - subresource.arrayLayer = 0; - - VkSubresourceLayout stagingImageLayout; - vkGetImageSubresourceLayout(device, stagingImage, &subresource, &stagingImageLayout); + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; - vkMapMemory(device, stagingImageMemory, 0, imageSize, 0, &data); - - if (stagingImageLayout.rowPitch == texWidth * 4) { - memcpy(data, pixels, (size_t) imageSize); - } else { - uint8_t* dataBytes = reinterpret_cast(data); - - for (int y = 0; y < texHeight; y++) { - memcpy(&dataBytes[y * stagingImageLayout.rowPitch], &pixels[y * texWidth * 4], texWidth * 4); - } - } - - vkUnmapMemory(device, stagingImageMemory); + vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(device, stagingBufferMemory); stbi_image_free(pixels); -} -``` - -The next step is to create the actual texture image. Define two new class -members to hold the handle to the image and its memory: -```c++ -VDeleter commandPool{device, vkDestroyCommandPool}; -VDeleter textureImage{device, vkDestroyImage}; -VDeleter textureImageMemory{device, vkFreeMemory}; -VDeleter vertexBuffer{device, vkDestroyBuffer}; -``` - -The final texture image can now be created using the same function: - -```c++ -createImage( - texWidth, texHeight, - VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - textureImage, - textureImageMemory -); + createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); +} ``` -The dimensions of the image should be the same as the staging image. The formats -should also be *compatible*, because the command simply copies the raw image -data. Two color formats are compatible if they have the same number of bytes per -pixel. Depth/stencil formats, which we'll see in one of the next chapters, need -to be exactly equal. The tiling mode on the other hand does not need to be the -same. The texture image will be used as the destination in the transfer, and we -want to be able to sample texels from it in the shader. The -`VK_IMAGE_USAGE_SAMPLED_BIT` flag is necessary to allow that. The memory of the -image should be device local for best performance, just like the vertex buffer. - ## Layout transitions The function we're going to write now involves recording and executing a command @@ -552,9 +453,9 @@ void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { ``` If we were still using buffers, then we could now write a function to record and -execute `vkCmdCopyImage` to finish the job, but this command requires the images -to be in the right layout first. Create a new function to handle layout -transitions: +execute `vkCmdCopyBufferToImage` to finish the job, but this command requires +the image to be in the right layout first. Create a new function to handle +layout transitions: ```c++ void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) { @@ -619,7 +520,7 @@ back to this once we've figured out which transitions we're going to use. ```c++ vkCmdPipelineBarrier( commandBuffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + 0 /* TODO */, 0 /* TODO */, 0, 0, nullptr, 0, nullptr, @@ -628,10 +529,19 @@ vkCmdPipelineBarrier( ``` All types of pipeline barriers are submitted using the same function. The first -parameter specifies in which pipeline stage the operations occur that should -happen before the barrier. The second parameter specifies the pipeline stage in -which operations will wait on the barrier. We want it to happen immediately, so -we're going with the top of the pipeline. +parameter after the command buffer specifies in which pipeline stage the +operations occur that should happen before the barrier. The second parameter +specifies the pipeline stage in which operations will wait on the barrier. The +pipeline stages that you are allowed to specify before and after the barrier +depend on how you use the resource before and after the barrier. The allowed +values are listed in [this table](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-access-types-supported) +of the specification. For example, if you're going to read from a uniform after +the barrier, you would specify a usage of `VK_ACCESS_UNIFORM_READ_BIT` and the +earliest shader that will read from the uniform as pipeline stage, for example +`VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT`. It would not make sense to specify +a non-shader pipeline stage for this type of usage and the validation layers +will warn you when you specify a pipeline stage that does not match the type of +usage. The third parameter is either `0` or `VK_DEPENDENCY_BY_REGION_BIT`. The latter turns the barrier into a per-region condition. That means that the @@ -644,81 +554,94 @@ barriers like the one we're using here. Note that we're not using the `VkFormat` parameter yet, but we'll be using that one for special transitions in the depth buffer chapter. -## Copying images +## Copying buffer to image Before we get back to `createTextureImage`, we're going to write one more helper -function: `copyImage`: +function: `copyBufferToImage`: ```c++ -void copyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height) { +void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { VkCommandBuffer commandBuffer = beginSingleTimeCommands(); endSingleTimeCommands(commandBuffer); } ``` -Just like with buffers, you need to specify which part of the image needs to be -copied to which part of the other image. This happens through `VkImageCopy` -structs: +Just like with buffer copies, you need to specify which part of the buffer is +going to be copied to which part of the image. This happens through +`VkBufferImageCopy` structs: ```c++ -VkImageSubresourceLayers subResource = {}; -subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; -subResource.baseArrayLayer = 0; -subResource.mipLevel = 0; -subResource.layerCount = 1; +VkBufferImageCopy region = {}; +region.bufferOffset = 0; +region.bufferRowLength = 0; +region.bufferImageHeight = 0; -VkImageCopy region = {}; -region.srcSubresource = subResource; -region.dstSubresource = subResource; -region.srcOffset = {0, 0, 0}; -region.dstOffset = {0, 0, 0}; -region.extent.width = width; -region.extent.height = height; -region.extent.depth = 1; +region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; +region.imageSubresource.mipLevel = 0; +region.imageSubresource.baseArrayLayer = 0; +region.imageSubresource.layerCount = 1; + +region.imageOffset = {0, 0, 0}; +region.imageExtent = { + width, + height, + 1 +}; ``` -All of these fields are fairly self-explanatory. Image copy operations are -enqueued using the `vkCmdCopyImage` function: +Most of these fields are self-explanatory. The `bufferOffset` specifies the byte +offset in the buffer at which the pixel values start. The `bufferRowLength` and +`bufferImageHeight` fields specify how the pixels are laid out in memory. For +example, you could have some padding bytes between rows of the image. Specifying +`0` for both indicates that the pixels are simply tightly packed like they are +in our case. The `imageSubresource`, `imageOffset` and `imageExtent` fields +indicate to which part of the image we want to copy the pixels. + +Buffer to image copy operations are enqueued using the `vkCmdCopyBufferToImage` +function: ```c++ -vkCmdCopyImage( +vkCmdCopyBufferToImage( commandBuffer, - srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ®ion + buffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ®ion ); ``` -The first two pairs of parameters specify the source image/layout and -destination image/layout. I'm assuming here that they've been previously -transitioned to the optimal transfer layouts. +The fourth parameter indicates which layout the image is currently using. I'm +assuming here that the image has already been transitioned to the layout that is +optimal for copying pixels to. Right now we're only copying one chunk of pixels +to the whole image, but it's possible to specify an array of `VkBufferImageCopy` +to perform many different copies from this buffer to the image in one operation. ## Preparing the texture image We now have all of the tools we need to finish setting up the texture image, so we're going back to the `createTextureImage` function. The last thing we did -there was creating the texture image. The next step is to copy the staging image -to the texture image. This involves three operations: +there was creating the texture image. The next step is to copy the staging +buffer to the texture image. This involves two steps: -* Transition the staging image to `VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL` * Transition the texture image to `VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL` -* Execute the image copy operation +* Execute the buffer to image copy operation This is easy to do with the functions we just created: ```c++ -transitionImageLayout(stagingImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); -transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); -copyImage(stagingImage, textureImage, texWidth, texHeight); +transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); +copyBufferToImage(stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); ``` -Both `VK_IMAGE_LAYOUT_PREINITIALIZED` and `VK_IMAGE_LAYOUT_UNDEFINED` are valid -values for old layout when transitioning `textureImage`, because we don't care -about its contents before the copy operation. +The image was created with the `VK_IMAGE_LAYOUT_UNDEFINED` layout, so that one +should be specified as old layout when transitioning `textureImage`. Remember +that we can do this because we don't care about its contents before performing +the copy operation. To be able to start sampling from the texture image in the shader, we need one -last transition: +last transition to prepare it for shader access: ```c++ transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); @@ -727,43 +650,70 @@ transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TR ## Transition barrier masks If run your application with validation layers enabled now, then you'll see that -it complains about the access masks in `transitionImageLayout` being invalid. -We still need to set those based on the layouts in the transition. +it complains about the access masks and pipeline stages in +`transitionImageLayout` being invalid. We still need to set those based on the +layouts in the transition. -There are three transitions we need to handle: +There are two transitions we need to handle: -* Preinitialized → transfer source: transfer reads should wait on host writes -* Preinitialized → transfer destination: transfer writes should wait on host -writes +* Undefined → transfer destination: transfer writes that don't need to wait on +anything * Transfer destination → shader reading: shader reads should wait on transfer -writes +writes, specifically the shader reads in the fragment shader, because that's +where we're going to use the texture -These rules are specified using the following access masks: +These rules are specified using the following access masks and pipeline stages: ```c++ -if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; -} else if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; +VkPipelineStageFlags sourceStage; +VkPipelineStageFlags destinationStage; + +if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } else { throw std::invalid_argument("unsupported layout transition!"); } + +vkCmdPipelineBarrier( + commandBuffer, + sourceStage, destinationStage, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier +); ``` +As you can see in the aforementioned table, transfer writes must occur in the +pipeline transfer stage. Since the writes don't have to wait on anything, you +may specify an empty access mask and the earliest possible pipeline stage +`VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT` for the pre-barrier operations. + +The image will be written in the same pipeline stage and subsequently read by +the fragment shader, which is why we specify shader reading access in the +fragment shader pipeline stage. + If we need to do more transitions in the future, then we'll extend the function. The application should now run successfully, although there are of course no -visual changes yet. One thing to note is that command buffer submission results -in implicit `VK_ACCESS_HOST_WRITE_BIT` synchronization at the beginning. Since -the `transitionImageLayout` function executes a command buffer with only a -single command, we can use this implicit synchronization and set `srcAccessMask` -to `0` for the first two types of transitions. It's up to you if you want to be -explicit about it or not, but I'm personally not a fan of relying on these -OpenGL-like "hidden" operations. +visual changes yet. + +One thing to note is that command buffer submission results in implicit +`VK_ACCESS_HOST_WRITE_BIT` synchronization at the beginning. Since the +`transitionImageLayout` function executes a command buffer with only a single +command, you could use this implicit synchronization and set `srcAccessMask` to +`0` if you ever needed a `VK_ACCESS_HOST_WRITE_BIT` dependency in a layout +transition. It's up to you if you want to be explicit about it or not, but I'm +personally not a fan of relying on these OpenGL-like "hidden" operations. There is actually a special type of image layout that supports all operations, `VK_IMAGE_LAYOUT_GENERAL`. The problem with it, of course, is that it doesn't @@ -781,15 +731,35 @@ commands into, and add a `flushSetupCommands` to execute the commands that have been recorded so far. It's best to do this after the texture mapping works to check if the texture resources are still set up correctly. -In this tutorial we used another image as staging resource for the texture, but -it's also possible to use a buffer and copy pixels from it using -`vkCmdCopyBufferToImage`. It is recommended to use this approach for improved -performance on [some hardware](https://developer.nvidia.com/vulkan-memory-management) -if you need to update the data in an image often. +## Cleanup + +Finish the `createTextureImage` function by cleaning up the staging buffer and +its memory at the end: + +```c++ + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); +} +``` + +The main texture image is used until the end of the program: + +```c++ +void cleanup() { + cleanupSwapChain(); + + vkDestroyImage(device, textureImage, nullptr); + vkFreeMemory(device, textureImageMemory, nullptr); + + ... +} +``` The image now contains the texture, but we still need a way to access it from the graphics pipeline. We'll work on that in the next chapter. [C++ code](/code/texture_image.cpp) / [Vertex shader](/code/shader_ubo.vert) / -[Fragment shader](/code/shader_ubo.frag) \ No newline at end of file +[Fragment shader](/code/shader_ubo.frag) diff --git a/06_Texture_mapping/01_Image_view_and_sampler.md b/06_Texture_mapping/01_Image_view_and_sampler.md index cf658e46..580fe012 100644 --- a/06_Texture_mapping/01_Image_view_and_sampler.md +++ b/06_Texture_mapping/01_Image_view_and_sampler.md @@ -13,7 +13,7 @@ Add a class member to hold a `VkImageView` for the texture image and create a new function `createTextureImageView` where we'll create it: ```c++ -VDeleter textureImageView{device, vkDestroyImageView}; +VkImageView textureImageView; ... @@ -53,7 +53,7 @@ I've left out the explicit `viewInfo.components` initialization, because image view by calling `vkCreateImageView`: ```c++ -if (vkCreateImageView(device, &viewInfo, nullptr, textureImageView.replace()) != VK_SUCCESS) { +if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) { throw std::runtime_error("failed to create texture image view!"); } ``` @@ -62,7 +62,7 @@ Because so much of the logic is duplicated from `createImageViews`, you may wish to abstract it into a new `createImageView` function: ```c++ -void createImageView(VkImage image, VkFormat format, VDeleter& imageView) { +VkImageView createImageView(VkImage image, VkFormat format) { VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; @@ -74,9 +74,12 @@ void createImageView(VkImage image, VkFormat format, VDeleter& imag viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &viewInfo, nullptr, imageView.replace()) != VK_SUCCESS) { + VkImageView imageView; + if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { throw std::runtime_error("failed to create texture image view!"); } + + return imageView; } ``` @@ -84,7 +87,7 @@ The `createTextureImageView` function can now be simplified to: ```c++ void createTextureImageView() { - createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, textureImageView); + textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM); } ``` @@ -92,14 +95,27 @@ And `createImageViews` can be simplified to: ```c++ void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); for (uint32_t i = 0; i < swapChainImages.size(); i++) { - createImageView(swapChainImages[i], swapChainImageFormat, swapChainImageViews[i]); + swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat); } } ``` +Make sure to destroy the image view at the end of the program, right before +destroying the image itself: + +```c++ +void cleanup() { + cleanupSwapChain(); + + vkDestroyImageView(device, textureImageView, nullptr); + + vkDestroyImage(device, textureImage, nullptr); + vkFreeMemory(device, textureImageMemory, nullptr); +``` + ## Samplers It is possible for shaders to read texels directly from images, but that is not @@ -213,7 +229,7 @@ There is no graphics hardware available today that will use more than 16 samples, because the difference is negligible beyond that point. ```c++ -samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK ; +samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; ``` The `borderColor` field specifies which color is returned when sampling beyond @@ -240,7 +256,7 @@ samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; If a comparison function is enabled, then texels will first be compared to a value, and the result of that comparison is used in filtering operations. This -is mainly used for [percentage-closer filtering](http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html) +is mainly used for [percentage-closer filtering](https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch11.html) on shadow maps. We'll look at this in a future chapter. ```c++ @@ -258,15 +274,15 @@ hold the handle of the sampler object and create the sampler with `vkCreateSampler`: ```c++ -VDeleter textureImageView{device, vkDestroyImageView}; -VDeleter textureSampler{device, vkDestroySampler}; +VkImageView textureImageView; +VkSampler textureSampler; ... void createTextureSampler() { ... - if (vkCreateSampler(device, &samplerInfo, nullptr, textureSampler.replace()) != VK_SUCCESS) { + if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { throw std::runtime_error("failed to create texture sampler!"); } } @@ -278,9 +294,64 @@ can be applied to any image you want, whether it is 1D, 2D or 3D. This is different from many older APIs, which combined texture images and filtering into a single state. +Destroy the sampler at the end of the program when we'll no longer be accessing +the image: + +```c++ +void cleanup() { + cleanupSwapChain(); + + vkDestroySampler(device, textureSampler, nullptr); + vkDestroyImageView(device, textureImageView, nullptr); + + ... +} +``` + +## Anisotropy device feature + +If you run your program right now, you'll see a validation layer message like +this: + +![](/images/validation_layer_anisotropy.png) + +That's because anisotropic filtering is actually an optional device feature. We +need to update the `createLogicalDevice` function to request it: + +```c++ +VkPhysicalDeviceFeatures deviceFeatures = {}; +deviceFeatures.samplerAnisotropy = VK_TRUE; +``` + +And even though it is very unlikely that a modern graphics card will not support +it, we should update `isDeviceSuitable` to check if it is available: + +```c++ +bool isDeviceSuitable(VkPhysicalDevice device) { + ... + + VkPhysicalDeviceFeatures supportedFeatures; + vkGetPhysicalDeviceFeatures(device, &supportedFeatures); + + return indices.isComplete() && extensionsSupported && supportedFeatures.samplerAnisotropy; +} +``` + +The `vkGetPhysicalDeviceFeatures` repurposes the `VkPhysicalDeviceFeatures` +struct to indicate which features are supported rather than requested by setting +the boolean values. + +Instead of enforcing the availability of anisotropic filtering, it's also +possible to simply not use it by conditionally setting: + +```c++ +samplerInfo.anisotropyEnable = VK_FALSE; +samplerInfo.maxAnisotropy = 1; +``` + In the next chapter we will expose the image and sampler objects to the shaders to draw the texture onto the square. [C++ code](/code/sampler.cpp) / [Vertex shader](/code/shader_ubo.vert) / -[Fragment shader](/code/shader_ubo.frag) \ No newline at end of file +[Fragment shader](/code/shader_ubo.frag) diff --git a/06_Texture_mapping/02_Combined_image_sampler.md b/06_Texture_mapping/02_Combined_image_sampler.md index 10138400..d791dc81 100644 --- a/06_Texture_mapping/02_Combined_image_sampler.md +++ b/06_Texture_mapping/02_Combined_image_sampler.md @@ -28,7 +28,7 @@ samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; std::array bindings = {uboLayoutBinding, samplerLayoutBinding}; VkDescriptorSetLayoutCreateInfo layoutInfo = {}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; -layoutInfo.bindingCount = bindings.size(); +layoutInfo.bindingCount = static_cast(bindings.size()); layoutInfo.pBindings = bindings.data(); ``` @@ -53,7 +53,7 @@ poolSizes[1].descriptorCount = 1; VkDescriptorPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; -poolInfo.poolSizeCount = poolSizes.size(); +poolInfo.poolSizeCount = static_cast(poolSizes.size()); poolInfo.pPoolSizes = poolSizes.data(); poolInfo.maxSets = 1; ``` @@ -92,7 +92,7 @@ descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptorWrites[1].descriptorCount = 1; descriptorWrites[1].pImageInfo = &imageInfo; -vkUpdateDescriptorSets(device, descriptorWrites.size(), descriptorWrites.data(), 0, nullptr); +vkUpdateDescriptorSets(device, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); ``` The descriptor must be updated with this image info, just like the buffer. This @@ -151,10 +151,10 @@ square. ```c++ const std::vector vertices = { - {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, - {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, - {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}} + {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}, + {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, + {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}} }; ``` diff --git a/07_Depth_buffering.md b/07_Depth_buffering.md index a4051350..497b4dae 100644 --- a/07_Depth_buffering.md +++ b/07_Depth_buffering.md @@ -130,9 +130,9 @@ running at once. The depth image will again require the trifecta of resources: image, memory and image view. ```c++ -VDeleter depthImage{device, vkDestroyImage}; -VDeleter depthImageMemory{device, vkFreeMemory}; -VDeleter depthImageView{device, vkDestroyImageView}; +VkImage depthImage; +VkDeviceMemory depthImageMemory; +VkImageView depthImageView; ``` Create a new function `createDepthResources` to set up these resources: @@ -272,7 +272,7 @@ We now have all the required information to invoke our `createImage` and ```c++ createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory); -createImageView(depthImage, depthFormat, depthImageView); +depthImageView = createImageView(depthImage, depthFormat); ``` However, the `createImageView` function currently assumes that the subresource @@ -280,7 +280,7 @@ is always the `VK_IMAGE_ASPECT_COLOR_BIT`, so we will need to turn that field into a parameter: ```c++ -void createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, VDeleter& imageView) { +VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) { ... viewInfo.subresourceRange.aspectMask = aspectFlags; ... @@ -290,11 +290,11 @@ void createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFl Update all calls to this function to use the right aspect: ```c++ -createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, swapChainImageViews[i]); +swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT); ... -createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, depthImageView); +depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); ... -createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT, textureImageView); +textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); ``` That's it for creating the depth image. We don't need to map it or copy another @@ -327,27 +327,38 @@ if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { Although we're not using the stencil component, we do need to include it in the layout transitions of the depth image. -Finally, add the correct access masks: +Finally, add the correct access masks and pipeline stages: ```c++ -if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; -} else if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; +if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; } else { throw std::invalid_argument("unsupported layout transition!"); } ``` -The image is now completely ready for usage as depth attachment. +The depth buffer will be read from to perform depth tests to see if a fragment +is visible, and will be written to when a new fragment is drawn. The reading +happens in the `VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT` stage and the +writing in the `VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT`. You should pick the +earliest pipeline stage that matches the specified operations, so that it is +ready for usage as depth attachment when it needs to be. ## Render pass @@ -396,7 +407,7 @@ buffers. std::array attachments = {colorAttachment, depthAttachment}; VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; -renderPassInfo.attachmentCount = attachments.size(); +renderPassInfo.attachmentCount = static_cast(attachments.size()); renderPassInfo.pAttachments = attachments.data(); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; @@ -422,7 +433,7 @@ std::array attachments = { VkFramebufferCreateInfo framebufferInfo = {}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.renderPass = renderPass; -framebufferInfo.attachmentCount = attachments.size(); +framebufferInfo.attachmentCount = static_cast(attachments.size()); framebufferInfo.pAttachments = attachments.data(); framebufferInfo.width = swapChainExtent.width; framebufferInfo.height = swapChainExtent.height; @@ -456,7 +467,7 @@ std::array clearValues = {}; clearValues[0].color = {0.0f, 0.0f, 0.0f, 1.0f}; clearValues[1].depthStencil = {1.0f, 0}; -renderPassInfo.clearValueCount = clearValues.size(); +renderPassInfo.clearValueCount = static_cast(clearValues.size()); renderPassInfo.pClearValues = clearValues.data(); ``` @@ -548,6 +559,18 @@ void recreateSwapChain() { } ``` +The cleanup operations should happen in the swap chain cleanup function: + +```c++ +void cleanupSwapChain() { + vkDestroyImageView(device, depthImageView, nullptr); + vkDestroyImage(device, depthImage, nullptr); + vkFreeMemory(device, depthImageMemory, nullptr); + + ... +} +``` + Congratulations, your application is now finally ready to render arbitrary 3D geometry and have it look right. We're going to try this out in the next chapter by drawing a textured model! diff --git a/08_Loading_models.md b/08_Loading_models.md index 956bc068..67aa4129 100644 --- a/08_Loading_models.md +++ b/08_Loading_models.md @@ -88,8 +88,8 @@ non-const containers as class members: ```c++ std::vector vertices; std::vector indices; -VDeleter vertexBuffer{device, vkDestroyBuffer}; -VDeleter vertexBufferMemory{device, vkFreeMemory}; +VkBuffer vertexBuffer; +VkDeviceMemory vertexBufferMemory; ``` You should change the type of the indices from `uint16_t` to `uint32_t`, because @@ -249,14 +249,14 @@ Unfortunately we're not really taking advantage of the index buffer yet. The vertices are included in multiple triangles. We should keep only the unique vertices and use the index buffer to reuse them whenever they come up. A straightforward way to implement this is to use a `map` or `unordered_map` to -keep track of the unique vertices and their index: +keep track of the unique vertices and respective indices: ```c++ #include ... -std::unordered_map uniqueVertices = {}; +std::unordered_map uniqueVertices = {}; for (const auto& shape : shapes) { for (const auto& index : shape.mesh.indices) { @@ -265,7 +265,7 @@ for (const auto& shape : shapes) { ... if (uniqueVertices.count(vertex) == 0) { - uniqueVertices[vertex] = vertices.size(); + uniqueVertices[vertex] = static_cast(vertices.size()); vertices.push_back(vertex); } @@ -336,6 +336,7 @@ like: * Pipeline cache * Multi-threaded command buffer generation * Multiple subpasses +* Compute shaders The current program can be extended in many ways, like adding Blinn-Phong lighting, post-processing effects and shadow mapping. You should be able to diff --git a/README.md b/README.md index 7613a09f..54141df3 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,20 @@ have a problem with your code, then use the comments section in the related chapter to ask a question. Please provide your operating system, graphics card, driver version, source code, expected behaviour and actual behaviour. +E-book +------ + +This guide is now available in e-book formats as well: + +* [EPUB](https://raw.githubusercontent.com/Overv/VulkanTutorial/master/ebook/Vulkan%20Tutorial.epub) +* [PDF](https://raw.githubusercontent.com/Overv/VulkanTutorial/master/ebook/Vulkan%20Tutorial.pdf) + Changing code across chapters ----------------------------- It is sometimes necessary to change code that is reused across many chapters, -for example the `VDeleter` class or a function like `createBuffer`. If you make -such a change, then you should update the code files using the following steps: +for example a function like `createBuffer`. If you make such a change, then you +should update the code files using the following steps: * Update any chapters that reference the modified code. * Make a copy of the first file that uses it and modify the code there, e.g. diff --git a/code/base_code.cpp b/code/base_code.cpp index 8b5fe910..67bad1d3 100644 --- a/code/base_code.cpp +++ b/code/base_code.cpp @@ -3,75 +3,17 @@ #include #include -#include const int WIDTH = 800; const int HEIGHT = 600; -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - class HelloTriangleApplication { public: void run() { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: @@ -87,14 +29,16 @@ class HelloTriangleApplication { } void initVulkan() { - + } void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + void cleanup() { glfwDestroyWindow(window); glfwTerminate(); @@ -112,4 +56,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/command_buffers.cpp b/code/command_buffers.cpp index f1da66b0..f2993e35 100644 --- a/code/command_buffers.cpp +++ b/code/command_buffers.cpp @@ -2,9 +2,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -43,64 +42,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -122,33 +63,34 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; std::vector commandBuffers; void initWindow() { @@ -179,6 +121,28 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + vkDestroyCommandPool(device, commandPool, nullptr); + + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -203,17 +167,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -226,13 +190,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -281,22 +245,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -345,10 +309,10 @@ class HelloTriangleApplication { createInfo.oldSwapchain = VK_NULL_HANDLE; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -358,9 +322,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -376,7 +340,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -409,7 +373,7 @@ class HelloTriangleApplication { renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -418,10 +382,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -500,8 +462,8 @@ class HelloTriangleApplication { pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -520,13 +482,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -542,7 +507,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -554,8 +519,8 @@ class HelloTriangleApplication { VkCommandPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create command pool!"); } } @@ -605,19 +570,18 @@ class HelloTriangleApplication { } } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -656,7 +620,7 @@ class HelloTriangleApplication { actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - + return actualExtent; } } @@ -826,4 +790,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/depth_buffering.cpp b/code/depth_buffering.cpp index 5aa24560..4a358f3d 100644 --- a/code/depth_buffering.cpp +++ b/code/depth_buffering.cpp @@ -10,11 +10,10 @@ #include #include -#include -#include -#include #include +#include #include +#include #include #include #include @@ -53,64 +52,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -169,15 +110,15 @@ struct UniformBufferObject { }; const std::vector vertices = { - {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, - {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, - {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, - - {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - {{0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, - {{0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, - {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}} + {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}, + {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, + {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, + + {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}, + {{0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, + {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}} }; const std::vector indices = { @@ -191,61 +132,60 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - VDeleter swapChain{device, vkDestroySwapchainKHR}; + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter descriptorSetLayout{device, vkDestroyDescriptorSetLayout}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; + VkRenderPass renderPass; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; - VDeleter commandPool{device, vkDestroyCommandPool}; + VkCommandPool commandPool; - VDeleter depthImage{device, vkDestroyImage}; - VDeleter depthImageMemory{device, vkFreeMemory}; - VDeleter depthImageView{device, vkDestroyImageView}; + VkImage depthImage; + VkDeviceMemory depthImageMemory; + VkImageView depthImageView; - VDeleter textureImage{device, vkDestroyImage}; - VDeleter textureImageMemory{device, vkFreeMemory}; - VDeleter textureImageView{device, vkDestroyImageView}; - VDeleter textureSampler{device, vkDestroySampler}; + VkImage textureImage; + VkDeviceMemory textureImageMemory; + VkImageView textureImageView; + VkSampler textureSampler; - VDeleter vertexBuffer{device, vkDestroyBuffer}; - VDeleter vertexBufferMemory{device, vkFreeMemory}; - VDeleter indexBuffer{device, vkDestroyBuffer}; - VDeleter indexBufferMemory{device, vkFreeMemory}; + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; - VDeleter uniformStagingBuffer{device, vkDestroyBuffer}; - VDeleter uniformStagingBufferMemory{device, vkFreeMemory}; - VDeleter uniformBuffer{device, vkDestroyBuffer}; - VDeleter uniformBufferMemory{device, vkFreeMemory}; + VkBuffer uniformBuffer; + VkDeviceMemory uniformBufferMemory; - VDeleter descriptorPool{device, vkDestroyDescriptorPool}; + VkDescriptorPool descriptorPool; VkDescriptorSet descriptorSet; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -293,6 +233,60 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + vkDestroyImageView(device, depthImageView, nullptr); + vkDestroyImage(device, depthImage, nullptr); + vkFreeMemory(device, depthImageMemory, nullptr); + + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroySampler(device, textureSampler, nullptr); + vkDestroyImageView(device, textureImageView, nullptr); + + vkDestroyImage(device, textureImage, nullptr); + vkFreeMemory(device, textureImageMemory, nullptr); + + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyBuffer(device, uniformBuffer, nullptr); + vkFreeMemory(device, uniformBufferMemory, nullptr); + + vkDestroyBuffer(device, indexBuffer, nullptr); + vkFreeMemory(device, indexBufferMemory, nullptr); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -309,6 +303,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -336,17 +332,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -359,13 +355,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -410,26 +406,27 @@ class HelloTriangleApplication { } VkPhysicalDeviceFeatures deviceFeatures = {}; + deviceFeatures.samplerAnisotropy = VK_TRUE; VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -476,16 +473,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -495,10 +486,10 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); for (uint32_t i = 0; i < swapChainImages.size(); i++) { - createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, swapChainImageViews[i]); + swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT); } } @@ -548,14 +539,14 @@ class HelloTriangleApplication { std::array attachments = {colorAttachment, depthAttachment}; VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = attachments.size(); + renderPassInfo.attachmentCount = static_cast(attachments.size()); renderPassInfo.pAttachments = attachments.data(); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -578,10 +569,10 @@ class HelloTriangleApplication { std::array bindings = {uboLayoutBinding, samplerLayoutBinding}; VkDescriptorSetLayoutCreateInfo layoutInfo = {}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = bindings.size(); + layoutInfo.bindingCount = static_cast(bindings.size()); layoutInfo.pBindings = bindings.data(); - if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, descriptorSetLayout.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor set layout!"); } } @@ -590,10 +581,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -616,7 +605,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -682,13 +671,12 @@ class HelloTriangleApplication { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - VkDescriptorSetLayout setLayouts[] = {descriptorSetLayout}; VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = setLayouts; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -708,13 +696,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { std::array attachments = { @@ -725,13 +716,13 @@ class HelloTriangleApplication { VkFramebufferCreateInfo framebufferInfo = {}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.renderPass = renderPass; - framebufferInfo.attachmentCount = attachments.size(); + framebufferInfo.attachmentCount = static_cast(attachments.size()); framebufferInfo.pAttachments = attachments.data(); framebufferInfo.width = swapChainExtent.width; framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -744,7 +735,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics command pool!"); } } @@ -753,7 +744,7 @@ class HelloTriangleApplication { VkFormat depthFormat = findDepthFormat(); createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory); - createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, depthImageView); + depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); } @@ -794,46 +785,29 @@ class HelloTriangleApplication { throw std::runtime_error("failed to load texture image!"); } - VDeleter stagingImage{device, vkDestroyImage}; - VDeleter stagingImageMemory{device, vkFreeMemory}; - createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingImage, stagingImageMemory); - - VkImageSubresource subresource = {}; - subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresource.mipLevel = 0; - subresource.arrayLayer = 0; - - VkSubresourceLayout stagingImageLayout; - vkGetImageSubresourceLayout(device, stagingImage, &subresource, &stagingImageLayout); + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; - vkMapMemory(device, stagingImageMemory, 0, imageSize, 0, &data); - - if (stagingImageLayout.rowPitch == texWidth * 4) { - memcpy(data, pixels, (size_t) imageSize); - } else { - uint8_t* dataBytes = reinterpret_cast(data); - - for (int y = 0; y < texHeight; y++) { - memcpy(&dataBytes[y * stagingImageLayout.rowPitch], &pixels[y * texWidth * 4], texWidth * 4); - } - } - - vkUnmapMemory(device, stagingImageMemory); + vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(device, stagingBufferMemory); stbi_image_free(pixels); createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); - transitionImageLayout(stagingImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - copyImage(stagingImage, textureImage, texWidth, texHeight); - + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + copyBufferToImage(stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createTextureImageView() { - createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT, textureImageView); + textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); } void createTextureSampler() { @@ -852,12 +826,12 @@ class HelloTriangleApplication { samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - if (vkCreateSampler(device, &samplerInfo, nullptr, textureSampler.replace()) != VK_SUCCESS) { + if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { throw std::runtime_error("failed to create texture sampler!"); } } - void createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, VDeleter& imageView) { + VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) { VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; @@ -869,12 +843,15 @@ class HelloTriangleApplication { viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &viewInfo, nullptr, imageView.replace()) != VK_SUCCESS) { + VkImageView imageView; + if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { throw std::runtime_error("failed to create texture image view!"); } + + return imageView; } - void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& image, VDeleter& imageMemory) { + void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) { VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.imageType = VK_IMAGE_TYPE_2D; @@ -885,12 +862,12 @@ class HelloTriangleApplication { imageInfo.arrayLayers = 1; imageInfo.format = format; imageInfo.tiling = tiling; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.usage = usage; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateImage(device, &imageInfo, nullptr, image.replace()) != VK_SUCCESS) { + if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) { throw std::runtime_error("failed to create image!"); } @@ -902,7 +879,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, imageMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate image memory!"); } @@ -935,25 +912,34 @@ class HelloTriangleApplication { barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; - if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - } else if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; } else { throw std::invalid_argument("unsupported layout transition!"); } vkCmdPipelineBarrier( commandBuffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, @@ -963,30 +949,25 @@ class HelloTriangleApplication { endSingleTimeCommands(commandBuffer); } - void copyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height) { + void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { VkCommandBuffer commandBuffer = beginSingleTimeCommands(); - VkImageSubresourceLayers subResource = {}; - subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subResource.baseArrayLayer = 0; - subResource.mipLevel = 0; - subResource.layerCount = 1; - - VkImageCopy region = {}; - region.srcSubresource = subResource; - region.dstSubresource = subResource; - region.srcOffset = {0, 0, 0}; - region.dstOffset = {0, 0, 0}; - region.extent.width = width; - region.extent.height = height; - region.extent.depth = 1; - - vkCmdCopyImage( - commandBuffer, - srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ®ion - ); + VkBufferImageCopy region = {}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = { + width, + height, + 1 + }; + + vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); endSingleTimeCommands(commandBuffer); } @@ -994,8 +975,8 @@ class HelloTriangleApplication { void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -1006,13 +987,16 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createIndexBuffer() { VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -1023,13 +1007,14 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createUniformBuffer() { VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformStagingBuffer, uniformStagingBufferMemory); - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, uniformBuffer, uniformBufferMemory); + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffer, uniformBufferMemory); } void createDescriptorPool() { @@ -1041,11 +1026,11 @@ class HelloTriangleApplication { VkDescriptorPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.poolSizeCount = poolSizes.size(); + poolInfo.poolSizeCount = static_cast(poolSizes.size()); poolInfo.pPoolSizes = poolSizes.data(); poolInfo.maxSets = 1; - if (vkCreateDescriptorPool(device, &poolInfo, nullptr, descriptorPool.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor pool!"); } } @@ -1090,17 +1075,17 @@ class HelloTriangleApplication { descriptorWrites[1].descriptorCount = 1; descriptorWrites[1].pImageInfo = &imageInfo; - vkUpdateDescriptorSets(device, descriptorWrites.size(), descriptorWrites.data(), 0, nullptr); + vkUpdateDescriptorSets(device, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } - void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& buffer, VDeleter& bufferMemory) { + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = usage; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, buffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } @@ -1112,7 +1097,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, bufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } @@ -1176,10 +1161,6 @@ class HelloTriangleApplication { } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -1210,7 +1191,7 @@ class HelloTriangleApplication { clearValues[0].color = {0.0f, 0.0f, 0.0f, 1.0f}; clearValues[1].depthStencil = {1.0f, 0}; - renderPassInfo.clearValueCount = clearValues.size(); + renderPassInfo.clearValueCount = static_cast(clearValues.size()); renderPassInfo.pClearValues = clearValues.data(); vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); @@ -1225,7 +1206,7 @@ class HelloTriangleApplication { vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); - vkCmdDrawIndexed(commandBuffers[i], indices.size(), 1, 0, 0, 0); + vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffers[i]); @@ -1239,8 +1220,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -1253,17 +1234,15 @@ class HelloTriangleApplication { float time = std::chrono::duration_cast(currentTime - startTime).count() / 1000.0f; UniformBufferObject ubo = {}; - ubo.model = glm::rotate(glm::mat4(), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 10.0f); ubo.proj[1][1] *= -1; void* data; - vkMapMemory(device, uniformStagingBufferMemory, 0, sizeof(ubo), 0, &data); + vkMapMemory(device, uniformBufferMemory, 0, sizeof(ubo), 0, &data); memcpy(data, &ubo, sizeof(ubo)); - vkUnmapMemory(device, uniformStagingBufferMemory); - - copyBuffer(uniformStagingBuffer, uniformBuffer, sizeof(ubo)); + vkUnmapMemory(device, uniformBufferMemory); } void drawFrame() { @@ -1316,21 +1295,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -1368,7 +1348,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -1412,7 +1395,10 @@ class HelloTriangleApplication { swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); } - return indices.isComplete() && extensionsSupported && swapChainAdequate; + VkPhysicalDeviceFeatures supportedFeatures; + vkGetPhysicalDeviceFeatures(device, &supportedFeatures); + + return indices.isComplete() && extensionsSupported && supportedFeatures.samplerAnisotropy; } bool checkDeviceExtensionSupport(VkPhysicalDevice device) { diff --git a/code/descriptor_layout.cpp b/code/descriptor_layout.cpp index 6b40847a..43c45d59 100644 --- a/code/descriptor_layout.cpp +++ b/code/descriptor_layout.cpp @@ -6,11 +6,10 @@ #include #include -#include -#include -#include #include +#include #include +#include #include #include #include @@ -49,64 +48,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -175,49 +116,48 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter descriptorSetLayout{device, vkDestroyDescriptorSetLayout}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; - - VDeleter vertexBuffer{device, vkDestroyBuffer}; - VDeleter vertexBufferMemory{device, vkFreeMemory}; - VDeleter indexBuffer{device, vkDestroyBuffer}; - VDeleter indexBufferMemory{device, vkFreeMemory}; - - VDeleter uniformStagingBuffer{device, vkDestroyBuffer}; - VDeleter uniformStagingBufferMemory{device, vkFreeMemory}; - VDeleter uniformBuffer{device, vkDestroyBuffer}; - VDeleter uniformBufferMemory{device, vkFreeMemory}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; + + VkBuffer uniformBuffer; + VkDeviceMemory uniformBufferMemory; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -259,6 +199,48 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyBuffer(device, uniformBuffer, nullptr); + vkFreeMemory(device, uniformBufferMemory, nullptr); + + vkDestroyBuffer(device, indexBuffer, nullptr); + vkFreeMemory(device, indexBufferMemory, nullptr); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -267,7 +249,7 @@ class HelloTriangleApplication { static void onWindowResized(GLFWwindow* window, int width, int height) { if (width == 0 || height == 0) return; - + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->recreateSwapChain(); } @@ -275,6 +257,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -301,17 +285,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -324,13 +308,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -379,22 +363,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -441,16 +425,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -460,9 +438,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -478,7 +456,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -521,7 +499,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -539,7 +517,7 @@ class HelloTriangleApplication { layoutInfo.bindingCount = 1; layoutInfo.pBindings = &uboLayoutBinding; - if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, descriptorSetLayout.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor set layout!"); } } @@ -548,10 +526,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -574,7 +550,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -632,13 +608,12 @@ class HelloTriangleApplication { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - VkDescriptorSetLayout setLayouts[] = {descriptorSetLayout}; VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = setLayouts; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -657,13 +632,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -679,7 +657,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -692,7 +670,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics command pool!"); } } @@ -700,8 +678,8 @@ class HelloTriangleApplication { void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -712,13 +690,16 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createIndexBuffer() { VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -729,23 +710,24 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createUniformBuffer() { VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformStagingBuffer, uniformStagingBufferMemory); - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, uniformBuffer, uniformBufferMemory); + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffer, uniformBufferMemory); } - void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& buffer, VDeleter& bufferMemory) { + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = usage; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, buffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } @@ -757,7 +739,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, bufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } @@ -811,10 +793,6 @@ class HelloTriangleApplication { } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -855,7 +833,7 @@ class HelloTriangleApplication { vkCmdBindIndexBuffer(commandBuffers[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16); - vkCmdDrawIndexed(commandBuffers[i], indices.size(), 1, 0, 0, 0); + vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffers[i]); @@ -869,8 +847,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -883,17 +861,15 @@ class HelloTriangleApplication { float time = std::chrono::duration_cast(currentTime - startTime).count() / 1000.0f; UniformBufferObject ubo = {}; - ubo.model = glm::rotate(glm::mat4(), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 10.0f); ubo.proj[1][1] *= -1; void* data; - vkMapMemory(device, uniformStagingBufferMemory, 0, sizeof(ubo), 0, &data); + vkMapMemory(device, uniformBufferMemory, 0, sizeof(ubo), 0, &data); memcpy(data, &ubo, sizeof(ubo)); - vkUnmapMemory(device, uniformStagingBufferMemory); - - copyBuffer(uniformStagingBuffer, uniformBuffer, sizeof(ubo)); + vkUnmapMemory(device, uniformBufferMemory); } void drawFrame() { @@ -946,21 +922,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -998,7 +975,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -1172,4 +1152,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/descriptor_set.cpp b/code/descriptor_set.cpp index e66d8d90..df6ddbb2 100644 --- a/code/descriptor_set.cpp +++ b/code/descriptor_set.cpp @@ -6,11 +6,10 @@ #include #include -#include -#include -#include #include +#include #include +#include #include #include #include @@ -49,64 +48,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -175,52 +116,51 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter descriptorSetLayout{device, vkDestroyDescriptorSetLayout}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; - - VDeleter vertexBuffer{device, vkDestroyBuffer}; - VDeleter vertexBufferMemory{device, vkFreeMemory}; - VDeleter indexBuffer{device, vkDestroyBuffer}; - VDeleter indexBufferMemory{device, vkFreeMemory}; - - VDeleter uniformStagingBuffer{device, vkDestroyBuffer}; - VDeleter uniformStagingBufferMemory{device, vkFreeMemory}; - VDeleter uniformBuffer{device, vkDestroyBuffer}; - VDeleter uniformBufferMemory{device, vkFreeMemory}; - - VDeleter descriptorPool{device, vkDestroyDescriptorPool}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; + + VkBuffer uniformBuffer; + VkDeviceMemory uniformBufferMemory; + + VkDescriptorPool descriptorPool; VkDescriptorSet descriptorSet; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -264,6 +204,50 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyBuffer(device, uniformBuffer, nullptr); + vkFreeMemory(device, uniformBufferMemory, nullptr); + + vkDestroyBuffer(device, indexBuffer, nullptr); + vkFreeMemory(device, indexBufferMemory, nullptr); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -272,7 +256,7 @@ class HelloTriangleApplication { static void onWindowResized(GLFWwindow* window, int width, int height) { if (width == 0 || height == 0) return; - + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->recreateSwapChain(); } @@ -280,6 +264,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -306,17 +292,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -329,13 +315,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -384,22 +370,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -446,16 +432,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -465,9 +445,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -483,7 +463,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -526,7 +506,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -544,7 +524,7 @@ class HelloTriangleApplication { layoutInfo.bindingCount = 1; layoutInfo.pBindings = &uboLayoutBinding; - if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, descriptorSetLayout.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor set layout!"); } } @@ -553,10 +533,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -579,7 +557,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -637,13 +615,12 @@ class HelloTriangleApplication { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - VkDescriptorSetLayout setLayouts[] = {descriptorSetLayout}; VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = setLayouts; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -662,13 +639,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -684,7 +664,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -697,7 +677,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics command pool!"); } } @@ -705,8 +685,8 @@ class HelloTriangleApplication { void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -717,13 +697,16 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createIndexBuffer() { VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -734,13 +717,14 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createUniformBuffer() { VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformStagingBuffer, uniformStagingBufferMemory); - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, uniformBuffer, uniformBufferMemory); + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffer, uniformBufferMemory); } void createDescriptorPool() { @@ -754,7 +738,7 @@ class HelloTriangleApplication { poolInfo.pPoolSizes = &poolSize; poolInfo.maxSets = 1; - if (vkCreateDescriptorPool(device, &poolInfo, nullptr, descriptorPool.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor pool!"); } } @@ -788,14 +772,14 @@ class HelloTriangleApplication { vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr); } - void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& buffer, VDeleter& bufferMemory) { + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = usage; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, buffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } @@ -807,7 +791,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, bufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } @@ -861,10 +845,6 @@ class HelloTriangleApplication { } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -907,7 +887,7 @@ class HelloTriangleApplication { vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); - vkCmdDrawIndexed(commandBuffers[i], indices.size(), 1, 0, 0, 0); + vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffers[i]); @@ -921,8 +901,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -935,17 +915,15 @@ class HelloTriangleApplication { float time = std::chrono::duration_cast(currentTime - startTime).count() / 1000.0f; UniformBufferObject ubo = {}; - ubo.model = glm::rotate(glm::mat4(), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 10.0f); ubo.proj[1][1] *= -1; void* data; - vkMapMemory(device, uniformStagingBufferMemory, 0, sizeof(ubo), 0, &data); + vkMapMemory(device, uniformBufferMemory, 0, sizeof(ubo), 0, &data); memcpy(data, &ubo, sizeof(ubo)); - vkUnmapMemory(device, uniformStagingBufferMemory); - - copyBuffer(uniformStagingBuffer, uniformBuffer, sizeof(ubo)); + vkUnmapMemory(device, uniformBufferMemory); } void drawFrame() { @@ -998,21 +976,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -1050,7 +1029,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -1224,4 +1206,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/fixed_functions.cpp b/code/fixed_functions.cpp index 364c39b5..a70806ef 100644 --- a/code/fixed_functions.cpp +++ b/code/fixed_functions.cpp @@ -2,9 +2,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -43,64 +42,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -122,30 +63,29 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; + std::vector swapChainImageViews; + + VkPipelineLayout pipelineLayout; void initWindow() { glfwInit(); @@ -171,6 +111,20 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -195,17 +149,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -218,13 +172,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -273,22 +227,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -337,10 +291,10 @@ class HelloTriangleApplication { createInfo.oldSwapchain = VK_NULL_HANDLE; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -350,9 +304,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -368,7 +322,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -378,10 +332,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -460,25 +412,27 @@ class HelloTriangleApplication { pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -517,7 +471,7 @@ class HelloTriangleApplication { actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - + return actualExtent; } } @@ -687,4 +641,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/framebuffers.cpp b/code/framebuffers.cpp index 598ccc92..b03ec990 100644 --- a/code/framebuffers.cpp +++ b/code/framebuffers.cpp @@ -2,9 +2,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -43,64 +42,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -122,31 +63,32 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; void initWindow() { glfwInit(); @@ -174,6 +116,26 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -198,17 +160,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -221,13 +183,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -276,22 +238,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -340,10 +302,10 @@ class HelloTriangleApplication { createInfo.oldSwapchain = VK_NULL_HANDLE; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -353,9 +315,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -371,7 +333,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -404,7 +366,7 @@ class HelloTriangleApplication { renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -413,10 +375,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -495,8 +455,8 @@ class HelloTriangleApplication { pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -515,13 +475,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -537,25 +500,24 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -594,7 +556,7 @@ class HelloTriangleApplication { actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - + return actualExtent; } } @@ -764,4 +726,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/graphics_pipeline.cpp b/code/graphics_pipeline.cpp index 34b1d80c..5c4f8b4e 100644 --- a/code/graphics_pipeline.cpp +++ b/code/graphics_pipeline.cpp @@ -3,8 +3,6 @@ #include #include -#include -#include #include #include #include @@ -43,64 +41,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -122,26 +62,27 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; + std::vector swapChainImageViews; void initWindow() { glfwInit(); @@ -167,6 +108,18 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -191,17 +144,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -214,13 +167,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -269,22 +222,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -333,10 +286,10 @@ class HelloTriangleApplication { createInfo.oldSwapchain = VK_NULL_HANDLE; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -346,9 +299,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -364,8 +317,8 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { - throw std::runtime_error("failed to create image views"); + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create image views!"); } } } @@ -410,7 +363,7 @@ class HelloTriangleApplication { actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - + return actualExtent; } } @@ -562,4 +515,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/graphics_pipeline_complete.cpp b/code/graphics_pipeline_complete.cpp index c22f9892..0de2251b 100644 --- a/code/graphics_pipeline_complete.cpp +++ b/code/graphics_pipeline_complete.cpp @@ -2,9 +2,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -43,64 +42,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -122,31 +63,31 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; + std::vector swapChainImageViews; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; void initWindow() { glfwInit(); @@ -173,6 +114,22 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -197,17 +154,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -220,13 +177,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -275,22 +232,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -339,10 +296,10 @@ class HelloTriangleApplication { createInfo.oldSwapchain = VK_NULL_HANDLE; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -352,9 +309,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -370,7 +327,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -403,7 +360,7 @@ class HelloTriangleApplication { renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -412,10 +369,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -494,8 +449,8 @@ class HelloTriangleApplication { pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -514,24 +469,26 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -570,7 +527,7 @@ class HelloTriangleApplication { actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - + return actualExtent; } } @@ -740,4 +697,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/hello_triangle.cpp b/code/hello_triangle.cpp index d09876d5..f33e9d07 100644 --- a/code/hello_triangle.cpp +++ b/code/hello_triangle.cpp @@ -2,9 +2,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -43,64 +42,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -122,37 +63,38 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -186,6 +128,31 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanup() { + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -210,17 +177,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -233,13 +200,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -288,22 +255,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -352,7 +319,7 @@ class HelloTriangleApplication { createInfo.oldSwapchain = VK_NULL_HANDLE; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } @@ -365,9 +332,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -383,7 +350,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -426,7 +393,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -435,10 +402,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -518,7 +483,7 @@ class HelloTriangleApplication { pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -537,13 +502,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -559,7 +527,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -572,7 +540,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create command pool!"); } } @@ -621,13 +589,13 @@ class HelloTriangleApplication { } } } - + void createSemaphores() { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -670,21 +638,22 @@ class HelloTriangleApplication { presentInfo.pImageIndices = &imageIndex; vkQueuePresentKHR(presentQueue, &presentInfo); + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -893,4 +862,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/image_views.cpp b/code/image_views.cpp index 7bd4a6ef..b204e03a 100644 --- a/code/image_views.cpp +++ b/code/image_views.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -42,64 +41,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -121,26 +62,27 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; + std::vector swapChainImageViews; void initWindow() { glfwInit(); @@ -165,6 +107,18 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -189,17 +143,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -212,13 +166,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -267,22 +221,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -331,10 +285,10 @@ class HelloTriangleApplication { createInfo.oldSwapchain = VK_NULL_HANDLE; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -344,9 +298,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -362,7 +316,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -404,7 +358,7 @@ class HelloTriangleApplication { actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - + return actualExtent; } } @@ -556,4 +510,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/index_buffer.cpp b/code/index_buffer.cpp index ad87d999..5e2a8ba9 100644 --- a/code/index_buffer.cpp +++ b/code/index_buffer.cpp @@ -4,9 +4,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -46,64 +45,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -166,43 +107,44 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; - - VDeleter vertexBuffer{device, vkDestroyBuffer}; - VDeleter vertexBufferMemory{device, vkFreeMemory}; - VDeleter indexBuffer{device, vkDestroyBuffer}; - VDeleter indexBufferMemory{device, vkFreeMemory}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -240,6 +182,44 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroyBuffer(device, indexBuffer, nullptr); + vkFreeMemory(device, indexBufferMemory, nullptr); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -248,7 +228,7 @@ class HelloTriangleApplication { static void onWindowResized(GLFWwindow* window, int width, int height) { if (width == 0 || height == 0) return; - + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->recreateSwapChain(); } @@ -256,6 +236,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -282,17 +264,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -305,13 +287,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -360,22 +342,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -422,16 +404,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -441,9 +417,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -459,7 +435,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -502,7 +478,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -511,10 +487,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -537,7 +511,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -600,7 +574,7 @@ class HelloTriangleApplication { pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -619,13 +593,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -641,7 +618,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -654,7 +631,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics command pool!"); } } @@ -662,8 +639,8 @@ class HelloTriangleApplication { void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -674,13 +651,16 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createIndexBuffer() { VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -691,16 +671,19 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } - void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& buffer, VDeleter& bufferMemory) { + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = usage; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, buffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } @@ -712,7 +695,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, bufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } @@ -766,10 +749,6 @@ class HelloTriangleApplication { } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -810,7 +789,7 @@ class HelloTriangleApplication { vkCmdBindIndexBuffer(commandBuffers[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16); - vkCmdDrawIndexed(commandBuffers[i], indices.size(), 1, 0, 0, 0); + vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffers[i]); @@ -824,8 +803,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -881,21 +860,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -933,7 +913,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -1107,4 +1090,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/instance_creation.cpp b/code/instance_creation.cpp index 20c4a082..fa0bc6a4 100644 --- a/code/instance_creation.cpp +++ b/code/instance_creation.cpp @@ -3,81 +3,23 @@ #include #include -#include const int WIDTH = 800; const int HEIGHT = 600; -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - class HelloTriangleApplication { public: void run() { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; + VkInstance instance; void initWindow() { glfwInit(); @@ -96,6 +38,10 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -124,7 +70,7 @@ class HelloTriangleApplication { createInfo.enabledLayerCount = 0; - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } diff --git a/code/logical_device.cpp b/code/logical_device.cpp index 47f5e896..2304b87f 100644 --- a/code/logical_device.cpp +++ b/code/logical_device.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -36,64 +35,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; @@ -108,17 +49,17 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; @@ -142,6 +83,12 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -166,17 +113,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -189,7 +136,7 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } @@ -239,15 +186,15 @@ class HelloTriangleApplication { createInfo.pEnabledFeatures = &deviceFeatures; createInfo.enabledExtensionCount = 0; - + if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -346,4 +293,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/model_loading.cpp b/code/model_loading.cpp index 67d625e5..1bc280c7 100644 --- a/code/model_loading.cpp +++ b/code/model_loading.cpp @@ -14,11 +14,10 @@ #include #include -#include -#include -#include #include +#include #include +#include #include #include #include @@ -61,64 +60,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -194,63 +135,62 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - VDeleter swapChain{device, vkDestroySwapchainKHR}; + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter descriptorSetLayout{device, vkDestroyDescriptorSetLayout}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; + VkRenderPass renderPass; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; - VDeleter commandPool{device, vkDestroyCommandPool}; + VkCommandPool commandPool; - VDeleter depthImage{device, vkDestroyImage}; - VDeleter depthImageMemory{device, vkFreeMemory}; - VDeleter depthImageView{device, vkDestroyImageView}; + VkImage depthImage; + VkDeviceMemory depthImageMemory; + VkImageView depthImageView; - VDeleter textureImage{device, vkDestroyImage}; - VDeleter textureImageMemory{device, vkFreeMemory}; - VDeleter textureImageView{device, vkDestroyImageView}; - VDeleter textureSampler{device, vkDestroySampler}; + VkImage textureImage; + VkDeviceMemory textureImageMemory; + VkImageView textureImageView; + VkSampler textureSampler; std::vector vertices; std::vector indices; - VDeleter vertexBuffer{device, vkDestroyBuffer}; - VDeleter vertexBufferMemory{device, vkFreeMemory}; - VDeleter indexBuffer{device, vkDestroyBuffer}; - VDeleter indexBufferMemory{device, vkFreeMemory}; + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; - VDeleter uniformStagingBuffer{device, vkDestroyBuffer}; - VDeleter uniformStagingBufferMemory{device, vkFreeMemory}; - VDeleter uniformBuffer{device, vkDestroyBuffer}; - VDeleter uniformBufferMemory{device, vkFreeMemory}; + VkBuffer uniformBuffer; + VkDeviceMemory uniformBufferMemory; - VDeleter descriptorPool{device, vkDestroyDescriptorPool}; + VkDescriptorPool descriptorPool; VkDescriptorSet descriptorSet; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -299,6 +239,60 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + vkDestroyImageView(device, depthImageView, nullptr); + vkDestroyImage(device, depthImage, nullptr); + vkFreeMemory(device, depthImageMemory, nullptr); + + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroySampler(device, textureSampler, nullptr); + vkDestroyImageView(device, textureImageView, nullptr); + + vkDestroyImage(device, textureImage, nullptr); + vkFreeMemory(device, textureImageMemory, nullptr); + + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyBuffer(device, uniformBuffer, nullptr); + vkFreeMemory(device, uniformBufferMemory, nullptr); + + vkDestroyBuffer(device, indexBuffer, nullptr); + vkFreeMemory(device, indexBufferMemory, nullptr); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -315,6 +309,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -342,17 +338,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -365,13 +361,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -416,26 +412,27 @@ class HelloTriangleApplication { } VkPhysicalDeviceFeatures deviceFeatures = {}; + deviceFeatures.samplerAnisotropy = VK_TRUE; VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -482,16 +479,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -501,10 +492,10 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); for (uint32_t i = 0; i < swapChainImages.size(); i++) { - createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, swapChainImageViews[i]); + swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT); } } @@ -554,14 +545,14 @@ class HelloTriangleApplication { std::array attachments = {colorAttachment, depthAttachment}; VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = attachments.size(); + renderPassInfo.attachmentCount = static_cast(attachments.size()); renderPassInfo.pAttachments = attachments.data(); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -584,10 +575,10 @@ class HelloTriangleApplication { std::array bindings = {uboLayoutBinding, samplerLayoutBinding}; VkDescriptorSetLayoutCreateInfo layoutInfo = {}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = bindings.size(); + layoutInfo.bindingCount = static_cast(bindings.size()); layoutInfo.pBindings = bindings.data(); - if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, descriptorSetLayout.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor set layout!"); } } @@ -596,10 +587,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -622,7 +611,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -688,13 +677,12 @@ class HelloTriangleApplication { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - VkDescriptorSetLayout setLayouts[] = {descriptorSetLayout}; VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = setLayouts; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -714,13 +702,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { std::array attachments = { @@ -731,13 +722,13 @@ class HelloTriangleApplication { VkFramebufferCreateInfo framebufferInfo = {}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.renderPass = renderPass; - framebufferInfo.attachmentCount = attachments.size(); + framebufferInfo.attachmentCount = static_cast(attachments.size()); framebufferInfo.pAttachments = attachments.data(); framebufferInfo.width = swapChainExtent.width; framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -750,7 +741,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics command pool!"); } } @@ -759,7 +750,7 @@ class HelloTriangleApplication { VkFormat depthFormat = findDepthFormat(); createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory); - createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, depthImageView); + depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); } @@ -800,46 +791,29 @@ class HelloTriangleApplication { throw std::runtime_error("failed to load texture image!"); } - VDeleter stagingImage{device, vkDestroyImage}; - VDeleter stagingImageMemory{device, vkFreeMemory}; - createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingImage, stagingImageMemory); - - VkImageSubresource subresource = {}; - subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresource.mipLevel = 0; - subresource.arrayLayer = 0; - - VkSubresourceLayout stagingImageLayout; - vkGetImageSubresourceLayout(device, stagingImage, &subresource, &stagingImageLayout); + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; - vkMapMemory(device, stagingImageMemory, 0, imageSize, 0, &data); - - if (stagingImageLayout.rowPitch == texWidth * 4) { - memcpy(data, pixels, (size_t) imageSize); - } else { - uint8_t* dataBytes = reinterpret_cast(data); - - for (int y = 0; y < texHeight; y++) { - memcpy(&dataBytes[y * stagingImageLayout.rowPitch], &pixels[y * texWidth * 4], texWidth * 4); - } - } - - vkUnmapMemory(device, stagingImageMemory); + vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(device, stagingBufferMemory); stbi_image_free(pixels); createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); - transitionImageLayout(stagingImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - copyImage(stagingImage, textureImage, texWidth, texHeight); - + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + copyBufferToImage(stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createTextureImageView() { - createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT, textureImageView); + textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); } void createTextureSampler() { @@ -858,12 +832,12 @@ class HelloTriangleApplication { samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - if (vkCreateSampler(device, &samplerInfo, nullptr, textureSampler.replace()) != VK_SUCCESS) { + if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { throw std::runtime_error("failed to create texture sampler!"); } } - void createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, VDeleter& imageView) { + VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) { VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; @@ -875,12 +849,15 @@ class HelloTriangleApplication { viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &viewInfo, nullptr, imageView.replace()) != VK_SUCCESS) { + VkImageView imageView; + if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { throw std::runtime_error("failed to create texture image view!"); } + + return imageView; } - void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& image, VDeleter& imageMemory) { + void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) { VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.imageType = VK_IMAGE_TYPE_2D; @@ -891,12 +868,12 @@ class HelloTriangleApplication { imageInfo.arrayLayers = 1; imageInfo.format = format; imageInfo.tiling = tiling; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.usage = usage; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateImage(device, &imageInfo, nullptr, image.replace()) != VK_SUCCESS) { + if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) { throw std::runtime_error("failed to create image!"); } @@ -908,7 +885,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, imageMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate image memory!"); } @@ -941,25 +918,34 @@ class HelloTriangleApplication { barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; - if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - } else if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; } else { throw std::invalid_argument("unsupported layout transition!"); } vkCmdPipelineBarrier( commandBuffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, @@ -969,30 +955,25 @@ class HelloTriangleApplication { endSingleTimeCommands(commandBuffer); } - void copyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height) { + void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { VkCommandBuffer commandBuffer = beginSingleTimeCommands(); - VkImageSubresourceLayers subResource = {}; - subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subResource.baseArrayLayer = 0; - subResource.mipLevel = 0; - subResource.layerCount = 1; - - VkImageCopy region = {}; - region.srcSubresource = subResource; - region.dstSubresource = subResource; - region.srcOffset = {0, 0, 0}; - region.dstOffset = {0, 0, 0}; - region.extent.width = width; - region.extent.height = height; - region.extent.depth = 1; - - vkCmdCopyImage( - commandBuffer, - srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ®ion - ); + VkBufferImageCopy region = {}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = { + width, + height, + 1 + }; + + vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); endSingleTimeCommands(commandBuffer); } @@ -1007,7 +988,7 @@ class HelloTriangleApplication { throw std::runtime_error(err); } - std::unordered_map uniqueVertices = {}; + std::unordered_map uniqueVertices = {}; for (const auto& shape : shapes) { for (const auto& index : shape.mesh.indices) { @@ -1027,7 +1008,7 @@ class HelloTriangleApplication { vertex.color = {1.0f, 1.0f, 1.0f}; if (uniqueVertices.count(vertex) == 0) { - uniqueVertices[vertex] = vertices.size(); + uniqueVertices[vertex] = static_cast(vertices.size()); vertices.push_back(vertex); } @@ -1039,8 +1020,8 @@ class HelloTriangleApplication { void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -1051,13 +1032,16 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createIndexBuffer() { VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -1068,13 +1052,14 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createUniformBuffer() { VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformStagingBuffer, uniformStagingBufferMemory); - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, uniformBuffer, uniformBufferMemory); + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffer, uniformBufferMemory); } void createDescriptorPool() { @@ -1086,11 +1071,11 @@ class HelloTriangleApplication { VkDescriptorPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.poolSizeCount = poolSizes.size(); + poolInfo.poolSizeCount = static_cast(poolSizes.size()); poolInfo.pPoolSizes = poolSizes.data(); poolInfo.maxSets = 1; - if (vkCreateDescriptorPool(device, &poolInfo, nullptr, descriptorPool.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor pool!"); } } @@ -1135,17 +1120,17 @@ class HelloTriangleApplication { descriptorWrites[1].descriptorCount = 1; descriptorWrites[1].pImageInfo = &imageInfo; - vkUpdateDescriptorSets(device, descriptorWrites.size(), descriptorWrites.data(), 0, nullptr); + vkUpdateDescriptorSets(device, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } - void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& buffer, VDeleter& bufferMemory) { + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = usage; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, buffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } @@ -1157,7 +1142,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, bufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } @@ -1221,10 +1206,6 @@ class HelloTriangleApplication { } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -1255,7 +1236,7 @@ class HelloTriangleApplication { clearValues[0].color = {0.0f, 0.0f, 0.0f, 1.0f}; clearValues[1].depthStencil = {1.0f, 0}; - renderPassInfo.clearValueCount = clearValues.size(); + renderPassInfo.clearValueCount = static_cast(clearValues.size()); renderPassInfo.pClearValues = clearValues.data(); vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); @@ -1270,7 +1251,7 @@ class HelloTriangleApplication { vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); - vkCmdDrawIndexed(commandBuffers[i], indices.size(), 1, 0, 0, 0); + vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffers[i]); @@ -1284,8 +1265,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -1298,17 +1279,15 @@ class HelloTriangleApplication { float time = std::chrono::duration_cast(currentTime - startTime).count() / 1000.0f; UniformBufferObject ubo = {}; - ubo.model = glm::rotate(glm::mat4(), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 10.0f); ubo.proj[1][1] *= -1; void* data; - vkMapMemory(device, uniformStagingBufferMemory, 0, sizeof(ubo), 0, &data); + vkMapMemory(device, uniformBufferMemory, 0, sizeof(ubo), 0, &data); memcpy(data, &ubo, sizeof(ubo)); - vkUnmapMemory(device, uniformStagingBufferMemory); - - copyBuffer(uniformStagingBuffer, uniformBuffer, sizeof(ubo)); + vkUnmapMemory(device, uniformBufferMemory); } void drawFrame() { @@ -1361,21 +1340,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -1413,7 +1393,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -1457,7 +1440,10 @@ class HelloTriangleApplication { swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); } - return indices.isComplete() && extensionsSupported && swapChainAdequate; + VkPhysicalDeviceFeatures supportedFeatures; + vkGetPhysicalDeviceFeatures(device, &supportedFeatures); + + return indices.isComplete() && extensionsSupported && supportedFeatures.samplerAnisotropy; } bool checkDeviceExtensionSupport(VkPhysicalDevice device) { diff --git a/code/physical_device_selection.cpp b/code/physical_device_selection.cpp index 5c1d657b..13fa20d6 100644 --- a/code/physical_device_selection.cpp +++ b/code/physical_device_selection.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -36,64 +35,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; @@ -108,14 +49,14 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; @@ -138,6 +79,11 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -162,17 +108,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -185,7 +131,7 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } diff --git a/code/render_passes.cpp b/code/render_passes.cpp index d239acdf..b1039de6 100644 --- a/code/render_passes.cpp +++ b/code/render_passes.cpp @@ -2,9 +2,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -43,64 +42,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -122,30 +63,30 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; + std::vector swapChainImageViews; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; void initWindow() { glfwInit(); @@ -172,6 +113,21 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -196,17 +152,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -219,13 +175,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -274,22 +230,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -338,10 +294,10 @@ class HelloTriangleApplication { createInfo.oldSwapchain = VK_NULL_HANDLE; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -351,9 +307,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -369,7 +325,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -402,7 +358,7 @@ class HelloTriangleApplication { renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -411,10 +367,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -493,25 +447,27 @@ class HelloTriangleApplication { pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -550,7 +506,7 @@ class HelloTriangleApplication { actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - + return actualExtent; } } @@ -720,4 +676,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/sampler.cpp b/code/sampler.cpp index e24a4fa8..e9784b2d 100644 --- a/code/sampler.cpp +++ b/code/sampler.cpp @@ -9,11 +9,10 @@ #include #include -#include -#include -#include #include +#include #include +#include #include #include #include @@ -52,64 +51,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -178,57 +119,56 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter descriptorSetLayout{device, vkDestroyDescriptorSetLayout}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; - - VDeleter textureImage{device, vkDestroyImage}; - VDeleter textureImageMemory{device, vkFreeMemory}; - VDeleter textureImageView{device, vkDestroyImageView}; - VDeleter textureSampler{device, vkDestroySampler}; - - VDeleter vertexBuffer{device, vkDestroyBuffer}; - VDeleter vertexBufferMemory{device, vkFreeMemory}; - VDeleter indexBuffer{device, vkDestroyBuffer}; - VDeleter indexBufferMemory{device, vkFreeMemory}; - - VDeleter uniformStagingBuffer{device, vkDestroyBuffer}; - VDeleter uniformStagingBufferMemory{device, vkFreeMemory}; - VDeleter uniformBuffer{device, vkDestroyBuffer}; - VDeleter uniformBufferMemory{device, vkFreeMemory}; - - VDeleter descriptorPool{device, vkDestroyDescriptorPool}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; + + VkImage textureImage; + VkDeviceMemory textureImageMemory; + VkImageView textureImageView; + VkSampler textureSampler; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; + + VkBuffer uniformBuffer; + VkDeviceMemory uniformBufferMemory; + + VkDescriptorPool descriptorPool; VkDescriptorSet descriptorSet; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -275,6 +215,56 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroySampler(device, textureSampler, nullptr); + vkDestroyImageView(device, textureImageView, nullptr); + + vkDestroyImage(device, textureImage, nullptr); + vkFreeMemory(device, textureImageMemory, nullptr); + + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyBuffer(device, uniformBuffer, nullptr); + vkFreeMemory(device, uniformBufferMemory, nullptr); + + vkDestroyBuffer(device, indexBuffer, nullptr); + vkFreeMemory(device, indexBufferMemory, nullptr); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -283,7 +273,7 @@ class HelloTriangleApplication { static void onWindowResized(GLFWwindow* window, int width, int height) { if (width == 0 || height == 0) return; - + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->recreateSwapChain(); } @@ -291,6 +281,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -317,17 +309,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -340,13 +332,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -391,26 +383,27 @@ class HelloTriangleApplication { } VkPhysicalDeviceFeatures deviceFeatures = {}; + deviceFeatures.samplerAnisotropy = VK_TRUE; VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -457,16 +450,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -476,10 +463,10 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { - createImageView(swapChainImages[i], swapChainImageFormat, swapChainImageViews[i]); + for (size_t i = 0; i < swapChainImages.size(); i++) { + swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat); } } @@ -520,7 +507,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -538,7 +525,7 @@ class HelloTriangleApplication { layoutInfo.bindingCount = 1; layoutInfo.pBindings = &uboLayoutBinding; - if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, descriptorSetLayout.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor set layout!"); } } @@ -547,10 +534,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -573,7 +558,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -631,13 +616,12 @@ class HelloTriangleApplication { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - VkDescriptorSetLayout setLayouts[] = {descriptorSetLayout}; VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = setLayouts; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -656,13 +640,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -678,7 +665,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -691,7 +678,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics command pool!"); } } @@ -705,46 +692,29 @@ class HelloTriangleApplication { throw std::runtime_error("failed to load texture image!"); } - VDeleter stagingImage{device, vkDestroyImage}; - VDeleter stagingImageMemory{device, vkFreeMemory}; - createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingImage, stagingImageMemory); - - VkImageSubresource subresource = {}; - subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresource.mipLevel = 0; - subresource.arrayLayer = 0; - - VkSubresourceLayout stagingImageLayout; - vkGetImageSubresourceLayout(device, stagingImage, &subresource, &stagingImageLayout); + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; - vkMapMemory(device, stagingImageMemory, 0, imageSize, 0, &data); - - if (stagingImageLayout.rowPitch == texWidth * 4) { - memcpy(data, pixels, (size_t) imageSize); - } else { - uint8_t* dataBytes = reinterpret_cast(data); - - for (int y = 0; y < texHeight; y++) { - memcpy(&dataBytes[y * stagingImageLayout.rowPitch], &pixels[y * texWidth * 4], texWidth * 4); - } - } + vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(device, stagingBufferMemory); - vkUnmapMemory(device, stagingImageMemory); - stbi_image_free(pixels); createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); - transitionImageLayout(stagingImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - copyImage(stagingImage, textureImage, texWidth, texHeight); - + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + copyBufferToImage(stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createTextureImageView() { - createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, textureImageView); + textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM); } void createTextureSampler() { @@ -763,12 +733,12 @@ class HelloTriangleApplication { samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - if (vkCreateSampler(device, &samplerInfo, nullptr, textureSampler.replace()) != VK_SUCCESS) { + if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { throw std::runtime_error("failed to create texture sampler!"); } } - void createImageView(VkImage image, VkFormat format, VDeleter& imageView) { + VkImageView createImageView(VkImage image, VkFormat format) { VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; @@ -780,12 +750,15 @@ class HelloTriangleApplication { viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &viewInfo, nullptr, imageView.replace()) != VK_SUCCESS) { + VkImageView imageView; + if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { throw std::runtime_error("failed to create texture image view!"); } + + return imageView; } - void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& image, VDeleter& imageMemory) { + void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) { VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.imageType = VK_IMAGE_TYPE_2D; @@ -796,12 +769,12 @@ class HelloTriangleApplication { imageInfo.arrayLayers = 1; imageInfo.format = format; imageInfo.tiling = tiling; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.usage = usage; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateImage(device, &imageInfo, nullptr, image.replace()) != VK_SUCCESS) { + if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) { throw std::runtime_error("failed to create image!"); } @@ -813,7 +786,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, imageMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate image memory!"); } @@ -836,22 +809,28 @@ class HelloTriangleApplication { barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; - if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - } else if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } else { throw std::invalid_argument("unsupported layout transition!"); } - + vkCmdPipelineBarrier( commandBuffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, @@ -861,30 +840,25 @@ class HelloTriangleApplication { endSingleTimeCommands(commandBuffer); } - void copyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height) { + void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { VkCommandBuffer commandBuffer = beginSingleTimeCommands(); - VkImageSubresourceLayers subResource = {}; - subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subResource.baseArrayLayer = 0; - subResource.mipLevel = 0; - subResource.layerCount = 1; - - VkImageCopy region = {}; - region.srcSubresource = subResource; - region.dstSubresource = subResource; - region.srcOffset = {0, 0, 0}; - region.dstOffset = {0, 0, 0}; - region.extent.width = width; - region.extent.height = height; - region.extent.depth = 1; - - vkCmdCopyImage( - commandBuffer, - srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ®ion - ); + VkBufferImageCopy region = {}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = { + width, + height, + 1 + }; + + vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); endSingleTimeCommands(commandBuffer); } @@ -892,8 +866,8 @@ class HelloTriangleApplication { void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -904,13 +878,16 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createIndexBuffer() { VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -921,13 +898,14 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createUniformBuffer() { VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformStagingBuffer, uniformStagingBufferMemory); - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, uniformBuffer, uniformBufferMemory); + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffer, uniformBufferMemory); } void createDescriptorPool() { @@ -941,7 +919,7 @@ class HelloTriangleApplication { poolInfo.pPoolSizes = &poolSize; poolInfo.maxSets = 1; - if (vkCreateDescriptorPool(device, &poolInfo, nullptr, descriptorPool.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor pool!"); } } @@ -975,14 +953,14 @@ class HelloTriangleApplication { vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr); } - void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& buffer, VDeleter& bufferMemory) { + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = usage; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, buffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } @@ -994,7 +972,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, bufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } @@ -1058,10 +1036,6 @@ class HelloTriangleApplication { } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -1104,7 +1078,7 @@ class HelloTriangleApplication { vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); - vkCmdDrawIndexed(commandBuffers[i], indices.size(), 1, 0, 0, 0); + vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffers[i]); @@ -1118,8 +1092,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -1132,17 +1106,15 @@ class HelloTriangleApplication { float time = std::chrono::duration_cast(currentTime - startTime).count() / 1000.0f; UniformBufferObject ubo = {}; - ubo.model = glm::rotate(glm::mat4(), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 10.0f); ubo.proj[1][1] *= -1; void* data; - vkMapMemory(device, uniformStagingBufferMemory, 0, sizeof(ubo), 0, &data); + vkMapMemory(device, uniformBufferMemory, 0, sizeof(ubo), 0, &data); memcpy(data, &ubo, sizeof(ubo)); - vkUnmapMemory(device, uniformStagingBufferMemory); - - copyBuffer(uniformStagingBuffer, uniformBuffer, sizeof(ubo)); + vkUnmapMemory(device, uniformBufferMemory); } void drawFrame() { @@ -1195,21 +1167,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -1247,7 +1220,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -1291,7 +1267,10 @@ class HelloTriangleApplication { swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); } - return indices.isComplete() && extensionsSupported && swapChainAdequate; + VkPhysicalDeviceFeatures supportedFeatures; + vkGetPhysicalDeviceFeatures(device, &supportedFeatures); + + return indices.isComplete() && extensionsSupported && supportedFeatures.samplerAnisotropy; } bool checkDeviceExtensionSupport(VkPhysicalDevice device) { @@ -1421,4 +1400,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/shader_modules.cpp b/code/shader_modules.cpp index 982f1905..b08a7482 100644 --- a/code/shader_modules.cpp +++ b/code/shader_modules.cpp @@ -2,9 +2,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -43,64 +42,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -122,26 +63,27 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; + std::vector swapChainImageViews; void initWindow() { glfwInit(); @@ -167,6 +109,18 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -191,17 +145,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -214,13 +168,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -269,22 +223,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -333,10 +287,10 @@ class HelloTriangleApplication { createInfo.oldSwapchain = VK_NULL_HANDLE; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -346,9 +300,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -364,7 +318,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -374,10 +328,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -392,21 +344,23 @@ class HelloTriangleApplication { fragShaderStageInfo.pName = "main"; VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -445,7 +399,7 @@ class HelloTriangleApplication { actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - + return actualExtent; } } @@ -615,4 +569,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/staging_buffer.cpp b/code/staging_buffer.cpp index 63ef25d5..d5e086d3 100644 --- a/code/staging_buffer.cpp +++ b/code/staging_buffer.cpp @@ -4,9 +4,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -46,64 +45,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -161,41 +102,42 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; - - VDeleter vertexBuffer{device, vkDestroyBuffer}; - VDeleter vertexBufferMemory{device, vkFreeMemory}; - + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -232,6 +174,41 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -240,7 +217,7 @@ class HelloTriangleApplication { static void onWindowResized(GLFWwindow* window, int width, int height) { if (width == 0 || height == 0) return; - + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->recreateSwapChain(); } @@ -248,6 +225,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -274,17 +253,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -297,13 +276,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -352,22 +331,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -414,16 +393,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -433,9 +406,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -451,7 +424,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -494,7 +467,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -503,10 +476,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -529,7 +500,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -592,7 +563,7 @@ class HelloTriangleApplication { pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -611,13 +582,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -633,7 +607,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -646,7 +620,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics command pool!"); } } @@ -654,8 +628,8 @@ class HelloTriangleApplication { void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -666,16 +640,19 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } - void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& buffer, VDeleter& bufferMemory) { + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = usage; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, buffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } @@ -687,7 +664,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, bufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } @@ -741,10 +718,6 @@ class HelloTriangleApplication { } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -783,7 +756,7 @@ class HelloTriangleApplication { VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); - vkCmdDraw(commandBuffers[i], vertices.size(), 1, 0, 0); + vkCmdDraw(commandBuffers[i], static_cast(vertices.size()), 1, 0, 0); vkCmdEndRenderPass(commandBuffers[i]); @@ -797,8 +770,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -854,21 +827,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -906,7 +880,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -1080,4 +1057,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/swap_chain_creation.cpp b/code/swap_chain_creation.cpp index 954e6584..07e420b0 100644 --- a/code/swap_chain_creation.cpp +++ b/code/swap_chain_creation.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -42,64 +41,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -121,22 +62,23 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; @@ -163,6 +105,14 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -187,17 +137,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -210,13 +160,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -265,22 +215,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -329,10 +279,10 @@ class HelloTriangleApplication { createInfo.oldSwapchain = VK_NULL_HANDLE; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -377,7 +327,7 @@ class HelloTriangleApplication { actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - + return actualExtent; } } @@ -529,4 +479,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/swap_chain_recreation.cpp b/code/swap_chain_recreation.cpp index 21f09e57..a0755615 100644 --- a/code/swap_chain_recreation.cpp +++ b/code/swap_chain_recreation.cpp @@ -2,9 +2,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -43,64 +42,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -122,37 +63,38 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -188,6 +130,38 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -196,7 +170,7 @@ class HelloTriangleApplication { static void onWindowResized(GLFWwindow* window, int width, int height) { if (width == 0 || height == 0) return; - + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->recreateSwapChain(); } @@ -204,6 +178,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -230,17 +206,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -253,13 +229,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -308,22 +284,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -370,16 +346,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -389,9 +359,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -407,7 +377,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -450,7 +420,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -459,10 +429,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -542,7 +510,7 @@ class HelloTriangleApplication { pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -561,13 +529,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -583,7 +554,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -596,16 +567,12 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create command pool!"); } } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -649,13 +616,13 @@ class HelloTriangleApplication { } } } - + void createSemaphores() { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -711,21 +678,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -763,7 +731,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -937,4 +908,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/texture_image.cpp b/code/texture_image.cpp index e1fdfff8..036f0457 100644 --- a/code/texture_image.cpp +++ b/code/texture_image.cpp @@ -9,11 +9,10 @@ #include #include -#include -#include -#include #include +#include #include +#include #include #include #include @@ -52,64 +51,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -178,55 +119,54 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter descriptorSetLayout{device, vkDestroyDescriptorSetLayout}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; - - VDeleter textureImage{device, vkDestroyImage}; - VDeleter textureImageMemory{device, vkFreeMemory}; - - VDeleter vertexBuffer{device, vkDestroyBuffer}; - VDeleter vertexBufferMemory{device, vkFreeMemory}; - VDeleter indexBuffer{device, vkDestroyBuffer}; - VDeleter indexBufferMemory{device, vkFreeMemory}; - - VDeleter uniformStagingBuffer{device, vkDestroyBuffer}; - VDeleter uniformStagingBufferMemory{device, vkFreeMemory}; - VDeleter uniformBuffer{device, vkDestroyBuffer}; - VDeleter uniformBufferMemory{device, vkFreeMemory}; - - VDeleter descriptorPool{device, vkDestroyDescriptorPool}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; + + VkImage textureImage; + VkDeviceMemory textureImageMemory; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; + + VkBuffer uniformBuffer; + VkDeviceMemory uniformBufferMemory; + + VkDescriptorPool descriptorPool; VkDescriptorSet descriptorSet; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -271,6 +211,53 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroyImage(device, textureImage, nullptr); + vkFreeMemory(device, textureImageMemory, nullptr); + + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyBuffer(device, uniformBuffer, nullptr); + vkFreeMemory(device, uniformBufferMemory, nullptr); + + vkDestroyBuffer(device, indexBuffer, nullptr); + vkFreeMemory(device, indexBufferMemory, nullptr); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -279,7 +266,7 @@ class HelloTriangleApplication { static void onWindowResized(GLFWwindow* window, int width, int height) { if (width == 0 || height == 0) return; - + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->recreateSwapChain(); } @@ -287,6 +274,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -313,17 +302,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -336,13 +325,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -391,22 +380,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -453,16 +442,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -472,9 +455,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -490,7 +473,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -533,7 +516,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -551,7 +534,7 @@ class HelloTriangleApplication { layoutInfo.bindingCount = 1; layoutInfo.pBindings = &uboLayoutBinding; - if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, descriptorSetLayout.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor set layout!"); } } @@ -560,10 +543,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -586,7 +567,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -644,13 +625,12 @@ class HelloTriangleApplication { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - VkDescriptorSetLayout setLayouts[] = {descriptorSetLayout}; VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = setLayouts; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -669,13 +649,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -691,7 +674,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -704,7 +687,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics command pool!"); } } @@ -718,45 +701,28 @@ class HelloTriangleApplication { throw std::runtime_error("failed to load texture image!"); } - VDeleter stagingImage{device, vkDestroyImage}; - VDeleter stagingImageMemory{device, vkFreeMemory}; - createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingImage, stagingImageMemory); - - VkImageSubresource subresource = {}; - subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresource.mipLevel = 0; - subresource.arrayLayer = 0; - - VkSubresourceLayout stagingImageLayout; - vkGetImageSubresourceLayout(device, stagingImage, &subresource, &stagingImageLayout); + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; - vkMapMemory(device, stagingImageMemory, 0, imageSize, 0, &data); - - if (stagingImageLayout.rowPitch == texWidth * 4) { - memcpy(data, pixels, (size_t) imageSize); - } else { - uint8_t* dataBytes = reinterpret_cast(data); - - for (int y = 0; y < texHeight; y++) { - memcpy(&dataBytes[y * stagingImageLayout.rowPitch], &pixels[y * texWidth * 4], texWidth * 4); - } - } + vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(device, stagingBufferMemory); - vkUnmapMemory(device, stagingImageMemory); - stbi_image_free(pixels); createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); - transitionImageLayout(stagingImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - copyImage(stagingImage, textureImage, texWidth, texHeight); - + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + copyBufferToImage(stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } - void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& image, VDeleter& imageMemory) { + void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) { VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.imageType = VK_IMAGE_TYPE_2D; @@ -767,12 +733,12 @@ class HelloTriangleApplication { imageInfo.arrayLayers = 1; imageInfo.format = format; imageInfo.tiling = tiling; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.usage = usage; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateImage(device, &imageInfo, nullptr, image.replace()) != VK_SUCCESS) { + if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) { throw std::runtime_error("failed to create image!"); } @@ -784,7 +750,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, imageMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate image memory!"); } @@ -807,22 +773,28 @@ class HelloTriangleApplication { barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; - if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - } else if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } else { throw std::invalid_argument("unsupported layout transition!"); } - + vkCmdPipelineBarrier( commandBuffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, @@ -832,30 +804,25 @@ class HelloTriangleApplication { endSingleTimeCommands(commandBuffer); } - void copyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height) { + void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { VkCommandBuffer commandBuffer = beginSingleTimeCommands(); - VkImageSubresourceLayers subResource = {}; - subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subResource.baseArrayLayer = 0; - subResource.mipLevel = 0; - subResource.layerCount = 1; - - VkImageCopy region = {}; - region.srcSubresource = subResource; - region.dstSubresource = subResource; - region.srcOffset = {0, 0, 0}; - region.dstOffset = {0, 0, 0}; - region.extent.width = width; - region.extent.height = height; - region.extent.depth = 1; - - vkCmdCopyImage( - commandBuffer, - srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ®ion - ); + VkBufferImageCopy region = {}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = { + width, + height, + 1 + }; + + vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); endSingleTimeCommands(commandBuffer); } @@ -863,8 +830,8 @@ class HelloTriangleApplication { void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -875,13 +842,16 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createIndexBuffer() { VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -892,13 +862,14 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createUniformBuffer() { VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformStagingBuffer, uniformStagingBufferMemory); - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, uniformBuffer, uniformBufferMemory); + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffer, uniformBufferMemory); } void createDescriptorPool() { @@ -912,7 +883,7 @@ class HelloTriangleApplication { poolInfo.pPoolSizes = &poolSize; poolInfo.maxSets = 1; - if (vkCreateDescriptorPool(device, &poolInfo, nullptr, descriptorPool.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor pool!"); } } @@ -946,14 +917,14 @@ class HelloTriangleApplication { vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr); } - void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& buffer, VDeleter& bufferMemory) { + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = usage; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, buffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } @@ -965,7 +936,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, bufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } @@ -1029,10 +1000,6 @@ class HelloTriangleApplication { } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -1075,7 +1042,7 @@ class HelloTriangleApplication { vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); - vkCmdDrawIndexed(commandBuffers[i], indices.size(), 1, 0, 0, 0); + vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffers[i]); @@ -1089,8 +1056,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -1103,17 +1070,15 @@ class HelloTriangleApplication { float time = std::chrono::duration_cast(currentTime - startTime).count() / 1000.0f; UniformBufferObject ubo = {}; - ubo.model = glm::rotate(glm::mat4(), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 10.0f); ubo.proj[1][1] *= -1; void* data; - vkMapMemory(device, uniformStagingBufferMemory, 0, sizeof(ubo), 0, &data); + vkMapMemory(device, uniformBufferMemory, 0, sizeof(ubo), 0, &data); memcpy(data, &ubo, sizeof(ubo)); - vkUnmapMemory(device, uniformStagingBufferMemory); - - copyBuffer(uniformStagingBuffer, uniformBuffer, sizeof(ubo)); + vkUnmapMemory(device, uniformBufferMemory); } void drawFrame() { @@ -1166,21 +1131,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -1218,7 +1184,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -1392,4 +1361,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/texture_mapping.cpp b/code/texture_mapping.cpp index 89ca8d41..d2e9711c 100644 --- a/code/texture_mapping.cpp +++ b/code/texture_mapping.cpp @@ -9,11 +9,10 @@ #include #include -#include -#include -#include #include +#include #include +#include #include #include #include @@ -52,64 +51,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -168,10 +109,10 @@ struct UniformBufferObject { }; const std::vector vertices = { - {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, - {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, - {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}} + {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}, + {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, + {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}} }; const std::vector indices = { @@ -184,57 +125,56 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter descriptorSetLayout{device, vkDestroyDescriptorSetLayout}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; - - VDeleter textureImage{device, vkDestroyImage}; - VDeleter textureImageMemory{device, vkFreeMemory}; - VDeleter textureImageView{device, vkDestroyImageView}; - VDeleter textureSampler{device, vkDestroySampler}; - - VDeleter vertexBuffer{device, vkDestroyBuffer}; - VDeleter vertexBufferMemory{device, vkFreeMemory}; - VDeleter indexBuffer{device, vkDestroyBuffer}; - VDeleter indexBufferMemory{device, vkFreeMemory}; - - VDeleter uniformStagingBuffer{device, vkDestroyBuffer}; - VDeleter uniformStagingBufferMemory{device, vkFreeMemory}; - VDeleter uniformBuffer{device, vkDestroyBuffer}; - VDeleter uniformBufferMemory{device, vkFreeMemory}; - - VDeleter descriptorPool{device, vkDestroyDescriptorPool}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; + + VkImage textureImage; + VkDeviceMemory textureImageMemory; + VkImageView textureImageView; + VkSampler textureSampler; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; + + VkBuffer uniformBuffer; + VkDeviceMemory uniformBufferMemory; + + VkDescriptorPool descriptorPool; VkDescriptorSet descriptorSet; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -281,6 +221,56 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroySampler(device, textureSampler, nullptr); + vkDestroyImageView(device, textureImageView, nullptr); + + vkDestroyImage(device, textureImage, nullptr); + vkFreeMemory(device, textureImageMemory, nullptr); + + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyBuffer(device, uniformBuffer, nullptr); + vkFreeMemory(device, uniformBufferMemory, nullptr); + + vkDestroyBuffer(device, indexBuffer, nullptr); + vkFreeMemory(device, indexBufferMemory, nullptr); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -289,7 +279,7 @@ class HelloTriangleApplication { static void onWindowResized(GLFWwindow* window, int width, int height) { if (width == 0 || height == 0) return; - + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->recreateSwapChain(); } @@ -297,6 +287,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -323,17 +315,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -346,13 +338,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -397,26 +389,27 @@ class HelloTriangleApplication { } VkPhysicalDeviceFeatures deviceFeatures = {}; + deviceFeatures.samplerAnisotropy = VK_TRUE; VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -463,16 +456,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -482,10 +469,10 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { - createImageView(swapChainImages[i], swapChainImageFormat, swapChainImageViews[i]); + for (size_t i = 0; i < swapChainImages.size(); i++) { + swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat); } } @@ -526,7 +513,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -545,14 +532,14 @@ class HelloTriangleApplication { samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; samplerLayoutBinding.pImmutableSamplers = nullptr; samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - + std::array bindings = {uboLayoutBinding, samplerLayoutBinding}; VkDescriptorSetLayoutCreateInfo layoutInfo = {}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = bindings.size(); + layoutInfo.bindingCount = static_cast(bindings.size()); layoutInfo.pBindings = bindings.data(); - if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, descriptorSetLayout.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor set layout!"); } } @@ -561,10 +548,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -587,7 +572,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -645,13 +630,12 @@ class HelloTriangleApplication { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - VkDescriptorSetLayout setLayouts[] = {descriptorSetLayout}; VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = setLayouts; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -670,13 +654,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -692,7 +679,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -705,7 +692,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics command pool!"); } } @@ -719,46 +706,29 @@ class HelloTriangleApplication { throw std::runtime_error("failed to load texture image!"); } - VDeleter stagingImage{device, vkDestroyImage}; - VDeleter stagingImageMemory{device, vkFreeMemory}; - createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingImage, stagingImageMemory); - - VkImageSubresource subresource = {}; - subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresource.mipLevel = 0; - subresource.arrayLayer = 0; - - VkSubresourceLayout stagingImageLayout; - vkGetImageSubresourceLayout(device, stagingImage, &subresource, &stagingImageLayout); + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; - vkMapMemory(device, stagingImageMemory, 0, imageSize, 0, &data); - - if (stagingImageLayout.rowPitch == texWidth * 4) { - memcpy(data, pixels, (size_t) imageSize); - } else { - uint8_t* dataBytes = reinterpret_cast(data); - - for (int y = 0; y < texHeight; y++) { - memcpy(&dataBytes[y * stagingImageLayout.rowPitch], &pixels[y * texWidth * 4], texWidth * 4); - } - } + vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(device, stagingBufferMemory); - vkUnmapMemory(device, stagingImageMemory); - stbi_image_free(pixels); createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); - transitionImageLayout(stagingImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - copyImage(stagingImage, textureImage, texWidth, texHeight); - + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + copyBufferToImage(stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createTextureImageView() { - createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, textureImageView); + textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM); } void createTextureSampler() { @@ -777,12 +747,12 @@ class HelloTriangleApplication { samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - if (vkCreateSampler(device, &samplerInfo, nullptr, textureSampler.replace()) != VK_SUCCESS) { + if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { throw std::runtime_error("failed to create texture sampler!"); } } - void createImageView(VkImage image, VkFormat format, VDeleter& imageView) { + VkImageView createImageView(VkImage image, VkFormat format) { VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; @@ -794,12 +764,15 @@ class HelloTriangleApplication { viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &viewInfo, nullptr, imageView.replace()) != VK_SUCCESS) { + VkImageView imageView; + if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { throw std::runtime_error("failed to create texture image view!"); } + + return imageView; } - void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& image, VDeleter& imageMemory) { + void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) { VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.imageType = VK_IMAGE_TYPE_2D; @@ -810,12 +783,12 @@ class HelloTriangleApplication { imageInfo.arrayLayers = 1; imageInfo.format = format; imageInfo.tiling = tiling; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.usage = usage; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateImage(device, &imageInfo, nullptr, image.replace()) != VK_SUCCESS) { + if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) { throw std::runtime_error("failed to create image!"); } @@ -827,7 +800,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, imageMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate image memory!"); } @@ -850,22 +823,28 @@ class HelloTriangleApplication { barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; - if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - } else if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } else { throw std::invalid_argument("unsupported layout transition!"); } - + vkCmdPipelineBarrier( commandBuffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, @@ -875,30 +854,25 @@ class HelloTriangleApplication { endSingleTimeCommands(commandBuffer); } - void copyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height) { + void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { VkCommandBuffer commandBuffer = beginSingleTimeCommands(); - VkImageSubresourceLayers subResource = {}; - subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subResource.baseArrayLayer = 0; - subResource.mipLevel = 0; - subResource.layerCount = 1; - - VkImageCopy region = {}; - region.srcSubresource = subResource; - region.dstSubresource = subResource; - region.srcOffset = {0, 0, 0}; - region.dstOffset = {0, 0, 0}; - region.extent.width = width; - region.extent.height = height; - region.extent.depth = 1; - - vkCmdCopyImage( - commandBuffer, - srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ®ion - ); + VkBufferImageCopy region = {}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = { + width, + height, + 1 + }; + + vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); endSingleTimeCommands(commandBuffer); } @@ -906,8 +880,8 @@ class HelloTriangleApplication { void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -918,13 +892,16 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createIndexBuffer() { VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - VDeleter stagingBuffer{device, vkDestroyBuffer}; - VDeleter stagingBufferMemory{device, vkFreeMemory}; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; @@ -935,13 +912,14 @@ class HelloTriangleApplication { createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); } void createUniformBuffer() { VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformStagingBuffer, uniformStagingBufferMemory); - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, uniformBuffer, uniformBufferMemory); + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffer, uniformBufferMemory); } void createDescriptorPool() { @@ -953,11 +931,11 @@ class HelloTriangleApplication { VkDescriptorPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.poolSizeCount = poolSizes.size(); + poolInfo.poolSizeCount = static_cast(poolSizes.size()); poolInfo.pPoolSizes = poolSizes.data(); poolInfo.maxSets = 1; - if (vkCreateDescriptorPool(device, &poolInfo, nullptr, descriptorPool.replace()) != VK_SUCCESS) { + if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor pool!"); } } @@ -1002,17 +980,17 @@ class HelloTriangleApplication { descriptorWrites[1].descriptorCount = 1; descriptorWrites[1].pImageInfo = &imageInfo; - vkUpdateDescriptorSets(device, descriptorWrites.size(), descriptorWrites.data(), 0, nullptr); + vkUpdateDescriptorSets(device, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } - void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& buffer, VDeleter& bufferMemory) { + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = usage; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, buffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } @@ -1024,7 +1002,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - if (vkAllocateMemory(device, &allocInfo, nullptr, bufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } @@ -1088,10 +1066,6 @@ class HelloTriangleApplication { } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -1134,7 +1108,7 @@ class HelloTriangleApplication { vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); - vkCmdDrawIndexed(commandBuffers[i], indices.size(), 1, 0, 0, 0); + vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffers[i]); @@ -1148,8 +1122,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -1162,17 +1136,15 @@ class HelloTriangleApplication { float time = std::chrono::duration_cast(currentTime - startTime).count() / 1000.0f; UniformBufferObject ubo = {}; - ubo.model = glm::rotate(glm::mat4(), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 10.0f); ubo.proj[1][1] *= -1; void* data; - vkMapMemory(device, uniformStagingBufferMemory, 0, sizeof(ubo), 0, &data); + vkMapMemory(device, uniformBufferMemory, 0, sizeof(ubo), 0, &data); memcpy(data, &ubo, sizeof(ubo)); - vkUnmapMemory(device, uniformStagingBufferMemory); - - copyBuffer(uniformStagingBuffer, uniformBuffer, sizeof(ubo)); + vkUnmapMemory(device, uniformBufferMemory); } void drawFrame() { @@ -1225,21 +1197,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -1277,7 +1250,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -1321,7 +1297,10 @@ class HelloTriangleApplication { swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); } - return indices.isComplete() && extensionsSupported && swapChainAdequate; + VkPhysicalDeviceFeatures supportedFeatures; + vkGetPhysicalDeviceFeatures(device, &supportedFeatures); + + return indices.isComplete() && extensionsSupported && supportedFeatures.samplerAnisotropy; } bool checkDeviceExtensionSupport(VkPhysicalDevice device) { @@ -1451,4 +1430,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/validation_layers.cpp b/code/validation_layers.cpp index 0dd637ec..05c1019b 100644 --- a/code/validation_layers.cpp +++ b/code/validation_layers.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -36,77 +35,20 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - class HelloTriangleApplication { public: void run() { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; + VkInstance instance; + VkDebugReportCallbackEXT callback; void initWindow() { glfwInit(); @@ -126,6 +68,11 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -150,17 +97,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -173,7 +120,7 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } diff --git a/code/vertex_buffer.cpp b/code/vertex_buffer.cpp index b3253243..607a7cd3 100644 --- a/code/vertex_buffer.cpp +++ b/code/vertex_buffer.cpp @@ -4,9 +4,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -46,64 +45,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -161,41 +102,42 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; - - VDeleter vertexBuffer{device, vkDestroyBuffer}; - VDeleter vertexBufferMemory{device, vkFreeMemory}; - + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -232,6 +174,41 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -240,7 +217,7 @@ class HelloTriangleApplication { static void onWindowResized(GLFWwindow* window, int width, int height) { if (width == 0 || height == 0) return; - + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->recreateSwapChain(); } @@ -248,6 +225,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -274,17 +253,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -297,13 +276,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -352,22 +331,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -414,16 +393,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -433,9 +406,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -451,7 +424,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -494,7 +467,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -503,10 +476,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -529,7 +500,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -592,7 +563,7 @@ class HelloTriangleApplication { pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -611,13 +582,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -633,7 +607,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -646,7 +620,7 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create command pool!"); } } @@ -658,7 +632,7 @@ class HelloTriangleApplication { bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, vertexBuffer.replace()) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &vertexBuffer) != VK_SUCCESS) { throw std::runtime_error("failed to create vertex buffer!"); } @@ -670,7 +644,7 @@ class HelloTriangleApplication { allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - if (vkAllocateMemory(device, &allocInfo, nullptr, vertexBufferMemory.replace()) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &vertexBufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate vertex buffer memory!"); } @@ -696,10 +670,6 @@ class HelloTriangleApplication { } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -738,7 +708,7 @@ class HelloTriangleApplication { VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); - vkCmdDraw(commandBuffers[i], vertices.size(), 1, 0, 0); + vkCmdDraw(commandBuffers[i], static_cast(vertices.size()), 1, 0, 0); vkCmdEndRenderPass(commandBuffers[i]); @@ -752,8 +722,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -809,21 +779,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -861,7 +832,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -1035,4 +1009,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/vertex_input.cpp b/code/vertex_input.cpp index c0725821..a94a8c7d 100644 --- a/code/vertex_input.cpp +++ b/code/vertex_input.cpp @@ -4,9 +4,8 @@ #include #include -#include -#include #include +#include #include #include #include @@ -46,64 +45,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -161,37 +102,38 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; - - VDeleter swapChain{device, vkDestroySwapchainKHR}; + + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - std::vector> swapChainFramebuffers; - - VDeleter renderPass{device, vkDestroyRenderPass}; - VDeleter pipelineLayout{device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline{device, vkDestroyPipeline}; - - VDeleter commandPool{device, vkDestroyCommandPool}; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; std::vector commandBuffers; - VDeleter imageAvailableSemaphore{device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore{device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; void initWindow() { glfwInit(); @@ -227,6 +169,38 @@ class HelloTriangleApplication { } vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -235,7 +209,7 @@ class HelloTriangleApplication { static void onWindowResized(GLFWwindow* window, int width, int height) { if (width == 0 || height == 0) return; - + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->recreateSwapChain(); } @@ -243,6 +217,8 @@ class HelloTriangleApplication { void recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); @@ -269,17 +245,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -292,13 +268,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -347,22 +323,22 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -409,16 +385,10 @@ class HelloTriangleApplication { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - VkSwapchainKHR oldSwapChain = swapChain; - createInfo.oldSwapchain = oldSwapChain; - - VkSwapchainKHR newSwapChain; - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } - swapChain = newSwapChain; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); @@ -428,9 +398,9 @@ class HelloTriangleApplication { } void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size()); - for (uint32_t i = 0; i < swapChainImages.size(); i++) { + for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; @@ -446,7 +416,7 @@ class HelloTriangleApplication { createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } @@ -489,7 +459,7 @@ class HelloTriangleApplication { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } @@ -498,10 +468,8 @@ class HelloTriangleApplication { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -524,7 +492,7 @@ class HelloTriangleApplication { auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); @@ -587,7 +555,7 @@ class HelloTriangleApplication { pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -606,13 +574,16 @@ class HelloTriangleApplication { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter{device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { @@ -628,7 +599,7 @@ class HelloTriangleApplication { framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } @@ -641,16 +612,12 @@ class HelloTriangleApplication { poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; - if (vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create command pool!"); } } void createCommandBuffers() { - if (commandBuffers.size() > 0) { - vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); - } - commandBuffers.resize(swapChainFramebuffers.size()); VkCommandBufferAllocateInfo allocInfo = {}; @@ -699,8 +666,8 @@ class HelloTriangleApplication { VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } @@ -756,21 +723,22 @@ class HelloTriangleApplication { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } + + vkQueueWaitIdle(presentQueue); } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + VkShaderModule createShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); - std::vector codeAligned(code.size() / 4 + 1); - memcpy(codeAligned.data(), code.data(), code.size()); - - createInfo.pCode = codeAligned.data(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } + + return shaderModule; } VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { @@ -808,7 +776,10 @@ class HelloTriangleApplication { int width, height; glfwGetWindowSize(window, &width, &height); - VkExtent2D actualExtent = {width, height}; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); @@ -982,4 +953,4 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/code/window_surface.cpp b/code/window_surface.cpp index de4a36d4..2faba073 100644 --- a/code/window_surface.cpp +++ b/code/window_surface.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -37,64 +36,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1; @@ -110,17 +51,18 @@ class HelloTriangleApplication { initWindow(); initVulkan(); mainLoop(); + cleanup(); } private: GLFWwindow* window; - VDeleter instance{vkDestroyInstance}; - VDeleter callback{instance, DestroyDebugReportCallbackEXT}; - VDeleter surface{instance, vkDestroySurfaceKHR}; + VkInstance instance; + VkDebugReportCallbackEXT callback; + VkSurfaceKHR surface; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VDeleter device{vkDestroyDevice}; + VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; @@ -146,6 +88,13 @@ class HelloTriangleApplication { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } + } + + void cleanup() { + vkDestroyDevice(device, nullptr); + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); @@ -170,17 +119,17 @@ class HelloTriangleApplication { createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); - createInfo.enabledExtensionCount = extensions.size(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } @@ -193,13 +142,13 @@ class HelloTriangleApplication { createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } void createSurface() { - if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } @@ -248,21 +197,21 @@ class HelloTriangleApplication { VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); createInfo.pEnabledFeatures = &deviceFeatures; createInfo.enabledExtensionCount = 0; - + if (enableValidationLayers) { - createInfo.enabledLayerCount = validationLayers.size(); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } diff --git a/daux.patch b/daux.patch index 7571a6d3..8970b874 100644 --- a/daux.patch +++ b/daux.patch @@ -1,11 +1,11 @@ -From 40008c88788c97fd4b81e1117a5f029d9a457acd Mon Sep 17 00:00:00 2001 +From b3713faf02ab4ace5be4dbc57f9deb45510c57dd Mon Sep 17 00:00:00 2001 From: Alexander Overvoorde -Date: Mon, 17 Apr 2017 20:02:04 +0200 +Date: Thu, 11 May 2017 15:48:53 +0200 Subject: [PATCH] Adjust theme for Vulkan tutorial --- daux/VulkanLinkProcessor.php | 69 ++++ - templates/content.php | 26 +- + templates/content.php | 25 +- templates/layout/00_layout.php | 2 +- templates/layout/05_page.php | 17 +- themes/daux/css/theme-blue.min.css | 2 +- @@ -20,7 +20,7 @@ Subject: [PATCH] Adjust theme for Vulkan tutorial themes/daux/less/structure.less | 27 +- themes/daux/less/theme-blue.less | 10 +- themes/daux_singlepage/css/main.min.css | 2 +- - 16 files changed, 924 insertions(+), 137 deletions(-) + 16 files changed, 923 insertions(+), 137 deletions(-) create mode 100644 daux/VulkanLinkProcessor.php diff --git a/daux/VulkanLinkProcessor.php b/daux/VulkanLinkProcessor.php @@ -99,7 +99,7 @@ index 0000000..4e4d456 + } +?> diff --git a/templates/content.php b/templates/content.php -index 2febe38..03017b7 100644 +index 2febe38..f1083bd 100644 --- a/templates/content.php +++ b/templates/content.php @@ -2,17 +2,14 @@ @@ -122,7 +122,7 @@ index 2febe38..03017b7 100644 -@@ -26,5 +23,24 @@ +@@ -26,5 +23,23 @@ @@ -131,8 +131,7 @@ index 2febe38..03017b7 100644 +