Reputation: 7905
I'm following vulkan-tutorial and I've successfully rendered a spinning square. I'm at the point in the lessons right before applying textures. Before moving on within the lessons, I've been modularizing the code into a framework of interfaces one piece at a time. I have successfully managed to extract various Vulkan objects out of the main engine class into their own classes. Each of these class objects has an interface with an initialize, create, and cleanup function at a minimum.
I've done this with a Buffer class that is an abstract base class that my IndexBuffer, VertexBuffer, and UniformBuffer all derived from. I've done this with my CommandPool class, SyncObjects(VkSemaphore and VkFence) classes, my Pipelines(only MainGraphicsPipeline for now), and my SwapChain.
With all of these in their own classes, I'm now storing these classes as either shared_ptr<Class>
or vector<shared_ptr<Class>>
within my classes that have internal instances of these. This is the design flow that I've been staying with.
Everything was working perfectly fine until I started to have VkImageView
types contained within their own class. Within my SwapChain
class it contained the private member:
std::vector<VkImageView> imageViews_
and these member functions that work on them:
void SwapChain::cleanupImageViews() {
for (auto imageView : imageViews_) {
vkDestroyImageView(*device_, imageView, nullptr);
}
}
void SwapChain::createImageViews() {
imageViews_.resize(images_.size());
for (size_t i = 0; i < images_.size(); i++) {
VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = images_[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = imageFormat_;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(*device_, &createInfo, nullptr, &imageViews_[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create image views!");
}
}
}
With my code in this state, everything works fine. I'm able to render a spinning colored square, I can resize the window and close the window with 0 errors from both Visual Studio and from Vulkan Layers.
When I change to this pattern that I've done before:
std::vector<std::shared_ptr<ImageView>> imageViews_;
and my functions become:
void SwapChain::cleanupImageViews() {
for (auto& imageView : imageViews_ ) {
imageView->cleanup();
}
}
void SwapChain::createImageViews() {
imageViews_.resize(images_.size());
for(auto& i : imageViews_) {
i = std::shared_ptr<ImageView>();
i->initialize(device_);
}
for (size_t i = 0; i < images_.size(); i++ ) {
imagesViews_[i]->create(images_[i], imageFormat_);
}
}
It fails when It calls the create() for the ImageViews giving me an unhandled exception: access read/write violation stating that the "this" pointer within my ImageView class is nullptr
.
Here is what my ImageView class looks like:
ImageView.h
#pragma once
#include "utility.h"
namespace ForceEngine {
namespace vk {
class ImageView {
private:
VkImageView imageView_;
VkDevice* device_;
VkImage image_;
VkFormat format_;
public:
ImageView() = default;
~ImageView() = default;
void initialize(VkDevice* device);
void create(VkImage image, VkFormat format);
void cleanup();
VkImageView* get() { return &imageView_; }
private:
void createImageView();
};
} // namespace vk
} // namespace ForceEngine
ImageView.cpp
#include "ImageView.h"
namespace ForceEngine {
namespace vk {
void ImageView::initialize(VkDevice* device) {
if (device == nullptr) {
throw std::runtime_error("failed to initialize ImageView: device was nullptr!");
device_ = device;
}
}
void ImageView::create(VkImage image, VkFormat format) {
//if (image == nullptr) throw std::runtime_error("failed to create Image View: image was nullptr!");
//if (format == nullptr) throw std::runtime_error("failed to create Image View: format was nullptr!");
image_ = image; // This is where it is throwing the exception.
format_ = format;
createImageView();
}
void ImageView::createImageView() {
VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = image_;
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = format_;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(*device_, &createInfo, nullptr, &imageView_) != VK_SUCCESS) {
throw std::runtime_error("failed to create image views!");
}
}
void ImageView::cleanup() {
vkDestroyImageView(*device_, imageView_, nullptr);
}
} // namespace vk
} // namespace ForceEngine
Within my class, I've tried having image_
as a pointer and as a non-pointer and as for the signature of create()
, I've tried passing the parameters by copy, reference, pointer, const reference, const pointer, etc. and to no avail, none of the above has worked. Everything keeps causing an exception. I don't know what's causing the access violation. For some reason, the ImageView class is not properly allocating memory for the image_
member as it states that this
was nullptr
for this class or it can't write to memory, etc. Yet, I've followed this same pattern for all of my other Vulkan Objects and did not have this issue until now.
What are the takes on the usages and proper setup of VkImageView and VkImage within the context of the SwapChain? Currently, the VkImage
s are still stored within the SwapChain as std::vector<VkImage>
I can create it successfully within the class directly, but when I try to extract the VkImageView out into its own class object is when I start to run into this problem. I'm learning Vulkan through this tutorial and I'm starting to get a grasp of how it is designed, but I'm still no expert at the API. Right now any help would be appreciated. And, yes I've stepped through the debugger, I've watched my variables, I've watched the call stack, and for the life of me, I'm completely stumped. If you need more information than this please don't hesitate to ask.
Upvotes: 0
Views: 1730
Reputation: 7905
User nicol-bolas pointed out in the comment section that I had i = std::shared_ptr<ImageView>()
and that I should have had i = std::make_shared<ImageView>()
and yes that is correct and that is how my other independent classes are being created. This was an overlooked typo mistake. So I give credit to NicolBolas for that. However, after fixing that bug, it allowed me to find and fix the real bug.
The actual bug that was causing the Unhandled Exception
had nothing to do with the ImageView
, VkImageView
, VkImage
objects. The real culprit was within the ImageView
class itself within its initialize()
method. I was assigning its member device_
within the scope of the if statement, where I was checking to see if the device
pointer being passed in was nullptr
or not.
I originally had this:
void ImageView::initialize(VkDevice* device) {
if (device == nullptr) { throw std::runtime_error("failed to initialize Image View: device was nullptr!");
device_ = nullptr;
}
}
And the device_
was never being set. It should have been:
void ImageView::initialize(VkDevice* device) {
if (device == nullptr) throw std::runtime_error("failed to initialize Image View: device was nullptr!");
device_ = nullptr;
}
Now the code works again and I have a spinning colored square. The code exits with 0 errors and no messages from Vulkan Layers.
I had simply overlooked the {}
within the if statement of the function.
Upvotes: 3