Index buffer support to allow for reusing of the same vertices. Next commits will be refactors and documentation, graphs and flows of execution

This commit is contained in:
Lillian Salehi 2024-10-10 02:26:13 -05:00
parent 7e625a3db5
commit edfbddea55
8 changed files with 131 additions and 39 deletions

View File

@ -269,7 +269,7 @@ namespace DeviceControl {
createSwapChainInfo.clipped = VK_TRUE;
// This is something that needs to be implemented later, operations like resizing the window invalidate the swap chain and
// require you to recreate it and reference the old one specified here, will revisit in a few days.
createSwapChainInfo.oldSwapchain = VK_NULL_HANDLE;
//createSwapChainInfo.oldSwapchain = VK_NULL_HANDLE;
if(vkCreateSwapchainKHR(Global::device, &createSwapChainInfo, nullptr, &Global::swapChain) != VK_SUCCESS) {
throw std::runtime_error("Failed to create the swap chain!!");

View File

@ -16,15 +16,13 @@ bool EntryApp::getFramebufferResized() const {
}
static void framebufferResizeCallback(GLFWwindow* window, int width, int height) {
auto app = reinterpret_cast<EntryApp*>(glfwGetWindowUserPointer(window));
app->EntryApp::getInstance().setFramebufferResized(true);
app->setFramebufferResized(true);
}
// Initialize GLFW Window. First, Initialize GLFW lib, disable resizing for
// now, and create window.
void initWindow() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
// Settings for the window are set, create window reference.
Global::window = glfwCreateWindow(Global::WIDTH, Global::HEIGHT, "Vulkan", nullptr, nullptr);
glfwSetWindowUserPointer(Global::window, &EntryApp::getInstance());
@ -65,6 +63,7 @@ void initVulkan() {
graphicsPipeline.createFramebuffers();
graphicsPipeline.createCommandPool();
buffers.createVertexBuffer();
buffers.createIndexBuffer();
graphicsPipeline.createCommandBuffer();
renderPresentation.createSyncObject();
}
@ -79,9 +78,10 @@ void mainLoop() { // This loop jus
void cleanup() { // Similar to the last handoff, destroy the utils in a safe manner in the library!
renderPresentation.cleanupSwapChain();
buffers.destroyVertexBuffer();
graphicsPipeline.destroyGraphicsPipeline();
graphicsPipeline.destroyRenderPass();
buffers.destroyBuffers();
renderPresentation.destroyFenceSemaphores();
graphicsPipeline.destroyCommandPool();

View File

@ -1,16 +1,26 @@
#include "buffers.h"
#include <iostream>
#include <cstdint>
#include <vector>
#include <vulkan/vulkan_core.h>
VkBuffer vertexBuffer;
VkDeviceMemory vertexBufferMemory;
VkBuffer indexBuffer;
VkDeviceMemory indexBufferMemory;
namespace Buffers {
const std::vector<Global::Vertex> vertices = {
{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}},
{{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
{{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}
{{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}},
{{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}}
};
// Index buffer definition, showing which points to reuse.
const std::vector<uint16_t> indices = {
0, 1, 2, 2, 3, 0
};
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
// Graphics cards offer different types of memory to allocate from, here we query to find the right type of memory for our needs.
// Query the available types of memory to iterate over.
@ -25,47 +35,125 @@ namespace Buffers {
throw std::runtime_error("failed to find suitable memory type!");
}
void bufferslibrary::createVertexBuffer() {
// Create a Vertex Buffer to hold the vertex information in memory so it doesn't have to be hardcoded!
// Size denotes the size of the buffer in bytes, usage in this case is the buffer behaviour, using a bitwise OR.
// Sharing mode denostes the same as the images in the swap chain! in this case, only the graphics queue uses this buffer, so we make it exclusive.
VkBufferCreateInfo bufferInfo;
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = sizeof(vertices[0]) * vertices.size();
bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
bufferInfo.pNext = NULL;
std::cerr << &bufferInfo << " " << &vertexBuffer << "\n";
if (vkCreateBuffer(Global::device, &bufferInfo, nullptr, &vertexBuffer) != VK_SUCCESS) {
throw std::runtime_error("failed to create vertex buffer!");
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = Global::commandPool;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(Global::device, &allocInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
VkBufferCopy copyRegion{};
copyRegion.size = size;
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(Global::graphicsQueue);
vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer);
}
// Query the memory requirements of the buffer.
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = usage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(Global::device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {
throw std::runtime_error("failed to create buffer!");
}
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(Global::device, vertexBuffer, &memRequirements);
vkGetBufferMemoryRequirements(Global::device, buffer, &memRequirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
if (vkAllocateMemory(Global::device, &allocInfo, nullptr, &vertexBufferMemory) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate vertex buffer memory!");
if (vkAllocateMemory(Global::device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate buffer memory!");
}
vkBindBufferMemory(Global::device, vertexBuffer, vertexBufferMemory, 0);
vkBindBufferMemory(Global::device, buffer, bufferMemory, 0);
}
void bufferslibrary::createIndexBuffer() {
VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
void* data;
vkMapMemory(Global::device, vertexBufferMemory, 0, bufferInfo.size, 0, &data);
memcpy(data, vertices.data(), (size_t) bufferInfo.size);
vkUnmapMemory(Global::device, vertexBufferMemory);
vkMapMemory(Global::device, stagingBufferMemory, 0, bufferSize, 0, &data);
memcpy(data, indices.data(), (size_t) bufferSize);
vkUnmapMemory(Global::device, stagingBufferMemory);
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
copyBuffer(stagingBuffer, indexBuffer, bufferSize);
vkDestroyBuffer(Global::device, stagingBuffer, nullptr);
vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
}
void bufferslibrary::destroyVertexBuffer() {
void bufferslibrary::createVertexBuffer() {
// Create a Vertex Buffer to hold the vertex information in memory so it doesn't have to be hardcoded!
// Size denotes the size of the buffer in bytes, usage in this case is the buffer behaviour, using a bitwise OR.
// Sharing mode denostes the same as the images in the swap chain! in this case, only the graphics queue uses this buffer, so we make it exclusive.
VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(bufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
void* data;
vkMapMemory(Global::device, stagingBufferMemory, 0, bufferSize, 0, &data);
memcpy(data, vertices.data(), (size_t) bufferSize);
vkUnmapMemory(Global::device, stagingBufferMemory);
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
copyBuffer(stagingBuffer, vertexBuffer, bufferSize);
vkDestroyBuffer(Global::device, stagingBuffer, nullptr);
vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
}
void bufferslibrary::destroyBuffers() {
vkDestroyBuffer(Global::device, indexBuffer, nullptr);
vkFreeMemory(Global::device, indexBufferMemory, nullptr);
vkDestroyBuffer(Global::device, vertexBuffer, nullptr);
vkFreeMemory(Global::device, vertexBufferMemory, nullptr);
}
VkBuffer bufferslibrary::getVertexBuffer() {
return vertexBuffer;
}
VkBuffer bufferslibrary::getIndexBuffer() {
return indexBuffer;
}
std::vector<Global::Vertex> bufferslibrary::getVertices() {
return vertices;
}
std::vector<uint16_t> bufferslibrary::getIndices() {
return indices;
}
}

View File

@ -4,9 +4,12 @@
namespace Buffers {
class bufferslibrary {
public:
void createIndexBuffer();
void createVertexBuffer();
void destroyVertexBuffer();
void destroyBuffers();
VkBuffer getVertexBuffer();
VkBuffer getIndexBuffer();
std::vector<Global::Vertex> getVertices();
std::vector<uint16_t> getIndices();
};
}

View File

@ -96,8 +96,8 @@ namespace Graphics {
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
// MODE_FILL, fill polygons, MODE_LINE, draw wireframe, MODE_POINT, draw vertices. Anything other than fill requires GPU feature *fillModeNonSolid*
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.polygonMode = VK_POLYGON_MODE_LINE;
rasterizer.lineWidth = 2.0f;
// How to cull the faces, right here we cull the back faces and tell the rasterizer front facing vertices are ordered clockwise.
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
@ -308,8 +308,9 @@ namespace Graphics {
VkBuffer vertexBuffers[] = {buffers.getVertexBuffer()};
VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(commandBuffer, buffers.getIndexBuffer(), 0, VK_INDEX_TYPE_UINT16);
vkCmdDraw(commandBuffer, static_cast<uint32_t>(buffers.getVertices().size()), 1, 0, 0);
vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(buffers.getIndices().size()), 1, 0, 0, 0);
vkCmdEndRenderPass(commandBuffer);
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {

View File

@ -144,8 +144,8 @@ namespace RenderPresent {
}
void render::destroyFenceSemaphores() {
for (size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroySemaphore(Global::device, imageAvailableSemaphores[i], nullptr);
vkDestroySemaphore(Global::device, renderFinishedSemaphores[i], nullptr);
vkDestroySemaphore(Global::device, imageAvailableSemaphores[i], nullptr);
vkDestroyFence(Global::device, inFlightFences[i], nullptr);
}
}

BIN
src/shaders/fragment.spv Normal file

Binary file not shown.

BIN
src/shaders/vertex.spv Normal file

Binary file not shown.