Use allocator for buffers/images

everything now uses VulkanAllocator for allocations (through
VulkanInstance::createXXX functions)
This commit is contained in:
matthias@arch 2022-10-23 01:13:51 +02:00
parent e8f736d27f
commit 2bc7fe05f2
17 changed files with 435 additions and 172 deletions

View File

@ -13,7 +13,7 @@ CXXFLAGS += $(IFLAGS)
# SETTINGS
OBJECT_DIR = ../build
EXEC = ../vulkan_test
LOG_LEVEL = LOG_LEVEL_2
LOG_LEVEL = LOG_LEVEL_0
CXXFLAGS += -D $(LOG_LEVEL)

View File

@ -64,13 +64,13 @@ namespace gz::vk {
try {
uint32_t imageIndex;
/* std::chrono::time_point now = std::chrono::system_clock::now(); */
while (! glfwWindowShouldClose(vulkanInstance.window)) {
while (! glfwWindowShouldClose(vulkanInstance.getWindow())) {
glfwPollEvents();
imageIndex = vulkanInstance.beginFrameDraw();
r2D.drawFrame(imageIndex);
r3D.drawFrame(imageIndex);
vulkanInstance.endFrameDraw(imageIndex);
auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.settings.get<uint32_t>("framerate"));
auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.getSettings().get<uint32_t>("framerate"));
std::this_thread::sleep_for(SLEEP_TIME);
/* std::chrono::time_point now2 = std::chrono::system_clock::now(); */
/* std::chrono::nanoseconds dur = std::chrono::duration_cast<std::chrono::nanoseconds>(now2 - now); */

View File

@ -43,6 +43,9 @@ namespace gz::vk {
void Renderer2D::cleanup() {
rLog.log0("cleanup: cleaning up");
// UPDATE DOC ON CHANGES!
/* vk.destroyCommandBuffers(commandBuffers); */
cleanupSwapChainDependantResources();
cleanup_();
@ -68,9 +71,8 @@ namespace gz::vk {
vk.destroyFramebuffers(framebuffers);
for (size_t i = 0; i < images.size(); i++) {
vkDestroyImageView(vk.getDevice(), imageViews[i], nullptr);
vkDestroyImage(vk.getDevice(), images[i], nullptr);
vkFreeMemory(vk.getDevice(), imageMemory[i], nullptr);
vk.destroyImageView(imageViews[i]);
vk.destroyImage(images[i], imageMemory[i]);
}
vkDestroyRenderPass(vk.getDevice(), renderPass, nullptr);

View File

@ -3,6 +3,7 @@
#include "renderer.hpp"
#include "shape.hpp"
#include "vulkan_allocator.hpp"
#include "vulkan_util.hpp"
namespace gz::vk {
@ -67,7 +68,7 @@ namespace gz::vk {
* @{
*/
std::vector<VkImage> images;
std::vector<VkDeviceMemory> imageMemory;
std::vector<MemoryInfo> imageMemory;
std::vector<VkImageView> imageViews;
/**
* @brief Creates the images (on imageMemory) and imageViews with the format of the VulkanInstance::swapChain images

View File

@ -1,6 +1,7 @@
#include "renderer3D.hpp"
#include "vertex.hpp"
#include "vulkan_allocator.hpp"
#include "vulkan_instance.hpp"
#include "texture_manager.hpp"
#include "exceptions.hpp"
@ -49,10 +50,14 @@ namespace gz::vk {
}
void Renderer3D::cleanup() {
rLog.log0("cleanup: cleaning up");
// UPDATE DOC ON CHANGES!
for (size_t i = 0; i < vk.getMaxFramesInFlight(); i++) {
vkDestroyBuffer(vk.getDevice(), uniformBuffers[i], nullptr);
vkFreeMemory(vk.getDevice(), uniformBuffersMemory[i], nullptr);
vk.destroyBuffer(uniformBuffers[i], uniformBuffersMemory[i]);
}
vkDestroyDescriptorSetLayout(vk.getDevice(), descriptorSetLayout, NO_ALLOC);
vkDestroyDescriptorPool(vk.getDevice(), descriptorPool, NO_ALLOC);
cleanupSwapChainDependantResources();
cleanup_();
}
@ -77,9 +82,8 @@ namespace gz::vk {
vk.destroyFramebuffers(framebuffers);
for (size_t i = 0; i < images.size(); i++) {
vkDestroyImageView(vk.getDevice(), imageViews[i], nullptr);
vkDestroyImage(vk.getDevice(), images[i], nullptr);
vkFreeMemory(vk.getDevice(), imageMemory[i], nullptr);
vk.destroyImageView(imageViews[i]);
vk.destroyImage(images[i], imageMemory[i]);
}
vkDestroyRenderPass(vk.getDevice(), renderPass, nullptr);
@ -313,32 +317,28 @@ namespace gz::vk {
// copy to vertexBuffer
// create staging buffer
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
MemoryInfo stagingBufferMemory;
vk.createBuffer(requiredVertexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
// fill staging buffer
void* data;
vkMapMemory(vk.getDevice(), stagingBufferMemory, NO_OFFSET, requiredVertexBufferSize, NO_FLAGS, &data);
vkMapMemory(vk.getDevice(), stagingBufferMemory.memory, stagingBufferMemory.offset, requiredVertexBufferSize, NO_FLAGS, &data);
memcpy(data, model.vertices.data(), requiredVertexBufferSize);
vkUnmapMemory(vk.getDevice(), stagingBufferMemory);
vkUnmapMemory(vk.getDevice(), stagingBufferMemory.memory);
// fill vertex buffer
vk.copyBuffer(stagingBuffer, vertexBuffer, requiredVertexBufferSize);
vkDestroyBuffer(vk.getDevice(), stagingBuffer, nullptr);
vkFreeMemory(vk.getDevice(), stagingBufferMemory, nullptr);
vk.destroyBuffer(stagingBuffer, stagingBufferMemory);
rLog.log0("Renderer3D: loadModel: filling index buffer");
data = nullptr;
stagingBuffer = VK_NULL_HANDLE;
stagingBufferMemory = VK_NULL_HANDLE;
// copy to index buffer
vk.createBuffer(requiredIndexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
// fill staging buffer
vkMapMemory(vk.getDevice(), stagingBufferMemory, NO_OFFSET, requiredIndexBufferSize, NO_FLAGS, &data);
vkMapMemory(vk.getDevice(), stagingBufferMemory.memory, stagingBufferMemory.offset, requiredIndexBufferSize, NO_FLAGS, &data);
memcpy(data, model.indices.data(), requiredIndexBufferSize);
vkUnmapMemory(vk.getDevice(), stagingBufferMemory);
vkUnmapMemory(vk.getDevice(), stagingBufferMemory.memory);
// fill index buffer
vk.copyBuffer(stagingBuffer, indexBuffer, requiredIndexBufferSize);
vkDestroyBuffer(vk.getDevice(), stagingBuffer, nullptr);
vkFreeMemory(vk.getDevice(), stagingBufferMemory, nullptr);
vk.destroyBuffer(stagingBuffer, stagingBufferMemory);
}
//
@ -434,10 +434,11 @@ namespace gz::vk {
/* ubo.view = glm::mat4(1); */
/* ubo.projection = glm::mat4(1); */
/* ubo.projection[1][1] *= -1; // y coordinate inverted in opengl */
MemoryInfo& uniformBufferMI = uniformBuffersMemory[vk.getCurrentFrame()];
void* data;
vkMapMemory(vk.getDevice(), uniformBuffersMemory[vk.getCurrentFrame()], NO_OFFSET, sizeof(ubo), NO_FLAGS, &data);
vkMapMemory(vk.getDevice(), uniformBufferMI.memory, uniformBufferMI.offset, sizeof(ubo), NO_FLAGS, &data);
memcpy(data, &ubo, sizeof(ubo));
vkUnmapMemory(vk.getDevice(), uniformBuffersMemory[vk.getCurrentFrame()]);
vkUnmapMemory(vk.getDevice(), uniformBufferMI.memory);
}

View File

@ -2,6 +2,7 @@
#include "renderer.hpp"
#include "vulkan_allocator.hpp"
#include "vulkan_util.hpp"
namespace gz::vk {
@ -34,7 +35,7 @@ namespace gz::vk {
private:
void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame);
std::vector<VkBuffer> uniformBuffers;
std::vector<VkDeviceMemory> uniformBuffersMemory;
std::vector<MemoryInfo> uniformBuffersMemory;
void createUniformBuffers();
void updateUniformBuffer();
@ -45,7 +46,7 @@ namespace gz::vk {
*/
/// @{
std::vector<VkImage> images;
std::vector<VkDeviceMemory> imageMemory;
std::vector<MemoryInfo> imageMemory;
std::vector<VkImageView> imageViews;
/**
* @brief Creates the images (on imageMemory) and imageViews with the format of the VulkanInstance::swapChain images
@ -137,6 +138,7 @@ namespace gz::vk {
* @details:
* Does:
* -# destroy uniform buffers
* -# destroy descriptor set layout and pool
* -# call cleanupSwapChainDependantResources()
* -# call Renderer::cleanup_()
*/

View File

@ -20,17 +20,28 @@ std::string TextureImageArea::toString() const {
return "<(" + gz::toString(x) + "," + gz::toString(y) + "), (" + gz::toString(width) + "x" + gz::toString(height) + ")>";
}
//
// INIT & CLEANUP
//
TextureAtlas::TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY)
: vk(instance), slotWidth(slotWidth), slotHeight(slotHeight), slotCountX(slotCountX), slotCountY(slotCountY)
: vk(instance),
slotWidth(slotWidth), slotHeight(slotHeight),
slotCountX(slotCountX), slotCountY(slotCountY)
{
#ifdef LOG_LEVEL_0
LogCreateInfo logCI{};
logCI.logfile = "textureAtlas.log";
logCI.storeLog = false;
logCI.prefix = "TextureAtlas (" + gz::toString(slotCountX) + "x" + gz::toString(slotCountY) + ")X(" + gz::toString(slotWidth) + "x" + gz::toString(slotHeight) + ")";
logCI.prefixColor = Color::GREEN;
logCI.timeColor = VULKAN_MESSAGE_TIME_COLOR;
tLog = Log(std::move(logCI));
#endif
// whole image
freeAreas.insert({ 0, 0, slotCountX, slotCountY});
createImageResources();
vk.registerCleanupCallback(std::bind(&TextureAtlas::cleanup, this));
/* vk.registerCleanupCallback(std::bind(&TextureAtlas::cleanup, this)); */
VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan(std::string("TextureAtlas-") + gz::toString(slotWidth) + "x" + gz::toString(slotHeight),
{ &textureImage, &textureImageMemory, &textureImageView, &textureSampler },
{}));
@ -38,11 +49,12 @@ TextureAtlas::TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_
void TextureAtlas::cleanup() {
VulkanInstance::vLog("TextureAtlas::cleanup, textureSampler", reinterpret_cast<uint64_t>(textureSampler), "textureImageView", reinterpret_cast<uint64_t>(textureImageView));
vkDestroySampler(vk.getDevice(), textureSampler, nullptr);
vkDestroyImageView(vk.getDevice(), textureImageView, nullptr);
vkDestroyImage(vk.getDevice(), textureImage, nullptr);
vkFreeMemory(vk.getDevice(), textureImageMemory, nullptr);
#ifdef LOG_LEVEL_0
tLog.log0("cleanup: cleaning up");
#endif
vk.destroyTextureSampler(textureSampler);
vk.destroyImageView(textureImageView);
vk.destroyImage(textureImage, textureImageMemory);
}
@ -51,7 +63,7 @@ void TextureAtlas::createImageResources() {
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
textureImage, textureImageMemory);
VkCommandBuffer cmdBuffer = vk.beginSingleTimeCommands(vk.commandPoolGraphics);
VkCommandBuffer cmdBuffer = vk.beginSingleTimeCommands(POOL_GRAPHICS);
vk.transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cmdBuffer);
VkImageSubresourceRange subresourceRange{};
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@ -61,7 +73,7 @@ void TextureAtlas::createImageResources() {
subresourceRange.layerCount = 1;
vkCmdClearColorImage(cmdBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &missingTextureColor, 1, &subresourceRange);
vk.transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, &cmdBuffer);
vk.endSingleTimeCommands(cmdBuffer, vk.commandPoolGraphics, vk.graphicsQ);
vk.endSingleTimeCommands(cmdBuffer, POOL_GRAPHICS);
vk.createImageView(VK_FORMAT_R8G8B8A8_SRGB, textureImage, textureImageView, VK_IMAGE_ASPECT_COLOR_BIT);
vk.createTextureSampler(textureSampler);
@ -135,7 +147,9 @@ std::pair<glm::vec2, glm::vec2> TextureAtlas::addTexture(uint8_t* pixels, uint16
freeAreas.insert(std::move(node));
}
mergeFreeAreas();
VulkanInstance::vLog("TextureAtlas::addTexture: Adding texture at position x,y=(", x, y, "), with slotCountX,Y=(", slotsX, slotsY, "), with dimensions w,h=(", width, height, ")");
#ifdef LOG_LEVEL_0
tLog.log0("addTexture: Adding texture at position x,y=(", x, y, "), with slotCountX,Y=(", slotsX, slotsY, "), with dimensions w,h=(", width, height, ")");
#endif
blitTextureOnImage(pixels, x, y, width, height);
// topleft normalized: slot / slotCount
// bottomright normalized: (slot + (textureSize/slotCountize)) / slotCount
@ -149,14 +163,14 @@ void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t
constexpr size_t BYTES_PER_PIXEL = 4;
VkDeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL;
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
MemoryInfo stagingBufferMemory;
vk.createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer, stagingBufferMemory);
void* data;
vkMapMemory(vk.getDevice(), stagingBufferMemory, NO_OFFSET, imageSize, NO_FLAGS, &data);
vkMapMemory(vk.getDevice(), stagingBufferMemory.memory, stagingBufferMemory.offset, imageSize, NO_FLAGS, &data);
memcpy(data, pixels, static_cast<size_t>(imageSize));
vkUnmapMemory(vk.getDevice(), stagingBufferMemory);
vkUnmapMemory(vk.getDevice(), stagingBufferMemory.memory);
vk.transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vk.copyBufferToImage(stagingBuffer, textureImage,
@ -164,14 +178,15 @@ void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t
static_cast<uint32_t>(textureWidth), static_cast<uint32_t>(textureHeight)); // dimensions
vk.transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
vkDestroyBuffer(vk.getDevice(), stagingBuffer, nullptr);
vkFreeMemory(vk.getDevice(), stagingBufferMemory, nullptr);
vk.destroyBuffer(stagingBuffer, stagingBufferMemory);
}
void TextureAtlas::mergeFreeAreas() {
begin:
VulkanInstance::vLog("TextureAtlas::mergeFreeAreas before merge:", freeAreas);
#ifdef LOG_LEVEL_0
tLog.log0("mergeFreeAreas before merge:", freeAreas);
#endif
// surely not the most efficient way, but it should only be run at load time and thus not be too problematic
// iterate over the areas for each area
for (auto it = freeAreas.begin(); it != freeAreas.end(); it++) {
@ -195,8 +210,9 @@ begin:
}
}
}
VulkanInstance::vLog("TextureAtlas::mergeFreeAreas after merge:", freeAreas);
#ifdef LOG_LEVEL_0
tLog.log0("mergeFreeAreas after merge:", freeAreas);
#endif
}
std::string TextureAtlas::toString() const {

View File

@ -1,11 +1,18 @@
#pragma once
#include "vulkan_allocator.hpp"
#include <gz-util/log.hpp>
#include <glm/glm.hpp>
#include <vulkan/vulkan_core.h>
#include <vector>
#include <string>
#include <set>
// only log when trace
#ifdef LOG_LEVEL_0
#include <gz-util/log.hpp>
#endif
namespace gz::vk {
/**
@ -34,6 +41,7 @@ namespace gz::vk {
/// Defined in vulkan_instance.hpp
class VulkanInstance;
class TextureAtlas {
public:
/**
@ -58,12 +66,14 @@ namespace gz::vk {
std::string toString() const;
private:
VulkanInstance& vk;
/**
* @brief Destroy all vulkan objects owned by this object
* @note This function has to be called manually (by TextureManager). This class does not register a cleanupCallback with the instance!
*/
void cleanup();
private:
VulkanInstance& vk;
void blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight);
/**
* @brief Create textureImage, textureImageView and textureSampler
@ -72,7 +82,7 @@ namespace gz::vk {
*/
void createImageResources();
VkImage textureImage;
VkDeviceMemory textureImageMemory;
MemoryInfo textureImageMemory;
VkImageView textureImageView;
VkSampler textureSampler;
@ -87,5 +97,10 @@ namespace gz::vk {
uint16_t slotHeight;
uint16_t slotCountX;
uint16_t slotCountY;
/// Use log only with trace
#ifdef LOG_LEVEL_0
Log tLog;
#endif
};
} // namespace gz::vk

View File

@ -9,6 +9,14 @@ namespace gz::vk {
TextureManager::TextureManager(VulkanInstance& instance)
: vk(instance)
{
LogCreateInfo logCI{};
logCI.logfile = "textureManager.log";
logCI.storeLog = false;
logCI.prefix = "Texture";
logCI.prefixColor = Color::GREEN;
logCI.timeColor = VULKAN_MESSAGE_TIME_COLOR;
tLog = Log(std::move(logCI));
vk.registerCleanupCallback(std::bind(&TextureManager::cleanup, this));
atlases.insert({ 0, TextureAtlas(vk, 24, 24, 24, 24) });
createDescriptorSetLayout();
@ -21,9 +29,14 @@ namespace gz::vk {
void TextureManager::cleanup() {
tLog.log0("cleanup: cleaning up");
/* vkFreeDescriptorSets(vk.getDevice(), descriptorPool, 1, &descriptorSet); */
vkDestroyDescriptorSetLayout(vk.getDevice(), descriptorSetLayout, NO_ALLOC);
vkDestroyDescriptorPool(vk.getDevice(), descriptorPool, NO_ALLOC);
for (auto& [i, atlas] : atlases) {
atlas.cleanup();
}
}
@ -32,7 +45,7 @@ namespace gz::vk {
loadTextureFromFile(textureName);
}
TextureInfo& texI = textureInfos[textureName];
VulkanInstance::vLog("getTexCoords", texCoords, "textureInfo: topleft", texI.texCoordTopLeft, "botright", texI.texCoordBottomRight);
tLog.log0("getTexCoords", texCoords, "textureInfo: topleft", texI.texCoordTopLeft, "botright", texI.texCoordBottomRight);
/* texCoords.x = texI.texCoordTopLeft.x + texCoords.x * (texI.texCoordBottomRight.x - texI.texCoordTopLeft.x); */
/* texCoords.y = texI.texCoordTopLeft.y + texCoords.y * (texI.texCoordBottomRight.y - texI.texCoordTopLeft.y); */
texCoords = texI.texCoordTopLeft + texCoords * (texI.texCoordBottomRight - texI.texCoordTopLeft);
@ -49,10 +62,10 @@ namespace gz::vk {
texI.atlas = 0;
auto texCoords = atlases.at(0).addTexture(pixels, textureWidth, textureHeight);
stbi_image_free(pixels);
VulkanInstance::vLog("TextureManager::loadTextureFromFile: TexCoords of new image:", texCoords.first, texCoords.second);
tLog.log0("TextureManager::loadTextureFromFile: TexCoords of new image:", texCoords.first, texCoords.second);
texI.texCoordTopLeft = std::move(texCoords.first);
texI.texCoordBottomRight = std::move(texCoords.second);
VulkanInstance::vLog("TextureManager::loadTextureFromFile: After loading:", atlases.at(0));
tLog.log0("TextureManager::loadTextureFromFile: After loading:", atlases.at(0));
}
@ -84,9 +97,9 @@ void TextureManager::createDescriptorSetLayout() {
void TextureManager::createDescriptorPool() {
std::array<VkDescriptorPoolSize, 1> poolSizes;
/* poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; */
/* poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT); */
/* poolSizes[0].descriptorCount = static_cast<uint32_t>(vk.getMaxFramesInFlight()); */
poolSizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
poolSizes[0].descriptorCount = static_cast<uint32_t>(vk.getMaxFramesInFlight());
VkDescriptorPoolCreateInfo poolCI{};
poolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
@ -102,16 +115,16 @@ void TextureManager::createDescriptorPool() {
void TextureManager::createDescriptorSet() {
/* std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); */
/* std::vector<VkDescriptorSetLayout> layouts(vk.getMaxFramesInFlight(), descriptorSetLayout); */
VkDescriptorSetAllocateInfo setAI{};
setAI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
setAI.descriptorPool = descriptorPool;
/* setAI.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT); */
/* setAI.descriptorSetCount = static_cast<uint32_t>(vk.getMaxFramesInFlight()); */
/* setAI.pSetLayouts = layouts.data(); */
setAI.descriptorSetCount = 1;
setAI.pSetLayouts = &descriptorSetLayout;
/* descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); */
/* descriptorSets.resize(vk.getMaxFramesInFlight()); */
/* VkResult result = vkAllocateDescriptorSets(vk.getDevice(), &setAI, descriptorSets.data()); */
VkResult result = vkAllocateDescriptorSets(vk.getDevice(), &setAI, &descriptorSet);
if (result != VK_SUCCESS) {

View File

@ -3,8 +3,11 @@
#include "texture_atlas.hpp"
#include "stb_image.h"
#include <gz-util/util/string.hpp>
#include <gz-util/log.hpp>
#include <glm/fwd.hpp>
#include <unordered_map>
namespace gz::vk {
@ -38,6 +41,7 @@ namespace gz::vk {
std::unordered_map<uint32_t, TextureAtlas> atlases;
util::unordered_string_map<TextureInfo> textureInfos;
VulkanInstance& vk;
Log tLog;
/**
* @name Create desciptors
* @details These functions create a desciptor with bindings for

View File

@ -95,6 +95,28 @@ namespace gz::vk {
}
void getBufferMemoryRequirements(const VkDevice& device, const VkBuffer& buffer, VkBufferMemoryRequirementsInfo2& bufMemReq, VkMemoryRequirements2& memReq) {
bufMemReq.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2;
bufMemReq.pNext = nullptr;
bufMemReq.buffer = buffer;
memReq.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
memReq.pNext = nullptr;
vkGetBufferMemoryRequirements2(device, &bufMemReq, &memReq);
}
void getImageMemoryRequirements(const VkDevice& device, const VkImage& image, VkImageMemoryRequirementsInfo2& imMemReq, VkMemoryRequirements2& memReq) {
imMemReq.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
imMemReq.pNext = nullptr;
imMemReq.image = image;
memReq.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
memReq.pNext = nullptr;
vkGetImageMemoryRequirements2(device, &imMemReq, &memReq);
}
//
//
//

View File

@ -127,6 +127,20 @@ namespace gz::vk {
* @brief Get a VkDependencyInfo struct for a single image memory barrier
*/
VkDependencyInfo getDepInfo(const VkImageMemoryBarrier2& barrier);
/**
* @brief Fill bufMeqReq and memReq
* @details
* Fill the structs and call vkGetBufferMemoryRequirements2()
*/
void getBufferMemoryRequirements(const VkDevice& device, const VkBuffer& buffer, VkBufferMemoryRequirementsInfo2& bufMemReq, VkMemoryRequirements2& memReq);
/**
* @brief Fill imMemReq and memReq
* @details
* Fill the structs and call vkGetImageMemoryRequirements2()
*/
void getImageMemoryRequirements(const VkDevice& device, const VkImage& image, VkImageMemoryRequirementsInfo2& imMemReq, VkMemoryRequirements2& memReq);
/// @}
/**

View File

@ -4,26 +4,19 @@
#include "vulkan_instance.hpp"
#include <glm/vector_relational.hpp>
#include <gz-util/util/string_conversion.hpp>
#include <vulkan/vulkan_core.h>
namespace gz::vk {
struct MemoryBlock {
size_t size;
size_t offset;
bool free;
};
std::string MemoryBlock::toString() const {
return std::string("<offset: " + gz::toHexString(offset, 0) + ", size: " + gz::toHexString(size, 0) + ", free: " + gz::toString(free) + ">");
}
struct DeviceMemory {
DeviceMemory() = delete;
DeviceMemory(VkDeviceSize size_) {
size = size_;
memory = VK_NULL_HANDLE;
blocks.emplace_back(MemoryBlock{size, 0, true});
};
VkDeviceSize size;
VkDeviceMemory memory;
std::list<MemoryBlock> blocks;
DeviceMemory::DeviceMemory(VkDeviceSize size_) {
size = size_;
memory = VK_NULL_HANDLE;
blocks.emplace_back(MemoryBlock{size, 0, true});
};
@ -40,30 +33,46 @@ namespace gz::vk {
}
void VulkanAllocator::allocate(const VkMemoryAllocateInfo& allocI, MemoryInfo& memoryInfo) {
void VulkanAllocator::allocate(const VkMemoryAllocateInfo& allocI, const VkMemoryRequirements2& memReq, MemoryInfo& memoryInfo) {
aLog.log0("allocate: Requesting memory with ( size", toHexString(allocI.allocationSize),
"), ( memoryTypeIndex", allocI.memoryTypeIndex,
"), ( alignment", toHexString(memReq.memoryRequirements.alignment), ")");
// go through allocated memories with matching memoryType
for (auto& deviceMemory : memory[allocI.memoryTypeIndex]) {
for (auto deviceMemory = memory[allocI.memoryTypeIndex].begin(); deviceMemory != memory[allocI.memoryTypeIndex].end(); deviceMemory++) {
/* auto bestBlockIt = deviceMemory.blocks.end(); */
// go through blocks in memory
for (auto block = deviceMemory.blocks.begin(); block != deviceMemory.blocks.end(); block++) {
for (auto block = deviceMemory->blocks.begin(); block != deviceMemory->blocks.end(); block++) {
if (!block->free) { continue; }
if (block->size >= allocI.allocationSize) {
aLog.log0("allocate: memoryTypeIndex:", allocI.memoryTypeIndex,
"size:", allocI.allocationSize, "block size:", block->size, "block offset:", block->offset);
// check if alignment is ok
if (block->offset % memReq.memoryRequirements.alignment != 0) {
// move non-aligned space to previous block
// new offset === offset + (align - offset % align)
size_t moveToPreviousBlock = memReq.memoryRequirements.alignment - block->offset % memReq.memoryRequirements.alignment;
// check if still large enough
if (block->size - moveToPreviousBlock < allocI.allocationSize) { continue; }
block--;
block->size += moveToPreviousBlock;
block++;
block->offset += moveToPreviousBlock;
block->size -= moveToPreviousBlock;
}
// if the block is larger than the needed size, split the block
if (block->size > allocI.allocationSize) {
// emplace free part of block after used part of block
auto newBlock = ++block;
block--;
deviceMemory.blocks.emplace(newBlock,
deviceMemory->blocks.emplace(newBlock,
MemoryBlock{ block->size - allocI.allocationSize, block->offset + allocI.allocationSize, true });
block--;
/* block--; */
block->size = allocI.allocationSize;
}
block->free = false;
memoryInfo.memory = deviceMemory.memory;
memoryInfo.memory = deviceMemory->memory;
memoryInfo.offset = block->offset;
memoryInfo.memoryTypeIndex = allocI.memoryTypeIndex;
aLog.log0("allocate: (memory", memoryInfo.memoryTypeIndex, "-", gz::toString(deviceMemory - memory[memoryInfo.memoryTypeIndex].begin()) + ") Blocks:", deviceMemory->blocks);
return;
}
}
@ -75,7 +84,7 @@ namespace gz::vk {
allocI_.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocI_.memoryTypeIndex = allocI.memoryTypeIndex;
allocI_.allocationSize = memory[allocI.memoryTypeIndex].back().size;
aLog.log0("allocate: Allocating new memory of size:", allocI_.allocationSize, " and memoryTypeIndex:", allocI.memoryTypeIndex);
aLog.log0("allocate: Allocating new memory of size:", gz::toHexString(allocI_.allocationSize, 0), "(memory", memoryInfo.memoryTypeIndex, "-", gz::toString(memory[memoryInfo.memoryTypeIndex].size()) + ")");
VkResult result = vkAllocateMemory(vk.getDevice(), &allocI_, NO_ALLOC, &memory[allocI.memoryTypeIndex].back().memory);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to allocate memory", "VulkanAllocator::allocate");
@ -87,9 +96,11 @@ namespace gz::vk {
deviceMemory.blocks.front().size -= allocI.allocationSize;
deviceMemory.blocks.emplace_front(MemoryBlock{ allocI.allocationSize, 0, false });
// alignment always satisfied with vkAllocateMemory() and offset 0
memoryInfo.memory = deviceMemory.memory;
memoryInfo.offset = 0;
memoryInfo.memoryTypeIndex = allocI.memoryTypeIndex;
aLog.log0("allocate: (memory", memoryInfo.memoryTypeIndex, "-", gz::toString(memory[memoryInfo.memoryTypeIndex].size()) + ") Blocks:", deviceMemory.blocks);
}
@ -100,29 +111,35 @@ namespace gz::vk {
// go through blocks in memory
for (auto block = deviceMemory->blocks.begin(); block != deviceMemory->blocks.end(); block++) {
if (block->offset != memoryInfo.offset) { continue; }
aLog.log0("free: Freeing block at offset", memoryInfo.offset);
aLog.log0("free: (memory", memoryInfo.memoryTypeIndex, "-", gz::toString(deviceMemory - memory[memoryInfo.memoryTypeIndex].begin()) + ") Freeing block at offset:", gz::toHexString(memoryInfo.offset, 0));
block->free = true;
// merge with previous block, if free
auto otherBlock = block;
otherBlock--;
if (otherBlock->free) {
otherBlock->size += block->size;
deviceMemory->blocks.erase(block);
block = otherBlock;
if (block != deviceMemory->blocks.begin()) {
// merge with previous block, if free
auto otherBlock = block;
otherBlock--;
if (otherBlock->free) {
otherBlock->size += block->size;
deviceMemory->blocks.erase(block);
block = otherBlock;
}
}
// merge with next block, if free
otherBlock = block;
otherBlock++;
if (otherBlock->free) {
block->size += otherBlock->size;
deviceMemory->blocks.erase(otherBlock);
if (block != --(deviceMemory->blocks.end())) {
// merge with next block, if free
auto otherBlock = block;
otherBlock++;
if (otherBlock->free) {
block->size += otherBlock->size;
deviceMemory->blocks.erase(otherBlock);
}
}
memoryInfo.memory = VK_NULL_HANDLE;
memoryInfo.offset = 0;
aLog.log0("free: (memory", memoryInfo.memoryTypeIndex, "-", gz::toString(deviceMemory - memory[memoryInfo.memoryTypeIndex].begin()) + ") Blocks:", deviceMemory->blocks);
// if now there is only one free block, everything is free -> deallocate memory
if (deviceMemory->blocks.size() == 1) {
aLog.log0("free: Deallocting memory of size:", deviceMemory->size, "and memoryTypeIndex:", memoryInfo.memoryTypeIndex);
aLog.log0("free: Deallocating (memory", memoryInfo.memoryTypeIndex, "-", gz::toString(deviceMemory - memory[memoryInfo.memoryTypeIndex].begin()) + ") of size:", gz::toHexString(deviceMemory->size, 0));
vkFreeMemory(vk.getDevice(), deviceMemory->memory, NO_ALLOC);
memory[memoryInfo.memoryTypeIndex].erase(deviceMemory);
}
@ -133,4 +150,21 @@ namespace gz::vk {
}
void VulkanAllocator::cleanup() {
aLog.log0("cleanup: cleaning up");
for (auto memType = memory.begin(); memType != memory.end(); memType++) {
for (auto deviceMemory = memType->second.begin(); deviceMemory != memType->second.end(); deviceMemory++) {
// check if all blocks are free
for (auto block = deviceMemory->blocks.begin(); block != deviceMemory->blocks.end(); block++) {
if (block->free) { continue; }
aLog.warning("cleanup: (memory", memType->first, "-", gz::toString(deviceMemory - memType->second.begin()) + ") Block not freed: ", *block);
}
aLog.log0("free: Deallocating (memory", memType->first, "-", gz::toString(deviceMemory - memType->second.begin()) + ") of size:", gz::toHexString(deviceMemory->size, 0));
vkFreeMemory(vk.getDevice(), deviceMemory->memory, NO_ALLOC);
}
}
memory.clear();
}
} // namespace gz::vk

View File

@ -13,20 +13,99 @@
/* #include <vulkan/vulkan_core.h> */
namespace gz::vk {
/**
* @brief Contains information about a subsection (block) of a VkDeviceMemory
*/
struct MemoryInfo {
/**
* @brief Handle of the memory
*/
VkDeviceMemory memory = VK_NULL_HANDLE;
/**
* @brief Offset into memory
*/
VkDeviceSize offset = 0;
/**
* @brief The memoryTypeIndex memory was allocated from. Needed for VulkanAllocator::free()
*/
uint32_t memoryTypeIndex = 0;
};
/**
* @brief Information on a single block of memory
*/
struct MemoryBlock {
size_t size;
size_t offset;
bool free;
std::string toString() const;
};
/**
* @brief Manage a single VkDeviceMemory chunk
*/
struct DeviceMemory {
DeviceMemory() = delete;
DeviceMemory(VkDeviceSize size_);
VkDeviceSize size;
VkDeviceMemory memory;
std::list<MemoryBlock> blocks;
};
// if no memory is available, allocate a chunk of multiplier * requested size
constexpr VkDeviceSize TODO_ALLOCATION_SIZE_MULTIPLIIER = 10;
// defined in vulkan_instance.hpp
class VulkanInstance;
// defined in vulkan_allocator.cpp
struct DeviceMemory;
/**
* @brief Allocator for device local memory
* @details
* Allocates larger chunks of VkDeviceMemory from which blocks can be @ref allocate() "allocated".
*
* This class is for device local memory only, not for host memory.
* You can not use it for vulkan memory allocation callbacks (pAllocator).
*
* @subsection usage How to use
* The usage is explained with the example of creating and destroying a single buffer.
* @code
* // Create buffer handle
* VkBuffer buffer;
* // Create empty memory info struct
* MemoryInfo bufferMI;
*
* VkBufferCreateInfo bufferCI{}
* ...
* vkCreateBuffer(...);
*
* // get memory requirements
* VkMemoryRequirements2 memReq;
* VkBufferMemoryRequirementsInfo2 bufMemReq;
* // from vulkan_util.hpp, same exists for imageMemoryRequirements
* getBufferMemoryRequirements(device, buffer, bufMemReq, memReq);
*
* // set allocation info
* VkMemoryAllocateInfo memAI{};
* ...
* memAI.memoryTypeIndex = VulkanInstance::findMemoryType(memReq.memoryRequirements.memoryTypeBits, wantedProperties);
*
* // allocate memory for the buffer
* VulkanAllocator::allocate(memAI, memReq, bufferMI);
*
* // bind the buffer to the memory
* vkBindBufferMemory(device, buffer, bufferMI.memory, bufferMI.offset);
*
* ...
*
* vkDestroyBuffer(device, buffer);
* VulkanAllocator::free(bufferMemoryInfo);
* @endcode
*
* You will of course have to use a VulkanAllocator and VulkanInstance object, since the used methods are not static.
*/
class VulkanAllocator {
public:
VulkanAllocator(VulkanInstance& instance);
@ -39,8 +118,10 @@ namespace gz::vk {
* You can then bind something of size allocI.allocationSize to memoryInfo.memory at offset memoryInfo.offset.
* @throws VkException when a call to vkAllocateMemory is needed and fails
* @todo Determine the size of new allocations
* @todo alignment, maybe use VkMemoryRequirements instead of VkMemoryAllocateInfo?
* @todo maybe increase the block size of the allocated block so that the following blocks is already 16-aligned?
*/
void allocate(const VkMemoryAllocateInfo& allocI, MemoryInfo& memoryInfo);
void allocate(const VkMemoryAllocateInfo& allocI, const VkMemoryRequirements2& memReq, MemoryInfo& memoryInfo);
/**
* @brief Free a block allocated with allocate()
*
@ -48,10 +129,21 @@ namespace gz::vk {
* @throws VkUserError if memoryInfo was not allocated from this allocator
*/
void free(MemoryInfo& memoryInfo);
/**
* @brief Deallocate all memory
* @details
* Prints warnings if blocks have not been freed.
*
* @note This function has to be called manually (by VulkanInstance). This class does not register a cleanupCallback with the instance!
*
*/
void cleanup();
private:
/// allocated memory for memoryIndexType
/// Allocated memory for memoryIndexType
std::map<uint32_t, std::vector<DeviceMemory>> memory;
Log aLog;
/// Needed for access to logical device
VulkanInstance& vk;
}; // class VulkanAllocator

View File

@ -5,6 +5,7 @@
#include "vk_enum_string.h"
#include "vulkan_allocator.hpp"
#include "vulkan_util.hpp"
#include <bits/types/cookie_io_functions_t.h>
#include <chrono>
#include <glm/vector_relational.hpp>
@ -248,6 +249,8 @@ using std::uint32_t;
vkDestroyCommandPool(device, commandPoolGraphics, nullptr);
vkDestroyCommandPool(device, commandPoolTransfer, nullptr);
allocator.cleanup();
vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
cleanupDebugMessenger();
@ -465,6 +468,43 @@ using std::uint32_t;
}
void VulkanInstance::loadModel(VerticesAndIndices<uint32_t>& model) {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warnings, errors;
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warnings, &errors, MODEL_PATH.c_str())) {
vLog.warning("Warning from tinyobj::LoadObj: ", warnings);
throw gz::Exception("Error loading obj: " + errors, "loadModel");
}
if (!warnings.empty()) {
vLog.warning("Warning from tinyobj::LoadObj: ", warnings);
}
std::unordered_map<Vertex3D, uint32_t> uniqueVertices;
for (const auto& shape : shapes) {
for (const auto& index : shape.mesh.indices) {
Vertex3D vertex;
vertex.pos = {
attrib.vertices[3 * index.vertex_index + 0],
attrib.vertices[3 * index.vertex_index + 1],
attrib.vertices[3 * index.vertex_index + 2]
};
vertex.texCoord = {
attrib.vertices[2 * index.texcoord_index + 0],
// obj: y = 0 means bottom, vulkan: y = 0 means top
1.0f - attrib.vertices[2 * index.texcoord_index + 1],
};
vertex.color = { 1.0f, 1.0f, 1.0f };
if (!uniqueVertices.contains(vertex)) {
uniqueVertices.insert({ vertex, model.vertices.size() });
model.vertices.push_back(vertex);
}
/* model.vertices.push_back(vertex); */
model.indices.push_back(uniqueVertices[vertex]);
}
}
}
// TODO
// DEPTH
/* VkFormat VulkanInstance::findDepthFormat() { */
@ -702,7 +742,7 @@ using std::uint32_t;
// BUFFER
void VulkanInstance::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, MemoryInfo& memI, VkSharingMode sharingMode, std::vector<uint32_t>* qFamiliesWithAccess) {
void VulkanInstance::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, MemoryInfo& bufferMI, VkSharingMode sharingMode, std::vector<uint32_t>* qFamiliesWithAccess) {
VkBufferCreateInfo bufferCI{};
bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferCI.size = size;
@ -722,18 +762,23 @@ using std::uint32_t;
throw getVkException(result, "Failed to create buffer", "createBuffer");
}
VkMemoryRequirements memoryRequirements{};
vkGetBufferMemoryRequirements(device, buffer, &memoryRequirements);
VkMemoryRequirements2 memReq;
VkBufferMemoryRequirementsInfo2 bufMemReq;
getBufferMemoryRequirements(device, buffer, bufMemReq, memReq);
VkMemoryAllocateInfo memoryAI{};
memoryAI.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memoryAI.allocationSize = memoryRequirements.size;
memoryAI.allocationSize = memReq.memoryRequirements.size;
VkMemoryPropertyFlags wantedProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
memoryAI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, wantedProperties);
memoryAI.memoryTypeIndex = findMemoryType(memReq.memoryRequirements.memoryTypeBits, wantedProperties);
allocator.allocate(memoryAI, memI);
allocator.allocate(memoryAI, memReq, bufferMI);
vkBindBufferMemory(device, buffer, memI.memory, memI.offset);
result = vkBindBufferMemory(device, buffer, bufferMI.memory, bufferMI.offset);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to bind buffer to its memory", "createBuffer");
}
vLog.log0("createBuffer: created and bound buffer of size", memoryAI.allocationSize);
}
@ -843,8 +888,15 @@ using std::uint32_t;
}
}
void VulkanInstance::destroyTextureSampler(VkSampler& sampler) {
vkDestroySampler(device, sampler, NO_ALLOC);
sampler = VK_NULL_HANDLE;
}
// PUBLIC INTERFACE: IMAGE UTILITY
void VulkanInstance::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, VkDeviceMemory& imageMemory) {
void VulkanInstance::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, MemoryInfo& imageMI) {
VkImageCreateInfo imageCI{};
imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCI.imageType = VK_IMAGE_TYPE_2D;
@ -866,25 +918,32 @@ using std::uint32_t;
throw getVkException(result, "Failed to create image", "createImage");
}
VkMemoryRequirements memoryRequirements;
vkGetImageMemoryRequirements(device, image, &memoryRequirements);
VkMemoryRequirements2 memReq;
VkImageMemoryRequirementsInfo2 imMemReq;
getImageMemoryRequirements(device, image, imMemReq, memReq);
VkMemoryAllocateInfo memoryAI{};
memoryAI.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memoryAI.allocationSize = memoryRequirements.size;
memoryAI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, memoryProperties);
memoryAI.allocationSize = memReq.memoryRequirements.size;
memoryAI.memoryTypeIndex = findMemoryType(memReq.memoryRequirements.memoryTypeBits, memoryProperties);
MemoryInfo memI;
allocator.allocate(memoryAI, memI);
/* result = vkAllocateMemory(device, &memoryAI, NO_ALLOC, &imageMemory); */
allocator.allocate(memoryAI, memReq, imageMI);
result = vkBindImageMemory(device, image, memI.memory, memI.offset);
result = vkBindImageMemory(device, image, imageMI.memory, imageMI.offset);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create image", "createImage");
throw getVkException(result, "Failed to bind image to its memory", "createImage");
}
vLog.log0("createImage: created and bound image of size", memoryAI.allocationSize);
}
void VulkanInstance::destroyImage(VkImage& image, MemoryInfo& imageMemory) {
vkDestroyImage(device, image, NO_ALLOC);
allocator.free(imageMemory);
image = VK_NULL_HANDLE;
}
// IMAGE VIEW
void VulkanInstance::createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags) {
VkImageViewCreateInfo imageViewCI{};
@ -908,6 +967,12 @@ using std::uint32_t;
}
void VulkanInstance::destroyImageView(VkImageView& imageView) {
vkDestroyImageView(device, imageView, NO_ALLOC);
imageView = VK_NULL_HANDLE;
}
void VulkanInstance::copyBufferToImage(VkBuffer buffer, VkImage image, int32_t offsetX, int32_t offsetY, uint32_t width, uint32_t height) {
VkCommandBuffer cmdBuffer = beginSingleTimeCommands(POOL_TRANSFER);
VkBufferImageCopy region{};
@ -1724,45 +1789,6 @@ using std::uint32_t;
}
// MODEL
void VulkanInstance::loadModel(VerticesAndIndices<uint32_t>& model) {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warnings, errors;
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warnings, &errors, MODEL_PATH.c_str())) {
vLog.warning("Warning from tinyobj::LoadObj: ", warnings);
throw gz::Exception("Error loading obj: " + errors, "loadModel");
}
if (!warnings.empty()) {
vLog.warning("Warning from tinyobj::LoadObj: ", warnings);
}
std::unordered_map<Vertex3D, uint32_t> uniqueVertices;
for (const auto& shape : shapes) {
for (const auto& index : shape.mesh.indices) {
Vertex3D vertex;
vertex.pos = {
attrib.vertices[3 * index.vertex_index + 0],
attrib.vertices[3 * index.vertex_index + 1],
attrib.vertices[3 * index.vertex_index + 2]
};
vertex.texCoord = {
attrib.vertices[2 * index.texcoord_index + 0],
// obj: y = 0 means bottom, vulkan: y = 0 means top
1.0f - attrib.vertices[2 * index.texcoord_index + 1],
};
vertex.color = { 1.0f, 1.0f, 1.0f };
if (!uniqueVertices.contains(vertex)) {
uniqueVertices.insert({ vertex, model.vertices.size() });
model.vertices.push_back(vertex);
}
/* model.vertices.push_back(vertex); */
model.indices.push_back(uniqueVertices[vertex]);
}
}
}
// DEBUG
void VulkanInstance::setupDebugMessenger() {
// get the address of the vkCreateDebugUtilsMessengerEXT function

View File

@ -97,8 +97,25 @@ namespace gz::vk {
*/
uint32_t beginFrameDraw();
void endFrameDraw(uint32_t imageIndex);
/**
* @brief Cleanup the instance
* @details
* -# wait for the device to idle
* -# call @ref registerCleanupCallback() "cleanup callbacks" in reverse order to the registration
* -# destroy command buffers
* -# call cleanupSwapChain()
* -# destroy @ref createSyncObjects() "synchronization objects"
* -# destroy @ref createCommandPools() "command pools"
* -# call VulkanAllocator::cleanup()
* -# destroy device
* -# destroy surface
* -# call cleanupDebugMessenger()
* -# destroy instance
* -# call glfwDestroyWindow()
* -# call glfwTerminate()
*/
void cleanup();
const GLFWwindow* getWindow() const { return window; }
GLFWwindow* getWindow() { return window; }
/// @}
/**
@ -142,21 +159,22 @@ namespace gz::vk {
public:
/// @{
const VkDevice& getDevice() const { return device; }
const SettingsManager<SettingsTypes>& getSettings() const { return settings; }
const VkExtent2D& getScExtent() const { return scExtent; }
const std::vector<VkImage>& getScImages() const { return scImages; }
const VkFormat& getScImageFormat() const { return scImageFormat; }
SettingsManager<SettingsTypes>& getSettings() { return settings; }
/**
* @brief Get the maximum number of frames in flight
* @see MAX_FRAMES_IN_FLIGHT
*/
const uint32_t getMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; }
uint32_t getMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; }
/**
* @brief Get the frame that is currently in flight
* @see currentFrame
*/
const uint32_t getCurrentFrame() const { return currentFrame; }
uint32_t getCurrentFrame() const { return currentFrame; }
void submitThisFrame(VkCommandBuffer& cmdBuffer);
@ -178,6 +196,8 @@ namespace gz::vk {
* @param commandPool: The same pool as passed to beginSingleTimeCommands()
*/
void endSingleTimeCommands(VkCommandBuffer cmdBuffer, InstanceCommandPool commandPool);
void loadModel(VerticesAndIndices<uint32_t>& model);
/// @}
/**
@ -234,6 +254,9 @@ namespace gz::vk {
* @param buffer The (null) handle to the buffer
* @param bufferMemory Reference to an (uninitialized) MemoryInfo struct. It will be valid when the function returns
* @param properties Properties that the buffers memory needs to satisfy
* @details
* The memory the buffer will be bound to is HOST_VISIBLE and HOST_COHERENT
*
*/
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, MemoryInfo& bufferMemory, VkSharingMode sharingMode=VK_SHARING_MODE_EXCLUSIVE, std::vector<uint32_t>* qFamiliesWithAccess=nullptr);
/**
@ -284,6 +307,8 @@ namespace gz::vk {
*/
void createTextureSampler(VkSampler& sampler);
void destroyTextureSampler(VkSampler& sampler);
/**
* @name Public Interface: Image utility
* @details
@ -292,13 +317,16 @@ namespace gz::vk {
*/
/// @{
/**
* @brief Create the image, allocate the imageMemory and bind the image to it
* @brief Create a 2D image, allocate the imageMemory and bind the image to it
*/
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, VkDeviceMemory& imageMemory);
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, MemoryInfo& imageMI);
void destroyImage(VkImage& image, MemoryInfo& imageMemory);
/**
* @brief Create a 2D imageView with format for image.
*/
void createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags);
void destroyImageView(VkImageView& imageView);
void copyBufferToImage(VkBuffer buffer, VkImage image, int32_t offsetX, int32_t offsetY, uint32_t width, uint32_t height);
/**
* @todo make a version using vkCmdResolveImage for multisampled images
@ -601,14 +629,6 @@ namespace gz::vk {
static std::vector<char> readFile(const std::string& filename);
/// @}
/**
* @name Models
*/
/// @{
void loadModel(VerticesAndIndices<uint32_t>& model);
/// @}
/**
* @name Utility
*/

View File

@ -1,4 +1,5 @@
# Written by writeKeyValueFile
max_frames_in_flight = 3
max_anisotropy = 1
anisotropy_enable = false
framerate = 60