Use allocator for buffers/images
everything now uses VulkanAllocator for allocations (through VulkanInstance::createXXX functions)
This commit is contained in:
parent
e8f736d27f
commit
2bc7fe05f2
@ -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)
|
||||
|
||||
|
@ -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); */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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_()
|
||||
*/
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
@ -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);
|
||||
/// @}
|
||||
|
||||
/**
|
||||
|
@ -4,28 +4,21 @@
|
||||
#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_) {
|
||||
|
||||
DeviceMemory::DeviceMemory(VkDeviceSize size_) {
|
||||
size = size_;
|
||||
memory = VK_NULL_HANDLE;
|
||||
blocks.emplace_back(MemoryBlock{size, 0, true});
|
||||
};
|
||||
|
||||
VkDeviceSize size;
|
||||
VkDeviceMemory memory;
|
||||
std::list<MemoryBlock> blocks;
|
||||
};
|
||||
|
||||
|
||||
VulkanAllocator::VulkanAllocator(VulkanInstance& instance)
|
||||
: vk(instance)
|
||||
@ -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,8 +111,9 @@ 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;
|
||||
if (block != deviceMemory->blocks.begin()) {
|
||||
// merge with previous block, if free
|
||||
auto otherBlock = block;
|
||||
otherBlock--;
|
||||
@ -110,19 +122,24 @@ namespace gz::vk {
|
||||
deviceMemory->blocks.erase(block);
|
||||
block = otherBlock;
|
||||
}
|
||||
}
|
||||
if (block != --(deviceMemory->blocks.end())) {
|
||||
// merge with next block, if free
|
||||
otherBlock = block;
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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,22 +918,29 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -1,4 +1,5 @@
|
||||
# Written by writeKeyValueFile
|
||||
max_frames_in_flight = 3
|
||||
max_anisotropy = 1
|
||||
anisotropy_enable = false
|
||||
framerate = 60
|
||||
|
Loading…
Reference in New Issue
Block a user