Added texture atlas
This commit is contained in:
parent
db90bb2c31
commit
7049f2c708
@ -1,6 +1,6 @@
|
||||
{
|
||||
"configurations": {
|
||||
"gze": {
|
||||
"vulkan_test": {
|
||||
"adapter": "vscode-cpptools",
|
||||
"configuration": {
|
||||
"name": "vulkan (cpp)",
|
||||
|
1
Makefile
1
Makefile
@ -18,6 +18,7 @@ CXXFLAGS += $(IFLAGS)
|
||||
default: $(EXEC)
|
||||
echo $(OBJECTS)
|
||||
|
||||
# TODO: REMOVE -g!
|
||||
release: CXXFLAGS += -O3
|
||||
release : default
|
||||
|
||||
|
46
main.cpp
46
main.cpp
@ -1,11 +1,15 @@
|
||||
#include "main.hpp"
|
||||
|
||||
#include "shape.hpp"
|
||||
#include "vulkan_instance.hpp"
|
||||
#include "renderer2D.hpp"
|
||||
#include <chrono>
|
||||
#include <ratio>
|
||||
#include <thread>
|
||||
|
||||
namespace gz::vk {
|
||||
int mainLoop() {
|
||||
Log log("", true, false, "Main", Color::BG_RED);
|
||||
gz::SettingsManagerCreateInfo<SettingsTypes> smCI{};
|
||||
smCI.filepath = gz::vk::CONFIG_FILE;
|
||||
smCI.readFileOnCreation = true;
|
||||
@ -15,18 +19,32 @@ namespace gz::vk {
|
||||
|
||||
VulkanInstance vulkanInstance(smCI);
|
||||
vulkanInstance.init();
|
||||
Renderer2D r2D(vulkanInstance);
|
||||
TextureManager textureManager(vulkanInstance);
|
||||
Renderer2D r2D(vulkanInstance, textureManager);
|
||||
|
||||
Rectangle rect1( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f});
|
||||
Rectangle rect2(190, 191, 192, 193, { 0.7f, 0.6f, 0.5f});
|
||||
Rectangle rect3(420, 64, 512, 64, { 0.0f, 1.0f, 0.0f});
|
||||
Rectangle rect4( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f});
|
||||
Rectangle rect5( -600, -600, 800, 400, { 1.0f, 0.0f, 0.0f});
|
||||
r2D.drawShape(rect1);
|
||||
r2D.drawShape(rect2);
|
||||
r2D.drawShape(rect3);
|
||||
r2D.drawShape(rect4);
|
||||
r2D.drawShape(rect5);
|
||||
log("Startup complete. Drawing shapes");
|
||||
|
||||
Rectangle rect1( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}, "blocks/leaves.png");
|
||||
Rectangle rect2( 190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}, "blocks/crate.png");
|
||||
Rectangle rect3( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}, "blocks/dirt.png");
|
||||
Rectangle rect4(-600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}, "blocks/ice.png");
|
||||
Rectangle rect5( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}, "items/sword.png");
|
||||
Rectangle rect6( 190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}, "items/crossbow.png");
|
||||
Rectangle rect7( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}, "special/hotbar.png");
|
||||
Rectangle rect8(-600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}, "special/func_nocol.png");
|
||||
Rectangle rect9(-200, -400, 200, 200, { 0.0f, 1.0f, 0.0f}, "entities/sheep.png");
|
||||
Rectangle rect10(-400, -400, 800, 800, { 0.0f, 1.0f, 0.0f}, "atlas");
|
||||
r2D.drawShape(&rect1);
|
||||
r2D.drawShape(&rect2);
|
||||
r2D.drawShape(&rect3);
|
||||
r2D.drawShape(&rect4);
|
||||
r2D.drawShape(&rect5);
|
||||
r2D.drawShape(&rect6);
|
||||
r2D.drawShape(&rect7);
|
||||
r2D.drawShape(&rect8);
|
||||
r2D.drawShape(&rect9);
|
||||
r2D.drawShape(&rect10);
|
||||
log("Drawing complete. Filling r2D with shapes.");
|
||||
r2D.fillVertexBufferWithShapes();
|
||||
r2D.fillIndexBufferWithShapes();
|
||||
/* gz::FontManager fm("fonts"); */
|
||||
@ -34,6 +52,7 @@ namespace gz::vk {
|
||||
/* /1* fm.getFaces().at("menu.ttf"). *1/ */
|
||||
try {
|
||||
uint32_t imageIndex;
|
||||
/* std::chrono::time_point now = std::chrono::system_clock::now(); */
|
||||
while (! glfwWindowShouldClose(vulkanInstance.window)) {
|
||||
glfwPollEvents();
|
||||
imageIndex = vulkanInstance.beginFrameDraw();
|
||||
@ -41,8 +60,11 @@ namespace gz::vk {
|
||||
vulkanInstance.endFrameDraw(imageIndex);
|
||||
auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.settings.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); */
|
||||
/* std::cout << "Frametime: (ns)" << dur.count() << " in fps: " << 1e9 / (dur.count()) << "\n"; */
|
||||
/* now = now2; */
|
||||
}
|
||||
r2D.cleanup();
|
||||
vulkanInstance.cleanup();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
|
@ -1,17 +1,15 @@
|
||||
#include "renderer.hpp"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "vulkan_instance.hpp"
|
||||
|
||||
namespace gz::vk {
|
||||
void Renderer::cleanup_(){
|
||||
vkFreeCommandBuffers(vk.device, vk.commandPoolGraphics, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
|
||||
vk.destroyCommandBuffers(commandBuffers);
|
||||
|
||||
vkDestroyBuffer(vk.device, indexBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, indexBufferMemory, nullptr);
|
||||
|
||||
vkDestroyBuffer(vk.device, vertexBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, vertexBufferMemory, nullptr);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
20
renderer.hpp
20
renderer.hpp
@ -1,12 +1,28 @@
|
||||
#include "vulkan_instance.hpp"
|
||||
#pragma once
|
||||
|
||||
// includes for child classes
|
||||
#include "texture_manager.hpp"
|
||||
#include "vertex.hpp"
|
||||
#include <gz-util/log.hpp>
|
||||
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <vector>
|
||||
|
||||
namespace gz::vk {
|
||||
/// Defined in texture_manager.hpp
|
||||
class TextureManager;
|
||||
/// Defined in vulkan_instance.hpp
|
||||
class VulkanInstance;
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
Renderer(VulkanInstance& instance) : vk(instance) {};
|
||||
Renderer(VulkanInstance& instance, TextureManager& textureManager) : vk(instance), textureManager(textureManager) {};
|
||||
protected:
|
||||
void cleanup_();
|
||||
VulkanInstance& vk;
|
||||
TextureManager& textureManager;
|
||||
|
||||
std::vector<VkCommandBuffer> commandBuffers;
|
||||
/// On device local memory
|
||||
VkBuffer vertexBuffer;
|
||||
|
511
renderer2D.cpp
511
renderer2D.cpp
@ -2,246 +2,321 @@
|
||||
|
||||
#include "exceptions.hpp"
|
||||
#include "vk_enum_string.h"
|
||||
#include "vulkan_instance.hpp"
|
||||
#include "texture_manager.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
|
||||
namespace gz::vk {
|
||||
|
||||
Renderer2D::Renderer2D(VulkanInstance& instance) :
|
||||
Renderer(instance),
|
||||
rLog("renderer2D.log", true, false, "2D-Renderer", Color::BMAGENTA, true, 100) {
|
||||
vk.createCommandBuffers(commandBuffers);
|
||||
const size_t vertexCount = 500;
|
||||
const size_t indexCount = 1000;
|
||||
vk.createVertexBuffer<Vertex2D>(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize);
|
||||
vk.createIndexBuffer<uint32_t>(indexCount, indexBuffer, indexBufferMemory, indexBufferSize);
|
||||
createRenderPass();
|
||||
createImages();
|
||||
renderPassID = vk.createFramebuffers(imageViews, renderPass);
|
||||
rLog("Created Renderer2D");
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::cleanup() {
|
||||
cleanup_();
|
||||
}
|
||||
|
||||
void Renderer2D::createImages() {
|
||||
images.resize(vk.scImages.size());
|
||||
imageMemory.resize(vk.scImages.size());
|
||||
imageViews.resize(vk.scImages.size());
|
||||
VkImageUsageFlags usage= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
vk.createImage(vk.scExtent.width, vk.scExtent.height, vk.scImageFormat, VK_IMAGE_TILING_OPTIMAL, usage, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, images[i], imageMemory[i]);
|
||||
vk.createImageView(vk.scImageFormat, images[i], imageViews[i], VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Renderer2D::createRenderPass() {
|
||||
VkAttachmentDescription colorBlendAttachment{};
|
||||
colorBlendAttachment.format = vk.scImageFormat;
|
||||
colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
|
||||
VkAttachmentReference colorAttachmentRef{};
|
||||
colorAttachmentRef.attachment = 0;
|
||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
/* VkAttachmentDescription depthAttachment{}; */
|
||||
/* depthAttachment.format = findDepthFormat(); */
|
||||
/* depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; */
|
||||
/* depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; */
|
||||
/* depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */
|
||||
/* depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; */
|
||||
/* depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */
|
||||
/* depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; */
|
||||
/* depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */
|
||||
|
||||
/* VkAttachmentReference depthAttachmentRef{}; */
|
||||
/* depthAttachmentRef.attachment = 1; */
|
||||
/* depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */
|
||||
|
||||
VkSubpassDescription subpass{};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &colorAttachmentRef;
|
||||
/* subpass.pDepthStencilAttachment = &depthAttachmentRef; */
|
||||
|
||||
VkSubpassDependency dependency{};
|
||||
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependency.dstSubpass = 0;
|
||||
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.srcAccessMask = 0;
|
||||
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
/* VkSubpassDependency dependency{}; */
|
||||
/* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */
|
||||
/* dependency.dstSubpass = 0; */
|
||||
/* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
|
||||
/* dependency.srcAccessMask = 0; */
|
||||
/* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
|
||||
/* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */
|
||||
|
||||
/* std::array<VkAttachmentDescription, 2> attachments = { colorBlendAttachment, depthAttachment }; */
|
||||
std::vector<VkAttachmentDescription> attachments = { colorBlendAttachment };
|
||||
VkRenderPassCreateInfo renderPassCI{};
|
||||
renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassCI.attachmentCount = static_cast<uint32_t>(attachments.size());
|
||||
renderPassCI.pAttachments = attachments.data();
|
||||
renderPassCI.subpassCount = 1;
|
||||
renderPassCI.pSubpasses = &subpass;
|
||||
renderPassCI.dependencyCount = 1;
|
||||
renderPassCI.pDependencies = &dependency;
|
||||
/* renderPassCI.dependencyCount = 0; */
|
||||
/* renderPassCI.pDependencies = nullptr; */
|
||||
/* renderPassCI.correlatedViewMaskCount = 0; */
|
||||
/* renderPassCI.pCorrelatedViewMasks = nullptr; */
|
||||
VkResult result = vkCreateRenderPass(vk.device, &renderPassCI, nullptr, &renderPass);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Could not create render pass", "Renderer2D::createRenderPass");
|
||||
}
|
||||
rLog("createRenderPass: Created render pass.");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) {
|
||||
VkCommandBufferBeginInfo commandBufferBI{};
|
||||
commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
/* commandBufferBI.flags = 0; */
|
||||
/* commandBufferBI.pInheritanceInfo = nullptr; */
|
||||
VkResult result = vkBeginCommandBuffer(commandBuffers[currentFrame], &commandBufferBI);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Failed to begin 2D command buffer", "Renderer2D::recordCommandBuffer");
|
||||
//
|
||||
// INIT & CLEANUP
|
||||
//
|
||||
Renderer2D::Renderer2D(VulkanInstance& instance, TextureManager& textureManager) :
|
||||
Renderer(instance, textureManager),
|
||||
rLog("renderer2D.log", true, false, "2D-Renderer", Color::LI_MAGENTA, VULKAN_MESSAGE_TIME_COLOR, true, 100)
|
||||
{
|
||||
vk.registerCleanupCallback(std::bind(&Renderer2D::cleanup, this));
|
||||
vk.registerSwapChainRecreateCallback(std::bind(&Renderer2D::swapChainRecreateCallback, this));
|
||||
vk.createCommandBuffers(commandBuffers);
|
||||
const size_t vertexCount = 500;
|
||||
const size_t indexCount = 1000;
|
||||
vk.createVertexBuffer<Vertex2D>(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize);
|
||||
vk.createIndexBuffer<uint32_t>(indexCount, indexBuffer, indexBufferMemory, indexBufferSize);
|
||||
initSwapChainDependantResources();
|
||||
VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan("Renderer2D",
|
||||
{ &pipelines[PL_2D].pipeline, &renderPass, &vertexBuffer, &vertexBufferMemory, &indexBuffer, &indexBufferMemory },
|
||||
{ &framebuffers, &images, &imageMemory, &imageViews, &commandBuffers }));
|
||||
rLog("Created Renderer2D");
|
||||
}
|
||||
|
||||
VkRenderPassBeginInfo renderPassBI{};
|
||||
renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassBI.renderPass = renderPass;
|
||||
renderPassBI.framebuffer = vk.getFramebuffers(renderPassID)[imageIndex];
|
||||
renderPassBI.renderArea.offset = { 0, 0 };
|
||||
renderPassBI.renderArea.extent = vk.scExtent;
|
||||
// clear
|
||||
std::array<VkClearValue, 2> clearValues{};
|
||||
clearValues[0].color = {{1.0f, 0.0f, 0.0f, 1.0f}};
|
||||
clearValues[1].depthStencil = {1.0f, 0};
|
||||
renderPassBI.clearValueCount = static_cast<uint32_t>(clearValues.size());
|
||||
renderPassBI.pClearValues = clearValues.data();
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffers[currentFrame], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
vkCmdBindPipeline(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipelines[PL_2D].pipeline);
|
||||
VkBuffer vertexBuffers[] = { vertexBuffer };
|
||||
VkDeviceSize offsets[] = {0};
|
||||
uint32_t bindingCount = 1;
|
||||
vkCmdBindVertexBuffers(commandBuffers[currentFrame], BINDING, bindingCount, vertexBuffers, offsets);
|
||||
// TODO use correct index type!
|
||||
vkCmdBindIndexBuffer(commandBuffers[currentFrame], indexBuffer, NO_OFFSET, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
/* uint32_t descriptorCount = 1; */
|
||||
/* uint32_t firstSet = 0; */
|
||||
/* uint32_t dynamicOffsetCount = 0; */
|
||||
/* uint32_t* dynamicOffsets = nullptr; */
|
||||
/* vkCmdBindDescriptorSets(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].layout, firstSet, descriptorCount, &descriptorSets[currentFrame], dynamicOffsetCount, dynamicOffsets); */
|
||||
|
||||
int instanceCount = 1;
|
||||
int firstIndex = 0;
|
||||
int firstInstance = 0;
|
||||
vkCmdDrawIndexed(commandBuffers[currentFrame], static_cast<uint32_t>(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance);
|
||||
|
||||
vkCmdEndRenderPass(commandBuffers[currentFrame]);
|
||||
vk.copyImageToImage(commandBuffers[currentFrame], images[imageIndex], vk.scImages[imageIndex], vk.scExtent.width, vk.scExtent.height);
|
||||
result = vkEndCommandBuffer(commandBuffers[currentFrame]);
|
||||
if (result != VK_SUCCESS) {
|
||||
rLog.error("Failed to record 2D - command buffer", "VkResult:", STR_VK_RESULT(result));
|
||||
throw getVkException(result, "Failed to record 2D - command buffer", "Renderer2D::recordCommandBuffer");
|
||||
}
|
||||
vk.commandBuffersToSubmitThisFrame.push_back(commandBuffers[currentFrame]);
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::fillVertexBufferWithShapes() {
|
||||
rLog("fillVertexBufferWithShapes");
|
||||
if (vertexBufferSize < shapesVerticesCount * sizeof(Vertex2D)) {
|
||||
throw VkException("vertex buffer too small! vertexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes");
|
||||
void Renderer2D::cleanup() {
|
||||
/* vk.destroyCommandBuffers(commandBuffers); */
|
||||
cleanupSwapChainDependantResources();
|
||||
cleanup_();
|
||||
}
|
||||
|
||||
// create staging buffer
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
vk.createBuffer(vertexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, vertexBufferSize, NO_FLAGS, &data);
|
||||
Vertex2D* vdata = reinterpret_cast<Vertex2D*>(data);
|
||||
size_t offset = 0;
|
||||
for (auto it = shapes.begin(); it != shapes.end(); it++) {
|
||||
rLog("fillVertexBufferWithShapes: copying vertex buffer nr", it - shapes.begin(), "-", it->getVertices(), "to address:", long(vdata + offset), " offset:", offset);
|
||||
memcpy(vdata+offset, it->getVertices().data(), it->getVertices().size() * sizeof(Vertex2D));
|
||||
offset += it->getVertices().size();
|
||||
}
|
||||
vkUnmapMemory(vk.device, stagingBufferMemory);
|
||||
// fill vertex buffer
|
||||
vk.copyBuffer(stagingBuffer, vertexBuffer, vertexBufferSize);
|
||||
vkDestroyBuffer(vk.device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, stagingBufferMemory, nullptr);
|
||||
}
|
||||
void Renderer2D::fillIndexBufferWithShapes() {
|
||||
rLog("fillIndexBufferWithShapes");
|
||||
if (indexBufferSize < shapesIndicesCount * sizeof(uint32_t)) {
|
||||
throw VkException("index buffer too small! indexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes");
|
||||
//
|
||||
// SWAPCHAIN DEPENDANT
|
||||
//
|
||||
void Renderer2D::initSwapChainDependantResources() {
|
||||
createRenderPass();
|
||||
createImages();
|
||||
vk.createFramebuffers(framebuffers, imageViews, renderPass);
|
||||
std::vector<VkDescriptorSetLayout> descriptorSetLayouts = { textureManager.getDescriptorSetLayout() };
|
||||
vk.createGraphicsPipeline<Vertex2D>("shaders/vert2D.spv", "shaders/frag2D.spv", descriptorSetLayouts, false, renderPass, pipelines[PL_2D]);
|
||||
}
|
||||
|
||||
// create staging buffer
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
vk.createBuffer(indexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, indexBufferSize, NO_FLAGS, &data);
|
||||
uint32_t* idata = reinterpret_cast<uint32_t*>(data);
|
||||
size_t offset = 0;
|
||||
for (auto it = shapes.begin(); it != shapes.end(); it++) {
|
||||
rLog("fillIndexBufferWithShapes: copying index buffer nr", it - shapes.begin(), "-", it->getIndices(), "to address:", long(idata + offset), " offset:", offset);
|
||||
memcpy(idata+offset, it->getIndices().data(), it->getIndices().size() * sizeof(uint32_t));
|
||||
offset += it->getIndices().size();
|
||||
void Renderer2D::cleanupSwapChainDependantResources() {
|
||||
// destroy pipelines
|
||||
pipelines.destroy(vk.device);
|
||||
|
||||
vk.destroyFramebuffers(framebuffers);
|
||||
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
vkDestroyImageView(vk.device, imageViews[i], nullptr);
|
||||
vkDestroyImage(vk.device, images[i], nullptr);
|
||||
vkFreeMemory(vk.device, imageMemory[i], nullptr);
|
||||
}
|
||||
vkDestroyRenderPass(vk.device, renderPass, nullptr);
|
||||
|
||||
|
||||
}
|
||||
rLog("fillIndexBufferWithShapes: indices count:", shapesIndicesCount);
|
||||
vkUnmapMemory(vk.device, stagingBufferMemory);
|
||||
|
||||
// fill index buffer
|
||||
vk.copyBuffer(stagingBuffer, indexBuffer, indexBufferSize);
|
||||
vkDestroyBuffer(vk.device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, stagingBufferMemory, nullptr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::drawShape(const Shape& shape) {
|
||||
shapes.emplace_back(shape);
|
||||
// make indices valid
|
||||
shapes.rbegin()->setIndexOffset(shapesVerticesCount);
|
||||
shapes.rbegin()->setNormalize(vk.scExtent.width, vk.scExtent.height);
|
||||
shapesVerticesCount += shape.getVertices().size();
|
||||
shapesIndicesCount += shape.getIndices().size();
|
||||
}
|
||||
void Renderer2D::swapChainRecreateCallback() {
|
||||
cleanupSwapChainDependantResources();
|
||||
initSwapChainDependantResources();
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::drawFrame(uint32_t imageIndex) {
|
||||
vkResetCommandBuffer(commandBuffers[vk.currentFrame], NO_FLAGS);
|
||||
recordCommandBuffer(imageIndex, vk.currentFrame);
|
||||
//
|
||||
// IMAGES
|
||||
//
|
||||
void Renderer2D::createImages() {
|
||||
images.resize(vk.scImages.size());
|
||||
imageMemory.resize(vk.scImages.size());
|
||||
imageViews.resize(vk.scImages.size());
|
||||
VkImageUsageFlags usage= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
vk.createImage(vk.scExtent.width, vk.scExtent.height, vk.scImageFormat, VK_IMAGE_TILING_OPTIMAL, usage, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, images[i], imageMemory[i]);
|
||||
vk.createImageView(vk.scImageFormat, images[i], imageViews[i], VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//
|
||||
// RENDER PASS
|
||||
//
|
||||
void Renderer2D::createRenderPass() {
|
||||
VkAttachmentDescription2 colorBlendAttachment{};
|
||||
colorBlendAttachment.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
|
||||
colorBlendAttachment.format = vk.scImageFormat;
|
||||
colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
|
||||
VkAttachmentReference2 colorAttachmentRef{};
|
||||
colorAttachmentRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
|
||||
colorAttachmentRef.attachment = 0;
|
||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
/* VkAttachmentDescription depthAttachment{}; */
|
||||
/* depthAttachment.format = findDepthFormat(); */
|
||||
/* depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; */
|
||||
/* depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; */
|
||||
/* depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */
|
||||
/* depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; */
|
||||
/* depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */
|
||||
/* depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; */
|
||||
/* depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */
|
||||
|
||||
/* VkAttachmentReference depthAttachmentRef{}; */
|
||||
/* depthAttachmentRef.attachment = 1; */
|
||||
/* depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */
|
||||
|
||||
VkSubpassDescription2 subpass{};
|
||||
subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2;
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &colorAttachmentRef;
|
||||
/* subpass.pDepthStencilAttachment = &depthAttachmentRef; */
|
||||
|
||||
VkSubpassDependency2 colorAttachmentSD{};
|
||||
colorAttachmentSD.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2;
|
||||
colorAttachmentSD.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
colorAttachmentSD.dstSubpass = 0;
|
||||
colorAttachmentSD.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
colorAttachmentSD.srcAccessMask = 0;
|
||||
colorAttachmentSD.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
colorAttachmentSD.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
// dependecy for the image layout transition to transfer dst
|
||||
VkSubpassDependency2 layoutTransitionSD{};
|
||||
colorAttachmentSD.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2;
|
||||
colorAttachmentSD.srcSubpass = 0;
|
||||
colorAttachmentSD.dstSubpass = VK_SUBPASS_EXTERNAL;
|
||||
colorAttachmentSD.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
colorAttachmentSD.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
colorAttachmentSD.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
|
||||
colorAttachmentSD.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT;
|
||||
colorAttachmentSD.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
/* VkSubpassDependency dependency{}; */
|
||||
/* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */
|
||||
/* dependency.dstSubpass = 0; */
|
||||
/* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
|
||||
/* dependency.srcAccessMask = 0; */
|
||||
/* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
|
||||
/* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */
|
||||
|
||||
/* std::array<VkAttachmentDescription, 2> attachments = { colorBlendAttachment, depthAttachment }; */
|
||||
std::vector<VkAttachmentDescription2> attachments = { colorBlendAttachment };
|
||||
std::vector<VkSubpassDependency2> dependencies = { colorAttachmentSD, layoutTransitionSD };
|
||||
VkRenderPassCreateInfo2 renderPassCI{};
|
||||
renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2;
|
||||
renderPassCI.attachmentCount = static_cast<uint32_t>(attachments.size());
|
||||
renderPassCI.pAttachments = attachments.data();
|
||||
renderPassCI.subpassCount = 1;
|
||||
renderPassCI.pSubpasses = &subpass;
|
||||
renderPassCI.dependencyCount = dependencies.size();
|
||||
renderPassCI.pDependencies = dependencies.data();
|
||||
/* renderPassCI.dependencyCount = 0; */
|
||||
/* renderPassCI.pDependencies = nullptr; */
|
||||
/* renderPassCI.correlatedViewMaskCount = 0; */
|
||||
/* renderPassCI.pCorrelatedViewMasks = nullptr; */
|
||||
VkResult result = vkCreateRenderPass2(vk.device, &renderPassCI, nullptr, &renderPass);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Could not create render pass", "Renderer2D::createRenderPass");
|
||||
}
|
||||
rLog("createRenderPass: Created render pass.");
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// RENDERING
|
||||
//
|
||||
void Renderer2D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) {
|
||||
VkCommandBufferBeginInfo commandBufferBI{};
|
||||
commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
/* commandBufferBI.flags = 0; */
|
||||
/* commandBufferBI.pInheritanceInfo = nullptr; */
|
||||
VkResult result = vkBeginCommandBuffer(commandBuffers[currentFrame], &commandBufferBI);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Failed to begin 2D command buffer", "Renderer2D::recordCommandBuffer");
|
||||
}
|
||||
|
||||
VkRenderPassBeginInfo renderPassBI{};
|
||||
renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassBI.renderPass = renderPass;
|
||||
renderPassBI.framebuffer = framebuffers[imageIndex];
|
||||
renderPassBI.renderArea.offset = { 0, 0 };
|
||||
renderPassBI.renderArea.extent = vk.scExtent;
|
||||
// clear
|
||||
std::array<VkClearValue, 1> clearValues{};
|
||||
clearValues[0].color = {{1.0f, 0.0f, 0.0f, 1.0f}};
|
||||
/* clearValues[1].depthStencil = {1.0f, 0}; */
|
||||
renderPassBI.clearValueCount = static_cast<uint32_t>(clearValues.size());
|
||||
renderPassBI.pClearValues = clearValues.data();
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffers[currentFrame], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
vkCmdBindPipeline(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].pipeline);
|
||||
VkBuffer vertexBuffers[] = { vertexBuffer };
|
||||
VkDeviceSize offsets[] = {0};
|
||||
uint32_t bindingCount = 1;
|
||||
vkCmdBindVertexBuffers(commandBuffers[currentFrame], BINDING, bindingCount, vertexBuffers, offsets);
|
||||
// TODO use correct index type!
|
||||
vkCmdBindIndexBuffer(commandBuffers[currentFrame], indexBuffer, NO_OFFSET, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
uint32_t descriptorCount = 1;
|
||||
uint32_t firstSet = 0;
|
||||
uint32_t dynamicOffsetCount = 0;
|
||||
uint32_t* dynamicOffsets = nullptr;
|
||||
vkCmdBindDescriptorSets(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets);
|
||||
|
||||
int instanceCount = 1;
|
||||
int firstIndex = 0;
|
||||
int firstInstance = 0;
|
||||
vkCmdDrawIndexed(commandBuffers[currentFrame], static_cast<uint32_t>(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance);
|
||||
vkCmdEndRenderPass(commandBuffers[currentFrame]);
|
||||
|
||||
vk.copyImageToImage(commandBuffers[currentFrame], images[imageIndex], vk.scImages[imageIndex], vk.scExtent);
|
||||
result = vkEndCommandBuffer(commandBuffers[currentFrame]);
|
||||
if (result != VK_SUCCESS) {
|
||||
rLog.error("Failed to record 2D - command buffer", "VkResult:", STR_VK_RESULT(result));
|
||||
throw getVkException(result, "Failed to record 2D - command buffer", "Renderer2D::recordCommandBufferWithTexture");
|
||||
}
|
||||
vk.submitThisFrame(commandBuffers[currentFrame]);
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::fillVertexBufferWithShapes() {
|
||||
rLog("fillVertexBufferWithShapes");
|
||||
if (vertexBufferSize < shapesVerticesCount * sizeof(Vertex2D)) {
|
||||
throw VkException("vertex buffer too small! vertexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes");
|
||||
}
|
||||
|
||||
// create staging buffer
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
vk.createBuffer(vertexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, vertexBufferSize, NO_FLAGS, &data);
|
||||
Vertex2D* vdata = reinterpret_cast<Vertex2D*>(data);
|
||||
size_t offset = 0;
|
||||
for (auto it = shapes.begin(); it != shapes.end(); it++) {
|
||||
rLog("fillVertexBufferWithShapes: copying vertex buffer nr", it - shapes.begin(), "-", it->getVertices(), "to address:", long(vdata + offset), " offset:", offset);
|
||||
memcpy(vdata+offset, it->getVertices().data(), it->getVertices().size() * sizeof(Vertex2D));
|
||||
offset += it->getVertices().size();
|
||||
}
|
||||
vkUnmapMemory(vk.device, stagingBufferMemory);
|
||||
// fill vertex buffer
|
||||
vk.copyBuffer(stagingBuffer, vertexBuffer, vertexBufferSize);
|
||||
vkDestroyBuffer(vk.device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, stagingBufferMemory, nullptr);
|
||||
}
|
||||
void Renderer2D::fillIndexBufferWithShapes() {
|
||||
rLog("fillIndexBufferWithShapes");
|
||||
if (indexBufferSize < shapesIndicesCount * sizeof(uint32_t)) {
|
||||
throw VkException("index buffer too small! indexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes");
|
||||
}
|
||||
|
||||
// create staging buffer
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
vk.createBuffer(indexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, indexBufferSize, NO_FLAGS, &data);
|
||||
uint32_t* idata = reinterpret_cast<uint32_t*>(data);
|
||||
size_t offset = 0;
|
||||
for (auto it = shapes.begin(); it != shapes.end(); it++) {
|
||||
rLog("fillIndexBufferWithShapes: copying index buffer nr", it - shapes.begin(), "-", it->getIndices(), "to address:", long(idata + offset), " offset:", offset);
|
||||
memcpy(idata+offset, it->getIndices().data(), it->getIndices().size() * sizeof(uint32_t));
|
||||
offset += it->getIndices().size();
|
||||
}
|
||||
rLog("fillIndexBufferWithShapes: indices count:", shapesIndicesCount);
|
||||
vkUnmapMemory(vk.device, stagingBufferMemory);
|
||||
|
||||
// fill index buffer
|
||||
vk.copyBuffer(stagingBuffer, indexBuffer, indexBufferSize);
|
||||
vkDestroyBuffer(vk.device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, stagingBufferMemory, nullptr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::drawShape(Shape* shape) {
|
||||
// make indices valid
|
||||
shape->setIndexOffset(shapesVerticesCount);
|
||||
shape->normalizeVertices(vk.scExtent.width, vk.scExtent.height);
|
||||
shape->setTextureCoordinates(textureManager);
|
||||
// object slicing here, need to call virtual setTextureCoordinates before this!
|
||||
shapes.push_back(*shape);
|
||||
shapesVerticesCount += shape->getVertices().size();
|
||||
shapesIndicesCount += shape->getIndices().size();
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::drawFrame(uint32_t imageIndex) {
|
||||
vkResetCommandBuffer(commandBuffers[vk.currentFrame], NO_FLAGS);
|
||||
/* recordCommandBuffer(imageIndex, vk.currentFrame); */
|
||||
recordCommandBuffer(imageIndex, vk.currentFrame);
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace gz::vk
|
||||
|
110
renderer2D.hpp
110
renderer2D.hpp
@ -1,31 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "renderer.hpp"
|
||||
|
||||
#include "shape.hpp"
|
||||
#include "vulkan_util.hpp"
|
||||
|
||||
namespace gz::vk {
|
||||
class Renderer2D : public Renderer {
|
||||
public:
|
||||
Renderer2D(VulkanInstance& instance);
|
||||
void drawShape(const Shape& shape);
|
||||
/**
|
||||
* @brief Create a 2D renderer
|
||||
* @details
|
||||
* -# @ref VulkanInstance::registerCleanupCallback "register" @ref cleanup() "cleanup callback"
|
||||
* -# @ref VulkanInstance::registerSwapChainRecreateCallback "register" @ref swapChainRecreateCallback "swapChain recreation callback"
|
||||
* -# create command buffers
|
||||
* -# create vertex & index buffers
|
||||
* -# call initSwapChainDependantResources
|
||||
*/
|
||||
Renderer2D(VulkanInstance& instance, TextureManager& textureManager);
|
||||
/**
|
||||
* @name Rendering
|
||||
*/
|
||||
/// @{
|
||||
void drawShape(Shape* shape);
|
||||
/**
|
||||
* @brief Copies the vertices from shapes into the vertex buffer, using a staging buffer
|
||||
*/
|
||||
void fillVertexBufferWithShapes();
|
||||
void fillIndexBufferWithShapes();
|
||||
void drawFrame(uint32_t imageIndex);
|
||||
void cleanup();
|
||||
/**
|
||||
* @brief Create a render pass
|
||||
* @details
|
||||
* Attachments:
|
||||
* - color blend:
|
||||
* - loadOp = VK_ATTACHMENT_LOAD_OP_LOAD (not clear!)
|
||||
* - storeOp = VK_ATTACHMENT_STORE_OP_STORE
|
||||
* - initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
* - finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
* - stencil load/store = dont care
|
||||
*/
|
||||
void createRenderPass();
|
||||
/// @}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Destroy all vulkan objects owned by this object
|
||||
* @details:
|
||||
* Does:
|
||||
* -# call cleanupSwapChainDependantResources()
|
||||
* -# call Renderer::cleanup_()
|
||||
*/
|
||||
void cleanup();
|
||||
std::vector<Shape> shapes;
|
||||
size_t shapesVerticesCount = 0;
|
||||
size_t shapesIndicesCount = 0;
|
||||
@ -37,26 +51,82 @@ namespace gz::vk {
|
||||
* - image layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
* - clear image
|
||||
* -# bind 2d pipeline, vertex and index buffer
|
||||
* -# bind texture sampler
|
||||
* -# draw indexed: draw the shapes from shapes vector
|
||||
* -# end render pass
|
||||
* - image layout: VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
|
||||
* -# copy image to swapChain image with same imageIndex
|
||||
*
|
||||
*/
|
||||
void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame);
|
||||
|
||||
// IMAGE
|
||||
/**
|
||||
* @name Image (render targets)
|
||||
* @details
|
||||
* The images are used as render targets. After rendering, the current image gets blitted onto the current swap chain image.
|
||||
* @{
|
||||
*/
|
||||
std::vector<VkImage> images;
|
||||
std::vector<VkDeviceMemory> imageMemory;
|
||||
std::vector<VkImageView> imageViews;
|
||||
/**
|
||||
* @brief Creates the images and imageViews with the format of the VulkanInstance::swapChain images
|
||||
* @brief Creates the images (on imageMemory) and imageViews with the format of the VulkanInstance::swapChain images
|
||||
*/
|
||||
void createImages();
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* @name Render pass
|
||||
* @details
|
||||
* Attachments:
|
||||
* - color blend:
|
||||
* - loadOp = VK_ATTACHMENT_LOAD_OP_LOAD (not clear!)
|
||||
* - storeOp = VK_ATTACHMENT_STORE_OP_STORE
|
||||
* - initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
* - finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
* - stencil load/store = dont care
|
||||
*/
|
||||
/// @{
|
||||
/**
|
||||
* @brief Create a render pass
|
||||
*/
|
||||
void createRenderPass();
|
||||
VkRenderPass renderPass;
|
||||
int renderPassID;
|
||||
/// @}
|
||||
|
||||
std::vector<VkFramebuffer> framebuffers;
|
||||
// PIPELINE
|
||||
PipelineContainer pipelines;
|
||||
|
||||
// SWAPCHAIN RECREATION
|
||||
/**
|
||||
* @brief Swapchain recreation
|
||||
*/
|
||||
/// @{
|
||||
/**
|
||||
* @brief Cleans up resources that were initialized by initSwapChainDependantResources
|
||||
*/
|
||||
void cleanupSwapChainDependantResources();
|
||||
/**
|
||||
* @brief Sets up resources that depend on the swap chain or its attributes
|
||||
* @details
|
||||
* Initializes up:
|
||||
* - render pass
|
||||
* - images, imageMemory, imageViews
|
||||
* - framebuffers
|
||||
* - pipeline
|
||||
*/
|
||||
void initSwapChainDependantResources();
|
||||
/**
|
||||
* @brief Recreates swap chain dependant resources
|
||||
* @details
|
||||
* Calls:
|
||||
* -# cleanupSwapChainDependantResources
|
||||
* -# initSwapChainDependantResources
|
||||
*/
|
||||
void swapChainRecreateCallback();
|
||||
/// @}
|
||||
|
||||
Log rLog;
|
||||
|
||||
};
|
||||
|
||||
} // namespace gz::vk
|
||||
|
0
renderer2D.log
Normal file
0
renderer2D.log
Normal file
506
renderer3D.cpp
506
renderer3D.cpp
@ -1,104 +1,436 @@
|
||||
#include "renderer3D.hpp"
|
||||
|
||||
#include "vulkan_instance.hpp"
|
||||
#include "texture_manager.hpp"
|
||||
#include "exceptions.hpp"
|
||||
#include "vk_enum_string.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace gz::vk {
|
||||
//
|
||||
// INIT & CLEANUP
|
||||
//
|
||||
Renderer3D::Renderer3D(VulkanInstance& instance, TextureManager& textureManager) :
|
||||
Renderer(instance, textureManager),
|
||||
rLog("renderer3D.log", true, false, "3D-Renderer", Color::CYAN, VULKAN_MESSAGE_TIME_COLOR, true, 100) {
|
||||
|
||||
Renderer3D::Renderer3D(VulkanInstance& instance) :
|
||||
Renderer(instance),
|
||||
rLog("renderer3D.log", true, false, "3D-Renderer", Color::BCYAN, true, 100) {
|
||||
vk.createCommandBuffers(commandBuffers);
|
||||
const size_t vertexCount = 500;
|
||||
const size_t indexCount = 1000;
|
||||
vk.createVertexBuffer<Vertex2D>(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize);
|
||||
vk.createIndexBuffer<uint32_t>(indexCount, indexBuffer, indexBufferMemory, indexBufferSize);
|
||||
rLog("Created Renderer3D");
|
||||
}
|
||||
vk.registerCleanupCallback(std::bind(&Renderer3D::cleanup, this));
|
||||
vk.registerSwapChainRecreateCallback(std::bind(&Renderer3D::swapChainRecreateCallback, this));
|
||||
|
||||
void Renderer3D::cleanup() {
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
vkDestroyBuffer(vk.device, uniformBuffers[i], nullptr);
|
||||
vkFreeMemory(vk.device, uniformBuffersMemory[i], nullptr);
|
||||
}
|
||||
cleanup_();
|
||||
}
|
||||
vk.createCommandBuffers(commandBuffers);
|
||||
const size_t vertexCount = 500;
|
||||
const size_t indexCount = 1000;
|
||||
vk.createVertexBuffer<Vertex3D>(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize);
|
||||
vk.createIndexBuffer<uint32_t>(indexCount, indexBuffer, indexBufferMemory, indexBufferSize);
|
||||
rLog("Created Renderer3D");
|
||||
|
||||
|
||||
void Renderer3D::updateUniformBuffer() {
|
||||
static auto startTime = std::chrono::high_resolution_clock::now();
|
||||
auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();
|
||||
|
||||
// TODO use push constant instead of ubo
|
||||
UniformBufferObject ubo{};
|
||||
/* ubo.model = glm::rotate(glm::mat4(1.0f), time * std::numbers::pi_v<float> / 2, glm::vec3(0.0f, 0.0f, 1.0f)); */
|
||||
/* ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); */
|
||||
/* ubo.projection = glm::perspective(glm::radians(45.0f), static_cast<float>(scExtent.width) / scExtent.height, 1.0f, 10.0f); */
|
||||
ubo.model = glm::mat4(1);
|
||||
ubo.view = glm::mat4(1);
|
||||
ubo.projection = glm::mat4(1);
|
||||
/* ubo.projection[1][1] *= -1; // y coordinate inverted in opengl */
|
||||
void* data;
|
||||
vkMapMemory(vk.device, uniformBuffersMemory[vk.currentFrame], NO_OFFSET, sizeof(ubo), NO_FLAGS, &data);
|
||||
memcpy(data, &ubo, sizeof(ubo));
|
||||
vkUnmapMemory(vk.device, uniformBuffersMemory[vk.currentFrame]);
|
||||
}
|
||||
|
||||
|
||||
void Renderer3D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) {
|
||||
VkCommandBufferBeginInfo commandBufferBI{};
|
||||
commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
/* commandBufferBI.flags = 0; */
|
||||
/* commandBufferBI.pInheritanceInfo = nullptr; */
|
||||
VkResult result = vkBeginCommandBuffer(commandBuffers[currentFrame], &commandBufferBI);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Failed to begin 3D - command buffer", "recordCommandBuffer");
|
||||
initSwapChainDependantResources();
|
||||
VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan("Renderer3D",
|
||||
{ &pipelines[PL_3D].pipeline, &renderPass, &vertexBuffer, &vertexBufferMemory, &indexBuffer, &indexBufferMemory },
|
||||
{ &framebuffers, &images, &imageMemory, &imageViews, &commandBuffers }));
|
||||
rLog("Created Renderer3D");
|
||||
}
|
||||
|
||||
VkRenderPassBeginInfo renderPassBI{};
|
||||
renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassBI.renderPass = vk.renderPassBegin;
|
||||
// TODO
|
||||
/* renderPassBI.framebuffer = vk.scFramebuffers[0][imageIndex]; */
|
||||
renderPassBI.renderArea.offset = { 0, 0 };
|
||||
renderPassBI.renderArea.extent = vk.scExtent;
|
||||
std::array<VkClearValue, 2> clearValues{};
|
||||
clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
clearValues[1].depthStencil = {1.0f, 0};
|
||||
renderPassBI.clearValueCount = static_cast<uint32_t>(clearValues.size());
|
||||
renderPassBI.pClearValues = clearValues.data();
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffers[currentFrame], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
vkCmdBindPipeline(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipelines[PL_3D].pipeline);
|
||||
VkBuffer vertexBuffers[] = { vertexBuffer };
|
||||
VkDeviceSize offsets[] = {0};
|
||||
uint32_t bindingCount = 1;
|
||||
vkCmdBindVertexBuffers(commandBuffers[currentFrame], BINDING, bindingCount, vertexBuffers, offsets);
|
||||
// TODO use correct index type!
|
||||
vkCmdBindIndexBuffer(commandBuffers[currentFrame], indexBuffer, NO_OFFSET, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
uint32_t descriptorCount = 1;
|
||||
uint32_t firstSet = 0;
|
||||
uint32_t dynamicOffsetCount = 0;
|
||||
uint32_t* dynamicOffsets = nullptr;
|
||||
vkCmdBindDescriptorSets(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipelines[PL_3D].layout, firstSet, descriptorCount, &vk.descriptorSets[currentFrame], dynamicOffsetCount, dynamicOffsets);
|
||||
|
||||
int instanceCount = 1;
|
||||
int firstIndex = 0;
|
||||
int firstInstance = 0;
|
||||
/* vkCmdDrawIndexed(commandBuffers[currentFrame], static_cast<uint32_t>(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance); */
|
||||
|
||||
vkCmdEndRenderPass(commandBuffers[currentFrame]);
|
||||
result = vkEndCommandBuffer(commandBuffers[currentFrame]);
|
||||
if (result != VK_SUCCESS) {
|
||||
rLog.error("Failed to record command buffer", "VkResult:", STR_VK_RESULT(result));
|
||||
throw getVkException(result, "Failed to record command buffer");
|
||||
void Renderer3D::cleanup() {
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
vkDestroyBuffer(vk.device, uniformBuffers[i], nullptr);
|
||||
vkFreeMemory(vk.device, uniformBuffersMemory[i], nullptr);
|
||||
}
|
||||
cleanupSwapChainDependantResources();
|
||||
cleanup_();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// SWAPCHAIN DEPENDANT
|
||||
//
|
||||
void Renderer3D::initSwapChainDependantResources() {
|
||||
createRenderPass();
|
||||
createImages();
|
||||
vk.createFramebuffers(framebuffers, imageViews, renderPass);
|
||||
std::vector<VkDescriptorSetLayout> descriptorSetLayouts = { textureManager.getDescriptorSetLayout() };
|
||||
vk.createGraphicsPipeline<Vertex2D>("shaders/vert2D.spv", "shaders/frag2D.spv", descriptorSetLayouts, false, renderPass, pipelines[PL_2D]);
|
||||
}
|
||||
|
||||
|
||||
void Renderer3D::cleanupSwapChainDependantResources() {
|
||||
// destroy pipelines
|
||||
pipelines.destroy(vk.device);
|
||||
|
||||
vk.destroyFramebuffers(framebuffers);
|
||||
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
vkDestroyImageView(vk.device, imageViews[i], nullptr);
|
||||
vkDestroyImage(vk.device, images[i], nullptr);
|
||||
vkFreeMemory(vk.device, imageMemory[i], nullptr);
|
||||
}
|
||||
vkDestroyRenderPass(vk.device, renderPass, nullptr);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Renderer3D::swapChainRecreateCallback() {
|
||||
cleanupSwapChainDependantResources();
|
||||
initSwapChainDependantResources();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// IMAGES
|
||||
//
|
||||
void Renderer3D::createImages() {
|
||||
images.resize(vk.scImages.size());
|
||||
imageMemory.resize(vk.scImages.size());
|
||||
imageViews.resize(vk.scImages.size());
|
||||
VkImageUsageFlags usage= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
vk.createImage(vk.scExtent.width, vk.scExtent.height, vk.scImageFormat, VK_IMAGE_TILING_OPTIMAL, usage, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, images[i], imageMemory[i]);
|
||||
vk.createImageView(vk.scImageFormat, images[i], imageViews[i], VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// RENDER PASS
|
||||
//
|
||||
void Renderer3D::createRenderPass() {
|
||||
VkAttachmentDescription2 colorBlendAttachment{};
|
||||
colorBlendAttachment.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
|
||||
colorBlendAttachment.format = vk.scImageFormat;
|
||||
colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
|
||||
VkAttachmentReference2 colorAttachmentRef{};
|
||||
colorAttachmentRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
|
||||
colorAttachmentRef.attachment = 0;
|
||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
/* VkAttachmentDescription depthAttachment{}; */
|
||||
/* depthAttachment.format = findDepthFormat(); */
|
||||
/* depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; */
|
||||
/* depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; */
|
||||
/* depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */
|
||||
/* depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; */
|
||||
/* depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */
|
||||
/* depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; */
|
||||
/* depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */
|
||||
|
||||
/* VkAttachmentReference depthAttachmentRef{}; */
|
||||
/* depthAttachmentRef.attachment = 1; */
|
||||
/* depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */
|
||||
|
||||
VkSubpassDescription2 subpass{};
|
||||
subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2;
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &colorAttachmentRef;
|
||||
/* subpass.pDepthStencilAttachment = &depthAttachmentRef; */
|
||||
|
||||
VkSubpassDependency2 colorAttachmentSD{};
|
||||
colorAttachmentSD.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2;
|
||||
colorAttachmentSD.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
colorAttachmentSD.dstSubpass = 0;
|
||||
colorAttachmentSD.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
colorAttachmentSD.srcAccessMask = 0;
|
||||
colorAttachmentSD.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
colorAttachmentSD.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
// dependecy for the image layout transition to transfer dst
|
||||
VkSubpassDependency2 layoutTransitionSD{};
|
||||
colorAttachmentSD.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2;
|
||||
colorAttachmentSD.srcSubpass = 0;
|
||||
colorAttachmentSD.dstSubpass = VK_SUBPASS_EXTERNAL;
|
||||
colorAttachmentSD.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
colorAttachmentSD.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
colorAttachmentSD.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
|
||||
colorAttachmentSD.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT;
|
||||
colorAttachmentSD.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
/* VkSubpassDependency dependency{}; */
|
||||
/* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */
|
||||
/* dependency.dstSubpass = 0; */
|
||||
/* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
|
||||
/* dependency.srcAccessMask = 0; */
|
||||
/* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
|
||||
/* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */
|
||||
|
||||
/* std::array<VkAttachmentDescription, 2> attachments = { colorBlendAttachment, depthAttachment }; */
|
||||
std::vector<VkAttachmentDescription2> attachments = { colorBlendAttachment };
|
||||
std::vector<VkSubpassDependency2> dependencies = { colorAttachmentSD, layoutTransitionSD };
|
||||
VkRenderPassCreateInfo2 renderPassCI{};
|
||||
renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2;
|
||||
renderPassCI.attachmentCount = static_cast<uint32_t>(attachments.size());
|
||||
renderPassCI.pAttachments = attachments.data();
|
||||
renderPassCI.subpassCount = 1;
|
||||
renderPassCI.pSubpasses = &subpass;
|
||||
renderPassCI.dependencyCount = dependencies.size();
|
||||
renderPassCI.pDependencies = dependencies.data();
|
||||
/* renderPassCI.dependencyCount = 0; */
|
||||
/* renderPassCI.pDependencies = nullptr; */
|
||||
/* renderPassCI.correlatedViewMaskCount = 0; */
|
||||
/* renderPassCI.pCorrelatedViewMasks = nullptr; */
|
||||
VkResult result = vkCreateRenderPass2(vk.device, &renderPassCI, nullptr, &renderPass);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Could not create render pass", "Renderer3D::createRenderPass");
|
||||
}
|
||||
rLog("createRenderPass: Created render pass.");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Renderer3D::updateUniformBuffer() {
|
||||
static auto startTime = std::chrono::high_resolution_clock::now();
|
||||
auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();
|
||||
|
||||
// TODO use push constant instead of ubo
|
||||
UniformBufferObject ubo{};
|
||||
ubo.model = glm::rotate(glm::mat4(1.0f), time * std::numbers::pi_v<float> / 2, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
ubo.projection = glm::perspective(glm::radians(45.0f), static_cast<float>(vk.scExtent.width) / vk.scExtent.height, 1.0f, 10.0f);
|
||||
/* ubo.model = glm::mat4(1); */
|
||||
/* ubo.view = glm::mat4(1); */
|
||||
/* ubo.projection = glm::mat4(1); */
|
||||
/* ubo.projection[1][1] *= -1; // y coordinate inverted in opengl */
|
||||
void* data;
|
||||
vkMapMemory(vk.device, uniformBuffersMemory[vk.currentFrame], NO_OFFSET, sizeof(ubo), NO_FLAGS, &data);
|
||||
memcpy(data, &ubo, sizeof(ubo));
|
||||
vkUnmapMemory(vk.device, uniformBuffersMemory[vk.currentFrame]);
|
||||
}
|
||||
|
||||
//
|
||||
// DESCRIPTORS
|
||||
//
|
||||
void Renderer3D::createDescriptorResources() {
|
||||
// LAYOUT
|
||||
// 1) uniform buffer object
|
||||
VkDescriptorSetLayoutBinding uboLayoutBinding{};
|
||||
uboLayoutBinding.binding = 0;
|
||||
uboLayoutBinding.descriptorCount = 1;
|
||||
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
/* uboLayoutBinding.pImmutableSamplers = nullptr; */
|
||||
|
||||
// 2) combined image sampler
|
||||
VkDescriptorSetLayoutBinding samplerLayoutBinding{};
|
||||
samplerLayoutBinding.binding = 1;
|
||||
samplerLayoutBinding.descriptorCount = 1;
|
||||
samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
/* samplerLayoutBinding.pImmutableSamplers = nullptr; */
|
||||
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings = { uboLayoutBinding, samplerLayoutBinding };
|
||||
vk.createDescriptorSetLayout(bindings, descriptorSetLayout);
|
||||
|
||||
// POOL
|
||||
std::array<VkDescriptorPoolSize, 2> poolSizes;
|
||||
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
|
||||
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
VkDescriptorPoolCreateInfo poolCI{};
|
||||
poolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
poolCI.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
|
||||
poolCI.pPoolSizes = poolSizes.data();
|
||||
poolCI.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
|
||||
VkResult result = vkCreateDescriptorPool(vk.device, &poolCI, nullptr, &descriptorPool);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Failed to create descriptor pool", "Renderer3D::createDescriptorResources");
|
||||
}
|
||||
|
||||
// SETS
|
||||
std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, 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.pSetLayouts = layouts.data();
|
||||
|
||||
descriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
result = vkAllocateDescriptorSets(vk.device, &setAI, descriptorSets.data());
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Failed to create descriptor sets", "Renderer3D::createDescriptorResources");
|
||||
}
|
||||
|
||||
// configure sets
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
VkDescriptorBufferInfo bufferI{};
|
||||
bufferI.buffer = uniformBuffers[i];
|
||||
bufferI.offset = 0;
|
||||
bufferI.range = VK_WHOLE_SIZE; // sizeof(UniformBufferObject);
|
||||
|
||||
VkDescriptorImageInfo imageI{};
|
||||
imageI.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
// TODO
|
||||
/* imageI.imageView = textureImageView; */
|
||||
/* imageI.sampler = textureSampler; */
|
||||
|
||||
std::array<VkWriteDescriptorSet, 2> descriptorW{};
|
||||
descriptorW[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
descriptorW[0].dstSet = descriptorSets[i];
|
||||
descriptorW[0].dstBinding = bindingUniformBuffer;
|
||||
descriptorW[0].dstArrayElement = 0;
|
||||
descriptorW[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
descriptorW[0].descriptorCount = 1;
|
||||
descriptorW[0].pBufferInfo = &bufferI;
|
||||
/* descriptorW[0].pImageInfo = nullptr; */
|
||||
/* descriptorW[0].pTexelBufferView = nullptr; */
|
||||
|
||||
descriptorW[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
descriptorW[1].dstSet = descriptorSets[i];
|
||||
descriptorW[1].dstBinding = bindingCombinedImageSampler;
|
||||
descriptorW[1].dstArrayElement = 0;
|
||||
descriptorW[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
descriptorW[1].descriptorCount = 1;
|
||||
/* descriptorW[1].pBufferInfo = &bufferI; */
|
||||
descriptorW[1].pImageInfo = &imageI;
|
||||
/* descriptorW[1].pTexelBufferView = nullptr; */
|
||||
|
||||
uint32_t descriptorWriteCount = static_cast<uint32_t>(descriptorW.size());
|
||||
uint32_t descriptorCopyCount = 0;
|
||||
vkUpdateDescriptorSets(vk.device, descriptorWriteCount, descriptorW.data(), descriptorCopyCount, nullptr);
|
||||
}
|
||||
rLog("createDescriptorResources: Created descriptor layouts, pool and sets.");
|
||||
}
|
||||
|
||||
//
|
||||
// RENDERING
|
||||
//
|
||||
void Renderer3D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) {}
|
||||
/* VkCommandBufferBeginInfo commandBufferBI{}; */
|
||||
/* commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; */
|
||||
/* /1* commandBufferBI.flags = 0; *1/ */
|
||||
/* /1* commandBufferBI.pInheritanceInfo = nullptr; *1/ */
|
||||
/* VkResult result = vkBeginCommandBuffer(commandBuffers[currentFrame], &commandBufferBI); */
|
||||
/* if (result != VK_SUCCESS) { */
|
||||
/* throw getVkException(result, "Failed to begin 2D command buffer", "Renderer3D::recordCommandBuffer"); */
|
||||
/* } */
|
||||
|
||||
/* VkRenderPassBeginInfo renderPassBI{}; */
|
||||
/* renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; */
|
||||
/* renderPassBI.renderPass = renderPass; */
|
||||
/* renderPassBI.framebuffer = framebuffers[imageIndex]; */
|
||||
/* renderPassBI.renderArea.offset = { 0, 0 }; */
|
||||
/* renderPassBI.renderArea.extent = vk.scExtent; */
|
||||
/* // clear */
|
||||
/* std::array<VkClearValue, 1> clearValues{}; */
|
||||
/* clearValues[0].color = {{1.0f, 0.0f, 0.0f, 1.0f}}; */
|
||||
/* /1* clearValues[1].depthStencil = {1.0f, 0}; *1/ */
|
||||
/* renderPassBI.clearValueCount = static_cast<uint32_t>(clearValues.size()); */
|
||||
/* renderPassBI.pClearValues = clearValues.data(); */
|
||||
|
||||
/* vkCmdBeginRenderPass(commandBuffers[currentFrame], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); */
|
||||
|
||||
/* vkCmdBindPipeline(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].pipeline); */
|
||||
/* VkBuffer vertexBuffers[] = { vertexBuffer }; */
|
||||
/* VkDeviceSize offsets[] = {0}; */
|
||||
/* uint32_t bindingCount = 1; */
|
||||
/* vkCmdBindVertexBuffers(commandBuffers[currentFrame], BINDING, bindingCount, vertexBuffers, offsets); */
|
||||
/* // TODO use correct index type! */
|
||||
/* vkCmdBindIndexBuffer(commandBuffers[currentFrame], indexBuffer, NO_OFFSET, VK_INDEX_TYPE_UINT32); */
|
||||
|
||||
/* uint32_t descriptorCount = 1; */
|
||||
/* uint32_t firstSet = 0; */
|
||||
/* uint32_t dynamicOffsetCount = 0; */
|
||||
/* uint32_t* dynamicOffsets = nullptr; */
|
||||
/* vkCmdBindDescriptorSets(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets); */
|
||||
|
||||
/* int instanceCount = 1; */
|
||||
/* int firstIndex = 0; */
|
||||
/* int firstInstance = 0; */
|
||||
/* vkCmdDrawIndexed(commandBuffers[currentFrame], static_cast<uint32_t>(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance); */
|
||||
/* vkCmdEndRenderPass(commandBuffers[currentFrame]); */
|
||||
|
||||
/* vk.copyImageToImage(commandBuffers[currentFrame], images[imageIndex], vk.scImages[imageIndex], vk.scExtent); */
|
||||
/* result = vkEndCommandBuffer(commandBuffers[currentFrame]); */
|
||||
/* if (result != VK_SUCCESS) { */
|
||||
/* rLog.error("Failed to record 2D - command buffer", "VkResult:", STR_VK_RESULT(result)); */
|
||||
/* throw getVkException(result, "Failed to record 2D - command buffer", "Renderer3D::recordCommandBufferWithTexture"); */
|
||||
/* } */
|
||||
/* vk.submitThisFrame(commandBuffers[currentFrame]); */
|
||||
/* } */
|
||||
|
||||
|
||||
/* void Renderer3D::fillVertexBufferWithShapes() { */
|
||||
/* rLog("fillVertexBufferWithShapes"); */
|
||||
/* if (vertexBufferSize < shapesVerticesCount * sizeof(Vertex2D)) { */
|
||||
/* throw VkException("vertex buffer too small! vertexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); */
|
||||
/* } */
|
||||
|
||||
/* // create staging buffer */
|
||||
/* VkBuffer stagingBuffer; */
|
||||
/* VkDeviceMemory stagingBufferMemory; */
|
||||
/* vk.createBuffer(vertexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, vertexBufferSize, NO_FLAGS, &data); */
|
||||
/* Vertex2D* vdata = reinterpret_cast<Vertex2D*>(data); */
|
||||
/* size_t offset = 0; */
|
||||
/* for (auto it = shapes.begin(); it != shapes.end(); it++) { */
|
||||
/* rLog("fillVertexBufferWithShapes: copying vertex buffer nr", it - shapes.begin(), "-", it->getVertices(), "to address:", long(vdata + offset), " offset:", offset); */
|
||||
/* memcpy(vdata+offset, it->getVertices().data(), it->getVertices().size() * sizeof(Vertex2D)); */
|
||||
/* offset += it->getVertices().size(); */
|
||||
/* } */
|
||||
/* vkUnmapMemory(vk.device, stagingBufferMemory); */
|
||||
/* // fill vertex buffer */
|
||||
/* vk.copyBuffer(stagingBuffer, vertexBuffer, vertexBufferSize); */
|
||||
/* vkDestroyBuffer(vk.device, stagingBuffer, nullptr); */
|
||||
/* vkFreeMemory(vk.device, stagingBufferMemory, nullptr); */
|
||||
/* } */
|
||||
/* void Renderer3D::fillIndexBufferWithShapes() { */
|
||||
/* rLog("fillIndexBufferWithShapes"); */
|
||||
/* if (indexBufferSize < shapesIndicesCount * sizeof(uint32_t)) { */
|
||||
/* throw VkException("index buffer too small! indexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); */
|
||||
/* } */
|
||||
|
||||
/* // create staging buffer */
|
||||
/* VkBuffer stagingBuffer; */
|
||||
/* VkDeviceMemory stagingBufferMemory; */
|
||||
/* vk.createBuffer(indexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, indexBufferSize, NO_FLAGS, &data); */
|
||||
/* uint32_t* idata = reinterpret_cast<uint32_t*>(data); */
|
||||
/* size_t offset = 0; */
|
||||
/* for (auto it = shapes.begin(); it != shapes.end(); it++) { */
|
||||
/* rLog("fillIndexBufferWithShapes: copying index buffer nr", it - shapes.begin(), "-", it->getIndices(), "to address:", long(idata + offset), " offset:", offset); */
|
||||
/* memcpy(idata+offset, it->getIndices().data(), it->getIndices().size() * sizeof(uint32_t)); */
|
||||
/* offset += it->getIndices().size(); */
|
||||
/* } */
|
||||
/* rLog("fillIndexBufferWithShapes: indices count:", shapesIndicesCount); */
|
||||
/* vkUnmapMemory(vk.device, stagingBufferMemory); */
|
||||
|
||||
/* // fill index buffer */
|
||||
/* vk.copyBuffer(stagingBuffer, indexBuffer, indexBufferSize); */
|
||||
/* vkDestroyBuffer(vk.device, stagingBuffer, nullptr); */
|
||||
/* vkFreeMemory(vk.device, stagingBufferMemory, nullptr); */
|
||||
|
||||
/* } */
|
||||
|
||||
|
||||
//
|
||||
// RENDERING
|
||||
//
|
||||
void Renderer3D::drawFrame(uint32_t imageIndex) {
|
||||
vkResetCommandBuffer(commandBuffers[vk.currentFrame], NO_FLAGS);
|
||||
/* recordCommandBuffer(imageIndex, vk.currentFrame); */
|
||||
recordCommandBuffer(imageIndex, vk.currentFrame);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
157
renderer3D.hpp
157
renderer3D.hpp
@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "renderer.hpp"
|
||||
|
||||
#include "vulkan_util.hpp"
|
||||
|
||||
namespace gz::vk {
|
||||
struct UniformBufferObject {
|
||||
alignas(16) glm::mat4 model;
|
||||
@ -7,43 +11,134 @@ namespace gz::vk {
|
||||
alignas(16) glm::mat4 projection;
|
||||
};
|
||||
|
||||
|
||||
const std::vector<Vertex3D> vertices2 = {
|
||||
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}},
|
||||
{{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
|
||||
{{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
|
||||
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
|
||||
|
||||
{{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
|
||||
{{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
|
||||
{{ 0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
|
||||
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
|
||||
|
||||
};
|
||||
/* const std::vector<Vertex> vertices = { */
|
||||
/* {{-1.0f, -1.0f}, {0.0f, 0.0f, 1.0f}}, */
|
||||
/* {{0.5f, 0.5f}, {0.0, 1.0f, 1.0f}}, */
|
||||
/* {{-1.0, 0.0f}, {1.0f, 0.0f, 0.0f}}, */
|
||||
/* {{1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}}, */
|
||||
/* {{-0.5f, -0.5f}, {0.0, 1.0f, 1.0f}}, */
|
||||
/* {{1.0, 0.0f}, {1.0f, 0.0f, 1.0f}}, */
|
||||
/* }; */
|
||||
|
||||
|
||||
const std::vector<uint16_t> indices2 = {
|
||||
0, 1, 2, 2, 3, 0,
|
||||
4, 5, 6, 6, 7, 4
|
||||
};
|
||||
|
||||
class Renderer3D : public Renderer {
|
||||
public:
|
||||
Renderer3D(VulkanInstance& instance);
|
||||
void cleanup();
|
||||
protected:
|
||||
/**
|
||||
* @brief Create a 3D renderer
|
||||
* @details
|
||||
* -# @ref VulkanInstance::registerCleanupCallback "register" @ref cleanup() "cleanup callback"
|
||||
* -# @ref VulkanInstance::registerSwapChainRecreateCallback "register" @ref swapChainRecreateCallback "swapChain recreation callback"
|
||||
* -# create command buffers
|
||||
* -# create vertex & index buffers
|
||||
* -# call initSwapChainDependantResources
|
||||
*/
|
||||
Renderer3D(VulkanInstance& instance, TextureManager& textureManager);
|
||||
/**
|
||||
* @name Rendering
|
||||
*/
|
||||
/// @{
|
||||
void drawFrame(uint32_t imageIndex);
|
||||
/// @}
|
||||
private:
|
||||
void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame);
|
||||
std::vector<VkBuffer> uniformBuffers;
|
||||
std::vector<VkDeviceMemory> uniformBuffersMemory;
|
||||
void updateUniformBuffer();
|
||||
|
||||
/**
|
||||
* @name Image (render targets)
|
||||
* @details
|
||||
* The images are used as render targets. After rendering, the current image gets blitted onto the current swap chain image.
|
||||
*/
|
||||
/// @{
|
||||
std::vector<VkImage> images;
|
||||
std::vector<VkDeviceMemory> imageMemory;
|
||||
std::vector<VkImageView> imageViews;
|
||||
/**
|
||||
* @brief Creates the images (on imageMemory) and imageViews with the format of the VulkanInstance::swapChain images
|
||||
*/
|
||||
void createImages();
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* @name Render pass
|
||||
* @details
|
||||
* Attachments:
|
||||
* - color blend:
|
||||
* - loadOp = VK_ATTACHMENT_LOAD_OP_LOAD (not clear!)
|
||||
* - storeOp = VK_ATTACHMENT_STORE_OP_STORE
|
||||
* - initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
* - finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
* - stencil load/store = dont care
|
||||
*/
|
||||
/// @{
|
||||
/**
|
||||
* @brief Create a render pass
|
||||
*/
|
||||
void createRenderPass();
|
||||
VkRenderPass renderPass;
|
||||
/// @}
|
||||
|
||||
std::vector<VkFramebuffer> framebuffers;
|
||||
// PIPELINE
|
||||
PipelineContainer pipelines;
|
||||
|
||||
/**
|
||||
* @name Desciptors
|
||||
* @details These functions create a desciptor with bindings for
|
||||
* -# UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
||||
* -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
*/
|
||||
/// @{
|
||||
VkDescriptorPool descriptorPool;
|
||||
VkDescriptorSetLayout descriptorSetLayout;
|
||||
std::vector<VkDescriptorSet> descriptorSets;
|
||||
/**
|
||||
* @brief Create a descriptor layout binding for the MVP uniform buffer object
|
||||
* @details Create a desciptor set layout with bindings for
|
||||
* -# UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
||||
* -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
*/
|
||||
void createDescriptorPool();
|
||||
/*
|
||||
* @bried Create one descriptor set with layout descriptorSetLayout for each frame
|
||||
*/
|
||||
void createDescriptorSets();
|
||||
const uint32_t bindingUniformBuffer = 0;
|
||||
const uint32_t bindingCombinedImageSampler = 1;
|
||||
/* const uint32_t bindingCombinedImageSampler = 1; */
|
||||
|
||||
|
||||
void createDescriptorResources();
|
||||
|
||||
/**
|
||||
* @brief Swapchain recreation
|
||||
*/
|
||||
/// @{
|
||||
/**
|
||||
* @brief Cleans up resources that were initialized by initSwapChainDependantResources
|
||||
*/
|
||||
void cleanupSwapChainDependantResources();
|
||||
/**
|
||||
* @brief Sets up resources that depend on the swap chain or its attributes
|
||||
* @details
|
||||
* Initializes up:
|
||||
* - images, imageMemory, imageViews
|
||||
* - framebuffers
|
||||
* - render pass
|
||||
* - pipeline
|
||||
*/
|
||||
void initSwapChainDependantResources();
|
||||
/**
|
||||
* @brief Recreates swap chain dependant resources
|
||||
* @details
|
||||
* Calls:
|
||||
* -# cleanupSwapChainDependantResources
|
||||
* -# initSwapChainDependantResources
|
||||
*/
|
||||
void swapChainRecreateCallback();
|
||||
/// @}
|
||||
|
||||
/*
|
||||
* @brief Destroy all vulkan objects owned by this object
|
||||
* @details:
|
||||
* Does:
|
||||
* -# destroy uniform buffers
|
||||
* -# call cleanupSwapChainDependantResources()
|
||||
* -# call Renderer::cleanup_()
|
||||
*/
|
||||
void cleanup();
|
||||
|
||||
Log rLog;
|
||||
};
|
||||
|
||||
|
@ -19,7 +19,7 @@ void main() {
|
||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
|
||||
/* gl_Position = vec4(inPosition, 1.0); */
|
||||
|
||||
debugPrintfEXT("inPosition %v3f, inColor %v3f", inPosition, inColor);
|
||||
/* debugPrintfEXT("inPosition %v3f, inColor %v3f", inPosition, inColor); */
|
||||
fragColor = inColor;
|
||||
fragTextureCoordinate = inTextureCoordinate;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#version 450
|
||||
#extension GL_EXT_debug_printf : enable
|
||||
|
||||
/* layout(binding = 1) uniform sampler2D textureSampler; */
|
||||
layout(binding = 0) uniform sampler2D textureSampler;
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
layout(location = 1) in vec2 fragTextureCoordinate;
|
||||
@ -9,8 +9,9 @@ layout(location = 1) in vec2 fragTextureCoordinate;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
/* debugPrintfEXT("outColor %v3f", fragColor); */
|
||||
/* outColor = vec4(fragColor, 1.0); */
|
||||
/* outColor = vec4(fragTextureCoordinate, 0.0, 1.0); */
|
||||
/* outColor = vec4(fragColor * texture(textureSampler, fragTextureCoordinate).rgb, 1.0); */
|
||||
outColor = texture(textureSampler, fragTextureCoordinate);
|
||||
/* debugPrintfEXT("outColor %v3f", outColor); */
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ void main() {
|
||||
/* gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); */
|
||||
gl_Position = vec4(inPosition, 0.0, 1.0);
|
||||
|
||||
debugPrintfEXT("inPosition %v2f, inColor %v3f", inPosition, inColor);
|
||||
/* debugPrintfEXT("inPosition %v2f, inColor %v3f", inPosition, inColor); */
|
||||
fragColor = inColor;
|
||||
fragTextureCoordinate = inTextureCoordinate;
|
||||
}
|
||||
|
36
shape.cpp
36
shape.cpp
@ -1,8 +1,12 @@
|
||||
#include "shape.hpp"
|
||||
|
||||
#include "texture_manager.hpp"
|
||||
|
||||
namespace gz::vk {
|
||||
Rectangle::Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color)
|
||||
: top(top), left(left), width(width), height(height), color(color) {
|
||||
Rectangle::Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color, std::string&& texture)
|
||||
: top(top), left(left), width(width), height(height), color(color)
|
||||
{
|
||||
this->texture = std::move(texture);
|
||||
generateVertices();
|
||||
|
||||
}
|
||||
@ -11,9 +15,9 @@ namespace gz::vk {
|
||||
void Rectangle::generateVertices() {
|
||||
vertices.clear();
|
||||
vertices.emplace_back(Vertex2D{glm::vec2(top, left), color, glm::vec2(0, 0)});
|
||||
vertices.emplace_back(Vertex2D{glm::vec2(top, left + width), color, glm::vec2(1, 0)});
|
||||
vertices.emplace_back(Vertex2D{glm::vec2(top, left + width), color, glm::vec2(0, 1)});
|
||||
vertices.emplace_back(Vertex2D{glm::vec2(top + height, left + width), color, glm::vec2(1, 1)});
|
||||
vertices.emplace_back(Vertex2D{glm::vec2(top + height, left), color, glm::vec2(0, 1)});
|
||||
vertices.emplace_back(Vertex2D{glm::vec2(top + height, left), color, glm::vec2(1, 0)});
|
||||
indices = { 0, 1, 2, 2, 3, 0 };
|
||||
/* indices = { 2, 1, 0, 2, 3, 0 }; */
|
||||
}
|
||||
@ -25,10 +29,30 @@ namespace gz::vk {
|
||||
|
||||
}
|
||||
|
||||
void Shape::setNormalize(float width, float height) {
|
||||
void Shape::normalizeVertices(float width, float height) {
|
||||
for (size_t i = 0; i < vertices.size(); i++) {
|
||||
vertices[i].pos.x /= width;
|
||||
vertices[i].pos.y /= height;
|
||||
}
|
||||
}
|
||||
} // namespaec gz::vk
|
||||
|
||||
void Rectangle::setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) {
|
||||
vertices[0].texCoord = topLeft;
|
||||
vertices[1].texCoord.x = bottomRight.x;
|
||||
vertices[1].texCoord.y = topLeft.y;
|
||||
vertices[2].texCoord = bottomRight;
|
||||
vertices[3].texCoord.x = topLeft.x;
|
||||
vertices[3].texCoord.y = bottomRight.y;
|
||||
}
|
||||
|
||||
void Rectangle::setTextureCoordinates(TextureManager& textureManager) {
|
||||
if (texture != "atlas") {
|
||||
textureManager.getTexCoords(texture, vertices[0].texCoord);
|
||||
textureManager.getTexCoords(texture, vertices[1].texCoord);
|
||||
textureManager.getTexCoords(texture, vertices[2].texCoord);
|
||||
textureManager.getTexCoords(texture, vertices[3].texCoord);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace gz::vk
|
||||
|
26
shape.hpp
26
shape.hpp
@ -4,14 +4,26 @@
|
||||
#include <cstdint>
|
||||
|
||||
namespace gz::vk {
|
||||
// defined in texture_manager.hpp
|
||||
class TextureManager;
|
||||
class Shape {
|
||||
public:
|
||||
const std::vector<Vertex2D>& getVertices() const { return vertices; }
|
||||
const std::vector<uint32_t>& getIndices() const { return indices; }
|
||||
const std::string& getTexture() const { return texture; }
|
||||
/**
|
||||
* @brief Add an offset to all indices
|
||||
*/
|
||||
void setIndexOffset(uint32_t offset);
|
||||
void setNormalize(float width, float height);
|
||||
|
||||
/**
|
||||
* @brief Normalize the vertices, so that (1, 1) is (width, height)
|
||||
*/
|
||||
void normalizeVertices(float width, float height);
|
||||
virtual void setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) {};
|
||||
virtual void setTextureCoordinates(TextureManager& textureManager) {};
|
||||
virtual ~Shape() {};
|
||||
protected:
|
||||
std::string texture = "texture.png";
|
||||
std::vector<Vertex2D> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
|
||||
@ -19,7 +31,15 @@ namespace gz::vk {
|
||||
|
||||
class Rectangle : public Shape {
|
||||
public:
|
||||
Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color);
|
||||
Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color, std::string&& texture);
|
||||
void setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) override;
|
||||
/**
|
||||
* @brief Get the correct texture coordinates from a TextureManager
|
||||
* @details
|
||||
* If the texture is "atlas", the texture coordinates will remain at (0,0), (0,1), (1, 1), (1, 0) and
|
||||
* thus show the entire atlas.
|
||||
*/
|
||||
void setTextureCoordinates(TextureManager& textureManager) override;
|
||||
private:
|
||||
float top, left;
|
||||
uint32_t width, height;
|
||||
|
208
texture_atlas.cpp
Normal file
208
texture_atlas.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
#include "texture_atlas.hpp"
|
||||
|
||||
#include "vulkan_instance.hpp"
|
||||
#include "exceptions.hpp"
|
||||
|
||||
#include "stb_image.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <gz-util/exceptions.hpp>
|
||||
#include <gz-util/util/string_concepts.hpp>
|
||||
#include <gz-util/util/string_conversion.hpp>
|
||||
#include <ratio>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace gz::vk {
|
||||
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)
|
||||
{
|
||||
// whole image
|
||||
freeAreas.insert({ 0, 0, slotCountX, slotCountY});
|
||||
createImageResources();
|
||||
vk.registerCleanupCallback(std::bind(&TextureAtlas::cleanup, this));
|
||||
VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan(std::string("TextureAtlas-") + gz::toString(slotWidth) + "x" + gz::toString(slotHeight),
|
||||
{ &textureImage, &textureImageMemory, &textureImageView, &textureSampler },
|
||||
{}));
|
||||
}
|
||||
|
||||
|
||||
void TextureAtlas::cleanup() {
|
||||
VulkanInstance::vLog("TextureAtlas::cleanup, textureSampler", reinterpret_cast<uint64_t>(textureSampler), "textureImageView", reinterpret_cast<uint64_t>(textureImageView));
|
||||
vkDestroySampler(vk.device, textureSampler, nullptr);
|
||||
vkDestroyImageView(vk.device, textureImageView, nullptr);
|
||||
vkDestroyImage(vk.device, textureImage, nullptr);
|
||||
vkFreeMemory(vk.device, textureImageMemory, nullptr);
|
||||
}
|
||||
|
||||
|
||||
void TextureAtlas::createImageResources() {
|
||||
vk.createImage(slotWidth * slotCountX, slotHeight * slotCountY, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_LINEAR,
|
||||
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);
|
||||
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;
|
||||
subresourceRange.baseMipLevel = 0;
|
||||
subresourceRange.levelCount = 1;
|
||||
subresourceRange.baseArrayLayer = 0;
|
||||
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.createImageView(VK_FORMAT_R8G8B8A8_SRGB, textureImage, textureImageView, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
vk.createTextureSampler(textureSampler);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ADD TEXTURE
|
||||
//
|
||||
std::pair<glm::vec2, glm::vec2> TextureAtlas::addTexture(uint8_t* pixels, uint16_t width, uint16_t height) {
|
||||
if (freeAreas.empty()) {
|
||||
throw Exception("No texture slots available", "TextureAtlas::addTexture");
|
||||
}
|
||||
// needed slot count in x/y direction
|
||||
uint16_t slotsX, slotsY;
|
||||
// width / slotHeight (+ 1 if another slot is not fully filled)
|
||||
slotsX = width / slotWidth + ((width % slotWidth != 0) ? 1 : 0);
|
||||
slotsY = height / slotHeight + ((height % slotHeight != 0) ? 1 : 0);
|
||||
|
||||
// find smallest area that fits the texture
|
||||
auto freeArea = freeAreas.end();
|
||||
for (auto it = freeAreas.begin(); it != freeAreas.end(); it++) {
|
||||
// if fits the texture
|
||||
if (it->width >= slotsX and it->height >= slotsY) {
|
||||
freeArea = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// check if match
|
||||
if (freeArea == freeAreas.end()) {
|
||||
throw Exception("Not enough texture slots available to fit the texture", "TextureAtlas::addTexture");
|
||||
}
|
||||
|
||||
// consume free area
|
||||
auto node = freeAreas.extract(freeArea);
|
||||
TextureImageArea& selectedArea = node.value();
|
||||
|
||||
// topleft slot for texture
|
||||
uint16_t x = selectedArea.x;
|
||||
uint16_t y = selectedArea.y;
|
||||
// update selectedArea
|
||||
uint16_t selectedAreaNewY = selectedArea.y + slotsY;
|
||||
uint16_t selectedAreaNewWidth = slotsX;
|
||||
uint16_t selectedAreaNewHeight = selectedArea.height - slotsY;
|
||||
// if split needed
|
||||
if (slotsX < selectedArea.width and slotsY < selectedArea.height) {
|
||||
// split so that the size difference between the new free area and the remaining free area is maximal
|
||||
TextureImageArea newFreeArea;
|
||||
newFreeArea.x = selectedArea.x + slotsX; // right of new texture
|
||||
newFreeArea.y = selectedArea.y;
|
||||
newFreeArea.width = selectedArea.width - slotsX;
|
||||
// try full height first first
|
||||
newFreeArea.height = selectedArea.height;
|
||||
|
||||
// size difference with (newArea.height == full height) < size difference with (newArea.height == textureHeight)
|
||||
int sizeDiffNewAreaFullHeight = static_cast<int>(newFreeArea.width * newFreeArea.height) - selectedAreaNewWidth * selectedAreaNewHeight;
|
||||
int sizeDiffNewAreaTextureHeight = static_cast<int>(newFreeArea.width * (selectedArea.height - slotsY)) - selectedArea.width * selectedAreaNewHeight;
|
||||
if (sizeDiffNewAreaFullHeight * sizeDiffNewAreaFullHeight < sizeDiffNewAreaTextureHeight * sizeDiffNewAreaTextureHeight) {
|
||||
newFreeArea.height = slotsY;
|
||||
selectedAreaNewWidth = selectedArea.width;
|
||||
}
|
||||
// insert new area
|
||||
freeAreas.insert(std::move(newFreeArea));
|
||||
}
|
||||
|
||||
if (selectedAreaNewHeight > 0 and selectedAreaNewWidth > 0) {
|
||||
// insert selected area with updated width, height and y
|
||||
selectedArea.width = selectedAreaNewWidth;
|
||||
selectedArea.height = selectedAreaNewHeight;
|
||||
selectedArea.y = selectedAreaNewY;
|
||||
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, ")");
|
||||
blitTextureOnImage(pixels, x, y, width, height);
|
||||
// topleft normalized: slot / slotCount
|
||||
// bottomright normalized: (slot + (textureSize/slotCountize)) / slotCount
|
||||
return { glm::vec2(static_cast<float>(x) / slotCountX, static_cast<float>(y) / slotCountY),
|
||||
glm::vec2((static_cast<float>(x) + static_cast<float>(width) / slotWidth) / slotCountX,
|
||||
(static_cast<float>(y) + static_cast<float>(height) / slotHeight) / slotCountY) };
|
||||
}
|
||||
|
||||
|
||||
void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight) {
|
||||
constexpr size_t BYTES_PER_PIXEL = 4;
|
||||
VkDeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL;
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory 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.device, stagingBufferMemory, NO_OFFSET, imageSize, NO_FLAGS, &data);
|
||||
memcpy(data, pixels, static_cast<size_t>(imageSize));
|
||||
vkUnmapMemory(vk.device, stagingBufferMemory);
|
||||
|
||||
vk.transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
vk.copyBufferToImage(stagingBuffer, textureImage,
|
||||
static_cast<int32_t>(slotWidth * slotX), static_cast<int32_t>(slotHeight * slotY), // offset
|
||||
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.device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, stagingBufferMemory, nullptr);
|
||||
}
|
||||
|
||||
|
||||
void TextureAtlas::mergeFreeAreas() {
|
||||
begin:
|
||||
VulkanInstance::vLog("TextureAtlas::mergeFreeAreas before merge:", freeAreas);
|
||||
// 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++) {
|
||||
for (auto it2 = freeAreas.begin(); it2 != freeAreas.end(); it2++) {
|
||||
if (it == it2) { continue; }
|
||||
// if same y and same height and next to each other
|
||||
if (it->y == it2->y and it->height == it2->height and it2->x == it->x + it->width) {
|
||||
auto area1 = freeAreas.extract(it); // node
|
||||
area1.value().width += it2->width;
|
||||
freeAreas.insert(std::move(area1));
|
||||
freeAreas.erase(it2);
|
||||
goto begin; // start over since the iterators are invalid and new merges might be possible
|
||||
}
|
||||
// if same x and same with and below/above each other
|
||||
if (it->x == it2->x and it->width == it2->width and it2->y == it->y + it->height) {
|
||||
auto area1 = freeAreas.extract(it); // node
|
||||
area1.value().height += it2->height;
|
||||
freeAreas.insert(std::move(area1));
|
||||
freeAreas.erase(it2);
|
||||
goto begin; // start over since the iterators are invalid and new merges might be possible
|
||||
}
|
||||
}
|
||||
}
|
||||
VulkanInstance::vLog("TextureAtlas::mergeFreeAreas after merge:", freeAreas);
|
||||
|
||||
}
|
||||
|
||||
std::string TextureAtlas::toString() const {
|
||||
return "TextureAtlas(SlotSize: " + gz::toString(slotWidth) + "x" + gz::toString(slotHeight)
|
||||
+ ", SlotCount: " + gz::toString(slotCountX) + "x" + gz::toString(slotCountY) + ", FreeAreas: " + gz::toString(freeAreas) + ")";
|
||||
}
|
||||
|
||||
|
||||
} // namespace gz
|
91
texture_atlas.hpp
Normal file
91
texture_atlas.hpp
Normal file
@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
|
||||
namespace gz::vk {
|
||||
/**
|
||||
* @brief Describes an area in the textureImage that is not in use
|
||||
*/
|
||||
struct TextureImageArea {
|
||||
// topleft slot
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
// in slots
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
std::string toString() const;
|
||||
};
|
||||
/// strict weak ordering comparison, true if a < b
|
||||
inline auto freeAreaCmp = [](const TextureImageArea& a, const TextureImageArea& b) {
|
||||
// if a size < b size
|
||||
if (a.width * a.height < b.width * b.height) { return true; }
|
||||
if (a.width * a.height != b.width * b.height) { return false; }
|
||||
// if a size == b size, return true if a is toplefter
|
||||
if (a.x + a.y < b.x + b.y) { return true; }
|
||||
if (a.x + a.y != b.x + b.y) { return false; }
|
||||
// if both are same topleft return if a is lefter
|
||||
return a.x < b.x;
|
||||
};
|
||||
|
||||
/// Defined in vulkan_instance.hpp
|
||||
class VulkanInstance;
|
||||
class TextureAtlas {
|
||||
public:
|
||||
/**
|
||||
* @brief Create a texture atlas
|
||||
* @details
|
||||
* -# @ref VulkanInstance::registerCleanupCallback "register" @ref cleanup() "cleanup callback"
|
||||
* -# @ref createImageResources "create texture image, view and sampler"
|
||||
*
|
||||
* The textureImage will have the dimensions slotWidth * slotCountX, slotHeight * slotCountY
|
||||
*/
|
||||
TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY);
|
||||
/**
|
||||
* @brief Add a texture to the atlas
|
||||
* @param textureWidth Width in pixels
|
||||
* @param textureHeight Height in pixels
|
||||
* @returns The topleft and bottomright texture coordinates in the image of the atlas
|
||||
* @throws Exception when there is no space in the atlas to add the texture
|
||||
*/
|
||||
std::pair<glm::vec2, glm::vec2> addTexture(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight);
|
||||
const VkImageView& getTextureImageView() const { return textureImageView; }
|
||||
const VkSampler& getTextureSampler() const { return textureSampler; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
private:
|
||||
VulkanInstance& vk;
|
||||
/**
|
||||
* @brief Destroy all vulkan objects owned by this object
|
||||
*/
|
||||
void cleanup();
|
||||
void blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight);
|
||||
/**
|
||||
* @brief Create textureImage, textureImageView and textureSampler
|
||||
* @details
|
||||
* the textureImage is created with missingTextureColor and then transitioned to SHADER_READ_ONLY_OPTIMAL layout.
|
||||
*/
|
||||
void createImageResources();
|
||||
VkImage textureImage;
|
||||
VkDeviceMemory textureImageMemory;
|
||||
VkImageView textureImageView;
|
||||
VkSampler textureSampler;
|
||||
|
||||
std::set<TextureImageArea, decltype(freeAreaCmp)> freeAreas;
|
||||
/**
|
||||
* @todo implement merge
|
||||
*/
|
||||
void mergeFreeAreas();
|
||||
|
||||
|
||||
uint16_t slotWidth;
|
||||
uint16_t slotHeight;
|
||||
uint16_t slotCountX;
|
||||
uint16_t slotCountY;
|
||||
};
|
||||
} // namespace gz::vk
|
145
texture_manager.cpp
Normal file
145
texture_manager.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include "texture_manager.hpp"
|
||||
#include "exceptions.hpp"
|
||||
#include "vulkan_instance.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace gz::vk {
|
||||
TextureManager::TextureManager(VulkanInstance& instance)
|
||||
: vk(instance)
|
||||
{
|
||||
vk.registerCleanupCallback(std::bind(&TextureManager::cleanup, this));
|
||||
atlases.insert({ 0, TextureAtlas(vk, 24, 24, 24, 24) });
|
||||
createDescriptorSetLayout();
|
||||
createDescriptorPool();
|
||||
createDescriptorSet();
|
||||
VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan("TextureManager",
|
||||
{ &descriptorSetLayout, &descriptorPool, &descriptorSet },
|
||||
{}));
|
||||
}
|
||||
|
||||
|
||||
void TextureManager::cleanup() {
|
||||
/* vkFreeDescriptorSets(vk.device, descriptorPool, 1, &descriptorSet); */
|
||||
vkDestroyDescriptorSetLayout(vk.device, descriptorSetLayout, NO_ALLOC);
|
||||
vkDestroyDescriptorPool(vk.device, descriptorPool, NO_ALLOC);
|
||||
}
|
||||
|
||||
|
||||
void TextureManager::getTexCoords(const std::string& textureName, glm::vec2& texCoords) {
|
||||
if (!textureInfos.contains(textureName)) {
|
||||
loadTextureFromFile(textureName);
|
||||
}
|
||||
TextureInfo& texI = textureInfos[textureName];
|
||||
VulkanInstance::vLog("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);
|
||||
}
|
||||
|
||||
|
||||
void TextureManager::loadTextureFromFile(const std::string& textureName) {
|
||||
int textureWidth, textureHeight, textureChannels;
|
||||
stbi_uc* pixels = stbi_load(("textures/" + textureName).c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha);
|
||||
if (!pixels) {
|
||||
throw Exception("Failed to load texture: textures/" + textureName, "TextureManager::loadTexture");
|
||||
}
|
||||
TextureInfo& texI = textureInfos[textureName];
|
||||
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);
|
||||
texI.texCoordTopLeft = std::move(texCoords.first);
|
||||
texI.texCoordBottomRight = std::move(texCoords.second);
|
||||
VulkanInstance::vLog("TextureManager::loadTextureFromFile: After loading:", atlases.at(0));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DESCRIPTOR SET
|
||||
//
|
||||
void TextureManager::createDescriptorSetLayout() {
|
||||
// combined image sampler
|
||||
VkDescriptorSetLayoutBinding samplerLayoutBinding{};
|
||||
samplerLayoutBinding.binding = 0;
|
||||
samplerLayoutBinding.descriptorCount = 1;
|
||||
samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
/* samplerLayoutBinding.pImmutableSamplers = nullptr; */
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{};
|
||||
descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
descriptorSetLayoutCI.bindingCount = 1;
|
||||
descriptorSetLayoutCI.pBindings = &samplerLayoutBinding;
|
||||
|
||||
VkResult result = vkCreateDescriptorSetLayout(vk.device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Could not create descriptorSetLayout", "TextureManager::createDescriptorSetLayout");
|
||||
}
|
||||
/* vLog("createDescriptorSetLayout: Created descriptor set layout."); */
|
||||
}
|
||||
|
||||
|
||||
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].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
VkDescriptorPoolCreateInfo poolCI{};
|
||||
poolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
poolCI.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
|
||||
poolCI.pPoolSizes = poolSizes.data();
|
||||
poolCI.maxSets = 1;
|
||||
VkResult result = vkCreateDescriptorPool(vk.device, &poolCI, nullptr, &descriptorPool);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Failed to create descriptor pool", "TextureManager::createDescriptorPool");
|
||||
}
|
||||
/* vLog("createDescriptorPool: Created descriptor pool."); */
|
||||
}
|
||||
|
||||
|
||||
void TextureManager::createDescriptorSet() {
|
||||
/* std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, 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.pSetLayouts = layouts.data(); */
|
||||
setAI.descriptorSetCount = 1;
|
||||
setAI.pSetLayouts = &descriptorSetLayout;
|
||||
|
||||
/* descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); */
|
||||
/* VkResult result = vkAllocateDescriptorSets(vk.device, &setAI, descriptorSets.data()); */
|
||||
VkResult result = vkAllocateDescriptorSets(vk.device, &setAI, &descriptorSet);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Failed to create descriptor set", "TextureManager::createDescriptorPool");
|
||||
}
|
||||
|
||||
// configure sets
|
||||
std::vector<VkDescriptorImageInfo> imageInfos;
|
||||
for (auto it = atlases.cbegin(); it != atlases.cend(); it++) {
|
||||
imageInfos.emplace_back();
|
||||
imageInfos.back().imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
imageInfos.back().imageView = it->second.getTextureImageView();
|
||||
imageInfos.back().sampler = it->second.getTextureSampler();
|
||||
}
|
||||
|
||||
std::array<VkWriteDescriptorSet, 1> descriptorW{};
|
||||
descriptorW[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
descriptorW[0].dstSet = descriptorSet;
|
||||
descriptorW[0].dstBinding = 0;
|
||||
descriptorW[0].dstArrayElement = 0;
|
||||
descriptorW[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
descriptorW[0].descriptorCount = static_cast<uint32_t>(imageInfos.size());
|
||||
descriptorW[0].pImageInfo = imageInfos.data();
|
||||
|
||||
uint32_t descriptorWriteCount = static_cast<uint32_t>(descriptorW.size());
|
||||
uint32_t descriptorCopyCount = 0;
|
||||
vkUpdateDescriptorSets(vk.device, descriptorWriteCount, descriptorW.data(), descriptorCopyCount, nullptr);
|
||||
/* } */
|
||||
/* vLog("createDescriptorSets: Created descriptor sets."); */
|
||||
}
|
||||
} // namespace gz::vk
|
65
texture_manager.hpp
Normal file
65
texture_manager.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "texture_atlas.hpp"
|
||||
|
||||
#include "stb_image.h"
|
||||
#include <gz-util/util/string.hpp>
|
||||
#include <glm/fwd.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace gz::vk {
|
||||
struct TextureInfo {
|
||||
uint32_t atlas;
|
||||
glm::vec2 texCoordTopLeft;
|
||||
glm::vec2 texCoordBottomRight;
|
||||
};
|
||||
/// Defined in vulkan_instance.hpp
|
||||
class VulkanInstance;
|
||||
class TextureManager {
|
||||
public:
|
||||
/**
|
||||
* @brief Create a texture manager
|
||||
* @details
|
||||
* -# @ref VulkanInstance::registerCleanupCallback "register" @ref cleanup() "cleanup callback"
|
||||
* -# @ref createDescriptor "create descriptor set layout, pool and set
|
||||
*/
|
||||
TextureManager(VulkanInstance& instance);
|
||||
void getTexCoords(const std::string& textureName, glm::vec2& texCoords);
|
||||
const VkDescriptorSet& getDescriptorSet() const { return descriptorSet; }
|
||||
const VkDescriptorSetLayout& getDescriptorSetLayout() const { return descriptorSetLayout; }
|
||||
|
||||
|
||||
private:
|
||||
void cleanup();
|
||||
void loadTextureFromFile(const std::string& textureName);
|
||||
std::unordered_map<uint32_t, TextureAtlas> atlases;
|
||||
util::unordered_string_map<TextureInfo> textureInfos;
|
||||
VulkanInstance& vk;
|
||||
/**
|
||||
* @name Create desciptors
|
||||
* @details These functions create a desciptor with bindings for
|
||||
* -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) at binding 0
|
||||
* @{
|
||||
*/
|
||||
VkDescriptorSetLayout descriptorSetLayout;
|
||||
VkDescriptorPool descriptorPool;
|
||||
VkDescriptorSet descriptorSet;
|
||||
/* std::vector<VkDescriptorSet> descriptorSets; */
|
||||
/**
|
||||
* @brief Create a descriptor set layout
|
||||
* @details
|
||||
* Bindings:
|
||||
* -0 COMBINED_IMAGE_SAMPLER
|
||||
*/
|
||||
void createDescriptorSetLayout();
|
||||
void createDescriptorPool();
|
||||
void createDescriptorSet();
|
||||
/* const uint32_t bindingCombinedImageSampler = 1; */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
};
|
||||
} // namespace gz::vk
|
@ -24,7 +24,7 @@ VkFormat getVkFormat() {
|
||||
//
|
||||
template<GLM_vec2_or_3 PosVec>
|
||||
std::string Vertex<PosVec>::toString() const {
|
||||
return "pos: " + gz::toString(pos) + ", color: " + gz::toString(color) + ", texCoords: <TODO>"; // + gz::toString(texCoord);
|
||||
return "pos: <" + gz::toString(pos) + ", color: " + gz::toString(color) + ", texCoords: " + gz::toString(texCoord) + ">";
|
||||
};
|
||||
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
# https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/master/layers/vk_layer_settings.txt
|
||||
khronos_validation.enables = VK_LAYER_ENABLES=VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT;VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD
|
||||
# khronos_validation.enables = VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT;VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -23,54 +23,100 @@
|
||||
#include <vector>
|
||||
|
||||
namespace gz::vk {
|
||||
const std::string CONFIG_FILE = "vulkan.conf";
|
||||
const int MAX_FRAMES_IN_FLIGHT = 3;
|
||||
|
||||
constexpr gz::Color VULKAN_MESSAGE_PREFIX_COLOR = gz::Color::BO_BLUE;
|
||||
constexpr gz::Color VULKAN_MESSAGE_TIME_COLOR = gz::Color::BLUE;
|
||||
const std::string CONFIG_FILE = "vulkan.conf";
|
||||
const int MAX_FRAMES_IN_FLIGHT = 3;
|
||||
#define SettingsTypes uint32_t, bool, float
|
||||
|
||||
/* const std::string MODEL_PATH = "models/gazebo-3d-model/gazebo.obj"; */
|
||||
/* const std::string TEXTURE_PATH = "models/gazebo-3d-model/gazebo_diffuse.png"; */
|
||||
/* const std::string MODEL_PATH = "models/gazebo-3d-model/gazebo.obj"; */
|
||||
/* const std::string TEXTURE_PATH = "models/gazebo-3d-model/gazebo_diffuse.png"; */
|
||||
|
||||
const std::string MODEL_PATH = "models/armoire-3d-model/Armoire.obj";
|
||||
const std::string TEXTURE_PATH = "models/armoire-3d-model/Armoire_diffuse.png";
|
||||
const std::string MODEL_PATH = "models/armoire-3d-model/Armoire.obj";
|
||||
const std::string TEXTURE_PATH = "models/armoire-3d-model/Armoire_diffuse.png";
|
||||
|
||||
const gz::util::unordered_string_map<std::string> INITIAL_SETTINGS = {
|
||||
{ "framerate", "60" },
|
||||
{ "anisotropy_enable", "false" },
|
||||
{ "max_anisotropy", "1" },
|
||||
/* { "", "" } */
|
||||
};
|
||||
const gz::util::unordered_string_map<std::string> INITIAL_SETTINGS = {
|
||||
{ "framerate", "60" },
|
||||
{ "anisotropy_enable", "false" },
|
||||
{ "max_anisotropy", "1" },
|
||||
/* { "", "" } */
|
||||
};
|
||||
|
||||
|
||||
const int BINDING = 0;
|
||||
const uint32_t NO_FLAGS = 0;
|
||||
const uint32_t NO_OFFSET = 0;
|
||||
const size_t VERTEX_BUFFER_SIZE = 512;
|
||||
const size_t INDEX_BUFFER_SIZE = 512;
|
||||
constexpr VkClearColorValue missingTextureColor = { { 0.4f, 0.0f, 0.4f, 1.0f } };
|
||||
const int BINDING = 0;
|
||||
const uint32_t NO_FLAGS = 0;
|
||||
const uint32_t NO_OFFSET = 0;
|
||||
constexpr VkAllocationCallbacks* NO_ALLOC = nullptr;
|
||||
const size_t VERTEX_BUFFER_SIZE = 512;
|
||||
const size_t INDEX_BUFFER_SIZE = 512;
|
||||
class VulkanInstance {
|
||||
friend class Renderer;
|
||||
friend class Renderer2D;
|
||||
friend class Renderer3D;
|
||||
friend class TextureManager;
|
||||
friend class TextureAtlas;
|
||||
public:
|
||||
VulkanInstance(gz::SettingsManagerCreateInfo<SettingsTypes>smCI) : settings(smCI) {};
|
||||
/**
|
||||
* @brief Initializes the vulkan instance
|
||||
* @details
|
||||
* -# @ref createWindow "create a window through glfw"
|
||||
* -# @ref createInstance "create the vulkan instance"
|
||||
* -# @ref setupDebugMessenger "sets up the debug messenger"
|
||||
* -# @ref createSurface "create a the window surface"
|
||||
* -# @ref selectPhysicalDevice "select a GPU"
|
||||
* -# @ref setValidSettings "set the possible settings for the SettingsManager"
|
||||
* -# @ref createLogicalDevice "create a logical device"
|
||||
* -# @ref createSwapChain "create the swap chain"
|
||||
* -# @ref createSwapChainImageViews "create the swap chain image views"
|
||||
* -# @ref createCommandPools "create the command pools"
|
||||
* @todo move depth image, texture, texture samples, model stuff to renderers
|
||||
* -# @ref createCommandBuffers "create command buffers for swap chain image layout transitions"
|
||||
* -# @ref createSyncObjects "create synchronization objects"
|
||||
*/
|
||||
void init();
|
||||
/**
|
||||
* @brief Acquires a new frame from the swap chain and sets the image layout
|
||||
* @details
|
||||
* How to draw stuff right now:
|
||||
* -# Call this function
|
||||
* -# Call the draw functions from the renderers.
|
||||
* The renderers need to put the recorded command buffers into the commandBuffersToSubmitThisFrame vector using submitThisFrame()
|
||||
* -# Call endFrameDraw
|
||||
*/
|
||||
uint32_t beginFrameDraw();
|
||||
void submitThisFrame(VkCommandBuffer);
|
||||
void submitThisFrame(VkCommandBuffer& cmdBuffer);
|
||||
void endFrameDraw(uint32_t imageIndex);
|
||||
void deInit();
|
||||
GLFWwindow* window;
|
||||
/**
|
||||
* @brief Destroys everything that was initalized in init
|
||||
* @details
|
||||
* -# Calls every callback registered by registerCleanupCallback() (FILO order)
|
||||
* -# calls cleanupSwapChain()
|
||||
* -# destroys everything initalized in init()
|
||||
* -# destroys the window
|
||||
* -# calls glfwTerminate()
|
||||
*/
|
||||
void cleanup();
|
||||
void registerCleanupCallback(std::function<void()> callbackF);
|
||||
void registerSwapChainRecreateCallback(std::function<void()> callbackF);
|
||||
/// The frame in the swap chain that is currently drawn to
|
||||
uint32_t currentFrame = 0;
|
||||
|
||||
|
||||
//
|
||||
// SETTINGS
|
||||
//
|
||||
gz::SettingsManager<SettingsTypes> settings;
|
||||
private:
|
||||
void mainLoop();
|
||||
std::vector<VkCommandBuffer> commandBuffersToSubmitThisFrame;
|
||||
|
||||
void createWindow();
|
||||
|
||||
std::vector<std::function<void()>> cleanupCallbacks;
|
||||
std::vector<std::function<void()>> scRecreateCallbacks;
|
||||
|
||||
//
|
||||
// INSTANCE
|
||||
@ -79,9 +125,9 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
/**
|
||||
* @brief Create the vulkan instance
|
||||
* @details
|
||||
* - check if validationLayers are available (if enabled)
|
||||
* - create instance with info
|
||||
* - check if all extensions required by glfw are available
|
||||
* -# check if validationLayers are available (if enabled)
|
||||
* -# create instance with info
|
||||
* -# check if all extensions required by glfw are available
|
||||
*/
|
||||
void createInstance();
|
||||
|
||||
@ -89,8 +135,17 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
// PHYSICAL DEVICE
|
||||
//
|
||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||
/// all the properties of the selected physcial device
|
||||
VkPhysicalDeviceProperties phDevProperties;
|
||||
VkPhysicalDeviceFeatures phDevFeatures;
|
||||
/// all the features that the selected physical device supports
|
||||
PhysicalDeviceFeatures phDevFeatures;
|
||||
/**
|
||||
* @brief Get rhe required physical device features
|
||||
* @details
|
||||
* The required features are:
|
||||
* - synchronization2
|
||||
*/
|
||||
PhysicalDeviceFeatures getRequiredFeatures() const;
|
||||
/**
|
||||
* @brief Assign the physicalDevice handle to the @ref rateDevice "best rated" GPU
|
||||
* @details
|
||||
@ -110,10 +165,11 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
/**
|
||||
* @brief Set valid values for the SettingsManager according to phDevFeatures and phDevProperties
|
||||
* @details
|
||||
* Must be called after selectPhysicalDevice
|
||||
* Must be called after selectPhysicalDevice and before createLogicalDevice
|
||||
* Sets valid values for:
|
||||
* - anisotropy_enable
|
||||
* - max_anisotropy
|
||||
*
|
||||
*/
|
||||
void setValidSettings();
|
||||
/**
|
||||
@ -152,6 +208,7 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
//
|
||||
VkSwapchainKHR swapChain;
|
||||
std::vector<VkImage> scImages;
|
||||
std::vector<VkImageView> scImageViews;
|
||||
VkFormat scImageFormat;
|
||||
VkExtent2D scExtent;
|
||||
|
||||
@ -166,13 +223,23 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
VkPresentModeKHR selectSwapChainPresentMode(const std::vector<VkPresentModeKHR>& availableModes);
|
||||
VkExtent2D selectSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
|
||||
void createSwapChain();
|
||||
void recreateSwapChain();
|
||||
void createSwapChainImageViews();
|
||||
void cleanupSwapChain();
|
||||
/**
|
||||
* @brief Recreate the swap chain for the current window size
|
||||
* @details
|
||||
* Does:
|
||||
* -# get new window dimensions
|
||||
* - blocks while dimensions == 0 (minimized)
|
||||
* -# calls cleanupSwapChain
|
||||
* -# calls createSwapChain
|
||||
* -# other stuff
|
||||
* -# calls all callbacks registered with registerSwapChainRecreateCallback
|
||||
*/
|
||||
void recreateSwapChain();
|
||||
//
|
||||
// IMAGE VIEW
|
||||
//
|
||||
std::vector<VkImageView> scImageViews;
|
||||
void createImageViews();
|
||||
/**
|
||||
* @brief Create a 2D imageView with format for image.
|
||||
*/
|
||||
@ -180,8 +247,8 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
//
|
||||
// RENDER PASS
|
||||
//
|
||||
VkRenderPass renderPassBegin;
|
||||
VkRenderPass renderPassEnd;
|
||||
/* VkRenderPass renderPassBegin; */
|
||||
/* VkRenderPass renderPassEnd; */
|
||||
/**
|
||||
* @brief Create the render pass
|
||||
* @details
|
||||
@ -189,40 +256,16 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
* -# color attachment
|
||||
* -# depth stencil attachment
|
||||
*/
|
||||
void createRenderPassBegin();
|
||||
void createRenderPassEnd();
|
||||
/* void createRenderPassBegin(); */
|
||||
/* void createRenderPassEnd(); */
|
||||
//
|
||||
// DESCRIPTORS
|
||||
//
|
||||
/* VkDescriptorSetLayout descriptorSetLayout; */
|
||||
/**
|
||||
* @name Create desciptors
|
||||
* @details These functions create a desciptor with bindings for
|
||||
* -# UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
||||
* -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
* @{
|
||||
* @brief Create a descriptor layout with bindings
|
||||
*/
|
||||
VkDescriptorSetLayout descriptorSetLayout;
|
||||
/**
|
||||
* @brief Create a descriptor layout binding for the MVP uniform buffer object
|
||||
*/
|
||||
void createDescriptorSetLayout();
|
||||
VkDescriptorPool descriptorPool;
|
||||
/**
|
||||
* @brief Create a descriptor layout binding for the MVP uniform buffer object
|
||||
* @details Create a desciptor set layout with bindings for
|
||||
* -# UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
||||
* -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
*/
|
||||
void createDescriptorPool();
|
||||
std::vector<VkDescriptorSet> descriptorSets;
|
||||
/*
|
||||
* @bried Create one descriptor set with layout descriptorSetLayout for each frame
|
||||
*/
|
||||
void createDescriptorSets();
|
||||
const uint32_t bindingUniformBuffer = 0;
|
||||
const uint32_t bindingCombinedImageSampler = 1;
|
||||
/* const uint32_t bindingCombinedImageSampler = 1; */
|
||||
|
||||
void createDescriptorSetLayout(std::vector<VkDescriptorSetLayoutBinding> bindings, VkDescriptorSetLayout& layout);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@ -230,7 +273,6 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
// PIPELINE
|
||||
//
|
||||
|
||||
PipelineContainer pipelines;
|
||||
/* void createGraphicsPipeline(); */
|
||||
VkShaderModule createShaderModule(const std::vector<char>& code);
|
||||
/**
|
||||
@ -254,7 +296,7 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
*
|
||||
*/
|
||||
template<VertexType T>
|
||||
void createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline);
|
||||
void createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector<VkDescriptorSetLayout>& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline);
|
||||
//
|
||||
// FONT
|
||||
//
|
||||
@ -306,28 +348,11 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
//
|
||||
// FRAMEBUFFERS
|
||||
//
|
||||
/* std::vector<VkFramebuffer> scFramebuffers; */
|
||||
std::set<int> framebufferIDs;
|
||||
// render pass is need for recreation of framebuffer
|
||||
std::unordered_map<int, std::pair<std::vector<VkFramebuffer>, VkRenderPass>> scFramebuffers;
|
||||
/**
|
||||
* @brief Create a vector of framebuffers and return the id as a handle
|
||||
* @returns id that can be used with getFramebuffers and destroyFramebuffers
|
||||
* @brief Destroy the framebuffers
|
||||
*/
|
||||
int createFramebuffers(std::vector<VkImageView>& imageViews, VkRenderPass& renderPass);
|
||||
/**
|
||||
* @brief Destroy the framebuffers from id
|
||||
*/
|
||||
void destroyFramebuffers(int id);
|
||||
void createFramebuffers_(std::vector<VkFramebuffer>& framebuffers, std::vector<VkImageView>& imageViews, VkRenderPass& renderPass);
|
||||
/**
|
||||
* @brief Recreate all framebuffers in scFramebuffers
|
||||
*/
|
||||
void recreateFramebuffers();
|
||||
/**
|
||||
* @brief Get the framebuffer vector to id
|
||||
*/
|
||||
std::vector<VkFramebuffer>& getFramebuffers(int id);
|
||||
void destroyFramebuffers(std::vector<VkFramebuffer>& framebuffers);
|
||||
void createFramebuffers(std::vector<VkFramebuffer>& framebuffers, std::vector<VkImageView>& imageViews, VkRenderPass& renderPass);
|
||||
bool frameBufferResized = false;
|
||||
static void frameBufferResizedCallback(GLFWwindow* window, int width, int height);
|
||||
//
|
||||
@ -339,15 +364,24 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
//
|
||||
// COMMAND BUFFER
|
||||
//
|
||||
/**
|
||||
* @brief Create MAX_FRAMES_IN_FLIGHT command buffers from the commandPoolGraphics
|
||||
*/
|
||||
void createCommandBuffers(std::vector<VkCommandBuffer>& commandBuffers);
|
||||
/**
|
||||
* @brief Destroy all command buffers (must be from commandPoolGraphics)
|
||||
*/
|
||||
void destroyCommandBuffers(std::vector<VkCommandBuffer>& commandBuffers);
|
||||
std::vector<VkCommandBuffer> commandBuffersBegin;
|
||||
std::vector<VkCommandBuffer> commandBuffersEnd;
|
||||
//
|
||||
// IMAGE UTILITY
|
||||
//
|
||||
/**
|
||||
* @brief Create the 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 copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
|
||||
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
|
||||
* @brief Copy srcImage to dstImage
|
||||
@ -362,18 +396,22 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
* @param srcImage Image with layout TRANSFER_SRC_OPTIMAL
|
||||
* @param dstImage Image with layout TRANSFER_DST_OPTIMAL
|
||||
*/
|
||||
void copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height); //, VkCommandPool& commandPool=commandPoolTransfer);
|
||||
void copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcImage, VkImage dstImage, VkExtent2D extent); //, VkCommandPool& commandPool=commandPoolTransfer);
|
||||
/**
|
||||
* @brief Transition the layout of image from oldLayout to newLayout
|
||||
* @details
|
||||
* Supported transitions:
|
||||
* - undefined -> depth stencil attachment (graphics q)
|
||||
* - undefined -> transfer dst optimal (transfer q)
|
||||
* - transfer dst optimal -> shader read only optimal (graphics q)
|
||||
* - transfer dst optimal -> present src (graphics q)
|
||||
* - UNDEFINED -> DEPTH_STENCIL_ATTACHMENT_OPTIMAL (graphics q)
|
||||
* - UNDEFINED -> TRANSFER_DST_OPTIMAL (transfer q)
|
||||
* - SHADER_READ_ONLY_OPTIMAL -> TRANSFER_DST_OPTIMAL (graphics q)
|
||||
* - TRANSFER_DST_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL (graphics q)
|
||||
* - TRANSFER_DST_OPTIMAL -> PRESENT_SRC_KHR (graphics q)
|
||||
*
|
||||
* If you do not provide a command buffer, a command buffer from the indicated queue will be created and submitted.
|
||||
* If you do provide a command buffer, the command is recorded but NOT submitted.
|
||||
*
|
||||
* The transition is done by submitting a VkImageMemoryBarrier2
|
||||
*
|
||||
* @param cmdBuffer [Optional] The command buffer where the command will be recorded to
|
||||
* @throws VkUserError if the transition is not supported.
|
||||
*/
|
||||
@ -382,21 +420,21 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
//
|
||||
// DEPTH
|
||||
//
|
||||
VkImage depthImage;
|
||||
VkDeviceMemory depthImageMemory;
|
||||
VkImageView depthImageView;
|
||||
/* VkImage depthImage; */
|
||||
/* VkDeviceMemory depthImageMemory; */
|
||||
/* VkImageView depthImageView; */
|
||||
VkFormat findDepthFormat();
|
||||
void createDepthImageAndView();
|
||||
/* void createDepthImageAndView(); */
|
||||
//
|
||||
// TEXTURES
|
||||
//
|
||||
VkImage textureImage;
|
||||
VkDeviceMemory textureImageMemory;
|
||||
VkImageView textureImageView;
|
||||
VkSampler textureSampler;
|
||||
/* VkImage textureImage; */
|
||||
/* VkDeviceMemory textureImageMemory; */
|
||||
/* VkImageView textureImageView; */
|
||||
/* VkSampler textureSampler; */
|
||||
/// Load textures/image.png
|
||||
void createTextureImageAndView();
|
||||
void createTextureSampler();
|
||||
/* void createTextureImageAndView(); */
|
||||
void createTextureSampler(VkSampler& sampler);
|
||||
|
||||
//
|
||||
// SYNCHRONIZATION
|
||||
@ -425,10 +463,27 @@ const size_t INDEX_BUFFER_SIZE = 512;
|
||||
//
|
||||
// UTILITY
|
||||
//
|
||||
/**
|
||||
* @name Handle ownership
|
||||
* @brief Retrieve the owner of a vulkan handle
|
||||
* @see ObjectUsingVulkan
|
||||
* @{
|
||||
*/
|
||||
static std::vector<ObjectUsingVulkan> objectsUsingVulkan;
|
||||
static void registerObjectUsingVulkan(const ObjectUsingVulkan& obj);
|
||||
static void getHandleOwnerString(std::string_view message);
|
||||
static std::string handleOwnerString;
|
||||
static bool lastColorWhite;
|
||||
/// @}
|
||||
/**
|
||||
* @brief Log messages from validation layers with the Apps logger
|
||||
* @details
|
||||
* Using the static vLog to log vulkan messages with a prefix dependant on the messageType.
|
||||
* - Validation and performace messages usually have a bracket containing a VUID,
|
||||
* these brackets are printed in yellow/red when its a warning/error.
|
||||
* - Handles printed by the validation layer are checked with objectsUsingVulkan, if found
|
||||
* the owner of the handle is added to the message @see registerObjectUsingVulkan, ObjectUsingVulkan
|
||||
* - Different message sources (layer, shader, general...) get a prefix with a different color
|
||||
*/
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL debugLog(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverety,
|
||||
|
105
vulkan_util.cpp
105
vulkan_util.cpp
@ -1,8 +1,73 @@
|
||||
#include "vulkan_util.hpp"
|
||||
#include <iostream>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
|
||||
namespace gz::vk {
|
||||
//
|
||||
// CONVENIENCE
|
||||
//
|
||||
PhysicalDeviceFeatures::PhysicalDeviceFeatures() {
|
||||
f.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
f11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
||||
f12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
|
||||
f13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
|
||||
f.pNext = &f11;
|
||||
f11.pNext = &f12;
|
||||
f12.pNext = &f13;
|
||||
}
|
||||
bool PhysicalDeviceFeatures::requiredFeaturesAvailable(const PhysicalDeviceFeatures& requiredFeatures) const {
|
||||
// iterate over all the features in the structs
|
||||
// VkPhysicalDeviceFeatures2
|
||||
size_t featureCount = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32);
|
||||
// pointer to first feature
|
||||
const VkBool32* pFeature = &f.features.robustBufferAccess;
|
||||
const VkBool32* pRequiredFeature = &requiredFeatures.f.features.robustBufferAccess;
|
||||
for (size_t i = 0; i < featureCount; i++) {
|
||||
if (*(pRequiredFeature + i) == VK_TRUE and *(pFeature + i) == VK_FALSE) {
|
||||
/* std::cout << "core feature not supported: i = " << i << "\n"; */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// VkPhysicalDeviceVulkan11Features
|
||||
// pointer to first feature: after sType and pNext
|
||||
featureCount = (sizeof(VkPhysicalDeviceVulkan11Features) - sizeof(VkStructureType) - sizeof(void*)) / sizeof(VkBool32);
|
||||
pFeature = &f11.storageBuffer16BitAccess;
|
||||
pRequiredFeature = &requiredFeatures.f11.storageBuffer16BitAccess;
|
||||
for (size_t i = 0; i < featureCount; i++) {
|
||||
if (*(pRequiredFeature + i) == VK_TRUE and *(pFeature + i) == VK_FALSE) {
|
||||
/* std::cout << "vulkan11 feature not supported: i = " << i << "\n"; */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// VkPhysicalDeviceVulkan12Features
|
||||
// pointer to first feature: after sType and pNext
|
||||
featureCount = (sizeof(VkPhysicalDeviceVulkan12Features) - sizeof(VkStructureType) - sizeof(void*)) / sizeof(VkBool32);
|
||||
pFeature = &f12.samplerMirrorClampToEdge;
|
||||
pRequiredFeature = &requiredFeatures.f12.samplerMirrorClampToEdge;
|
||||
for (size_t i = 0; i < featureCount; i++) {
|
||||
if (*(pRequiredFeature + i) == VK_TRUE and *(pFeature + i) == VK_FALSE) {
|
||||
/* std::cout << "vulkan12 feature not supported: i = " << i << "\n"; */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// VkPhysicalDeviceVulkan13Features
|
||||
// pointer to first feature: after sType and pNext
|
||||
featureCount = (sizeof(VkPhysicalDeviceVulkan13Features) - sizeof(VkStructureType) - sizeof(void*)) / sizeof(VkBool32);
|
||||
pFeature = &f13.robustImageAccess;
|
||||
pRequiredFeature = &requiredFeatures.f13.robustImageAccess;
|
||||
for (size_t i = 0; i < featureCount; i++) {
|
||||
if (*(pRequiredFeature + i) == VK_TRUE and *(pFeature + i) == VK_FALSE) {
|
||||
/* std::cout << "vulkan13 feature not supported: i = " << i << "\n"; */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PipelineContainer::erase(const PipelineT& key, VkDevice& device, const VkAllocationCallbacks* pAllocator) {
|
||||
vkDestroyPipeline(device, pipelines[key].pipeline, pAllocator);
|
||||
vkDestroyPipelineLayout(device, pipelines[key].layout, pAllocator);
|
||||
@ -14,6 +79,46 @@ namespace gz::vk {
|
||||
return pipelines.erase(it);
|
||||
|
||||
}
|
||||
void PipelineContainer::destroy(VkDevice& device, const VkAllocationCallbacks* pAllocator) {
|
||||
auto it = pipelines.begin(); while (it != pipelines.end()) {
|
||||
it = erase(it, device);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VkDependencyInfo getDepInfo(const VkImageMemoryBarrier2& barrier) {
|
||||
VkDependencyInfo depI{};
|
||||
depI.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
||||
depI.imageMemoryBarrierCount = 1;
|
||||
depI.pImageMemoryBarriers = &barrier;
|
||||
return depI;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
ObjectUsingVulkan::ObjectUsingVulkan(std::string&& name, std::vector<void*>&& ptrsToHandles, std::vector<void*>&& vectorWithPtrsToHandles) {
|
||||
objectName = std::move(name);
|
||||
for (auto it = vectorWithPtrsToHandles.begin(); it != vectorWithPtrsToHandles.end(); it++) {
|
||||
vectorWithPtrToHandle.emplace_back(reinterpret_cast<std::vector<void*>*>(*it));
|
||||
}
|
||||
ptrToHandle = std::move(ptrsToHandles);
|
||||
updateHandles();
|
||||
}
|
||||
|
||||
void ObjectUsingVulkan::updateHandles() {
|
||||
handles.clear();
|
||||
for (auto it = ptrToHandle.begin(); it != ptrToHandle.end(); it++) {
|
||||
handles.insert(reinterpret_cast<uint64_t>(reinterpret_cast<void*>(*it)));
|
||||
}
|
||||
for (auto it = vectorWithPtrToHandle.begin(); it != vectorWithPtrToHandle.end(); it++) {
|
||||
for (auto it2 = (*it)->begin(); it2 != (*it)->end(); it2++) {
|
||||
handles.insert(reinterpret_cast<uint64_t>(reinterpret_cast<void*>(*it2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace gz::vk
|
||||
|
||||
|
@ -1,10 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "vertex.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace gz::vk {
|
||||
/**
|
||||
* @name Convenience structs
|
||||
* @details
|
||||
* Structures and functionas making usiing the vulkan api more convenient
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Struct containing all physical device features structs
|
||||
* @details
|
||||
* Make sure to call vkGetPhysicalDeviceFeatures2 with the f member, not this struct itself
|
||||
*/
|
||||
struct PhysicalDeviceFeatures {
|
||||
/**
|
||||
* @brief Initialize the structs with sType and pNext chain f -> f11 -> f12 -> f13
|
||||
*/
|
||||
PhysicalDeviceFeatures();
|
||||
VkPhysicalDeviceFeatures2 f{};
|
||||
VkPhysicalDeviceVulkan11Features f11{};
|
||||
VkPhysicalDeviceVulkan12Features f12{};
|
||||
VkPhysicalDeviceVulkan13Features f13{};
|
||||
/**
|
||||
* @brief Check if all features enabled in requiredFeatures are enabled in this
|
||||
*/
|
||||
bool requiredFeaturesAvailable(const PhysicalDeviceFeatures& requiredFeatures) const;
|
||||
};
|
||||
|
||||
enum PipelineT {
|
||||
PL_3D, PL_2D
|
||||
};
|
||||
@ -22,10 +51,17 @@ namespace gz::vk {
|
||||
using iterator = std::unordered_map<PipelineT, Pipeline>::iterator;
|
||||
Pipeline& operator[](const PipelineT& key) { return pipelines[key]; }
|
||||
/**
|
||||
* @brief Destroy the pipeline+layout and then remove the handles
|
||||
* @brief Destroy the pipeline+layout and then remove the handles from the underlying map
|
||||
*/
|
||||
void erase(const PipelineT& key, VkDevice& device, const VkAllocationCallbacks* pAllocator=nullptr);
|
||||
/**
|
||||
* @brief Destroy the pipeline+layout and then remove the handles from the underlying map
|
||||
*/
|
||||
iterator erase(const iterator& it, VkDevice& device, const VkAllocationCallbacks* pAllocator=nullptr);
|
||||
/**
|
||||
* @brief Destroy all pipelines#layouts and then remove the handles from the underlying map
|
||||
*/
|
||||
void destroy(VkDevice& device, const VkAllocationCallbacks* pAllocator=nullptr);
|
||||
iterator begin() { return pipelines.begin(); }
|
||||
iterator end() { return pipelines.end(); }
|
||||
size_t size() const { return pipelines.size(); }
|
||||
@ -34,6 +70,12 @@ namespace gz::vk {
|
||||
|
||||
};
|
||||
|
||||
struct SwapChainSupport {
|
||||
VkSurfaceCapabilitiesKHR capabilities;
|
||||
std::vector<VkSurfaceFormatKHR> formats;
|
||||
std::vector<VkPresentModeKHR> presentModes;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
concept SupportedIndexType = std::same_as<T, uint16_t> or std::same_as<T, uint32_t>;
|
||||
@ -81,9 +123,36 @@ namespace gz::vk {
|
||||
};
|
||||
|
||||
|
||||
struct SwapChainSupport {
|
||||
VkSurfaceCapabilitiesKHR capabilities;
|
||||
std::vector<VkSurfaceFormatKHR> formats;
|
||||
std::vector<VkPresentModeKHR> presentModes;
|
||||
/**
|
||||
* @brief Get a VkDependencyInfo struct for a single image memory barrier
|
||||
*/
|
||||
VkDependencyInfo getDepInfo(const VkImageMemoryBarrier2& barrier);
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* @brief Used for debugging
|
||||
* @see VulkanInstance::debugLog
|
||||
* @details
|
||||
* Handles like VkDevice are pointers to the actual handle.
|
||||
* Register the handle to a vulkan object with this struct and call updateHandles().
|
||||
* The handles set will then contain the actual address of the handle, as printed by the validation layers.
|
||||
* That means you can check a handle returned by a validation layer
|
||||
* with this struct and see if this handle belongs to object that created this struct.
|
||||
* @warning
|
||||
* Every pointer you submit to this must stay valid!
|
||||
* Use this struct only when debugging!
|
||||
*/
|
||||
struct ObjectUsingVulkan {
|
||||
ObjectUsingVulkan(std::string&& name, std::vector<void*>&& ptrsToHandles, std::vector<void*>&& vectorWithPtrsToHandles);
|
||||
std::string objectName;
|
||||
std::vector<void*> ptrToHandle;
|
||||
std::vector<std::vector<void*>*> vectorWithPtrToHandle;
|
||||
std::set<uint64_t> handles;
|
||||
/**
|
||||
* @brief Update the handles map with the current handle (aka segfault generator)
|
||||
*/
|
||||
void updateHandles();
|
||||
|
||||
};
|
||||
|
||||
} // namespace gz::vk
|
||||
|
Loading…
Reference in New Issue
Block a user