diff options
Diffstat (limited to 'src/vk')
30 files changed, 3648 insertions, 0 deletions
diff --git a/src/vk/base_buffer.cpp b/src/vk/base_buffer.cpp new file mode 100644 index 0000000..0846b20 --- /dev/null +++ b/src/vk/base_buffer.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base_buffer.hpp" + +namespace VK +{ + +const CommandChain BaseBuffer::loader{ + {&BaseBuffer::load_buffer, &BaseBuffer::unload_buffer}, + {&BaseBuffer::load_memory, &BaseBuffer::unload_memory} +}; + +void +BaseBuffer::load_buffer(void *obj) +{ + auto self = static_cast<BaseBuffer*>(obj); + + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.pNext = nullptr; + buffer_info.flags = 0; + buffer_info.size = self->device_size; + buffer_info.usage = self->buffer_usage; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + buffer_info.queueFamilyIndexCount = 0; + buffer_info.pQueueFamilyIndices = nullptr; + + if(vkCreateBuffer( + self->device->device, &buffer_info, nullptr, &self->buffer) + != VK_SUCCESS) + throw CommandError{"Failed to create vertex buffer."}; +} + +void +BaseBuffer::unload_buffer(void *obj) +{ + auto self = static_cast<BaseBuffer*>(obj); + + if(self->buffer != VK_NULL_HANDLE) + vkDestroyBuffer(self->device->device, self->buffer, nullptr); +} + +void +BaseBuffer::load_memory(void *obj) +{ + auto self = static_cast<BaseBuffer*>(obj); + + VkMemoryRequirements memory_requirements; + vkGetBufferMemoryRequirements( + self->device->device, self->buffer, &memory_requirements); + + VkPhysicalDeviceMemoryProperties memory_properties; + vkGetPhysicalDeviceMemoryProperties( + self->device->physical_device, &memory_properties); + + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.allocationSize = memory_requirements.size; + if(!self->device->select_memory_type( + &alloc_info.memoryTypeIndex, &memory_requirements, + self->memory_properties)) + throw CommandError{"Could not allocate memory for Vulkan vertex buffer."}; + + if(vkAllocateMemory(self->device->device, &alloc_info, nullptr, + &self->device_memory) != VK_SUCCESS) + throw CommandError{"Could not allocate memory for Vulkan vertex buffer."}; + + vkBindBufferMemory( + self->device->device, self->buffer, self->device_memory, 0); +} + +void +BaseBuffer::unload_memory(void *obj) +{ + auto self = static_cast<BaseBuffer*>(obj); + + if(self->device_memory != VK_NULL_HANDLE) + vkFreeMemory(self->device->device, self->device_memory, nullptr); +} + +} diff --git a/src/vk/base_buffer.hpp b/src/vk/base_buffer.hpp new file mode 100644 index 0000000..0f3e513 --- /dev/null +++ b/src/vk/base_buffer.hpp @@ -0,0 +1,56 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_BASE_BUFFER_H +#define CANDY_GEAR_VK_BASE_BUFFER_H 1 + +#include "../command.hpp" +#include "core.hpp" +#include "device.hpp" + +namespace VK +{ + +class BaseBuffer +{ + public: + virtual ~BaseBuffer(){}; + + VkBuffer buffer; + VkDeviceMemory device_memory; + VkDeviceSize device_size; + VkBufferUsageFlags buffer_usage; + VkMemoryPropertyFlags memory_properties; + + protected: + static const CommandChain loader; + + Device *device; + + static void + load_buffer(void *obj); + static void + unload_buffer(void *obj); + + static void + load_memory(void *obj); + static void + unload_memory(void *obj); +}; + +} + +#endif /* CANDY_GEAR_VK_BASE_BUFFER_H */ diff --git a/src/vk/camera.hpp b/src/vk/camera.hpp new file mode 100644 index 0000000..5f09390 --- /dev/null +++ b/src/vk/camera.hpp @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_CAMERA_H +#define CANDY_GEAR_VK_CAMERA_H 1 + +#include "core.hpp" + +namespace VK +{ + +struct Camera +{ + glm::vec3 position; + glm::vec3 rotation; // Radians +}; + +} + +#endif /* CANDY_GEAR_VK_CAMERA_H */ diff --git a/src/vk/command_pool.cpp b/src/vk/command_pool.cpp new file mode 100644 index 0000000..3af62c1 --- /dev/null +++ b/src/vk/command_pool.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "command_pool.hpp" + +namespace VK +{ + +const CommandChain CommandPool::loader{ + {&CommandPool::load_command_pool, &CommandPool::unload_command_pool}, + {&CommandPool::load_command_buffers, nullptr} +}; + +CommandPool::CommandPool(QueueFamily *queue_family, uint32_t buffers_quantity): + queue_family{queue_family} +{ + this->command_buffers.resize(buffers_quantity); + + CommandPool::loader.execute(this); +} + +CommandPool::~CommandPool() +{ + CommandPool::loader.revert(this); +} + +void +CommandPool::load_command_pool(void *obj) +{ + auto *self = static_cast<CommandPool*>(obj); + + VkCommandPoolCreateInfo command_pool_create_info; + + command_pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + command_pool_create_info.pNext = nullptr; + command_pool_create_info.flags = + VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + command_pool_create_info.queueFamilyIndex = self->queue_family->family_index; + + if(vkCreateCommandPool( + self->queue_family->device->device, &command_pool_create_info, + nullptr, &self->command_pool) != VK_SUCCESS) + throw CommandError{"Vulkan command pool could not be created."}; +} + +void +CommandPool::unload_command_pool(void *obj) +{ + auto *self = static_cast<CommandPool*>(obj); + + vkDestroyCommandPool( + self->queue_family->device->device, self->command_pool, nullptr); +} + +void +CommandPool::load_command_buffers(void *obj) +{ + auto *self = static_cast<CommandPool*>(obj); + + VkCommandBufferAllocateInfo command_buffer_info; + command_buffer_info.sType = + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + command_buffer_info.pNext = nullptr; + command_buffer_info.commandPool = self->command_pool; + command_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + command_buffer_info.commandBufferCount = self->command_buffers.size(); + + if(vkAllocateCommandBuffers( + self->queue_family->device->device, + &command_buffer_info, self->command_buffers.data()) != VK_SUCCESS) + throw CommandError{"Vulkan command buffers could not be allocated."}; +} + +} diff --git a/src/vk/command_pool.hpp b/src/vk/command_pool.hpp new file mode 100644 index 0000000..68ab7a6 --- /dev/null +++ b/src/vk/command_pool.hpp @@ -0,0 +1,59 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_COMMAND_POOL_H +#define CANDY_GEAR_VK_COMMAND_POOL_H 1 + +#include <vector> + +#include "../command.hpp" +#include "core.hpp" +#include "device.hpp" + +namespace VK +{ + +class CommandPool +{ + CommandPool(const CommandPool &t) = delete; + CommandPool& operator=(const CommandPool &t) = delete; + CommandPool(const CommandPool &&t) = delete; + CommandPool& operator=(const CommandPool &&t) = delete; + +public: + std::vector<VkCommandBuffer> command_buffers; + + CommandPool(QueueFamily *queue_family, uint32_t buffers_quantity); + ~CommandPool(); + +private: + static const CommandChain loader; + + QueueFamily *queue_family; + VkCommandPool command_pool; + + static void + load_command_pool(void *obj); + static void + unload_command_pool(void *obj); + + static void + load_command_buffers(void *obj); +}; + +} + +#endif /* CANDY_GEAR_VK_COMMAND_POOL_H */ diff --git a/src/vk/core.hpp b/src/vk/core.hpp new file mode 100644 index 0000000..6d42bde --- /dev/null +++ b/src/vk/core.hpp @@ -0,0 +1,32 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_CORE_H +#define CANDY_GEAR_VK_CORE_H 1 + +// GLM uses some definitions to control their behavior, so you should not +// include it directly. Instead, use this header. +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE + +#include <glm/ext/vector_float3.hpp> +#include <glm/glm.hpp> +#include <glm/gtc/matrix_transform.hpp> +#include <glm/vec3.hpp> + +#include <vulkan/vulkan.h> + +#endif /* CANDY_GEAR_VK_CORE_H */ diff --git a/src/vk/destination_buffer.cpp b/src/vk/destination_buffer.cpp new file mode 100644 index 0000000..872f8a8 --- /dev/null +++ b/src/vk/destination_buffer.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "destination_buffer.hpp" + +#include "command_pool.hpp" + +namespace VK +{ + +DestinationBuffer::DestinationBuffer( + QueueFamily *queue_family, SourceBuffer *source_buffer, + VkBufferUsageFlags buffer_usage) +{ + this->device = queue_family->device; + this->device_size = source_buffer->device_size; + this->buffer_usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | buffer_usage; + this->memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + this->queue_family = queue_family; + this->source_buffer = source_buffer; + + BaseBuffer::loader.execute(dynamic_cast<BaseBuffer*>(this)); + + // Load command + { + CommandPool command_pool(this->queue_family, 1); + Queue transfer_queue{this->queue_family->get_queue()}; + + this->vk_command_buffer = command_pool.command_buffers[0]; + + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + VkBufferCopy copy_region = {}; + copy_region.srcOffset = 0; + copy_region.dstOffset = 0; + copy_region.size = this->device_size; + + vkBeginCommandBuffer(this->vk_command_buffer, &begin_info); + + vkCmdCopyBuffer( + this->vk_command_buffer, this->source_buffer->buffer, this->buffer, 1, + ©_region); + + vkEndCommandBuffer(this->vk_command_buffer); + + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &this->vk_command_buffer; + + vkQueueSubmit(transfer_queue.queue, 1, &submit_info, VK_NULL_HANDLE); + + vkQueueWaitIdle(transfer_queue.queue); + } +} + +DestinationBuffer::DestinationBuffer( + DestinationBuffer &&that) +{ + this->buffer = that.buffer; + this->device_memory = that.device_memory; + this->device_size = that.device_size; + this->buffer_usage = that.buffer_usage; + this->memory_properties = that.memory_properties; + + that.buffer = VK_NULL_HANDLE; + that.device_memory = VK_NULL_HANDLE; +} + +DestinationBuffer& +DestinationBuffer::operator=(DestinationBuffer &&that) +{ + this->buffer = that.buffer; + this->device_memory = that.device_memory; + this->device_size = that.device_size; + this->buffer_usage = that.buffer_usage; + this->memory_properties = that.memory_properties; + + that.buffer = VK_NULL_HANDLE; + that.device_memory = VK_NULL_HANDLE; + + return *this; +} + +DestinationBuffer::~DestinationBuffer() +{ + BaseBuffer::loader.revert(dynamic_cast<BaseBuffer*>(this)); +} + +} diff --git a/src/vk/destination_buffer.hpp b/src/vk/destination_buffer.hpp new file mode 100644 index 0000000..e856f4d --- /dev/null +++ b/src/vk/destination_buffer.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_DESTINATION_BUFFER_H +#define CANDY_GEAR_VK_DESTINATION_BUFFER_H 1 + +#include "base_buffer.hpp" +#include "core.hpp" +#include "source_buffer.hpp" +#include "queue_family.hpp" + +namespace VK +{ + +struct DestinationBuffer: public BaseBuffer +{ + DestinationBuffer(const DestinationBuffer &t) = delete; + DestinationBuffer& + operator=(const DestinationBuffer &t) = delete; + + QueueFamily *queue_family; + SourceBuffer *source_buffer; + VkCommandBuffer vk_command_buffer; + + DestinationBuffer( + QueueFamily *queue_family, SourceBuffer *source_buffer, + VkBufferUsageFlags buffer_usage); + + DestinationBuffer(DestinationBuffer &&that); + DestinationBuffer& + operator=(DestinationBuffer &&that); + + ~DestinationBuffer(); +}; + +} + +#endif /* CANDY_GEAR_VK_DESTINATION_BUFFER_H */ diff --git a/src/vk/device.cpp b/src/vk/device.cpp new file mode 100644 index 0000000..2434283 --- /dev/null +++ b/src/vk/device.cpp @@ -0,0 +1,254 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device.hpp" + +#include <fstream> +#include <iostream> +#include <new> +#include <vector> + +#include "../core.hpp" + +namespace +{ +VkShaderModule +create_shader_module(VkDevice vk_device, const char *filename) +{ + std::ifstream file(filename, std::ios::ate | std::ios::binary); + + if (!file.is_open()) + { + throw std::runtime_error("Failed to open shader module file."); + } + + size_t file_size = (size_t) file.tellg(); + std::vector<char> code(file_size); + + file.seekg(0); + file.read(code.data(), file_size); + + file.close(); + + VkShaderModuleCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + create_info.codeSize = code.size(); + create_info.pCode = reinterpret_cast<const uint32_t*>(code.data()); + + VkShaderModule shader_module; + if (vkCreateShaderModule(vk_device, &create_info, nullptr, + &shader_module) != VK_SUCCESS) + { + throw std::runtime_error("Failed to create shader module."); + } + + return shader_module; +} +} + +namespace VK +{ + +Device::Device(VkPhysicalDevice vk_physical_device, bool with_swapchain) +{ + this->physical_device = vk_physical_device; + + std::vector<VkQueueFamilyProperties> queue_family_properties; + + // Get queue families. + { + vkGetPhysicalDeviceQueueFamilyProperties( + vk_physical_device, &this->queue_families_count, nullptr); + queue_family_properties.resize(this->queue_families_count); + vkGetPhysicalDeviceQueueFamilyProperties( + vk_physical_device, &this->queue_families_count, + queue_family_properties.data()); + } + + // Get information from physical device. + { + VkPhysicalDeviceProperties physical_properties = {}; + vkGetPhysicalDeviceProperties(vk_physical_device, &physical_properties); + VkPhysicalDeviceFeatures supported_features = {}; + vkGetPhysicalDeviceFeatures(vk_physical_device, &supported_features); + +#ifdef DEBUG + std::cout << "Name: " << physical_properties.deviceName << std::endl; + std::cout << "API version: " << physical_properties.apiVersion << + std::endl; + std::cout << "Driver version: " << physical_properties.driverVersion << + std::endl; + std::cout << "Vendor ID: " << physical_properties.vendorID << std::endl; + std::cout << "Device ID: " << physical_properties.deviceID << std::endl; + std::cout << "Device type: " << physical_properties.deviceType << + std::endl; +#endif + + std::vector<VkDeviceQueueCreateInfo> device_queue_create_infos; + std::vector<std::vector<float>> queue_priorities( + queue_family_properties.size()); + device_queue_create_infos.resize(queue_family_properties.size()); + for(auto i{0}; i < queue_family_properties.size(); i++) + { + // Give different priorities to queues. + int queue_count = queue_family_properties[i].queueCount; + queue_priorities[i].resize(queue_count); + float priority_unity = 1.0f/queue_count; + for(auto ii{0}; ii < queue_count; ii++) + queue_priorities[i][ii] = priority_unity * (queue_count - ii); + + device_queue_create_infos[i].sType = + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + device_queue_create_infos[i].pNext = nullptr; + device_queue_create_infos[i].flags = 0; + device_queue_create_infos[i].queueFamilyIndex = i; + device_queue_create_infos[i].queueCount = queue_priorities[i].size(); + device_queue_create_infos[i].pQueuePriorities = + queue_priorities[i].data(); + } + + VkPhysicalDeviceFeatures required_features = {}; + required_features.multiDrawIndirect = supported_features.multiDrawIndirect; + required_features.geometryShader = VK_TRUE; + required_features.tessellationShader = VK_TRUE; + required_features.samplerAnisotropy = VK_TRUE; + + std::vector<const char*> device_extensions; + if(with_swapchain) + device_extensions.emplace_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + VkDeviceCreateInfo device_create_info = {}; + device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_create_info.pNext = nullptr; + device_create_info.flags = 0; + device_create_info.queueCreateInfoCount = device_queue_create_infos.size(); + device_create_info.pQueueCreateInfos = device_queue_create_infos.data(); + device_create_info.enabledLayerCount = 0; + device_create_info.ppEnabledLayerNames = nullptr; + device_create_info.enabledExtensionCount = device_extensions.size(); + if(device_extensions.size() == 0) + device_create_info.ppEnabledExtensionNames = nullptr; + else + device_create_info.ppEnabledExtensionNames = device_extensions.data(); + + device_create_info.pEnabledFeatures = &required_features; + + if(vkCreateDevice(this->physical_device, &device_create_info, nullptr, + &this->device) != VK_SUCCESS) + throw std::runtime_error("Failed to create Vulkan physical device."); + } + + // Load Shaders + { + this->vert_shader_module = create_shader_module( + this->device, DATA_DIR "/glsl/vert.spv"); + this->frag_shader_module = create_shader_module( + this->device, DATA_DIR "/glsl/frag.spv"); + } + + this->queue_families = static_cast<QueueFamily*>( + std::malloc(this->queue_families_count * sizeof(QueueFamily))); + for(auto i{0}; i < this->queue_families_count; i++) + { + new(&this->queue_families[i])QueueFamily( + this, i, queue_family_properties[i]); + + // Select families with graphics support. + auto &family_properties = this->queue_families[i].family_properties; + if(family_properties.queueCount > 0 && + family_properties.queueFlags & VK_QUEUE_GRAPHICS_BIT) + this->queue_families_with_graphics.push_back( + &this->queue_families[i]); + + // Select families with presentation support. + VkBool32 present_supported; + vkGetPhysicalDeviceSurfaceSupportKHR( + vk_physical_device, i, cg_core.window_surface, &present_supported); + if(present_supported) + this->queue_families_with_presentation.push_back( + &this->queue_families[i]); + } +} + +Device::~Device() +{ + for(auto i{0}; i < this->queue_families_count; i++) + this->queue_families[i].~QueueFamily(); + std::free(this->queue_families); + + // Destroy shaders + vkDestroyShaderModule(this->device, this->vert_shader_module, nullptr); + vkDestroyShaderModule(this->device, this->frag_shader_module, nullptr); + + vkDeviceWaitIdle(this->device); + vkDestroyDevice(this->device, nullptr); +} + +bool +Device::select_memory_type( + uint32_t *memory_type_index, VkMemoryRequirements *vk_memory_requirements, + VkMemoryPropertyFlags vk_property_flags) +{ + VkPhysicalDeviceMemoryProperties vk_memory_properties; + vkGetPhysicalDeviceMemoryProperties( + this->physical_device, &vk_memory_properties); + + for (auto i{0}; i < vk_memory_properties.memoryTypeCount; i++) + { + if(vk_memory_requirements->memoryTypeBits & (1 << i)) + { + const VkMemoryType& type = vk_memory_properties.memoryTypes[i]; + + if ((type.propertyFlags & vk_property_flags) == vk_property_flags) + { + *memory_type_index = i; + return true; + } + } + } + + return false; +} + +QueueFamily* +Device::get_queue_family_with_graphics() const +{ + /* + Returns a random queue family, so not all commands in the engine use the + same queue. + TODO: There must be a better way of doing this. + */ + std::uniform_int_distribution<std::size_t> random_distribution{ + 0, this->queue_families_with_graphics.size() -1}; + auto random = random_distribution(random_number_generator); + return this->queue_families_with_graphics[0]; +} + +QueueFamily* +Device::get_queue_family_with_presentation() const +{ + /* + Returns a random queue family, so not all commands in the engine use the + same queue. + TODO: There must be a better way of doing this. + */ + std::uniform_int_distribution<std::size_t> random_distribution{ + 0, this->queue_families_with_presentation.size() -1}; + auto random = random_distribution(random_number_generator); + return this->queue_families_with_presentation[0]; +} + +} diff --git a/src/vk/device.hpp b/src/vk/device.hpp new file mode 100644 index 0000000..56bd6e4 --- /dev/null +++ b/src/vk/device.hpp @@ -0,0 +1,61 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_DEVICE_H +#define CANDY_GEAR_VK_DEVICE_H 1 + +#include <cstdlib> +#include <vector> + +#include "core.hpp" +#include "queue_family.hpp" + +namespace VK +{ + +struct Device +{ + uint32_t queue_families_count; + QueueFamily *queue_families; + std::vector<QueueFamily*> queue_families_with_graphics; + std::vector<QueueFamily*> queue_families_with_presentation; + +public: + VkDevice device; + VkPhysicalDevice physical_device; + VkShaderModule vert_shader_module; + VkShaderModule frag_shader_module; + + bool with_swapchain; + + Device(VkPhysicalDevice vk_physical_device, bool with_swapchain); + ~Device(); + + bool + select_memory_type( + uint32_t *memoryTypeIndex, VkMemoryRequirements *vk_memory_requirements, + VkMemoryPropertyFlags vk_property_flags); + + QueueFamily* + get_queue_family_with_graphics() const; + + QueueFamily* + get_queue_family_with_presentation() const; +}; + +} + +#endif /* CANDY_GEAR_VK_DEVICE_H */ diff --git a/src/vk/graphics_pipeline.cpp b/src/vk/graphics_pipeline.cpp new file mode 100644 index 0000000..1c13425 --- /dev/null +++ b/src/vk/graphics_pipeline.cpp @@ -0,0 +1,1009 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "graphics_pipeline.hpp" + +#include <array> +#include <stdexcept> + +#include "../core.hpp" +#include "core.hpp" +#include "image.hpp" +#include "uniform_buffer.hpp" +#include "vertex.hpp" + +namespace +{ + +void +load_descriptor_set_layout_model_instance(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{}; + + layout_bindings[0].binding = 0; + layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + layout_bindings[0].descriptorCount = 1; + layout_bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + layout_bindings[0].pImmutableSamplers = nullptr; + + layout_bindings[1].binding = 1; + layout_bindings[1].descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + layout_bindings[1].descriptorCount = 1; + layout_bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layout_bindings[1].pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo layout_info{}; + layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layout_info.pNext = nullptr; + layout_info.flags = 0; + layout_info.bindingCount = static_cast<uint32_t>(layout_bindings.size()); + layout_info.pBindings = layout_bindings.data(); + + if(vkCreateDescriptorSetLayout( + cg_core.vk_device_with_swapchain->device, &layout_info, nullptr, + &self->descriptor_set_layout_model_instance) != VK_SUCCESS) + throw CommandError{ + "Failed to create Vulkan descriptor set layout for model instance."}; +} + +void +unload_descriptor_set_layout_model_instance(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + vkDestroyDescriptorSetLayout( + cg_core.vk_device_with_swapchain->device, + self->descriptor_set_layout_model_instance, nullptr); +} + +void +load_descriptor_set_layout_view_projection(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + VkDescriptorSetLayoutBinding layout_binding{}; + layout_binding.binding = 0; + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + layout_binding.descriptorCount = 1; + layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + layout_binding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo layout_info{}; + layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layout_info.pNext = nullptr; + layout_info.flags = 0; + layout_info.bindingCount = 1; + layout_info.pBindings = &layout_binding; + + if(vkCreateDescriptorSetLayout( + cg_core.vk_device_with_swapchain->device, &layout_info, nullptr, + &self->descriptor_set_layout_view_projection) != VK_SUCCESS) + throw CommandError{ + "Failed to create Vulkan descriptor set layout for view projection."}; +} + +void +unload_descriptor_set_layout_view_projection(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + vkDestroyDescriptorSetLayout( + cg_core.vk_device_with_swapchain->device, + self->descriptor_set_layout_view_projection, nullptr); +} + +void +load_pipeline_layout(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + std::array<VkDescriptorSetLayout, 2> set_layouts{ + self->descriptor_set_layout_model_instance, + self->descriptor_set_layout_view_projection}; + + VkPipelineLayoutCreateInfo pipeline_layout_info{}; + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.setLayoutCount = set_layouts.size(); + pipeline_layout_info.pSetLayouts = set_layouts.data(); + pipeline_layout_info.pushConstantRangeCount = 0; + pipeline_layout_info.pPushConstantRanges = nullptr; + + if(vkCreatePipelineLayout( + cg_core.vk_device_with_swapchain->device, + &pipeline_layout_info, nullptr, &self->pipeline_layout) != VK_SUCCESS) + throw CommandError{ + "Failed to create Vulkan descriptor set layout for view projection."}; +} + +void +unload_pipeline_layout(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + vkDestroyPipelineLayout( + cg_core.vk_device_with_swapchain->device, self->pipeline_layout, nullptr); +} + +void +load_uniform_buffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + try + { + self->ub_view_projection.reserve(cg_core.vk_swapchain->images_count); + for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) + self->ub_view_projection.emplace_back( + cg_core.vk_device_with_swapchain, sizeof(VK::UBOViewProjection)); + } + catch(const std::exception& e) + { + throw CommandError{e.what()}; + } +} + +void +unload_uniform_buffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + self->ub_view_projection.clear(); +} + +void +load_descriptor_pool(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + VkDescriptorPoolSize descriptor_pool_size{}; + descriptor_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_pool_size.descriptorCount = self->ub_view_projection.size(); + + VkDescriptorPoolCreateInfo pool_info{}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.pNext = nullptr; + pool_info.flags = 0; + pool_info.maxSets = self->ub_view_projection.size(); + pool_info.poolSizeCount = 1; + pool_info.pPoolSizes = &descriptor_pool_size; + + if(vkCreateDescriptorPool( + cg_core.vk_device_with_swapchain->device, + &pool_info, nullptr, &self->descriptor_pool) != VK_SUCCESS) + throw CommandError{"Failed to create a Vulkan descriptor pool."}; +} + +void +unload_descriptor_pool(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + vkDestroyDescriptorPool( + cg_core.vk_device_with_swapchain->device, self->descriptor_pool, nullptr); +} + +void +load_descriptor_sets(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + std::vector<VkDescriptorSetLayout> layouts( + cg_core.vk_swapchain->images_count, + self->descriptor_set_layout_view_projection); + + VkDescriptorSetAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = self->descriptor_pool; + alloc_info.descriptorSetCount = self->ub_view_projection.size(); + alloc_info.pSetLayouts = layouts.data(); + + self->view_projection_descriptor_sets.resize( + cg_core.vk_swapchain->images_count); + if(vkAllocateDescriptorSets( + cg_core.vk_device_with_swapchain->device, &alloc_info, + self->view_projection_descriptor_sets.data()) != VK_SUCCESS) + throw CommandError{"Failed to create Vulkan descriptor set."}; +} + +void +load_descriptor_sets_to_buffers(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + for(auto i{0}; i < self->ub_view_projection.size(); i++) + { + VkDescriptorBufferInfo buffer_info{}; + buffer_info.buffer = self->ub_view_projection[i].buffer; + buffer_info.offset = 0; + buffer_info.range = sizeof(VK::UBOViewProjection); + + VkWriteDescriptorSet write_descriptor{}; + write_descriptor.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptor.dstSet = self->view_projection_descriptor_sets[i]; + write_descriptor.dstBinding = 0; + write_descriptor.dstArrayElement = 0; + write_descriptor.descriptorCount = 1; + write_descriptor.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + write_descriptor.pBufferInfo = &buffer_info; + write_descriptor.pImageInfo = nullptr; + write_descriptor.pTexelBufferView = nullptr; + + vkUpdateDescriptorSets( + cg_core.vk_device_with_swapchain->device, 1, &write_descriptor, 0, + nullptr); + } +} + +void +load_depth_image(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + VkExtent3D extent3d{}; + extent3d.width = cg_core.screen_width; + extent3d.height = cg_core.screen_height; + extent3d.depth = 1; + + try + { + VK::Image::create( + cg_core.vk_device_with_swapchain, + &self->depth_image, + &self->depth_image_memory, + VK_FORMAT_D32_SFLOAT, + extent3d, + 1, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT + ); + } + catch(VK::Image::Error error) + { + std::string error_message{"Failed to create depth image → "}; + error_message += error.what(); + throw CommandError{error_message}; + } +} + +void +unload_depth_image(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + vkDestroyImage( + cg_core.vk_device_with_swapchain->device, self->depth_image, nullptr); + vkFreeMemory( + cg_core.vk_device_with_swapchain->device, self->depth_image_memory, + nullptr); +} + +void +load_depth_image_view(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + try + { + VK::Image::create_view( + cg_core.vk_device_with_swapchain, &self->depth_image_view, + self->depth_image, + VK_FORMAT_D32_SFLOAT, VK_IMAGE_ASPECT_DEPTH_BIT); + } + catch(VK::Image::Error error) + { + std::string error_message{"Failed to create depth image view → "}; + error_message += error.what(); + throw CommandError{error_message}; + } +} + +void +unload_depth_image_view(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + vkDestroyImageView( + cg_core.vk_device_with_swapchain->device, self->depth_image_view, nullptr); +} + +void +load_render_pass(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + std::array<VkAttachmentDescription, 2> attachments{}; + // Color attachment. + attachments[0].flags = 0; + attachments[0].format = cg_core.vk_swapchain->image_format; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + // Depth attachment. + attachments[1].flags = 0; + attachments[1].format = VK_FORMAT_D32_SFLOAT; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference color_attachment_ref{}; + color_attachment_ref.attachment = 0; + color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkAttachmentReference depth_attachment_ref{}; + depth_attachment_ref.attachment = 1; + depth_attachment_ref.layout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.flags = 0; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = nullptr; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment_ref; + subpass.pResolveAttachments = nullptr; + subpass.pDepthStencilAttachment = &depth_attachment_ref; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = nullptr; + + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo render_pass_info{}; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_info.pNext = nullptr; + render_pass_info.flags = 0; + render_pass_info.attachmentCount = attachments.size(); + render_pass_info.pAttachments = attachments.data(); + render_pass_info.subpassCount = 1; + render_pass_info.pSubpasses = &subpass; + render_pass_info.dependencyCount = 1; + render_pass_info.pDependencies = &dependency; + + if(vkCreateRenderPass( + cg_core.vk_device_with_swapchain->device, &render_pass_info, + nullptr, &self->render_pass) != VK_SUCCESS) + throw CommandError{"Failed to create Vulkan Render Pass."}; +} + +void +unload_render_pass(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + vkDestroyRenderPass( + cg_core.vk_device_with_swapchain->device, self->render_pass, nullptr); +} + +void +load_framebuffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + self->swapchain_framebuffers.resize(cg_core.vk_swapchain->images_count); + for (auto i{0}; i < cg_core.vk_swapchain->images_count; i++) + { + std::array<VkImageView, 2> attachments = { + cg_core.vk_swapchain->image_views[i], + self->depth_image_view + }; + + VkFramebufferCreateInfo framebuffer_info{}; + framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_info.renderPass = self->render_pass; + framebuffer_info.attachmentCount = attachments.size(); + framebuffer_info.pAttachments = attachments.data(); + framebuffer_info.width = cg_core.screen_width; + framebuffer_info.height = cg_core.screen_height; + + framebuffer_info.layers = 1; + + if(vkCreateFramebuffer( + cg_core.vk_device_with_swapchain->device, &framebuffer_info, nullptr, + &self->swapchain_framebuffers[i]) != VK_SUCCESS) + throw CommandError{"Failed to create Vulkan Framebuffer."}; + } +} + +void +unload_framebuffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + for(auto framebuffer: self->swapchain_framebuffers) + vkDestroyFramebuffer( + cg_core.vk_device_with_swapchain->device, framebuffer, nullptr); +} + +void +load_pipeline(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + VkPipelineShaderStageCreateInfo vert_shader_stage_info = {}; + vert_shader_stage_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vert_shader_stage_info.pNext = nullptr; + vert_shader_stage_info.flags = 0; + vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; + vert_shader_stage_info.module = + cg_core.vk_device_with_swapchain->vert_shader_module; + vert_shader_stage_info.pName = "main"; + vert_shader_stage_info.pSpecializationInfo = nullptr; + + VkPipelineShaderStageCreateInfo frag_shader_stage_info = {}; + frag_shader_stage_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + frag_shader_stage_info.pNext = nullptr; + frag_shader_stage_info.flags = 0; + frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + frag_shader_stage_info.module = + cg_core.vk_device_with_swapchain->frag_shader_module; + frag_shader_stage_info.pName = "main"; + frag_shader_stage_info.pSpecializationInfo = nullptr; + + VkPipelineShaderStageCreateInfo shader_stages[] = { + vert_shader_stage_info, + frag_shader_stage_info + }; + + VkVertexInputBindingDescription vertex_input_binding{}; + vertex_input_binding.binding = 0; + vertex_input_binding.stride = sizeof(VK::Vertex); + vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + std::array<VkVertexInputAttributeDescription, 4> vertex_attribute{}; + // Position. + vertex_attribute[0].location = 0; + vertex_attribute[0].binding = 0; + vertex_attribute[0].format = VK_FORMAT_R32G32B32_SFLOAT; + vertex_attribute[0].offset = offsetof(VK::Vertex, position); + // Normal. + vertex_attribute[1].location = 1; + vertex_attribute[1].binding = 0; + vertex_attribute[1].format = VK_FORMAT_R32G32B32_SFLOAT; + vertex_attribute[1].offset = offsetof(VK::Vertex, normal); + // Color. + vertex_attribute[2].location = 2; + vertex_attribute[2].binding = 0; + vertex_attribute[2].format = VK_FORMAT_R32G32B32_SFLOAT; + vertex_attribute[2].offset = offsetof(VK::Vertex, color); + // Texture coordinate. + vertex_attribute[3].location = 3; + vertex_attribute[3].binding = 0; + vertex_attribute[3].format = VK_FORMAT_R32G32_SFLOAT; + vertex_attribute[3].offset = offsetof(VK::Vertex, texture_coord); + + VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; + vertex_input_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input_info.pNext = nullptr; + vertex_input_info.flags = 0; + vertex_input_info.vertexBindingDescriptionCount = 1; + vertex_input_info.pVertexBindingDescriptions = &vertex_input_binding; + vertex_input_info.vertexAttributeDescriptionCount = + static_cast<uint32_t>(vertex_attribute.size()); + vertex_input_info.pVertexAttributeDescriptions = vertex_attribute.data(); + + VkPipelineInputAssemblyStateCreateInfo input_assembly = {}; + input_assembly.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly.pNext = nullptr; + input_assembly.flags = 0; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast<float>(cg_core.screen_width); + viewport.height = static_cast<float>(cg_core.screen_height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = {0, 0}; + scissor.extent = {cg_core.screen_width, cg_core.screen_height}; + + VkPipelineViewportStateCreateInfo viewport_state = {}; + viewport_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.pNext = nullptr; + viewport_state.flags = 0; + viewport_state.viewportCount = 1; + viewport_state.pViewports = &viewport; + viewport_state.scissorCount = 1; + viewport_state.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.pNext = nullptr; + rasterizer.flags = 0; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + rasterizer.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + VkPipelineDepthStencilStateCreateInfo depth_stencil = {}; + depth_stencil.sType = + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depth_stencil.depthTestEnable = VK_TRUE; + depth_stencil.depthWriteEnable = VK_TRUE; + depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; + depth_stencil.depthBoundsTestEnable = VK_FALSE; + depth_stencil.minDepthBounds = 0.0f; + depth_stencil.maxDepthBounds = 1.0f; + depth_stencil.stencilTestEnable = VK_FALSE; + depth_stencil.front = {}; + depth_stencil.back = {}; + + VkPipelineColorBlendAttachmentState color_blend_attachment = {}; + color_blend_attachment.blendEnable = VK_FALSE; + color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineColorBlendStateCreateInfo color_blending = {}; + color_blending.sType = + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blending.pNext = nullptr; + color_blending.flags = 0; + color_blending.logicOpEnable = VK_FALSE; + color_blending.logicOp = VK_LOGIC_OP_COPY; + color_blending.attachmentCount = 1; + color_blending.pAttachments = &color_blend_attachment; + color_blending.blendConstants[0] = 0.0f; + color_blending.blendConstants[1] = 0.0f; + color_blending.blendConstants[2] = 0.0f; + color_blending.blendConstants[3] = 0.0f; + + VkDynamicState dynamic_states[] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_LINE_WIDTH + }; + + VkPipelineDynamicStateCreateInfo dynamic_state_info = {}; + dynamic_state_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state_info.dynamicStateCount = 2; + dynamic_state_info.pDynamicStates = dynamic_states; + + VkGraphicsPipelineCreateInfo pipeline_info{}; + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.pNext = nullptr; + pipeline_info.flags = 0; + pipeline_info.stageCount = 2; + pipeline_info.pStages = shader_stages; + pipeline_info.pVertexInputState = &vertex_input_info; + pipeline_info.pInputAssemblyState = &input_assembly; + pipeline_info.pTessellationState = nullptr; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &rasterizer; + pipeline_info.pMultisampleState = &multisampling; + pipeline_info.pDepthStencilState = &depth_stencil; + pipeline_info.pColorBlendState = &color_blending; + pipeline_info.pDynamicState = &dynamic_state_info; + pipeline_info.layout = self->pipeline_layout; + pipeline_info.renderPass = self->render_pass; + pipeline_info.subpass = 0; + pipeline_info.basePipelineHandle = VK_NULL_HANDLE; + pipeline_info.basePipelineIndex = -1; + + if(vkCreateGraphicsPipelines( + cg_core.vk_device_with_swapchain->device, VK_NULL_HANDLE, 1, + &pipeline_info, nullptr, &self->graphic_pipeline) != VK_SUCCESS) + throw CommandError{"Failed to create graphics pipeline."}; +} + +void +unload_pipeline(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + vkDestroyPipeline( + cg_core.vk_device_with_swapchain->device, self->graphic_pipeline, nullptr); +} + +void +load_frame_sync(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + self->image_available_semaphores.resize(self->max_frames_in_flight); + self->render_finished_semaphores.resize(self->max_frames_in_flight); + self->in_flight_fences.resize(self->max_frames_in_flight); + + VkSemaphoreCreateInfo semaphore_info = {}; + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphore_info.pNext = nullptr; + semaphore_info.flags = 0; + + VkFenceCreateInfo fence_info = {}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_info.pNext = nullptr; + fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + // FIXME: if this loop fails, it will not destroy the semaphores. + for(auto i{0}; i < self->max_frames_in_flight; i++) + { + if(vkCreateSemaphore( + cg_core.vk_device_with_swapchain->device, &semaphore_info, + nullptr, &self->image_available_semaphores[i]) != VK_SUCCESS || + vkCreateSemaphore( + cg_core.vk_device_with_swapchain->device, &semaphore_info, + nullptr, &self->render_finished_semaphores[i]) != VK_SUCCESS || + vkCreateFence(cg_core.vk_device_with_swapchain->device, &fence_info, + nullptr, &self->in_flight_fences[i]) != VK_SUCCESS) + throw CommandError{"Failed to create semaphores."}; + } +} + +void +unload_frame_sync(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + vkDeviceWaitIdle(cg_core.vk_device_with_swapchain->device); + + for(auto i{0}; i < self->max_frames_in_flight; i++) + { + vkDestroySemaphore(cg_core.vk_device_with_swapchain->device, + self->render_finished_semaphores[i], nullptr); + vkDestroySemaphore(cg_core.vk_device_with_swapchain->device, + self->image_available_semaphores[i], nullptr); + vkDestroyFence(cg_core.vk_device_with_swapchain->device, + self->in_flight_fences[i], nullptr); + } +} + +void +load_queue_family(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + self->queue_family = + cg_core.vk_device_with_swapchain->get_queue_family_with_presentation(); +} + +void +load_command_pool(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + VkCommandPoolCreateInfo create_info{}; + create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + create_info.pNext = nullptr; + create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + create_info.queueFamilyIndex = self->queue_family->family_index; + + vkCreateCommandPool( + self->queue_family->device->device, &create_info, nullptr, + &self->command_pool); +} + +void +unload_command_pool(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + vkWaitForFences(cg_core.vk_device_with_swapchain->device, 2, + self->in_flight_fences.data(), VK_TRUE, + std::numeric_limits<uint64_t>::max()); + vkDestroyCommandPool( + self->queue_family->device->device, self->command_pool, nullptr); +} + +void +load_draw_command_buffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline*>(obj); + + // FIXME: 3 is a magical number, triple buffering. + self->draw_command_buffers.resize(3); + + VkCommandBufferAllocateInfo allocate_info{}; + allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocate_info.pNext = nullptr; + allocate_info.commandPool = self->command_pool; + allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocate_info.commandBufferCount = self->draw_command_buffers.size(); + + if(vkAllocateCommandBuffers( + self->queue_family->device->device, &allocate_info, + self->draw_command_buffers.data()) != VK_SUCCESS) + throw CommandError{"Vulkan draw command buffers could not be allocated."}; +} + +const CommandChain loader{ + {&load_descriptor_set_layout_model_instance, + &unload_descriptor_set_layout_model_instance}, + {&load_descriptor_set_layout_view_projection, + &unload_descriptor_set_layout_view_projection}, + {&load_pipeline_layout, &unload_pipeline_layout}, + {&load_uniform_buffer, &unload_uniform_buffer}, + {&load_descriptor_pool, &unload_descriptor_pool}, + // By destroying the pool the sets are also destroyed. + {&load_descriptor_sets, nullptr}, + {&load_descriptor_sets_to_buffers, nullptr}, + {&load_depth_image, &unload_depth_image}, + {&load_depth_image_view, &unload_depth_image_view}, + {&load_render_pass, &unload_render_pass}, + {&load_framebuffer, &unload_framebuffer}, + {&load_pipeline, &unload_pipeline}, + {&load_frame_sync, &unload_frame_sync}, + {&load_queue_family, nullptr}, + {&load_command_pool, &unload_command_pool}, + {&load_draw_command_buffer, nullptr} +}; + +} + +namespace VK +{ + +GraphicsPipeline::GraphicsPipeline(): + current_frame{0}, + camera{std::make_shared<VK::Camera>()}, + models_to_draw{cg_core.vk_swapchain->images_count} +{ + loader.execute(this); +} + +GraphicsPipeline::~GraphicsPipeline() +{ + loader.revert(this); +} + +void +GraphicsPipeline::draw() +{ + vkWaitForFences(cg_core.vk_device_with_swapchain->device, 1, + &this->in_flight_fences[this->current_frame], VK_TRUE, + std::numeric_limits<uint64_t>::max()); + vkResetFences(cg_core.vk_device_with_swapchain->device, 1, + &this->in_flight_fences[this->current_frame]); + + uint32_t image_index; + vkAcquireNextImageKHR( + cg_core.vk_device_with_swapchain->device, cg_core.vk_swapchain->swapchain, + std::numeric_limits<uint64_t>::max(), + this->image_available_semaphores[this->current_frame], + VK_NULL_HANDLE, &image_index); + + auto &draw_command_buffer = this->draw_command_buffers[this->current_frame]; + vkResetCommandBuffer(draw_command_buffer, 0); + + // Load command. + { + VkCommandBufferBeginInfo begin_info{}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = 0; + begin_info.pInheritanceInfo = nullptr; + if (vkBeginCommandBuffer(draw_command_buffer, &begin_info) != VK_SUCCESS) + { + throw std::runtime_error{"Failed to beggin draw command buffer."}; + } + + // Dark gray blue. + std::array<VkClearValue, 2> clear_values{}; + clear_values[0].color = {0.12f, 0.12f, 0.18f, 1.0f}; + clear_values[1].depthStencil = {1.0f, 0}; + + VkRenderPassBeginInfo render_pass_begin{}; + render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_begin.pNext = nullptr; + render_pass_begin.renderPass = this->render_pass; + render_pass_begin.framebuffer = this->swapchain_framebuffers[image_index]; + render_pass_begin.renderArea.offset = {0, 0}; + render_pass_begin.renderArea.extent = { + cg_core.screen_width, cg_core.screen_height}; + render_pass_begin.clearValueCount = clear_values.size(); + render_pass_begin.pClearValues = clear_values.data(); + + vkCmdBeginRenderPass( + draw_command_buffer, &render_pass_begin,VK_SUBPASS_CONTENTS_INLINE); + + VkViewport vk_viewport{}; + vk_viewport.width = static_cast<float>(cg_core.screen_width); + vk_viewport.height = static_cast<float>(cg_core.screen_height); + vk_viewport.minDepth = 0.0f; + vk_viewport.maxDepth = 1.0f; + vkCmdSetViewport(draw_command_buffer, 0, 1, &vk_viewport); + + VkRect2D vk_scissor{}; + vk_scissor.extent.width = cg_core.screen_width; + vk_scissor.extent.height = cg_core.screen_height; + vk_scissor.offset.x = 0; + vk_scissor.offset.y = 0; + vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor); + + // Draw models + for(auto& [model, instances] : this->models_to_draw[this->current_frame]) + { + // Commands + { + std::array<VkDescriptorSet, 2> vk_descriptor_sets{ + model->descriptor_sets[image_index], + this->view_projection_descriptor_sets[image_index]}; + VkBuffer vertex_buffers[]{model->vertex_buffer->buffer}; + VkDeviceSize offsets[]{0}; + + vkCmdBindDescriptorSets( + draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + this->pipeline_layout, 0, vk_descriptor_sets.size(), + vk_descriptor_sets.data(), 0, nullptr); + vkCmdBindPipeline( + draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + this->graphic_pipeline); + vkCmdBindVertexBuffers( + draw_command_buffer, 0, 1, vertex_buffers, offsets); + vkCmdBindIndexBuffer( + draw_command_buffer, model->index_buffer->buffer, 0, + VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed( + draw_command_buffer, model->index_count, instances.size(), 0, 0, 0); + } + + VK::UBOModelInstance ubo_model_instance; + + for(int i{0}; i < instances.size(); i++) + { + // Object matrix. + glm::mat4 instance_matrix{1.0f}; + instance_matrix = glm::rotate( + instance_matrix, glm::radians(instances[i].rotation.x), + glm::vec3{1.0, 0.0, 0.0}); + instance_matrix = glm::rotate( + instance_matrix, glm::radians(instances[i].rotation.y), + glm::vec3{0.0, 1.0, 0.0}); + instance_matrix = glm::rotate( + instance_matrix, glm::radians(instances[i].rotation.z), + glm::vec3{0.0, 0.0, 1.0}); + instance_matrix = glm::translate( + instance_matrix, instances[i].position); + + ubo_model_instance.model[i] = instance_matrix; + } + + model->ub_model_instance[image_index].copy_data(&ubo_model_instance); + } + vkCmdEndRenderPass(draw_command_buffer); + + if(vkEndCommandBuffer(draw_command_buffer) != VK_SUCCESS) + throw std::runtime_error{"Failed to end draw command buffer."}; + } + + // Update uniform buffers + { + VK::UBOViewProjection ubo_view_projection{}; + + // View matrix. + ubo_view_projection.view = glm::mat4{1.0f}; + ubo_view_projection.view = glm::translate( + ubo_view_projection.view, this->camera->position); + ubo_view_projection.view = glm::rotate( + ubo_view_projection.view, this->camera->rotation.x, + glm::vec3{1.0, 0.0, 0.0}); + ubo_view_projection.view = glm::rotate( + ubo_view_projection.view, this->camera->rotation.y, + glm::vec3{0.0, 1.0, 0.0}); + ubo_view_projection.view = glm::rotate( + ubo_view_projection.view, this->camera->rotation.z, + glm::vec3{0.0, 0.0, 1.0}); + ubo_view_projection.view = glm::inverse(ubo_view_projection.view); + + // Projection matrix. + ubo_view_projection.proj = glm::perspective( + glm::radians(45.0f), + cg_core.screen_width / static_cast<float>(cg_core.screen_height), + 0.1f, 10.0f); + ubo_view_projection.proj[1][1] *= -1; + + this->ub_view_projection[image_index].copy_data(&ubo_view_projection); + } + + // Submit drawing command. + { + auto queue{this->queue_family->get_queue()}; + + VkSemaphore wait_semaphores[]{ + this->image_available_semaphores[this->current_frame]}; + VkPipelineStageFlags wait_stages[] = + {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + VkSemaphore signal_semaphores[]{ + this->render_finished_semaphores[this->current_frame]}; + + VkSubmitInfo submit_info{}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = nullptr; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = wait_semaphores; + submit_info.pWaitDstStageMask = wait_stages; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &draw_command_buffer; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = signal_semaphores; + + if(vkQueueSubmit( + queue.queue, 1, &submit_info, + this->in_flight_fences[this->current_frame]) != VK_SUCCESS) + throw std::runtime_error{"Failed to submit draw command buffer."}; + + VkSwapchainKHR swap_chains[]{cg_core.vk_swapchain->swapchain}; + + VkPresentInfoKHR present_info{}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.pNext = nullptr; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = signal_semaphores; + present_info.swapchainCount = 1; + present_info.pSwapchains = swap_chains; + present_info.pImageIndices = &image_index; + present_info.pResults = nullptr; + + vkQueuePresentKHR(queue.queue, &present_info); + + // Prepare for the next frame. + this->current_frame = + (this->current_frame + 1) % this->max_frames_in_flight; + this->models_to_draw[this->current_frame].clear(); + } +} + +} diff --git a/src/vk/graphics_pipeline.hpp b/src/vk/graphics_pipeline.hpp new file mode 100644 index 0000000..c7cc0a0 --- /dev/null +++ b/src/vk/graphics_pipeline.hpp @@ -0,0 +1,80 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_GRAPHICS_PIPELINE_H +#define CANDY_GEAR_VK_GRAPHICS_PIPELINE_H 1 + +#include <memory> +#include <unordered_map> + +#include "../command.hpp" +#include "core.hpp" +#include "camera.hpp" +#include "command_pool.hpp" +#include "model.hpp" +#include "model_instance.hpp" +#include "uniform_buffer.hpp" + +namespace VK +{ + +struct GraphicsPipeline +{ + VkDescriptorSetLayout descriptor_set_layout_model_instance; + VkDescriptorSetLayout descriptor_set_layout_view_projection; + VkPipelineLayout pipeline_layout; + + // Depth image. + VkImage depth_image; + VkDeviceMemory depth_image_memory; + VkImageView depth_image_view; + + // FIXME: if this vector get resized, it will cause a segmentation fault! + std::vector<UniformBuffer> ub_view_projection; + + VkDescriptorPool descriptor_pool; + std::vector<VkDescriptorSet> view_projection_descriptor_sets; + + VkRenderPass render_pass; + std::vector<VkFramebuffer> swapchain_framebuffers; + VkPipeline graphic_pipeline; + + QueueFamily *queue_family; + VkCommandPool command_pool; + std::vector<VkCommandBuffer> draw_command_buffers; + + // Buffering control. + static const int max_frames_in_flight{2}; + size_t current_frame; + std::vector<VkSemaphore> image_available_semaphores; + std::vector<VkSemaphore> render_finished_semaphores; + std::vector<VkFence> in_flight_fences; + + std::shared_ptr<Camera> camera; + std::vector<std::unordered_map< + std::shared_ptr<Model>, std::vector<ModelInstance>>> + models_to_draw; + + GraphicsPipeline(); + ~GraphicsPipeline(); + + void + draw(); +}; + +} + +#endif /* CANDY_GEAR_VK_GRAPHICS_PIPELINE_H */ diff --git a/src/vk/image.cpp b/src/vk/image.cpp new file mode 100644 index 0000000..11dc9a5 --- /dev/null +++ b/src/vk/image.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "image.hpp" + +#include "../core.hpp" + +namespace VK::Image +{ + +Error::Error(const std::string &m): + error(m) +{ +} + +Error::Error(const char &m): + Error{std::string{m}} +{ +} + +const char* +Error::what() const noexcept +{ + return this->error.c_str(); +} + +void +create( + VK::Device *device, + VkImage *image, + VkDeviceMemory *image_memory, + VkFormat format, + const VkExtent3D &extent3d, + uint32_t mip_levels, + VkImageTiling image_tiling, + VkImageUsageFlags usage) +{ + VkImageCreateInfo image_info{}; + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.pNext = nullptr; + image_info.flags = 0; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = format; + image_info.extent = extent3d; + image_info.mipLevels = mip_levels; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.tiling = image_tiling; + image_info.usage = usage; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.queueFamilyIndexCount = 0; + image_info.pQueueFamilyIndices = nullptr; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + if(vkCreateImage( + device->device, &image_info, nullptr, image) != VK_SUCCESS) + throw Error{"Failed to create Vulkan image."}; + + VkMemoryRequirements memory_requirements; + vkGetImageMemoryRequirements(device->device, *image, &memory_requirements); + + VkMemoryAllocateInfo memory_alloc_info{}; + memory_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_alloc_info.allocationSize = memory_requirements.size; + device->select_memory_type(&memory_alloc_info.memoryTypeIndex, + &memory_requirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + if(vkAllocateMemory( + device->device, &memory_alloc_info, nullptr, image_memory) + != VK_SUCCESS) + { + vkDestroyImage(device->device, *image, nullptr); + throw Error{"Failed to allocate memory for Vulkan image."}; + } + + vkBindImageMemory(device->device, *image, *image_memory, 0); +} + +void create_view( + VK::Device *device, + VkImageView *image_view, + const VkImage &image, + VkFormat format, + VkImageAspectFlags image_aspect_flags) +{ + VkImageViewCreateInfo image_view_info{}; + image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_info.pNext = nullptr; + image_view_info.flags = 0; + image_view_info.image = image; + image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + image_view_info.format = format; + image_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.subresourceRange.aspectMask = image_aspect_flags; + image_view_info.subresourceRange.baseMipLevel = 0; + image_view_info.subresourceRange.levelCount = 1; + image_view_info.subresourceRange.baseArrayLayer = 0; + image_view_info.subresourceRange.layerCount = 1; + + if(vkCreateImageView(device->device, &image_view_info, + nullptr, image_view) != VK_SUCCESS) + throw Error{"Failed to create texture view."}; + +} + +} diff --git a/src/vk/image.hpp b/src/vk/image.hpp new file mode 100644 index 0000000..63ebab6 --- /dev/null +++ b/src/vk/image.hpp @@ -0,0 +1,61 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BLUE_KITTY_VK_IMAGE_H +#define BLUE_KITTY_VK_IMAGE_H 1 + +#include <memory> +#include <string> + +#include "core.hpp" +#include "device.hpp" + +namespace VK::Image +{ + +struct Error: public std::exception +{ + Error(const std::string &m); + Error(const char &m); + + const char* + what() const noexcept; + +private: + std::string error; +}; + +void +create( + VK::Device *device, + VkImage *image, + VkDeviceMemory *image_memory, + VkFormat format, + const VkExtent3D &extent3d, + uint32_t mip_levels, + VkImageTiling image_tiling, + VkImageUsageFlags usage); + +void +create_view( + VK::Device *device, + VkImageView *image_view, + const VkImage &image, + VkFormat format, + VkImageAspectFlags image_aspect_flags); +} + +#endif /* CANDY_GEAR_VK_IMAGE_H */ diff --git a/src/vk/model.cpp b/src/vk/model.cpp new file mode 100644 index 0000000..d47db8a --- /dev/null +++ b/src/vk/model.cpp @@ -0,0 +1,324 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "model.hpp" + +#include <array> +#include <fstream> + +#include "../command.hpp" +#include "../core.hpp" +#include "vertex.hpp" + +namespace +{ + +// Data that is only needed for the command chain but not for the Model goes +// here. +struct ModelBuilder +{ + std::string model_path; + VK::Model *model; + + ModelBuilder(VK::Model *m, std::string mp); + ModelBuilder(VK::Model *m, const char* mp); +}; + +ModelBuilder::ModelBuilder(VK::Model *m, std::string mp): + model{m}, + model_path{mp} +{ +} + +ModelBuilder::ModelBuilder(VK::Model *m, const char *mp): + ModelBuilder{m, std::string(mp)} +{ +} + +struct MeshData +{ + glm::vec3 color; + + uint32_t vertex_base; + uint32_t vertex_count; + uint32_t index_base; + uint32_t index_count; +}; + +uint32_t read_uint32_from_file(std::ifstream &input_file) +{ + uint32_t data{}; + input_file.read((char*)&data, sizeof(uint32_t)); + return data; +} + +glm::vec2 read_vec2_from_file(std::ifstream &input_file) +{ + glm::vec2 data{}; + input_file.read((char*)&data.x, sizeof(glm::vec2::value_type)); + input_file.read((char*)&data.y, sizeof(glm::vec2::value_type)); + return data; +} + +glm::vec3 read_vec3_from_file(std::ifstream &input_file) +{ + glm::vec3 data{}; + input_file.read((char*)&data.x, sizeof(glm::vec3::value_type)); + input_file.read((char*)&data.y, sizeof(glm::vec3::value_type)); + input_file.read((char*)&data.z, sizeof(glm::vec3::value_type)); + return data; +} + +void +load_mesh(void *obj) +{ + auto self = static_cast<ModelBuilder*>(obj); + + std::ifstream input_file{self->model_path}; + if(!input_file.is_open()) throw CommandError{"Failed to open file."}; + + std::vector<MeshData> meshes{}; + + self->model->queue_family = + cg_core.vk_device_with_swapchain->get_queue_family_with_graphics(); + VK::Queue transfer_queue{self->model->queue_family->get_queue()}; + + // Load meshes. + { + uint32_t meshes_count{read_uint32_from_file(input_file)}; + meshes.resize(meshes_count); + + for(uint32_t i{0}; i < meshes_count; i++) + { + meshes[i].color = read_vec3_from_file(input_file); + + meshes[i].vertex_base = read_uint32_from_file(input_file); + meshes[i].vertex_count = read_uint32_from_file(input_file); + meshes[i].index_base = read_uint32_from_file(input_file); + meshes[i].index_count = read_uint32_from_file(input_file); + } + } + + // Load vertexes. + { + uint32_t vertex_count{read_uint32_from_file(input_file)}; + std::vector<VK::Vertex> vertexes{vertex_count}; + + for(auto mesh: meshes) + { + for(uint32_t i{mesh.vertex_base}; i < mesh.vertex_count; i++) + { + vertexes[i].position = read_vec3_from_file(input_file); + vertexes[i].normal = read_vec3_from_file(input_file); + vertexes[i].texture_coord = read_vec2_from_file(input_file); + vertexes[i].color = mesh.color; + } + } + + void *vertexes_data{vertexes.data()}; + size_t vertexes_size = sizeof(vertexes[0]) * vertexes.size(); + self->model->source_vertex_buffer = new VK::SourceBuffer{ + self->model->queue_family->device, vertexes_data, vertexes_size}; + self->model->vertex_buffer = new VK::DestinationBuffer{ + self->model->queue_family, self->model->source_vertex_buffer, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT}; + } + + // Load indexes. + { + self->model->index_count = read_uint32_from_file(input_file); + std::vector<uint32_t> indexes(self->model->index_count); + + for(uint32_t i{0}; i < self->model->index_count; i++) + { + indexes[i] = read_uint32_from_file(input_file); + } + + void *indexes_data{indexes.data()}; + size_t indexes_size{sizeof(indexes[0]) * indexes.size()}; + VK::SourceBuffer source_index_buffer{ + self->model->queue_family->device, indexes_data, indexes_size}; + self->model->index_buffer = new VK::DestinationBuffer{ + self->model->queue_family, &source_index_buffer, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT}; + } +} + +void +unload_mesh(void *obj) +{ + auto self = static_cast<ModelBuilder*>(obj); + + delete self->model->index_buffer; + delete self->model->vertex_buffer; + delete self->model->source_vertex_buffer; +} + +auto +load_uniform_buffers(void *obj) +{ + auto self = static_cast<ModelBuilder*>(obj); + + self->model->ub_model_instance.reserve(cg_core.vk_swapchain->images_count); + for(int i{0}; i < cg_core.vk_swapchain->images_count; i++) + self->model->ub_model_instance.emplace_back( + cg_core.vk_device_with_swapchain, sizeof(VK::UBOModelInstance)); +} + +void +unload_uniform_buffers(void *obj) +{ + auto self = static_cast<ModelBuilder*>(obj); + + self->model->ub_model_instance.clear(); +} + +void +load_descriptor_set_pool(void *obj) +{ + auto self = static_cast<ModelBuilder*>(obj); + + std::array<VkDescriptorPoolSize, 2> descriptor_pool_sizes{}; + descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_pool_sizes[0].descriptorCount = + self->model->ub_model_instance.size(); + descriptor_pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_pool_sizes[1].descriptorCount = + self->model->ub_model_instance.size(); + + VkDescriptorPoolCreateInfo pool_info{}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.pNext = nullptr; + pool_info.flags = 0; + pool_info.maxSets = self->model->ub_model_instance.size(); + pool_info.poolSizeCount = descriptor_pool_sizes.size(); + pool_info.pPoolSizes = descriptor_pool_sizes.data(); + + if(vkCreateDescriptorPool( + self->model->queue_family->device->device, &pool_info, nullptr, + &self->model->descriptor_pool) != VK_SUCCESS) + throw CommandError{"Failed to create a Vulkan descriptor pool."}; +} + +void +unload_descriptor_set_pool(void *obj) +{ + auto self = static_cast<ModelBuilder*>(obj); + + vkDestroyDescriptorPool( + self->model->queue_family->device->device, self->model->descriptor_pool, + nullptr); +} + +void +load_descriptor_sets(void *obj) +{ + auto self = static_cast<ModelBuilder*>(obj); + + std::vector<VkDescriptorSetLayout> layouts( + cg_core.vk_swapchain->images_count, + cg_core.vk_graphics_pipeline->descriptor_set_layout_model_instance); + + VkDescriptorSetAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = self->model->descriptor_pool; + alloc_info.descriptorSetCount = layouts.size(); + alloc_info.pSetLayouts = layouts.data(); + + self->model->descriptor_sets.resize(layouts.size()); + if(vkAllocateDescriptorSets( + self->model->queue_family->device->device, &alloc_info, + self->model->descriptor_sets.data()) != VK_SUCCESS) + CommandError{"Failed to create Vulkan descriptor set."}; +} + +void +load_buffers_to_descriptor_sets(void *obj) +{ + auto self = static_cast<ModelBuilder*>(obj); + + for(auto i{0}; i < self->model->ub_model_instance.size(); i++) + { + VkDescriptorBufferInfo buffer_info{}; + buffer_info.buffer = self->model->ub_model_instance[i].buffer; + buffer_info.offset = 0; + buffer_info.range = sizeof(VK::UBOModelInstance); + + VkDescriptorImageInfo image_info{}; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_info.imageView = self->model->texture->view; + image_info.sampler = self->model->texture->sampler; + + std::array<VkWriteDescriptorSet, 2> write_descriptors{}; + write_descriptors[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptors[0].dstSet = self->model->descriptor_sets[i]; + write_descriptors[0].dstBinding = 0; + write_descriptors[0].dstArrayElement = 0; + write_descriptors[0].descriptorCount = 1; + write_descriptors[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + write_descriptors[0].pBufferInfo = &buffer_info; + write_descriptors[0].pImageInfo = nullptr; + write_descriptors[0].pTexelBufferView = nullptr; + + write_descriptors[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptors[1].dstSet = self->model->descriptor_sets[i]; + write_descriptors[1].dstBinding = 1; + write_descriptors[1].dstArrayElement = 0; + write_descriptors[1].descriptorCount = 1; + write_descriptors[1].descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_descriptors[1].pBufferInfo = nullptr; + write_descriptors[1].pImageInfo = &image_info; + write_descriptors[1].pTexelBufferView = nullptr; + + vkUpdateDescriptorSets( + cg_core.vk_device_with_swapchain->device, write_descriptors.size(), + write_descriptors.data(), 0, nullptr); + } +} + +static const CommandChain loader{ + {&load_mesh, &unload_mesh}, + {&load_uniform_buffers, &unload_uniform_buffers}, + {&load_descriptor_set_pool, &unload_descriptor_set_pool}, + {&load_descriptor_sets, nullptr}, + {&load_buffers_to_descriptor_sets, nullptr}, +}; + +} + +namespace VK +{ + +Model::Model(std::string model_path, std::shared_ptr<Texture> texture): + texture{texture} +{ + ModelBuilder model_builder(this, model_path); + loader.execute(&model_builder); +} + +Model::Model(const char* model_path, std::shared_ptr<Texture> texture): + Model{std::string(model_path), texture} +{ +} + +Model::~Model() +{ + ModelBuilder model_builder(this, ""); + loader.revert(&model_builder); +} + +} diff --git a/src/vk/model.hpp b/src/vk/model.hpp new file mode 100644 index 0000000..d6f8b69 --- /dev/null +++ b/src/vk/model.hpp @@ -0,0 +1,55 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_MODEL_H +#define CANDY_GEAR_VK_MODEL_H 1 + +#include <string> +#include <vector> + +#include "core.hpp" +#include "destination_buffer.hpp" +#include "queue_family.hpp" +#include "uniform_buffer.hpp" +#include "texture.hpp" + +namespace VK +{ + +struct Model +{ + QueueFamily *queue_family; + + uint32_t index_count; + SourceBuffer *source_vertex_buffer; + DestinationBuffer *index_buffer; + DestinationBuffer *vertex_buffer; + + std::vector<UniformBuffer> ub_model_instance; + + VkDescriptorPool descriptor_pool; + std::vector<VkDescriptorSet> descriptor_sets; + + std::shared_ptr<Texture> texture; + + Model(std::string model_path, std::shared_ptr<Texture> texture); + Model(const char* model_path, std::shared_ptr<Texture> texture); + ~Model(); +}; + +} + +#endif /* CANDY_GEAR_VK_MODEL_H */ diff --git a/src/vk/model_instance.hpp b/src/vk/model_instance.hpp new file mode 100644 index 0000000..1383737 --- /dev/null +++ b/src/vk/model_instance.hpp @@ -0,0 +1,32 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_MODEL_INSTANCE_H +#define CANDY_GEAR_VK_MODEL_INSTANCE_H 1 + +#include "core.hpp" + +namespace VK +{ + +struct ModelInstance +{ + glm::vec3 position, rotation; +}; + +} + +#endif /* CANDY_GEAR_VK_MODEL_INSTANCE_H */ diff --git a/src/vk/queue.cpp b/src/vk/queue.cpp new file mode 100644 index 0000000..deb59ba --- /dev/null +++ b/src/vk/queue.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "queue.hpp" + +#include "queue_family.hpp" + +namespace VK +{ + +Queue::Queue( + VK::QueueFamily *queue_family, VkQueue queue, int queue_index): + queue_family{queue_family}, + queue{queue}, + queue_index{queue_index} +{ + this->queue_family->queue_states[this->queue_index].busy = true; +} + +Queue::Queue(Queue &&that): + queue{that.queue}, + queue_family{that.queue_family}, + queue_index{that.queue_index} +{ + that.queue_family = nullptr; +} + +Queue& Queue::operator=(Queue &&that) +{ + this->queue = that.queue; + this->queue_family = that.queue_family; + this->queue_index = that.queue_index; + + that.queue_family = nullptr; + + return *this; +} + +Queue::~Queue() +{ + if(this->queue_family) + { + std::unique_lock<std::mutex> lock{this->queue_family->queue_mutex}; + this->queue_family->queue_states[this->queue_index].busy = false; + } +} + +} diff --git a/src/vk/queue.hpp b/src/vk/queue.hpp new file mode 100644 index 0000000..955daa5 --- /dev/null +++ b/src/vk/queue.hpp @@ -0,0 +1,82 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_QUEUE_H +#define CANDY_GEAR_VK_QUEUE_H 1 + +#include "core.hpp" + +namespace VK +{ +class QueueFamily; + +struct Queue +{ + friend class VK::QueueFamily; + + Queue(const Queue &t) = delete; + Queue& operator=(const Queue &t) = delete; + + VkQueue queue; + + template<typename T> + void submit_one_time_command( + const VkCommandBuffer vk_command_buffer, T commands); + + Queue(Queue &&that); + Queue& operator=(Queue &&that); + + ~Queue(); + +private: + VK::QueueFamily *queue_family; + int queue_index; + + Queue(VK::QueueFamily *queue_family, VkQueue queue, int queue_index); +}; + +template<typename T> void +Queue::submit_one_time_command( + const VkCommandBuffer vk_command_buffer, T commands) +{ + VkCommandBufferBeginInfo buffer_begin_info{}; + buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(vk_command_buffer, &buffer_begin_info); + + commands(); + + vkEndCommandBuffer(vk_command_buffer); + + VkSubmitInfo submit_info{}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = nullptr; + submit_info.waitSemaphoreCount = 0; + submit_info.pWaitSemaphores = nullptr; + submit_info.pWaitDstStageMask = nullptr; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &vk_command_buffer; + submit_info.signalSemaphoreCount = 0; + submit_info.pSignalSemaphores = nullptr; + + vkQueueSubmit(this->queue, 1, &submit_info, VK_NULL_HANDLE); + vkQueueWaitIdle(this->queue); +} + +} + +#endif /* CANDY_GEAR_VK_QUEUE_H */ diff --git a/src/vk/queue_family.cpp b/src/vk/queue_family.cpp new file mode 100644 index 0000000..7c91ba2 --- /dev/null +++ b/src/vk/queue_family.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "queue_family.hpp" + +#include <iostream> + +#include "../core.hpp" + +namespace VK +{ + +QueueFamily::QueueFamily( + VK::Device *device, uint32_t family_index, + const VkQueueFamilyProperties &queue_family_properties): + queue_mutex{} +{ + +#ifdef DEBUG + std::cout << "Queue quantity: " << queue_family_properties.queueCount << + std::endl; + std::cout << "Graphics: " << + (queue_family_properties.queueFlags & VK_QUEUE_GRAPHICS_BIT ? + "true" : "false") << std::endl; + std::cout << "Compute: " << + (queue_family_properties.queueFlags & VK_QUEUE_COMPUTE_BIT ? + "true" : "false") << std::endl; + std::cout << "Transfer: " << + (queue_family_properties.queueFlags & VK_QUEUE_TRANSFER_BIT ? + "true" : "false") << std::endl; + std::cout << "Sparse Binding: " << + (queue_family_properties.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT ? + "true" : "false") << std::endl; +#endif + + this->device = device; + this->family_index = family_index; + this->family_properties = queue_family_properties; + + // Create queues + { + auto queue_count = this->family_properties.queueCount; + this->queue_states.resize(queue_count); + + for(auto i{0}; i < queue_count; i++) + { + vkGetDeviceQueue(device->device, this->family_index, i, + &this->queue_states[i].queue); + if(this->queue_states[i].queue == VK_NULL_HANDLE) + throw std::runtime_error("Failed to get Vulkan queue."); + } + } +} + +Queue +QueueFamily::get_queue() +{ + std::unique_lock<std::mutex> lock{this->queue_mutex}; + + for(auto i{0}; i < this->queue_states.size(); i++) + if(!this->queue_states[i].busy) + return Queue(this, this->queue_states[i].queue, i); + + throw std::length_error("No free queues found."); +} + +} diff --git a/src/vk/queue_family.hpp b/src/vk/queue_family.hpp new file mode 100644 index 0000000..83efc00 --- /dev/null +++ b/src/vk/queue_family.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_QUEUE_FAMILY_H +#define CANDY_GEAR_VK_QUEUE_FAMILY_H 1 + +#include <mutex> +#include <vector> + +#include "core.hpp" +#include "queue.hpp" + +namespace VK +{ +class Device; + +struct QueueState +{ + VkQueue queue; + bool busy; +}; + +class QueueFamily +{ + friend class Queue; + + std::mutex queue_mutex; + std::vector<QueueState> queue_states; + +public: + VK::Device *device; + + uint32_t family_index; + VkQueueFamilyProperties family_properties; + + QueueFamily(VK::Device *device, uint32_t family_index, + const VkQueueFamilyProperties &queue_family_properties); + + Queue + get_queue(); +}; + +} + +#endif /* CANDY_GEAR_VK_QUEUE_FAMILY_H */ diff --git a/src/vk/source_buffer.cpp b/src/vk/source_buffer.cpp new file mode 100644 index 0000000..7e6c570 --- /dev/null +++ b/src/vk/source_buffer.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "source_buffer.hpp" + +#include <cstring> + +namespace VK +{ + +SourceBuffer::SourceBuffer(Device *device, void *data, size_t data_size): + data{data} +{ + this->device = device; + this->device_size = data_size; + this->buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + this->memory_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + + try + { + VK::BaseBuffer::loader.execute(static_cast<VK::BaseBuffer*>(this)); + } + catch(const CommandError &command_error) + { + std::string error{"Could not initialize Vulkan source buffer → "}; + error += command_error.what(); + throw std::runtime_error{error}; + } + this->copy_data(); +} + +SourceBuffer::SourceBuffer(SourceBuffer &&that) +{ + this->buffer = that.buffer; + this->device_memory = that.device_memory; + this->device_size = that.device_size; + this->buffer_usage = that.buffer_usage; + this->memory_properties = that.memory_properties; + this->data = that.data; + + that.buffer = VK_NULL_HANDLE; + that.device_memory = VK_NULL_HANDLE; + that.data = nullptr; +} + +SourceBuffer& +SourceBuffer::operator=(SourceBuffer &&that) +{ + this->buffer = that.buffer; + this->device_memory = that.device_memory; + this->device_size = that.device_size; + this->buffer_usage = that.buffer_usage; + this->memory_properties = that.memory_properties; + this->data = that.data; + + that.buffer = VK_NULL_HANDLE; + that.device_memory = VK_NULL_HANDLE; + that.data = nullptr; + + return *this; +} + +SourceBuffer::~SourceBuffer() +{ + VK::BaseBuffer::loader.revert(static_cast<VK::BaseBuffer*>(this)); +} + +void +SourceBuffer::copy_data() +{ + void *dst_data; + vkMapMemory(this->device->device, this->device_memory, 0, this->device_size, + 0, &dst_data); + memcpy(dst_data, this->data, static_cast<size_t>(this->device_size)); + vkUnmapMemory(this->device->device, this->device_memory); +} + +} diff --git a/src/vk/source_buffer.hpp b/src/vk/source_buffer.hpp new file mode 100644 index 0000000..713d708 --- /dev/null +++ b/src/vk/source_buffer.hpp @@ -0,0 +1,47 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_SOURCE_BUFFER_H +#define CANDY_GEAR_VK_SOURCE_BUFFER_H 1 + +#include "base_buffer.hpp" + +namespace VK +{ + +struct SourceBuffer: public BaseBuffer +{ + SourceBuffer(const SourceBuffer &t) = delete; + SourceBuffer& + operator=(const SourceBuffer &t) = delete; + + void *data; + + SourceBuffer(Device *device, void *data, size_t data_size); + + SourceBuffer(SourceBuffer &&that); + SourceBuffer& + operator=(SourceBuffer &&that); + + ~SourceBuffer(); + + void + copy_data(); +}; + +} + +#endif /* CANDY_GEAR_VK_SOURCE_BUFFER_H */ diff --git a/src/vk/swapchain.cpp b/src/vk/swapchain.cpp new file mode 100644 index 0000000..3b26b00 --- /dev/null +++ b/src/vk/swapchain.cpp @@ -0,0 +1,151 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "swapchain.hpp" + +#include "../core.hpp" + +#include <vector> +#include <iostream> + +namespace +{ + +void +load_swapchain(void *obj) +{ + auto self = static_cast<VK::Swapchain*>(obj); + + // Surface formats. + uint32_t vk_surface_format_count; + std::vector<VkSurfaceFormatKHR> vk_surface_formats; + vkGetPhysicalDeviceSurfaceFormatsKHR( + cg_core.vk_device_with_swapchain->physical_device, cg_core.window_surface, + &vk_surface_format_count, nullptr); + vk_surface_formats.resize(vk_surface_format_count); + vkGetPhysicalDeviceSurfaceFormatsKHR( + cg_core.vk_device_with_swapchain->physical_device, cg_core.window_surface, + &vk_surface_format_count, vk_surface_formats.data()); + + VkSwapchainCreateInfoKHR swapchain_create_info = {}; + swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchain_create_info.pNext = nullptr; + swapchain_create_info.flags = 0; + swapchain_create_info.surface = cg_core.window_surface; + swapchain_create_info.minImageCount = 3; // triple buffering. + + self->image_format = vk_surface_formats[0].format; + swapchain_create_info.imageFormat = self->image_format; + swapchain_create_info.imageColorSpace = vk_surface_formats[0].colorSpace; + + swapchain_create_info.imageExtent = { + cg_core.screen_width, cg_core.screen_height}; + swapchain_create_info.imageArrayLayers = 1; + swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_create_info.queueFamilyIndexCount = 0; + swapchain_create_info.pQueueFamilyIndices = nullptr; + swapchain_create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; + swapchain_create_info.clipped = VK_FALSE; + swapchain_create_info.oldSwapchain = VK_NULL_HANDLE; + + if(vkCreateSwapchainKHR( + cg_core.vk_device_with_swapchain->device, &swapchain_create_info, + nullptr, &self->swapchain) != VK_SUCCESS) + throw CommandError{"Vulkan failed to create swapchain."}; + + vkGetSwapchainImagesKHR( + cg_core.vk_device_with_swapchain->device, self->swapchain, + &self->images_count, nullptr); + self->images = new VkImage[self->images_count]; + vkGetSwapchainImagesKHR( + cg_core.vk_device_with_swapchain->device, self->swapchain, + &self->images_count, self->images); +} + +void +unload_swapchain(void *obj) +{ + auto self = static_cast<VK::Swapchain*>(obj); + + delete[] self->images; + vkDestroySwapchainKHR( + cg_core.vk_device_with_swapchain->device, self->swapchain, nullptr); +} + +void +load_image_view(void *obj) +{ + auto self = static_cast<VK::Swapchain*>(obj); + + self->image_views = new VkImageView[self->images_count]; + for(auto i{0}; i < self->images_count; i++) + { + VkImageViewCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + create_info.image = self->images[i]; + create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + create_info.format = self->image_format; + create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + create_info.subresourceRange.baseMipLevel = 0; + create_info.subresourceRange.levelCount = 1; + create_info.subresourceRange.baseArrayLayer = 0; + create_info.subresourceRange.layerCount = 1; + + if(vkCreateImageView( + cg_core.vk_device_with_swapchain->device, &create_info, nullptr, + &self->image_views[i])) + throw CommandError{"Could no create Image View for swapchain."}; + } +} + +void +unload_image_view(void *obj) +{ + auto self = static_cast<VK::Swapchain*>(obj); + + for(auto i{0}; i < self->images_count; i++) + vkDestroyImageView( + cg_core.vk_device_with_swapchain->device, self->image_views[i], nullptr); +} + +const CommandChain loader{ + {&load_swapchain, &unload_swapchain}, + {&load_image_view, &unload_image_view} +}; + +} + +namespace VK +{ + +Swapchain::Swapchain() +{ + loader.execute(this); +} + +Swapchain::~Swapchain() +{ + loader.revert(this); +} + +} diff --git a/src/vk/swapchain.hpp b/src/vk/swapchain.hpp new file mode 100644 index 0000000..64df208 --- /dev/null +++ b/src/vk/swapchain.hpp @@ -0,0 +1,41 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_SWAPCHAIN_H +#define CANDY_GEAR_VK_SWAPCHAIN_H 1 + +#include "core.hpp" +#include "../command.hpp" + +namespace VK +{ + +struct Swapchain +{ + VkSwapchainKHR swapchain; + VkFormat image_format; + + uint32_t images_count; + VkImage *images; + VkImageView *image_views; + + Swapchain(); + ~Swapchain(); +}; + +} + +#endif /* CANDY_GEAR_VK_SWAPCHAIN_H */ diff --git a/src/vk/texture.cpp b/src/vk/texture.cpp new file mode 100644 index 0000000..ae35035 --- /dev/null +++ b/src/vk/texture.cpp @@ -0,0 +1,292 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "texture.hpp" + +#include <SDL2/SDL.h> +#include <SDL2/SDL_image.h> + +#include "../command.hpp" +#include "../core.hpp" +#include "image.hpp" +#include "source_buffer.hpp" + +namespace +{ + +struct TextureBuilder +{ + VK::Texture *texture; + std::string texture_path; + + TextureBuilder(VK::Texture *t, std::string tp); + TextureBuilder(VK::Texture *t, const char* tp); +}; + +TextureBuilder::TextureBuilder(VK::Texture *t, std::string tp): + texture{t}, + texture_path{tp} +{ +} + +TextureBuilder::TextureBuilder(VK::Texture *t, const char* tp): + TextureBuilder{t, std::string(tp)} +{ +} + +void move_image_state( + VkCommandBuffer vk_command_buffer, VkImage vk_image, VkFormat format, + VkAccessFlags src_access_mask, VkAccessFlags dst_access_mask, + VkImageLayout old_layout, VkImageLayout new_layout, + VkPipelineStageFlags source_stage, VkPipelineStageFlags destination_stage) +{ + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.pNext = nullptr; + barrier.srcAccessMask = src_access_mask; + barrier.dstAccessMask = dst_access_mask; + barrier.oldLayout = old_layout; + barrier.newLayout = new_layout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = vk_image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier( + vk_command_buffer, source_stage, destination_stage, 0, 0, nullptr, + 0, nullptr, 1, &barrier); +} + +void +load_image(void *obj) +{ + auto self = static_cast<TextureBuilder*>(obj); + + SDL_Surface *image{nullptr}; + + // Load file image from file. + { + SDL_Surface *raw_surface{nullptr}; + raw_surface = IMG_Load(self->texture_path.c_str()); + if(raw_surface == nullptr) + { + std::string error{"Failed to load image. SDL2_image Error: "}; + error += IMG_GetError(); + throw CommandError{error}; + } + + image = SDL_ConvertSurfaceFormat(raw_surface, SDL_PIXELFORMAT_ARGB8888, 0); + if(image == nullptr) + { + std::string error{"Failed to convert image. SDL2 Error: "}; + error += SDL_GetError(); + throw CommandError{error}; + } + + self->texture->width = static_cast<uint32_t>(image->w); + self->texture->height = static_cast<uint32_t>(image->h); + self->texture->mip_levels = 1; + + SDL_FreeSurface(raw_surface); + } + + // Load file image into a vulkan buffer. + size_t image_size{static_cast<size_t>( + image->format->BytesPerPixel * image->w * image->h)}; + VK::SourceBuffer source_image_buffer{ + cg_core.vk_device_with_swapchain, image->pixels, image_size}; + + // Create vulkan image. + { + try + { + VkExtent3D vk_extent3d{}; + vk_extent3d.width = self->texture->width; + vk_extent3d.height = self->texture->height; + vk_extent3d.depth = 1; + + VK::Image::create( + cg_core.vk_device_with_swapchain, + &self->texture->image, + &self->texture->device_memory, + VK_FORMAT_R8G8B8A8_UNORM, + vk_extent3d, + self->texture->mip_levels, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + } + catch(VK::Image::Error error) + { + throw CommandError{error.what()}; + } + } + + // Copy image from vulkan buffer into vulkan image. + { + auto queue_family{ + cg_core.vk_device_with_swapchain->get_queue_family_with_presentation()}; + auto queue{queue_family->get_queue()}; + VK::CommandPool command_pool{queue_family, 1}; + VkCommandBuffer vk_command_buffer{command_pool.command_buffers[0]}; + + queue.submit_one_time_command(vk_command_buffer, [&](){ + move_image_state( + vk_command_buffer, self->texture->image, VK_FORMAT_R8G8B8A8_UNORM, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + + VkBufferImageCopy image_copy{}; + image_copy.bufferOffset = 0; + image_copy.bufferRowLength = 0; + image_copy.bufferImageHeight = 0; + image_copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_copy.imageSubresource.mipLevel = 0; + image_copy.imageSubresource.baseArrayLayer = 0; + image_copy.imageSubresource.layerCount = 1; + image_copy.imageOffset = {0, 0, 0}; + image_copy.imageExtent = {self->texture->width, self->texture->height, 1}; + + vkCmdCopyBufferToImage( + vk_command_buffer, source_image_buffer.buffer, self->texture->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + + move_image_state( + vk_command_buffer, self->texture->image, VK_FORMAT_R8G8B8A8_UNORM, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + }); + } + + // Free resources. + SDL_FreeSurface(image); +} + +void +unload_image(void *obj) +{ + auto self = static_cast<TextureBuilder*>(obj); + + vkDestroyImage( + cg_core.vk_device_with_swapchain->device, self->texture->image, nullptr); + vkFreeMemory( + cg_core.vk_device_with_swapchain->device, self->texture->device_memory, + nullptr); +} + +void +load_sampler(void *obj) +{ + auto self = static_cast<TextureBuilder*>(obj); + + VkSamplerCreateInfo sampler_info{}; + sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_info.pNext = nullptr; + sampler_info.flags = 0; + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.mipLodBias = 0.0f; + sampler_info.anisotropyEnable = VK_TRUE; + sampler_info.maxAnisotropy = 16; + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_NEVER; + sampler_info.minLod = 0.0f; + sampler_info.maxLod = 0.0f; + sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + sampler_info.unnormalizedCoordinates = VK_FALSE; + + if(vkCreateSampler( + cg_core.vk_device_with_swapchain->device, &sampler_info, nullptr, + &self->texture->sampler) != VK_SUCCESS) + throw CommandError{"Failed to create texture sampler."}; +} + +void +unload_sampler(void *obj) +{ + auto self = static_cast<TextureBuilder*>(obj); + + vkDestroySampler( + cg_core.vk_device_with_swapchain->device, self->texture->sampler, nullptr); +} + +void +load_view(void *obj) +{ + auto self = static_cast<TextureBuilder*>(obj); + + try + { + VK::Image::create_view( + cg_core.vk_device_with_swapchain, &self->texture->view, + self->texture->image, + VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); + } + catch(VK::Image::Error error) + { + throw CommandError{error.what()}; + } +} + +void +unload_view(void *obj) +{ + auto self = static_cast<TextureBuilder*>(obj); + + vkDestroyImageView( + cg_core.vk_device_with_swapchain->device, self->texture->view, nullptr); +} + +const CommandChain loader{ + {&load_image, &unload_image}, + {&load_sampler, &unload_sampler}, + {&load_view, &unload_view} +}; + +} + +namespace VK +{ + +Texture::Texture(std::string texture_path) +{ + TextureBuilder texture_builder(this, texture_path); + loader.execute(&texture_builder); +} + +Texture::Texture(const char* texture_path): + Texture{std::string(texture_path)} +{ +} + +Texture::~Texture() +{ + TextureBuilder texture_builder(this, ""); + loader.revert(&texture_builder); +} + +} diff --git a/src/vk/texture.hpp b/src/vk/texture.hpp new file mode 100644 index 0000000..35772c5 --- /dev/null +++ b/src/vk/texture.hpp @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_TEXTURE_H +#define CANDY_GEAR_VK_TEXTURE_H 1 + +#include <string> + +#include "core.hpp" + +namespace VK +{ + +struct Texture +{ + VkImage image; + VkSampler sampler; + VkImageView view; + VkDeviceMemory device_memory; + uint32_t width, height; + uint32_t mip_levels; + + Texture(std::string texture_path); + Texture(const char* texture_path); + ~Texture(); +}; + +} + +#endif /* CANDY_GEAR_TEXTURE_H */ diff --git a/src/vk/uniform_buffer.cpp b/src/vk/uniform_buffer.cpp new file mode 100644 index 0000000..dd61898 --- /dev/null +++ b/src/vk/uniform_buffer.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "uniform_buffer.hpp" + +#include <cstring> +#include <stdexcept> + +namespace VK +{ + +UniformBuffer::UniformBuffer(Device *device, VkDeviceSize data_size) +{ + this->device = device; + this->device_size = data_size; + this->buffer_usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + this->memory_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + + try + { + BaseBuffer::loader.execute(static_cast<BaseBuffer*>(this)); + } + catch(const CommandError &command_error) + { + std::string error{"Could not initialize Vulkan uniform buffer → "}; + error += command_error.what(); + throw CommandError{error}; + } +} + +UniformBuffer::~UniformBuffer() +{ + BaseBuffer::loader.revert(static_cast<BaseBuffer*>(this)); +} + +UniformBuffer::UniformBuffer(UniformBuffer &&that) +{ + this->device = that.device; + this->buffer = that.buffer; + this->device_memory = that.device_memory; + this->device_size = that.device_size; + this->buffer_usage = that.buffer_usage; + this->memory_properties = that.memory_properties; + + that.buffer = VK_NULL_HANDLE; + that.device_memory = VK_NULL_HANDLE; +} + +UniformBuffer& +UniformBuffer::operator=(UniformBuffer &&that) +{ + this->device = that.device; + this->buffer = that.buffer; + this->device_memory = that.device_memory; + this->device_size = that.device_size; + this->buffer_usage = that.buffer_usage; + this->memory_properties = that.memory_properties; + + that.buffer = VK_NULL_HANDLE; + that.device_memory = VK_NULL_HANDLE; + + return *this; +} + +void +UniformBuffer::copy_data(void *ubo) +{ + void *data; + vkMapMemory(this->device->device, this->device_memory, 0, + this->device_size, 0, &data); + memcpy(data, ubo, this->device_size); + vkUnmapMemory(this->device->device, this->device_memory); +} + +} diff --git a/src/vk/uniform_buffer.hpp b/src/vk/uniform_buffer.hpp new file mode 100644 index 0000000..c044699 --- /dev/null +++ b/src/vk/uniform_buffer.hpp @@ -0,0 +1,60 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_UNIFORM_BUFFER_H +#define CANDY_GEAR_VK_UNIFORM_BUFFER_H 1 + +#include <memory> + +#include "core.hpp" + +#include "base_buffer.hpp" + +namespace VK +{ + +struct UBOModelInstance +{ + glm::mat4 model[128]; +}; + +struct UBOViewProjection +{ + glm::mat4 view; + glm::mat4 proj; +}; + +// FIXME: this class need to delete or create custom copy constructors! +class UniformBuffer: public BaseBuffer +{ + UniformBuffer(const UniformBuffer &t) = delete; + UniformBuffer& + operator=(const UniformBuffer &t) = delete; + +public: + UniformBuffer(Device *device, VkDeviceSize data_size); + ~UniformBuffer(); + + UniformBuffer(UniformBuffer &&that); + UniformBuffer& + operator=(UniformBuffer &&that); + + void copy_data(void* ubo); +}; + +} + +#endif /* CANDY_GEAR_VK_UNIFORM_BUFFER_H */ diff --git a/src/vk/vertex.hpp b/src/vk/vertex.hpp new file mode 100644 index 0000000..735d0a7 --- /dev/null +++ b/src/vk/vertex.hpp @@ -0,0 +1,35 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_VERTEX_H +#define CANDY_GEAR_VK_VERTEX_H 1 + +#include "core.hpp" + +namespace VK +{ + +struct Vertex +{ + glm::vec3 position; + glm::vec3 normal; + glm::vec3 color; + glm::vec2 texture_coord; +}; + +} + +#endif /* CANDY_GEAR_VK_VERTEX_H */ |