0
\$\begingroup\$

I'm starting to learn the core concepts and features of the Vulkan API. I'm following the tutorials that can be found here: vulkan-tutorial.

I'm at the point of getting ready to implement the code to loading in textures to apply Texture Mapping. On their website, the author has almost everything contained within a single class object. This is leading me towards refactoring the code.

My structural intent is to have a Swap Chain object, a hierarchy of pipeline class objects, and eventually shader objects. Now when it comes to the components that these objects are built on such as VkImageView, VkBuffer, VkCommandPool, etc. I'm divided on which route I should go within my design decision.


Currently, I have started to put each of these into their own class objects with an interface that looks like this:

ImageView.h

#pragma once

#include "utility.h"

namespace ForceEngine {
    namespace vk {

        class ImageView {
        private:
            VkImageView imageView_;

        public:
            void create(VkDevice* device, VkImage image, VkFormat format);
            VkImageView get() const { return imageView_; }
            void destroy(VkDevice* device);

        private:
            void createImpl(VkDevice* device, VkImage image, VkFormat format);        
        };

    } // namespace vk
} // namespace ForceEngine

Note: -- I'm passing the device* around since I'm not using any global variables. The Vulkan class not shown here which is the core engine class that ties all of the components together is the class that owns the instance, surface, and device objects.

With this current implementation or design, all of the sub-objects that are used to create buffers, image views, semaphores, command pools, descriptor sets, etc. would be their own class object where they would contain a single instance of that type providing an interface to create, retrieve, and destroy the instance of that object.

With this type of design, when building the core components such as the SwapChain and Pipelines they wouldn't store the actual VkImageView directly as they would store my interface object such as ImageView instead. The owning class objects would then have to call the appropriate interface functions at the appropriate times.


As my project now stands, I have not broken any of the mechanics of the Engine as I'm still able to run the application and render a spinning colored square. Before I dive any further as I have only converted a few of these objects into classes and before I break or decouple anything; I was thinking of a few other approaches.

One of those being, instead of making these into class objects that maintain their own instance and provides an interface, the other approach would be to not have these as class objects but as either standalone or static methods that just take the required parameters, creates the objects and returns it back to the caller. Then it in this situation it would be the responsibility of the class object that is creating it to store the actual VkObjectName and it would be their responsibility to destroy these objects and to clean up any memory footprints.

A third possible implementation would be to have these as either function pointers, functors, or function objects but I'm not leaning in that direction as of yet.


That was to give a little bit of background of where I'm currently at in the design process. So before I continue what I would like to know is which approach would be the preferred method of design in regards to how the Vulkan API operates internally. Which approach would yield better performance and easier management in the long run as the project will grow in time? I'm wanting to keep this as modular as possible while being proficient with a high level of performance and still being easy to maintain. I'm looking to structure this code with the intent of having reusability and readability for use in future projects.

Since Vulkan is supposed to be quite fast and proficient at what it does when implemented properly compared to other APIs such as OpenGL and DirectX, I don't want to introduce any extra unnecessary overhead. With the first approach that I have shown as an example, it would be easy for another class to contain these objects into various containers, and I can easily traverse or search those containers and just call the appropriate interface method. However, over time if any of these individual components such as a VertexBuffer that is created from a VkBuffer object where to be changed from one draw call to another, then I would have to provide an update function for all of these objects to stay consistent with the design of the interface. On the other hand, if I just have these as standalone functions and not within a class object, then it would be the responsibility of the class objects such as the SwapChain and the Pipelines to store, manage, update and clean up the Vulkan types directly.

This is where I'm stuck in the design process. I'm looking for advice, tips, suggestions, all feedback is welcomed as this is a learning process and I want to hear the thoughts of those who have a strong background and knowledge in the use of designing Vulkan based applications.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.